From e76893d82fa2bc700d6e231a6e01c926aa2e0c1f Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 20 Jun 2016 15:04:53 +0200 Subject: [PATCH 01/17] starting rework for 2.2.0 --- .idea/calendarimporter.iml | 8 + .idea/encodings.xml | 4 + .idea/modules.xml | 8 + .idea/php.xml | 4 + .idea/scopes/scope_settings.xml | 5 + .idea/vcs.xml | 6 + build.xml | 210 +- java/Main.java | 87 - js/ABOUT.js | 4 +- js/data/ResponseHandler.js | 2 +- js/data/timezones.js | 1372 +-- js/dialogs/ImportContentPanel.js | 11 +- js/dialogs/ImportPanel.js | 229 +- js/plugin.calendarimporter.js | 2 +- js/ui/ContextMenu.js | 158 + manifest.xml | 2 +- php/ical/class.icalcreator.php | 10454 +----------------- php/ical/class.icalparser.php | 973 +- php/ical/lib/calendarComponent.class.php | 4661 ++++++++ php/ical/lib/iCal.XML.inc.php | 897 ++ php/ical/lib/iCal.tz.inc.php | 292 + php/ical/lib/iCal.vCard.inc.php | 166 + php/ical/lib/iCalBase.class.php | 427 + php/ical/lib/iCalUtilityFunctions.class.php | 2507 +++++ php/ical/lib/iCaldateTime.class.php | 129 + php/ical/lib/valarm.class.php | 135 + php/ical/lib/vcalendar.class.php | 2132 ++++ php/ical/lib/vevent.class.php | 269 + php/ical/lib/vfreebusy.class.php | 149 + php/ical/lib/vjournal.class.php | 222 + php/ical/lib/vtimezone.class.php | 164 + php/ical/lib/vtodo.class.php | 273 + php/module.calendar.php | 7 +- 33 files changed, 14133 insertions(+), 11836 deletions(-) create mode 100644 .idea/calendarimporter.iml create mode 100644 .idea/encodings.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/php.xml create mode 100644 .idea/scopes/scope_settings.xml create mode 100644 .idea/vcs.xml delete mode 100644 java/Main.java create mode 100644 js/ui/ContextMenu.js create mode 100644 php/ical/lib/calendarComponent.class.php create mode 100644 php/ical/lib/iCal.XML.inc.php create mode 100644 php/ical/lib/iCal.tz.inc.php create mode 100644 php/ical/lib/iCal.vCard.inc.php create mode 100644 php/ical/lib/iCalBase.class.php create mode 100644 php/ical/lib/iCalUtilityFunctions.class.php create mode 100644 php/ical/lib/iCaldateTime.class.php create mode 100644 php/ical/lib/valarm.class.php create mode 100644 php/ical/lib/vcalendar.class.php create mode 100644 php/ical/lib/vevent.class.php create mode 100644 php/ical/lib/vfreebusy.class.php create mode 100644 php/ical/lib/vjournal.class.php create mode 100644 php/ical/lib/vtimezone.class.php create mode 100644 php/ical/lib/vtodo.class.php diff --git a/.idea/calendarimporter.iml b/.idea/calendarimporter.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/calendarimporter.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..d821048 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..34195db --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..07f31c0 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/build.xml b/build.xml index 47601f7..dba3f1c 100644 --- a/build.xml +++ b/build.xml @@ -1,18 +1,15 @@ - - - - - - - + + + + + - @@ -21,30 +18,8 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -54,8 +29,7 @@ - - + @@ -68,16 +42,6 @@ - - - - - - - - - - @@ -89,27 +53,11 @@ - - - - - - - - - - - - - - - - - + @@ -118,8 +66,8 @@ - - + + @@ -163,16 +111,7 @@ var npgettext = function(msgctxt, msgid, msgid_plural, count) {}; var pgettext = function(msgctxt, msgid) {}; - - + @@ -180,105 +119,82 @@ - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - Processing manifest.xml - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/java/Main.java b/java/Main.java deleted file mode 100644 index ba2c238..0000000 --- a/java/Main.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * A small tool to create our timezone mappings list =) - */ -import java.util.TimeZone; - -public class Main { - static String map[] = new String[51]; // one field = 30 minutes step - - private static void initMap() { - map[0] = "Pacific/Midway"; - map[1] = ""; - map[2] = "Pacific/Fakaofo"; - map[3] = "Pacific/Marquesas"; - map[4] = "America/Anchorage"; - map[5] = ""; - map[6] = "America/Dawson"; - map[7] = ""; - map[8] = "America/Dawson_Creek"; - map[9] = ""; - map[10] = "America/Chicago"; - map[11] = "America/Caracas"; - map[12] = "America/Detroit"; - map[13] = "America/Caracas"; - map[14] = "America/Santiago"; - map[15] = "America/St_Johns"; - map[16] = "America/Sao_Paulo"; - map[17] = ""; - map[18] = "America/Noronha"; - map[19] = ""; - map[20] = "Atlantic/Cape_Verde"; - map[21] = ""; - map[22] = "Africa/Abidjan"; - map[23] = ""; - map[24] = "Europe/Vienna"; - map[25] = ""; - map[26] = "Asia/Jerusalem"; - map[27] = ""; - map[28] = "Africa/Addis_Ababa"; - map[29] = "Asia/Tehran"; - map[30] = "Asia/Dubai"; - map[31] = "Asia/Kabul"; - map[32] = "Antarctica/Mawson"; - map[33] = "Asia/Colombo"; - map[34] = "Antarctica/Vostok"; - map[35] = "Asia/Rangoon"; - map[36] = "Antarctica/Davis"; - map[37] = ""; - map[38] = "Antarctica/Casey"; - map[39] = ""; - map[40] = "Asia/Dili"; - map[41] = "Australia/Darwin"; - map[42] = "Australia/Currie"; - map[43] = "Australia/Lord_Howe"; - map[44] = "Antarctica/Macquarie"; - map[45] = "Pacific/Norfolk"; - map[46] = "Antarctica/McMurdo"; - map[47] = ""; - map[48] = "Pacific/Enderbury"; - map[49] = ""; - map[50] = "Pacific/Kiritimati"; - } - - /** - * @param args - */ - public static void main(String[] args) { - initMap(); - - int i = 0; - for(int time = -660; time < 900; time += 30) { - - int hours = time / 60; - int minutes = Math.abs(time) % 60; - - String[] avaiId = TimeZone.getAvailableIDs(time*60*1000); - - if(avaiId.length > 0) { - System.out.printf("\t\t/*%+d:%02d*/\n", hours, minutes); - for (String string : avaiId) { - System.out.println("\t\t'" + string + "' : '" + map[i] + "',"); - } - } - - i++; - } - } -} diff --git a/js/ABOUT.js b/js/ABOUT.js index 3ee5b09..984c28b 100644 --- a/js/ABOUT.js +++ b/js/ABOUT.js @@ -2,7 +2,7 @@ * ABOUT.js zarafa calender to ics im/exporter * * Author: Christoph Haas - * Copyright (C) 2012-2013 Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,7 +29,7 @@ Ext.namespace('Zarafa.plugins.calendarimporter'); * The copyright string holding the copyright notice for the Zarafa calendarimporter Plugin. */ Zarafa.plugins.calendarimporter.ABOUT = "" - + "

Copyright (C) 2012-2013 Christoph Haas <christoph.h@sprinternet.at>

" + + "

Copyright (C) 2012-2016 Christoph Haas <christoph.h@sprinternet.at>

" + "

This program is free software; you can redistribute it and/or " + "modify it under the terms of the GNU Lesser General Public " diff --git a/js/data/ResponseHandler.js b/js/data/ResponseHandler.js index a155466..6fc4f9f 100644 --- a/js/data/ResponseHandler.js +++ b/js/data/ResponseHandler.js @@ -2,7 +2,7 @@ * ResponseHandler.js zarafa calender to ics im/exporter * * Author: Christoph Haas - * Copyright (C) 2012-2013 Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/js/data/timezones.js b/js/data/timezones.js index 9fb34c7..3e36004 100644 --- a/js/data/timezones.js +++ b/js/data/timezones.js @@ -2,7 +2,7 @@ * timezones.js zarafa calender to ics im/exporter * * Author: Christoph Haas - * Copyright (C) 2012-2013 Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,742 +24,742 @@ * Timezone class * * This class can handle all our timezone operations and conversions. - */ -Ext.namespace("Zarafa.plugins.calendarimporter.data"); + */ +Ext.namespace("Zarafa.plugins.calendarimporter.data"); Zarafa.plugins.calendarimporter.data.Timezones = Ext.extend(Object, { - store : [ - ['Pacific/Midway','(UTC -11:00) Midway, Niue, Pago Pago', -660], - ['Pacific/Fakaofo','(UTC -10:00) Adak, Fakaofo, Honolulu, Johnston, Rarotonga, Tahiti', -600], - ['Pacific/Marquesas','(UTC -09:30) Marquesas', -570], - ['America/Anchorage','(UTC -09:00) Gambier, Anchorage, Juneau, Nome, Sitka, Yakutat', -540], - ['America/Dawson','(UTC -08:00) Dawson, Los Angeles, Tijuana, Vancouver, Whitehorse, Santa Isabel, Metlakatla, Pitcairn', -480], - ['America/Dawson_Creek','(UTC -07:00) Dawson Creek, Hermosillo, Phoenix, Chihuahua, Mazatlan, Boise, Cambridge Bay, Denver, Edmonton, Inuvik, Ojinaga, Shiprock, Yellowknife', -420], - ['America/Chicago','(UTC -06:00) Beulah, Center, Chicago, Knox, Matamoros, Menominee, New Salem, Rainy River, Rankin Inlet, Resolute, Tell City, Winnipeg', -360], - ['America/Belize','(UTC -06:00) Belize, Costa Rica, El Salvador, Galapagos, Guatemala, Managua, Regina, Swift Current, Tegucigalpa', -360], - ['Pacific/Easter','(UTC -06:00) Easter', -360], - ['America/Bahia_Banderas','(UTC -06:00) Bahia Banderas, Cancun, Merida, Mexico City, Monterrey', -360], - ['America/Detroit','(UTC -05:00) Detroit, Grand Turk, Indianapolis, Iqaluit, Louisville, Marengo, Monticello, Montreal, Nassau, New York, Nipigon, Pangnirtung, Petersburg, Thunder Bay, Toronto, Vevay, Vincennes, Winamac', -300], - ['America/Atikokan','(UTC -05:00) Atikokan, Bogota, Cayman, Guayaquil, Jamaica, Lima, Panama, Port-au-Prince', -300], - ['America/Havana','(UTC -05:00) Havana', -300], - ['America/Caracas','(UTC -04:30) Caracas', -270], - ['America/Glace_Bay','(UTC -04:00) Bermuda, Glace Bay, Goose Bay, Halifax, Moncton, Thule', -240], - ['Atlantic/Stanley','(UTC -04:00) Stanley', -240], - ['America/Santiago','(UTC -04:00) Palmer, Santiago', -240], - ['America/Anguilla','(UTC -04:00) Anguilla, Antigua, Aruba, Barbados, Blanc-Sablon, Boa Vista, Curacao, Dominica, Eirunepe, Grenada, Guadeloupe, Guyana, Kralendijk, La Paz, Lower Princes, Manaus, Marigot, Martinique, Montserrat, Port of Spain, Porto Velho, Puerto Rico, Rio Branco, Santo Domingo, St Barthelemy, St Kitts, St Lucia, St Thomas, St Vincent, Tortola', -240], - ['America/Campo_Grande','(UTC -04:00) Campo Grande, Cuiaba', -240], - ['America/Asuncion','(UTC -04:00) Asuncion', -240], - ['America/St_Johns','(UTC -03:30) St Johns', -210], - ['America/Sao_Paulo','(UTC -03:00) Sao Paulo', -180], - ['America/Araguaina','(UTC -03:00) Araguaina, Bahia, Belem, Buenos Aires, Catamarca, Cayenne, Cordoba, Fortaleza, Jujuy, La Rioja, Maceio, Mendoza, Paramaribo, Recife, Rio Gallegos, Rothera, Salta, San Juan, Santarem, Tucuman, Ushuaia', -180], - ['America/Montevideo','(UTC -03:00) Montevideo', -180], - ['America/Godthab','(UTC -03:00) Godthab', -180], - ['America/Argentina/San_Luis','(UTC -03:00) San Luis', -180], - ['America/Miquelon','(UTC -03:00) Miquelon', -180], - ['America/Noronha','(UTC -02:00) Noronha, South Georgia', -120], - ['Atlantic/Cape_Verde','(UTC -01:00) Cape Verde', -60], - ['America/Scoresbysund','(UTC -01:00) Azores, Scoresbysund', -60], - ['Atlantic/Canary','(UTC) Canary, Dublin, Faroe, Guernsey, Isle of Man, Jersey, Lisbon, London, Madeira', 0], - ['Africa/Abidjan','(UTC) Abidjan, Accra, Bamako, Banjul, Bissau, Casablanca, Conakry, Dakar, Danmarkshavn, El Aaiun, Freetown, Lome, Monrovia, Nouakchott, Ouagadougou, Reykjavik, Sao Tome, St Helena', 0], - ['Africa/Algiers','(UTC +01:00) Algiers, Bangui, Brazzaville, Douala, Kinshasa, Lagos, Libreville, Luanda, Malabo, Ndjamena, Niamey, Porto-Novo, Tunis', 60], - ['Europe/Vienna','(UTC +01:00) Amsterdam, Andorra, Belgrade, Berlin, Bratislava, Brussels, Budapest, Ceuta, Copenhagen, Gibraltar, Ljubljana, Longyearbyen, Luxembourg, Madrid, Malta, Monaco, Oslo, Paris, Podgorica, Prague, Rome, San Marino, Sarajevo, Skopje, Stockholm, Tirane, Vaduz, Vatican, Vienna, Warsaw, Zagreb, Zurich', 60], - ['Africa/Windhoek','(UTC +01:00) Windhoek', 60], - ['Asia/Damascus','(UTC +02:00) Damascus', 120], - ['Asia/Beirut','(UTC +02:00) Beirut', 120], - ['Asia/Jerusalem','(UTC +02:00) Jerusalem', 120], - ['Asia/Nicosia','(UTC +02:00) Athens, Bucharest, Chisinau, Helsinki, Istanbul, Mariehamn, Nicosia, Riga, Sofia, Tallinn, Vilnius', 120], - ['Africa/Blantyre','(UTC +02:00) Blantyre, Bujumbura, Cairo, Gaborone, Gaza, Harare, Hebron, Johannesburg, Kigali, Lubumbashi, Lusaka, Maputo, Maseru, Mbabane, Tripoli', 120], - ['Asia/Amman','(UTC +02:00) Amman', 120], - ['Africa/Addis_Ababa','(UTC +03:00) Addis Ababa, Aden, Antananarivo, Asmara, Baghdad, Bahrain, Comoro, Dar es Salaam, Djibouti, Juba, Kaliningrad, Kampala, Khartoum, Kiev, Kuwait, Mayotte, Minsk, Mogadishu, Nairobi, Qatar, Riyadh, Simferopol, Syowa, Uzhgorod, Zaporozhye', 180], - ['Asia/Tehran','(UTC +03:30) Tehran', 210], - ['Asia/Yerevan','(UTC +04:00) Yerevan', 240], - ['Asia/Dubai','(UTC +04:00) Dubai, Mahe, Mauritius, Moscow, Muscat, Reunion, Samara, Tbilisi, Volgograd', 240], - ['Asia/Baku','(UTC +04:00) Baku', 240], - ['Asia/Kabul','(UTC +04:30) Kabul', 270], - ['Antarctica/Mawson','(UTC +05:00) Aqtau, Aqtobe, Ashgabat, Dushanbe, Karachi, Kerguelen, Maldives, Mawson, Oral, Samarkand, Tashkent', 300], - ['Asia/Colombo','(UTC +05:30) Colombo, Kolkata', 330], - ['Asia/Kathmandu','(UTC +05:45) Kathmandu', 345], - ['Antarctica/Vostok','(UTC +06:00) Almaty, Bishkek, Chagos, Dhaka, Qyzylorda, Thimphu, Vostok, Yekaterinburg', 360], - ['Asia/Rangoon','(UTC +06:30) Cocos, Rangoon', 390], - ['Antarctica/Davis','(UTC +07:00) Bangkok, Christmas, Davis, Ho Chi Minh, Hovd, Jakarta, Novokuznetsk, Novosibirsk, Omsk, Phnom Penh, Pontianak, Vientiane', 420], - ['Antarctica/Casey','(UTC +08:00) Brunei, Casey, Choibalsan, Chongqing, Harbin, Hong Kong, Kashgar, Krasnoyarsk, Kuala Lumpur, Kuching, Macau, Makassar, Manila, Perth, Shanghai, Singapore, Taipei, Ulaanbaatar, Urumqi', 480], - ['Australia/Eucla','(UTC +08:45) Eucla', 525], - ['Asia/Dili','(UTC +09:00) Dili, Irkutsk, Jayapura, Palau, Pyongyang, Seoul, Tokyo', 540], - ['Australia/Adelaide','(UTC +09:30) Adelaide, Broken Hill', 570], - ['Australia/Darwin','(UTC +09:30) Darwin', 570], - ['Antarctica/DumontDUrville','(UTC +10:00) Brisbane, Chuuk, DumontDUrville, Guam, Lindeman, Port Moresby, Saipan, Yakutsk', 600], - ['Australia/Currie','(UTC +10:00) Currie, Hobart, Melbourne, Sydney', 600], - ['Australia/Lord_Howe','(UTC +10:30) Lord Howe', 630], - ['Antarctica/Macquarie','(UTC +11:00) Efate, Guadalcanal, Kosrae, Macquarie, Noumea, Pohnpei, Sakhalin, Vladivostok', 660], - ['Pacific/Norfolk','(UTC +11:30) Norfolk', 690], - ['Antarctica/McMurdo','(UTC +12:00) Auckland, McMurdo, South Pole', 720], - ['Asia/Anadyr','(UTC +12:00) Anadyr, Fiji, Funafuti, Kamchatka, Kwajalein, Magadan, Majuro, Nauru, Tarawa, Wake, Wallis', 720], - ['Pacific/Chatham','(UTC +12:45) Chatham', 765], - ['Pacific/Enderbury','(UTC +13:00) Enderbury, Tongatapu', 780], - ['Pacific/Apia','(UTC +13:00) Apia', 780], - ['Pacific/Kiritimati','(UTC +14:00) Kiritimati', 840] + store: [ + ['Pacific/Midway', '(UTC -11:00) Midway, Niue, Pago Pago', -660], + ['Pacific/Fakaofo', '(UTC -10:00) Adak, Fakaofo, Honolulu, Johnston, Rarotonga, Tahiti', -600], + ['Pacific/Marquesas', '(UTC -09:30) Marquesas', -570], + ['America/Anchorage', '(UTC -09:00) Gambier, Anchorage, Juneau, Nome, Sitka, Yakutat', -540], + ['America/Dawson', '(UTC -08:00) Dawson, Los Angeles, Tijuana, Vancouver, Whitehorse, Santa Isabel, Metlakatla, Pitcairn', -480], + ['America/Dawson_Creek', '(UTC -07:00) Dawson Creek, Hermosillo, Phoenix, Chihuahua, Mazatlan, Boise, Cambridge Bay, Denver, Edmonton, Inuvik, Ojinaga, Shiprock, Yellowknife', -420], + ['America/Chicago', '(UTC -06:00) Beulah, Center, Chicago, Knox, Matamoros, Menominee, New Salem, Rainy River, Rankin Inlet, Resolute, Tell City, Winnipeg', -360], + ['America/Belize', '(UTC -06:00) Belize, Costa Rica, El Salvador, Galapagos, Guatemala, Managua, Regina, Swift Current, Tegucigalpa', -360], + ['Pacific/Easter', '(UTC -06:00) Easter', -360], + ['America/Bahia_Banderas', '(UTC -06:00) Bahia Banderas, Cancun, Merida, Mexico City, Monterrey', -360], + ['America/Detroit', '(UTC -05:00) Detroit, Grand Turk, Indianapolis, Iqaluit, Louisville, Marengo, Monticello, Montreal, Nassau, New York, Nipigon, Pangnirtung, Petersburg, Thunder Bay, Toronto, Vevay, Vincennes, Winamac', -300], + ['America/Atikokan', '(UTC -05:00) Atikokan, Bogota, Cayman, Guayaquil, Jamaica, Lima, Panama, Port-au-Prince', -300], + ['America/Havana', '(UTC -05:00) Havana', -300], + ['America/Caracas', '(UTC -04:30) Caracas', -270], + ['America/Glace_Bay', '(UTC -04:00) Bermuda, Glace Bay, Goose Bay, Halifax, Moncton, Thule', -240], + ['Atlantic/Stanley', '(UTC -04:00) Stanley', -240], + ['America/Santiago', '(UTC -04:00) Palmer, Santiago', -240], + ['America/Anguilla', '(UTC -04:00) Anguilla, Antigua, Aruba, Barbados, Blanc-Sablon, Boa Vista, Curacao, Dominica, Eirunepe, Grenada, Guadeloupe, Guyana, Kralendijk, La Paz, Lower Princes, Manaus, Marigot, Martinique, Montserrat, Port of Spain, Porto Velho, Puerto Rico, Rio Branco, Santo Domingo, St Barthelemy, St Kitts, St Lucia, St Thomas, St Vincent, Tortola', -240], + ['America/Campo_Grande', '(UTC -04:00) Campo Grande, Cuiaba', -240], + ['America/Asuncion', '(UTC -04:00) Asuncion', -240], + ['America/St_Johns', '(UTC -03:30) St Johns', -210], + ['America/Sao_Paulo', '(UTC -03:00) Sao Paulo', -180], + ['America/Araguaina', '(UTC -03:00) Araguaina, Bahia, Belem, Buenos Aires, Catamarca, Cayenne, Cordoba, Fortaleza, Jujuy, La Rioja, Maceio, Mendoza, Paramaribo, Recife, Rio Gallegos, Rothera, Salta, San Juan, Santarem, Tucuman, Ushuaia', -180], + ['America/Montevideo', '(UTC -03:00) Montevideo', -180], + ['America/Godthab', '(UTC -03:00) Godthab', -180], + ['America/Argentina/San_Luis', '(UTC -03:00) San Luis', -180], + ['America/Miquelon', '(UTC -03:00) Miquelon', -180], + ['America/Noronha', '(UTC -02:00) Noronha, South Georgia', -120], + ['Atlantic/Cape_Verde', '(UTC -01:00) Cape Verde', -60], + ['America/Scoresbysund', '(UTC -01:00) Azores, Scoresbysund', -60], + ['Atlantic/Canary', '(UTC) Canary, Dublin, Faroe, Guernsey, Isle of Man, Jersey, Lisbon, London, Madeira', 0], + ['Africa/Abidjan', '(UTC) Abidjan, Accra, Bamako, Banjul, Bissau, Casablanca, Conakry, Dakar, Danmarkshavn, El Aaiun, Freetown, Lome, Monrovia, Nouakchott, Ouagadougou, Reykjavik, Sao Tome, St Helena', 0], + ['Africa/Algiers', '(UTC +01:00) Algiers, Bangui, Brazzaville, Douala, Kinshasa, Lagos, Libreville, Luanda, Malabo, Ndjamena, Niamey, Porto-Novo, Tunis', 60], + ['Europe/Vienna', '(UTC +01:00) Amsterdam, Andorra, Belgrade, Berlin, Bratislava, Brussels, Budapest, Ceuta, Copenhagen, Gibraltar, Ljubljana, Longyearbyen, Luxembourg, Madrid, Malta, Monaco, Oslo, Paris, Podgorica, Prague, Rome, San Marino, Sarajevo, Skopje, Stockholm, Tirane, Vaduz, Vatican, Vienna, Warsaw, Zagreb, Zurich', 60], + ['Africa/Windhoek', '(UTC +01:00) Windhoek', 60], + ['Asia/Damascus', '(UTC +02:00) Damascus', 120], + ['Asia/Beirut', '(UTC +02:00) Beirut', 120], + ['Asia/Jerusalem', '(UTC +02:00) Jerusalem', 120], + ['Asia/Nicosia', '(UTC +02:00) Athens, Bucharest, Chisinau, Helsinki, Istanbul, Mariehamn, Nicosia, Riga, Sofia, Tallinn, Vilnius', 120], + ['Africa/Blantyre', '(UTC +02:00) Blantyre, Bujumbura, Cairo, Gaborone, Gaza, Harare, Hebron, Johannesburg, Kigali, Lubumbashi, Lusaka, Maputo, Maseru, Mbabane, Tripoli', 120], + ['Asia/Amman', '(UTC +02:00) Amman', 120], + ['Africa/Addis_Ababa', '(UTC +03:00) Addis Ababa, Aden, Antananarivo, Asmara, Baghdad, Bahrain, Comoro, Dar es Salaam, Djibouti, Juba, Kaliningrad, Kampala, Khartoum, Kiev, Kuwait, Mayotte, Minsk, Mogadishu, Nairobi, Qatar, Riyadh, Simferopol, Syowa, Uzhgorod, Zaporozhye', 180], + ['Asia/Tehran', '(UTC +03:30) Tehran', 210], + ['Asia/Yerevan', '(UTC +04:00) Yerevan', 240], + ['Asia/Dubai', '(UTC +04:00) Dubai, Mahe, Mauritius, Moscow, Muscat, Reunion, Samara, Tbilisi, Volgograd', 240], + ['Asia/Baku', '(UTC +04:00) Baku', 240], + ['Asia/Kabul', '(UTC +04:30) Kabul', 270], + ['Antarctica/Mawson', '(UTC +05:00) Aqtau, Aqtobe, Ashgabat, Dushanbe, Karachi, Kerguelen, Maldives, Mawson, Oral, Samarkand, Tashkent', 300], + ['Asia/Colombo', '(UTC +05:30) Colombo, Kolkata', 330], + ['Asia/Kathmandu', '(UTC +05:45) Kathmandu', 345], + ['Antarctica/Vostok', '(UTC +06:00) Almaty, Bishkek, Chagos, Dhaka, Qyzylorda, Thimphu, Vostok, Yekaterinburg', 360], + ['Asia/Rangoon', '(UTC +06:30) Cocos, Rangoon', 390], + ['Antarctica/Davis', '(UTC +07:00) Bangkok, Christmas, Davis, Ho Chi Minh, Hovd, Jakarta, Novokuznetsk, Novosibirsk, Omsk, Phnom Penh, Pontianak, Vientiane', 420], + ['Antarctica/Casey', '(UTC +08:00) Brunei, Casey, Choibalsan, Chongqing, Harbin, Hong Kong, Kashgar, Krasnoyarsk, Kuala Lumpur, Kuching, Macau, Makassar, Manila, Perth, Shanghai, Singapore, Taipei, Ulaanbaatar, Urumqi', 480], + ['Australia/Eucla', '(UTC +08:45) Eucla', 525], + ['Asia/Dili', '(UTC +09:00) Dili, Irkutsk, Jayapura, Palau, Pyongyang, Seoul, Tokyo', 540], + ['Australia/Adelaide', '(UTC +09:30) Adelaide, Broken Hill', 570], + ['Australia/Darwin', '(UTC +09:30) Darwin', 570], + ['Antarctica/DumontDUrville', '(UTC +10:00) Brisbane, Chuuk, DumontDUrville, Guam, Lindeman, Port Moresby, Saipan, Yakutsk', 600], + ['Australia/Currie', '(UTC +10:00) Currie, Hobart, Melbourne, Sydney', 600], + ['Australia/Lord_Howe', '(UTC +10:30) Lord Howe', 630], + ['Antarctica/Macquarie', '(UTC +11:00) Efate, Guadalcanal, Kosrae, Macquarie, Noumea, Pohnpei, Sakhalin, Vladivostok', 660], + ['Pacific/Norfolk', '(UTC +11:30) Norfolk', 690], + ['Antarctica/McMurdo', '(UTC +12:00) Auckland, McMurdo, South Pole', 720], + ['Asia/Anadyr', '(UTC +12:00) Anadyr, Fiji, Funafuti, Kamchatka, Kwajalein, Magadan, Majuro, Nauru, Tarawa, Wake, Wallis', 720], + ['Pacific/Chatham', '(UTC +12:45) Chatham', 765], + ['Pacific/Enderbury', '(UTC +13:00) Enderbury, Tongatapu', 780], + ['Pacific/Apia', '(UTC +13:00) Apia', 780], + ['Pacific/Kiritimati', '(UTC +14:00) Kiritimati', 840] ], - + /* map all citys to the above timezones */ - map : { + map : { /*-11:00*/ - 'Etc/GMT+11' : 'Pacific/Midway', - 'Pacific/Midway' : 'Pacific/Midway', - 'Pacific/Niue' : 'Pacific/Midway', - 'Pacific/Pago_Pago' : 'Pacific/Midway', - 'Pacific/Samoa' : 'Pacific/Midway', - 'US/Samoa' : 'Pacific/Midway', + 'Etc/GMT+11' : 'Pacific/Midway', + 'Pacific/Midway' : 'Pacific/Midway', + 'Pacific/Niue' : 'Pacific/Midway', + 'Pacific/Pago_Pago' : 'Pacific/Midway', + 'Pacific/Samoa' : 'Pacific/Midway', + 'US/Samoa' : 'Pacific/Midway', /*-10:00*/ - 'America/Adak' : 'Pacific/Fakaofo', - 'America/Atka' : 'Pacific/Fakaofo', - 'Etc/GMT+10' : 'Pacific/Fakaofo', - 'HST' : 'Pacific/Fakaofo', - 'Pacific/Honolulu' : 'Pacific/Fakaofo', - 'Pacific/Johnston' : 'Pacific/Fakaofo', - 'Pacific/Rarotonga' : 'Pacific/Fakaofo', - 'Pacific/Tahiti' : 'Pacific/Fakaofo', - 'SystemV/HST10' : 'Pacific/Fakaofo', - 'US/Aleutian' : 'Pacific/Fakaofo', - 'US/Hawaii' : 'Pacific/Fakaofo', + 'America/Adak' : 'Pacific/Fakaofo', + 'America/Atka' : 'Pacific/Fakaofo', + 'Etc/GMT+10' : 'Pacific/Fakaofo', + 'HST' : 'Pacific/Fakaofo', + 'Pacific/Honolulu' : 'Pacific/Fakaofo', + 'Pacific/Johnston' : 'Pacific/Fakaofo', + 'Pacific/Rarotonga' : 'Pacific/Fakaofo', + 'Pacific/Tahiti' : 'Pacific/Fakaofo', + 'SystemV/HST10' : 'Pacific/Fakaofo', + 'US/Aleutian' : 'Pacific/Fakaofo', + 'US/Hawaii' : 'Pacific/Fakaofo', /*-9:30*/ - 'Pacific/Marquesas' : 'Pacific/Marquesas', + 'Pacific/Marquesas' : 'Pacific/Marquesas', /*-9:00*/ - 'AST' : 'America/Anchorage', - 'America/Anchorage' : 'America/Anchorage', - 'America/Juneau' : 'America/Anchorage', - 'America/Nome' : 'America/Anchorage', - 'America/Sitka' : 'America/Anchorage', - 'America/Yakutat' : 'America/Anchorage', - 'Etc/GMT+9' : 'America/Anchorage', - 'Pacific/Gambier' : 'America/Anchorage', - 'SystemV/YST9' : 'America/Anchorage', - 'SystemV/YST9YDT' : 'America/Anchorage', - 'US/Alaska' : 'America/Anchorage', + 'AST' : 'America/Anchorage', + 'America/Anchorage' : 'America/Anchorage', + 'America/Juneau' : 'America/Anchorage', + 'America/Nome' : 'America/Anchorage', + 'America/Sitka' : 'America/Anchorage', + 'America/Yakutat' : 'America/Anchorage', + 'Etc/GMT+9' : 'America/Anchorage', + 'Pacific/Gambier' : 'America/Anchorage', + 'SystemV/YST9' : 'America/Anchorage', + 'SystemV/YST9YDT' : 'America/Anchorage', + 'US/Alaska' : 'America/Anchorage', /*-8:00*/ - 'America/Dawson' : 'America/Dawson', - 'America/Ensenada' : 'America/Dawson', - 'America/Los_Angeles' : 'America/Dawson', - 'America/Metlakatla' : 'America/Dawson', - 'America/Santa_Isabel' : 'America/Dawson', - 'America/Tijuana' : 'America/Dawson', - 'America/Vancouver' : 'America/Dawson', - 'America/Whitehorse' : 'America/Dawson', - 'Canada/Pacific' : 'America/Dawson', - 'Canada/Yukon' : 'America/Dawson', - 'Etc/GMT+8' : 'America/Dawson', - 'Mexico/BajaNorte' : 'America/Dawson', - 'PST' : 'America/Dawson', - 'PST8PDT' : 'America/Dawson', - 'Pacific/Pitcairn' : 'America/Dawson', - 'SystemV/PST8' : 'America/Dawson', - 'SystemV/PST8PDT' : 'America/Dawson', - 'US/Pacific' : 'America/Dawson', - 'US/Pacific-New' : 'America/Dawson', + 'America/Dawson' : 'America/Dawson', + 'America/Ensenada' : 'America/Dawson', + 'America/Los_Angeles' : 'America/Dawson', + 'America/Metlakatla' : 'America/Dawson', + 'America/Santa_Isabel' : 'America/Dawson', + 'America/Tijuana' : 'America/Dawson', + 'America/Vancouver' : 'America/Dawson', + 'America/Whitehorse' : 'America/Dawson', + 'Canada/Pacific' : 'America/Dawson', + 'Canada/Yukon' : 'America/Dawson', + 'Etc/GMT+8' : 'America/Dawson', + 'Mexico/BajaNorte' : 'America/Dawson', + 'PST' : 'America/Dawson', + 'PST8PDT' : 'America/Dawson', + 'Pacific/Pitcairn' : 'America/Dawson', + 'SystemV/PST8' : 'America/Dawson', + 'SystemV/PST8PDT' : 'America/Dawson', + 'US/Pacific' : 'America/Dawson', + 'US/Pacific-New' : 'America/Dawson', /*-7:00*/ - 'America/Boise' : 'America/Dawson_Creek', - 'America/Cambridge_Bay' : 'America/Dawson_Creek', - 'America/Chihuahua' : 'America/Dawson_Creek', - 'America/Creston' : 'America/Dawson_Creek', - 'America/Dawson_Creek' : 'America/Dawson_Creek', - 'America/Denver' : 'America/Dawson_Creek', - 'America/Edmonton' : 'America/Dawson_Creek', - 'America/Hermosillo' : 'America/Dawson_Creek', - 'America/Inuvik' : 'America/Dawson_Creek', - 'America/Mazatlan' : 'America/Dawson_Creek', - 'America/Ojinaga' : 'America/Dawson_Creek', - 'America/Phoenix' : 'America/Dawson_Creek', - 'America/Shiprock' : 'America/Dawson_Creek', - 'America/Yellowknife' : 'America/Dawson_Creek', - 'Canada/Mountain' : 'America/Dawson_Creek', - 'Etc/GMT+7' : 'America/Dawson_Creek', - 'MST' : 'America/Dawson_Creek', - 'MST7MDT' : 'America/Dawson_Creek', - 'Mexico/BajaSur' : 'America/Dawson_Creek', - 'Navajo' : 'America/Dawson_Creek', - 'PNT' : 'America/Dawson_Creek', - 'SystemV/MST7' : 'America/Dawson_Creek', - 'SystemV/MST7MDT' : 'America/Dawson_Creek', - 'US/Arizona' : 'America/Dawson_Creek', - 'US/Mountain' : 'America/Dawson_Creek', + 'America/Boise' : 'America/Dawson_Creek', + 'America/Cambridge_Bay' : 'America/Dawson_Creek', + 'America/Chihuahua' : 'America/Dawson_Creek', + 'America/Creston' : 'America/Dawson_Creek', + 'America/Dawson_Creek' : 'America/Dawson_Creek', + 'America/Denver' : 'America/Dawson_Creek', + 'America/Edmonton' : 'America/Dawson_Creek', + 'America/Hermosillo' : 'America/Dawson_Creek', + 'America/Inuvik' : 'America/Dawson_Creek', + 'America/Mazatlan' : 'America/Dawson_Creek', + 'America/Ojinaga' : 'America/Dawson_Creek', + 'America/Phoenix' : 'America/Dawson_Creek', + 'America/Shiprock' : 'America/Dawson_Creek', + 'America/Yellowknife' : 'America/Dawson_Creek', + 'Canada/Mountain' : 'America/Dawson_Creek', + 'Etc/GMT+7' : 'America/Dawson_Creek', + 'MST' : 'America/Dawson_Creek', + 'MST7MDT' : 'America/Dawson_Creek', + 'Mexico/BajaSur' : 'America/Dawson_Creek', + 'Navajo' : 'America/Dawson_Creek', + 'PNT' : 'America/Dawson_Creek', + 'SystemV/MST7' : 'America/Dawson_Creek', + 'SystemV/MST7MDT' : 'America/Dawson_Creek', + 'US/Arizona' : 'America/Dawson_Creek', + 'US/Mountain' : 'America/Dawson_Creek', /*-6:00*/ - 'America/Bahia_Banderas' : 'America/Chicago', - 'America/Belize' : 'America/Chicago', - 'America/Cancun' : 'America/Chicago', - 'America/Chicago' : 'America/Chicago', - 'America/Costa_Rica' : 'America/Chicago', - 'America/El_Salvador' : 'America/Chicago', - 'America/Guatemala' : 'America/Chicago', - 'America/Indiana/Knox' : 'America/Chicago', - 'America/Indiana/Tell_City' : 'America/Chicago', - 'America/Knox_IN' : 'America/Chicago', - 'America/Managua' : 'America/Chicago', - 'America/Matamoros' : 'America/Chicago', - 'America/Menominee' : 'America/Chicago', - 'America/Merida' : 'America/Chicago', - 'America/Mexico_City' : 'America/Chicago', - 'America/Monterrey' : 'America/Chicago', - 'America/North_Dakota/Beulah' : 'America/Chicago', - 'America/North_Dakota/Center' : 'America/Chicago', - 'America/North_Dakota/New_Salem' : 'America/Chicago', - 'America/Rainy_River' : 'America/Chicago', - 'America/Rankin_Inlet' : 'America/Chicago', - 'America/Regina' : 'America/Chicago', - 'America/Resolute' : 'America/Chicago', - 'America/Swift_Current' : 'America/Chicago', - 'America/Tegucigalpa' : 'America/Chicago', - 'America/Winnipeg' : 'America/Chicago', - 'CST' : 'America/Chicago', - 'CST6CDT' : 'America/Chicago', - 'Canada/Central' : 'America/Chicago', - 'Canada/East-Saskatchewan' : 'America/Chicago', - 'Canada/Saskatchewan' : 'America/Chicago', - 'Chile/EasterIsland' : 'America/Chicago', - 'Etc/GMT+6' : 'America/Chicago', - 'Mexico/General' : 'America/Chicago', - 'Pacific/Easter' : 'America/Chicago', - 'Pacific/Galapagos' : 'America/Chicago', - 'SystemV/CST6' : 'America/Chicago', - 'SystemV/CST6CDT' : 'America/Chicago', - 'US/Central' : 'America/Chicago', - 'US/Indiana-Starke' : 'America/Chicago', + 'America/Bahia_Banderas' : 'America/Chicago', + 'America/Belize' : 'America/Chicago', + 'America/Cancun' : 'America/Chicago', + 'America/Chicago' : 'America/Chicago', + 'America/Costa_Rica' : 'America/Chicago', + 'America/El_Salvador' : 'America/Chicago', + 'America/Guatemala' : 'America/Chicago', + 'America/Indiana/Knox' : 'America/Chicago', + 'America/Indiana/Tell_City' : 'America/Chicago', + 'America/Knox_IN' : 'America/Chicago', + 'America/Managua' : 'America/Chicago', + 'America/Matamoros' : 'America/Chicago', + 'America/Menominee' : 'America/Chicago', + 'America/Merida' : 'America/Chicago', + 'America/Mexico_City' : 'America/Chicago', + 'America/Monterrey' : 'America/Chicago', + 'America/North_Dakota/Beulah' : 'America/Chicago', + 'America/North_Dakota/Center' : 'America/Chicago', + 'America/North_Dakota/New_Salem' : 'America/Chicago', + 'America/Rainy_River' : 'America/Chicago', + 'America/Rankin_Inlet' : 'America/Chicago', + 'America/Regina' : 'America/Chicago', + 'America/Resolute' : 'America/Chicago', + 'America/Swift_Current' : 'America/Chicago', + 'America/Tegucigalpa' : 'America/Chicago', + 'America/Winnipeg' : 'America/Chicago', + 'CST' : 'America/Chicago', + 'CST6CDT' : 'America/Chicago', + 'Canada/Central' : 'America/Chicago', + 'Canada/East-Saskatchewan' : 'America/Chicago', + 'Canada/Saskatchewan' : 'America/Chicago', + 'Chile/EasterIsland' : 'America/Chicago', + 'Etc/GMT+6' : 'America/Chicago', + 'Mexico/General' : 'America/Chicago', + 'Pacific/Easter' : 'America/Chicago', + 'Pacific/Galapagos' : 'America/Chicago', + 'SystemV/CST6' : 'America/Chicago', + 'SystemV/CST6CDT' : 'America/Chicago', + 'US/Central' : 'America/Chicago', + 'US/Indiana-Starke' : 'America/Chicago', /*-5:00*/ - 'America/Atikokan' : 'America/Detroit', - 'America/Bogota' : 'America/Detroit', - 'America/Cayman' : 'America/Detroit', - 'America/Coral_Harbour' : 'America/Detroit', - 'America/Detroit' : 'America/Detroit', - 'America/Fort_Wayne' : 'America/Detroit', - 'America/Grand_Turk' : 'America/Detroit', - 'America/Guayaquil' : 'America/Detroit', - 'America/Havana' : 'America/Detroit', - 'America/Indiana/Indianapolis' : 'America/Detroit', - 'America/Indiana/Marengo' : 'America/Detroit', - 'America/Indiana/Petersburg' : 'America/Detroit', - 'America/Indiana/Vevay' : 'America/Detroit', - 'America/Indiana/Vincennes' : 'America/Detroit', - 'America/Indiana/Winamac' : 'America/Detroit', - 'America/Indianapolis' : 'America/Detroit', - 'America/Iqaluit' : 'America/Detroit', - 'America/Jamaica' : 'America/Detroit', - 'America/Kentucky/Louisville' : 'America/Detroit', - 'America/Kentucky/Monticello' : 'America/Detroit', - 'America/Lima' : 'America/Detroit', - 'America/Louisville' : 'America/Detroit', - 'America/Montreal' : 'America/Detroit', - 'America/Nassau' : 'America/Detroit', - 'America/New_York' : 'America/Detroit', - 'America/Nipigon' : 'America/Detroit', - 'America/Panama' : 'America/Detroit', - 'America/Pangnirtung' : 'America/Detroit', - 'America/Port-au-Prince' : 'America/Detroit', - 'America/Thunder_Bay' : 'America/Detroit', - 'America/Toronto' : 'America/Detroit', - 'Canada/Eastern' : 'America/Detroit', - 'Cuba' : 'America/Detroit', - 'EST' : 'America/Detroit', - 'EST5EDT' : 'America/Detroit', - 'Etc/GMT+5' : 'America/Detroit', - 'IET' : 'America/Detroit', - 'Jamaica' : 'America/Detroit', - 'SystemV/EST5' : 'America/Detroit', - 'SystemV/EST5EDT' : 'America/Detroit', - 'US/East-Indiana' : 'America/Detroit', - 'US/Eastern' : 'America/Detroit', - 'US/Michigan' : 'America/Detroit', + 'America/Atikokan' : 'America/Detroit', + 'America/Bogota' : 'America/Detroit', + 'America/Cayman' : 'America/Detroit', + 'America/Coral_Harbour' : 'America/Detroit', + 'America/Detroit' : 'America/Detroit', + 'America/Fort_Wayne' : 'America/Detroit', + 'America/Grand_Turk' : 'America/Detroit', + 'America/Guayaquil' : 'America/Detroit', + 'America/Havana' : 'America/Detroit', + 'America/Indiana/Indianapolis' : 'America/Detroit', + 'America/Indiana/Marengo' : 'America/Detroit', + 'America/Indiana/Petersburg' : 'America/Detroit', + 'America/Indiana/Vevay' : 'America/Detroit', + 'America/Indiana/Vincennes' : 'America/Detroit', + 'America/Indiana/Winamac' : 'America/Detroit', + 'America/Indianapolis' : 'America/Detroit', + 'America/Iqaluit' : 'America/Detroit', + 'America/Jamaica' : 'America/Detroit', + 'America/Kentucky/Louisville' : 'America/Detroit', + 'America/Kentucky/Monticello' : 'America/Detroit', + 'America/Lima' : 'America/Detroit', + 'America/Louisville' : 'America/Detroit', + 'America/Montreal' : 'America/Detroit', + 'America/Nassau' : 'America/Detroit', + 'America/New_York' : 'America/Detroit', + 'America/Nipigon' : 'America/Detroit', + 'America/Panama' : 'America/Detroit', + 'America/Pangnirtung' : 'America/Detroit', + 'America/Port-au-Prince' : 'America/Detroit', + 'America/Thunder_Bay' : 'America/Detroit', + 'America/Toronto' : 'America/Detroit', + 'Canada/Eastern' : 'America/Detroit', + 'Cuba' : 'America/Detroit', + 'EST' : 'America/Detroit', + 'EST5EDT' : 'America/Detroit', + 'Etc/GMT+5' : 'America/Detroit', + 'IET' : 'America/Detroit', + 'Jamaica' : 'America/Detroit', + 'SystemV/EST5' : 'America/Detroit', + 'SystemV/EST5EDT' : 'America/Detroit', + 'US/East-Indiana' : 'America/Detroit', + 'US/Eastern' : 'America/Detroit', + 'US/Michigan' : 'America/Detroit', /*-4:30*/ - 'America/Caracas' : 'America/Caracas', + 'America/Caracas' : 'America/Caracas', /*-4:00*/ - 'America/Anguilla' : 'America/Santiago', - 'America/Antigua' : 'America/Santiago', - 'America/Argentina/San_Luis' : 'America/Santiago', - 'America/Aruba' : 'America/Santiago', - 'America/Asuncion' : 'America/Santiago', - 'America/Barbados' : 'America/Santiago', - 'America/Blanc-Sablon' : 'America/Santiago', - 'America/Boa_Vista' : 'America/Santiago', - 'America/Campo_Grande' : 'America/Santiago', - 'America/Cuiaba' : 'America/Santiago', - 'America/Curacao' : 'America/Santiago', - 'America/Dominica' : 'America/Santiago', - 'America/Eirunepe' : 'America/Santiago', - 'America/Glace_Bay' : 'America/Santiago', - 'America/Goose_Bay' : 'America/Santiago', - 'America/Grenada' : 'America/Santiago', - 'America/Guadeloupe' : 'America/Santiago', - 'America/Guyana' : 'America/Santiago', - 'America/Halifax' : 'America/Santiago', - 'America/Kralendijk' : 'America/Santiago', - 'America/La_Paz' : 'America/Santiago', - 'America/Lower_Princes' : 'America/Santiago', - 'America/Manaus' : 'America/Santiago', - 'America/Marigot' : 'America/Santiago', - 'America/Martinique' : 'America/Santiago', - 'America/Moncton' : 'America/Santiago', - 'America/Montserrat' : 'America/Santiago', - 'America/Port_of_Spain' : 'America/Santiago', - 'America/Porto_Acre' : 'America/Santiago', - 'America/Porto_Velho' : 'America/Santiago', - 'America/Puerto_Rico' : 'America/Santiago', - 'America/Rio_Branco' : 'America/Santiago', - 'America/Santiago' : 'America/Santiago', - 'America/Santo_Domingo' : 'America/Santiago', - 'America/St_Barthelemy' : 'America/Santiago', - 'America/St_Kitts' : 'America/Santiago', - 'America/St_Lucia' : 'America/Santiago', - 'America/St_Thomas' : 'America/Santiago', - 'America/St_Vincent' : 'America/Santiago', - 'America/Thule' : 'America/Santiago', - 'America/Tortola' : 'America/Santiago', - 'America/Virgin' : 'America/Santiago', - 'Antarctica/Palmer' : 'America/Santiago', - 'Atlantic/Bermuda' : 'America/Santiago', - 'Brazil/Acre' : 'America/Santiago', - 'Brazil/West' : 'America/Santiago', - 'Canada/Atlantic' : 'America/Santiago', - 'Chile/Continental' : 'America/Santiago', - 'Etc/GMT+4' : 'America/Santiago', - 'PRT' : 'America/Santiago', - 'SystemV/AST4' : 'America/Santiago', - 'SystemV/AST4ADT' : 'America/Santiago', + 'America/Anguilla' : 'America/Santiago', + 'America/Antigua' : 'America/Santiago', + 'America/Argentina/San_Luis' : 'America/Santiago', + 'America/Aruba' : 'America/Santiago', + 'America/Asuncion' : 'America/Santiago', + 'America/Barbados' : 'America/Santiago', + 'America/Blanc-Sablon' : 'America/Santiago', + 'America/Boa_Vista' : 'America/Santiago', + 'America/Campo_Grande' : 'America/Santiago', + 'America/Cuiaba' : 'America/Santiago', + 'America/Curacao' : 'America/Santiago', + 'America/Dominica' : 'America/Santiago', + 'America/Eirunepe' : 'America/Santiago', + 'America/Glace_Bay' : 'America/Santiago', + 'America/Goose_Bay' : 'America/Santiago', + 'America/Grenada' : 'America/Santiago', + 'America/Guadeloupe' : 'America/Santiago', + 'America/Guyana' : 'America/Santiago', + 'America/Halifax' : 'America/Santiago', + 'America/Kralendijk' : 'America/Santiago', + 'America/La_Paz' : 'America/Santiago', + 'America/Lower_Princes' : 'America/Santiago', + 'America/Manaus' : 'America/Santiago', + 'America/Marigot' : 'America/Santiago', + 'America/Martinique' : 'America/Santiago', + 'America/Moncton' : 'America/Santiago', + 'America/Montserrat' : 'America/Santiago', + 'America/Port_of_Spain' : 'America/Santiago', + 'America/Porto_Acre' : 'America/Santiago', + 'America/Porto_Velho' : 'America/Santiago', + 'America/Puerto_Rico' : 'America/Santiago', + 'America/Rio_Branco' : 'America/Santiago', + 'America/Santiago' : 'America/Santiago', + 'America/Santo_Domingo' : 'America/Santiago', + 'America/St_Barthelemy' : 'America/Santiago', + 'America/St_Kitts' : 'America/Santiago', + 'America/St_Lucia' : 'America/Santiago', + 'America/St_Thomas' : 'America/Santiago', + 'America/St_Vincent' : 'America/Santiago', + 'America/Thule' : 'America/Santiago', + 'America/Tortola' : 'America/Santiago', + 'America/Virgin' : 'America/Santiago', + 'Antarctica/Palmer' : 'America/Santiago', + 'Atlantic/Bermuda' : 'America/Santiago', + 'Brazil/Acre' : 'America/Santiago', + 'Brazil/West' : 'America/Santiago', + 'Canada/Atlantic' : 'America/Santiago', + 'Chile/Continental' : 'America/Santiago', + 'Etc/GMT+4' : 'America/Santiago', + 'PRT' : 'America/Santiago', + 'SystemV/AST4' : 'America/Santiago', + 'SystemV/AST4ADT' : 'America/Santiago', /*-3:30*/ - 'America/St_Johns' : 'America/St_Johns', - 'CNT' : '', - 'Canada/Newfoundland' : '', + 'America/St_Johns' : 'America/St_Johns', + 'CNT' : '', + 'Canada/Newfoundland' : '', /*-3:00*/ - 'AGT' : 'America/Sao_Paulo', - 'America/Araguaina' : 'America/Sao_Paulo', - 'America/Argentina/Buenos_Aires' : 'America/Sao_Paulo', - 'America/Argentina/Catamarca' : 'America/Sao_Paulo', - 'America/Argentina/ComodRivadavia' : 'America/Sao_Paulo', - 'America/Argentina/Cordoba' : 'America/Sao_Paulo', - 'America/Argentina/Jujuy' : 'America/Sao_Paulo', - 'America/Argentina/La_Rioja' : 'America/Sao_Paulo', - 'America/Argentina/Mendoza' : 'America/Sao_Paulo', - 'America/Argentina/Rio_Gallegos' : 'America/Sao_Paulo', - 'America/Argentina/Salta' : 'America/Sao_Paulo', - 'America/Argentina/San_Juan' : 'America/Sao_Paulo', - 'America/Argentina/Tucuman' : 'America/Sao_Paulo', - 'America/Argentina/Ushuaia' : 'America/Sao_Paulo', - 'America/Bahia' : 'America/Sao_Paulo', - 'America/Belem' : 'America/Sao_Paulo', - 'America/Buenos_Aires' : 'America/Sao_Paulo', - 'America/Catamarca' : 'America/Sao_Paulo', - 'America/Cayenne' : 'America/Sao_Paulo', - 'America/Cordoba' : 'America/Sao_Paulo', - 'America/Fortaleza' : 'America/Sao_Paulo', - 'America/Godthab' : 'America/Sao_Paulo', - 'America/Jujuy' : 'America/Sao_Paulo', - 'America/Maceio' : 'America/Sao_Paulo', - 'America/Mendoza' : 'America/Sao_Paulo', - 'America/Miquelon' : 'America/Sao_Paulo', - 'America/Montevideo' : 'America/Sao_Paulo', - 'America/Paramaribo' : 'America/Sao_Paulo', - 'America/Recife' : 'America/Sao_Paulo', - 'America/Rosario' : 'America/Sao_Paulo', - 'America/Santarem' : 'America/Sao_Paulo', - 'America/Sao_Paulo' : 'America/Sao_Paulo', - 'Antarctica/Rothera' : 'America/Sao_Paulo', - 'Atlantic/Stanley' : 'America/Sao_Paulo', - 'BET' : 'America/Sao_Paulo', - 'Brazil/East' : 'America/Sao_Paulo', - 'Etc/GMT+3' : 'America/Sao_Paulo', + 'AGT' : 'America/Sao_Paulo', + 'America/Araguaina' : 'America/Sao_Paulo', + 'America/Argentina/Buenos_Aires' : 'America/Sao_Paulo', + 'America/Argentina/Catamarca' : 'America/Sao_Paulo', + 'America/Argentina/ComodRivadavia': 'America/Sao_Paulo', + 'America/Argentina/Cordoba' : 'America/Sao_Paulo', + 'America/Argentina/Jujuy' : 'America/Sao_Paulo', + 'America/Argentina/La_Rioja' : 'America/Sao_Paulo', + 'America/Argentina/Mendoza' : 'America/Sao_Paulo', + 'America/Argentina/Rio_Gallegos' : 'America/Sao_Paulo', + 'America/Argentina/Salta' : 'America/Sao_Paulo', + 'America/Argentina/San_Juan' : 'America/Sao_Paulo', + 'America/Argentina/Tucuman' : 'America/Sao_Paulo', + 'America/Argentina/Ushuaia' : 'America/Sao_Paulo', + 'America/Bahia' : 'America/Sao_Paulo', + 'America/Belem' : 'America/Sao_Paulo', + 'America/Buenos_Aires' : 'America/Sao_Paulo', + 'America/Catamarca' : 'America/Sao_Paulo', + 'America/Cayenne' : 'America/Sao_Paulo', + 'America/Cordoba' : 'America/Sao_Paulo', + 'America/Fortaleza' : 'America/Sao_Paulo', + 'America/Godthab' : 'America/Sao_Paulo', + 'America/Jujuy' : 'America/Sao_Paulo', + 'America/Maceio' : 'America/Sao_Paulo', + 'America/Mendoza' : 'America/Sao_Paulo', + 'America/Miquelon' : 'America/Sao_Paulo', + 'America/Montevideo' : 'America/Sao_Paulo', + 'America/Paramaribo' : 'America/Sao_Paulo', + 'America/Recife' : 'America/Sao_Paulo', + 'America/Rosario' : 'America/Sao_Paulo', + 'America/Santarem' : 'America/Sao_Paulo', + 'America/Sao_Paulo' : 'America/Sao_Paulo', + 'Antarctica/Rothera' : 'America/Sao_Paulo', + 'Atlantic/Stanley' : 'America/Sao_Paulo', + 'BET' : 'America/Sao_Paulo', + 'Brazil/East' : 'America/Sao_Paulo', + 'Etc/GMT+3' : 'America/Sao_Paulo', /*-2:00*/ - 'America/Noronha' : 'America/Noronha', - 'Atlantic/South_Georgia' : 'America/Noronha', - 'Brazil/DeNoronha' : 'America/Noronha', - 'Etc/GMT+2' : 'America/Noronha', + 'America/Noronha' : 'America/Noronha', + 'Atlantic/South_Georgia' : 'America/Noronha', + 'Brazil/DeNoronha' : 'America/Noronha', + 'Etc/GMT+2' : 'America/Noronha', /*-1:00*/ - 'America/Scoresbysund' : 'Atlantic/Cape_Verde', - 'Atlantic/Azores' : 'Atlantic/Cape_Verde', - 'Atlantic/Cape_Verde' : 'Atlantic/Cape_Verde', - 'Etc/GMT+1' : 'Atlantic/Cape_Verde', + 'America/Scoresbysund' : 'Atlantic/Cape_Verde', + 'Atlantic/Azores' : 'Atlantic/Cape_Verde', + 'Atlantic/Cape_Verde' : 'Atlantic/Cape_Verde', + 'Etc/GMT+1' : 'Atlantic/Cape_Verde', /*+0:00*/ - 'Africa/Abidjan' : 'Africa/Abidjan', - 'Africa/Accra' : 'Africa/Abidjan', - 'Africa/Bamako' : 'Africa/Abidjan', - 'Africa/Banjul' : 'Africa/Abidjan', - 'Africa/Bissau' : 'Africa/Abidjan', - 'Africa/Casablanca' : 'Africa/Abidjan', - 'Africa/Conakry' : 'Africa/Abidjan', - 'Africa/Dakar' : 'Africa/Abidjan', - 'Africa/El_Aaiun' : 'Africa/Abidjan', - 'Africa/Freetown' : 'Africa/Abidjan', - 'Africa/Lome' : 'Africa/Abidjan', - 'Africa/Monrovia' : 'Africa/Abidjan', - 'Africa/Nouakchott' : 'Africa/Abidjan', - 'Africa/Ouagadougou' : 'Africa/Abidjan', - 'Africa/Sao_Tome' : 'Africa/Abidjan', - 'Africa/Timbuktu' : 'Africa/Abidjan', - 'America/Danmarkshavn' : 'Africa/Abidjan', - 'Atlantic/Canary' : 'Africa/Abidjan', - 'Atlantic/Faeroe' : 'Africa/Abidjan', - 'Atlantic/Faroe' : 'Africa/Abidjan', - 'Atlantic/Madeira' : 'Africa/Abidjan', - 'Atlantic/Reykjavik' : 'Africa/Abidjan', - 'Atlantic/St_Helena' : 'Africa/Abidjan', - 'Eire' : 'Africa/Abidjan', - 'Etc/GMT' : 'Africa/Abidjan', - 'Etc/GMT+0' : 'Africa/Abidjan', - 'Etc/GMT-0' : 'Africa/Abidjan', - 'Etc/GMT0' : 'Africa/Abidjan', - 'Etc/Greenwich' : 'Africa/Abidjan', - 'Etc/UCT' : 'Africa/Abidjan', - 'Etc/UTC' : 'Africa/Abidjan', - 'Etc/Universal' : 'Africa/Abidjan', - 'Etc/Zulu' : 'Africa/Abidjan', - 'Europe/Belfast' : 'Africa/Abidjan', - 'Europe/Dublin' : 'Africa/Abidjan', - 'Europe/Guernsey' : 'Africa/Abidjan', - 'Europe/Isle_of_Man' : 'Africa/Abidjan', - 'Europe/Jersey' : 'Africa/Abidjan', - 'Europe/Lisbon' : 'Africa/Abidjan', - 'Europe/London' : 'Africa/Abidjan', - 'GB' : 'Africa/Abidjan', - 'GB-Eire' : 'Africa/Abidjan', - 'GMT' : 'Africa/Abidjan', - 'GMT0' : 'Africa/Abidjan', - 'Greenwich' : 'Africa/Abidjan', - 'Iceland' : 'Africa/Abidjan', - 'Portugal' : 'Africa/Abidjan', - 'UCT' : 'Africa/Abidjan', - 'UTC' : 'Africa/Abidjan', - 'Universal' : 'Africa/Abidjan', - 'WET' : 'Africa/Abidjan', - 'Zulu' : 'Africa/Abidjan', + 'Africa/Abidjan' : 'Africa/Abidjan', + 'Africa/Accra' : 'Africa/Abidjan', + 'Africa/Bamako' : 'Africa/Abidjan', + 'Africa/Banjul' : 'Africa/Abidjan', + 'Africa/Bissau' : 'Africa/Abidjan', + 'Africa/Casablanca' : 'Africa/Abidjan', + 'Africa/Conakry' : 'Africa/Abidjan', + 'Africa/Dakar' : 'Africa/Abidjan', + 'Africa/El_Aaiun' : 'Africa/Abidjan', + 'Africa/Freetown' : 'Africa/Abidjan', + 'Africa/Lome' : 'Africa/Abidjan', + 'Africa/Monrovia' : 'Africa/Abidjan', + 'Africa/Nouakchott' : 'Africa/Abidjan', + 'Africa/Ouagadougou' : 'Africa/Abidjan', + 'Africa/Sao_Tome' : 'Africa/Abidjan', + 'Africa/Timbuktu' : 'Africa/Abidjan', + 'America/Danmarkshavn' : 'Africa/Abidjan', + 'Atlantic/Canary' : 'Africa/Abidjan', + 'Atlantic/Faeroe' : 'Africa/Abidjan', + 'Atlantic/Faroe' : 'Africa/Abidjan', + 'Atlantic/Madeira' : 'Africa/Abidjan', + 'Atlantic/Reykjavik' : 'Africa/Abidjan', + 'Atlantic/St_Helena' : 'Africa/Abidjan', + 'Eire' : 'Africa/Abidjan', + 'Etc/GMT' : 'Africa/Abidjan', + 'Etc/GMT+0' : 'Africa/Abidjan', + 'Etc/GMT-0' : 'Africa/Abidjan', + 'Etc/GMT0' : 'Africa/Abidjan', + 'Etc/Greenwich' : 'Africa/Abidjan', + 'Etc/UCT' : 'Africa/Abidjan', + 'Etc/UTC' : 'Africa/Abidjan', + 'Etc/Universal' : 'Africa/Abidjan', + 'Etc/Zulu' : 'Africa/Abidjan', + 'Europe/Belfast' : 'Africa/Abidjan', + 'Europe/Dublin' : 'Africa/Abidjan', + 'Europe/Guernsey' : 'Africa/Abidjan', + 'Europe/Isle_of_Man' : 'Africa/Abidjan', + 'Europe/Jersey' : 'Africa/Abidjan', + 'Europe/Lisbon' : 'Africa/Abidjan', + 'Europe/London' : 'Africa/Abidjan', + 'GB' : 'Africa/Abidjan', + 'GB-Eire' : 'Africa/Abidjan', + 'GMT' : 'Africa/Abidjan', + 'GMT0' : 'Africa/Abidjan', + 'Greenwich' : 'Africa/Abidjan', + 'Iceland' : 'Africa/Abidjan', + 'Portugal' : 'Africa/Abidjan', + 'UCT' : 'Africa/Abidjan', + 'UTC' : 'Africa/Abidjan', + 'Universal' : 'Africa/Abidjan', + 'WET' : 'Africa/Abidjan', + 'Zulu' : 'Africa/Abidjan', /*+1:00*/ - 'Africa/Algiers' : 'Europe/Vienna', - 'Africa/Bangui' : 'Europe/Vienna', - 'Africa/Brazzaville' : 'Europe/Vienna', - 'Africa/Ceuta' : 'Europe/Vienna', - 'Africa/Douala' : 'Europe/Vienna', - 'Africa/Kinshasa' : 'Europe/Vienna', - 'Africa/Lagos' : 'Europe/Vienna', - 'Africa/Libreville' : 'Europe/Vienna', - 'Africa/Luanda' : 'Europe/Vienna', - 'Africa/Malabo' : 'Europe/Vienna', - 'Africa/Ndjamena' : 'Europe/Vienna', - 'Africa/Niamey' : 'Europe/Vienna', - 'Africa/Porto-Novo' : 'Europe/Vienna', - 'Africa/Tunis' : 'Europe/Vienna', - 'Africa/Windhoek' : 'Europe/Vienna', - 'Arctic/Longyearbyen' : 'Europe/Vienna', - 'Atlantic/Jan_Mayen' : 'Europe/Vienna', - 'CET' : 'Europe/Vienna', - 'ECT' : 'Europe/Vienna', - 'Etc/GMT-1' : 'Europe/Vienna', - 'Europe/Amsterdam' : 'Europe/Vienna', - 'Europe/Andorra' : 'Europe/Vienna', - 'Europe/Belgrade' : 'Europe/Vienna', - 'Europe/Berlin' : 'Europe/Vienna', - 'Europe/Bratislava' : 'Europe/Vienna', - 'Europe/Brussels' : 'Europe/Vienna', - 'Europe/Budapest' : 'Europe/Vienna', - 'Europe/Copenhagen' : 'Europe/Vienna', - 'Europe/Gibraltar' : 'Europe/Vienna', - 'Europe/Ljubljana' : 'Europe/Vienna', - 'Europe/Luxembourg' : 'Europe/Vienna', - 'Europe/Madrid' : 'Europe/Vienna', - 'Europe/Malta' : 'Europe/Vienna', - 'Europe/Monaco' : 'Europe/Vienna', - 'Europe/Oslo' : 'Europe/Vienna', - 'Europe/Paris' : 'Europe/Vienna', - 'Europe/Podgorica' : 'Europe/Vienna', - 'Europe/Prague' : 'Europe/Vienna', - 'Europe/Rome' : 'Europe/Vienna', - 'Europe/San_Marino' : 'Europe/Vienna', - 'Europe/Sarajevo' : 'Europe/Vienna', - 'Europe/Skopje' : 'Europe/Vienna', - 'Europe/Stockholm' : 'Europe/Vienna', - 'Europe/Tirane' : 'Europe/Vienna', - 'Europe/Vaduz' : 'Europe/Vienna', - 'Europe/Vatican' : 'Europe/Vienna', - 'Europe/Vienna' : 'Europe/Vienna', - 'Europe/Warsaw' : 'Europe/Vienna', - 'Europe/Zagreb' : 'Europe/Vienna', - 'Europe/Zurich' : 'Europe/Vienna', - 'MET' : 'Europe/Vienna', - 'Poland' : 'Europe/Vienna', + 'Africa/Algiers' : 'Europe/Vienna', + 'Africa/Bangui' : 'Europe/Vienna', + 'Africa/Brazzaville' : 'Europe/Vienna', + 'Africa/Ceuta' : 'Europe/Vienna', + 'Africa/Douala' : 'Europe/Vienna', + 'Africa/Kinshasa' : 'Europe/Vienna', + 'Africa/Lagos' : 'Europe/Vienna', + 'Africa/Libreville' : 'Europe/Vienna', + 'Africa/Luanda' : 'Europe/Vienna', + 'Africa/Malabo' : 'Europe/Vienna', + 'Africa/Ndjamena' : 'Europe/Vienna', + 'Africa/Niamey' : 'Europe/Vienna', + 'Africa/Porto-Novo' : 'Europe/Vienna', + 'Africa/Tunis' : 'Europe/Vienna', + 'Africa/Windhoek' : 'Europe/Vienna', + 'Arctic/Longyearbyen' : 'Europe/Vienna', + 'Atlantic/Jan_Mayen' : 'Europe/Vienna', + 'CET' : 'Europe/Vienna', + 'ECT' : 'Europe/Vienna', + 'Etc/GMT-1' : 'Europe/Vienna', + 'Europe/Amsterdam' : 'Europe/Vienna', + 'Europe/Andorra' : 'Europe/Vienna', + 'Europe/Belgrade' : 'Europe/Vienna', + 'Europe/Berlin' : 'Europe/Vienna', + 'Europe/Bratislava' : 'Europe/Vienna', + 'Europe/Brussels' : 'Europe/Vienna', + 'Europe/Budapest' : 'Europe/Vienna', + 'Europe/Copenhagen' : 'Europe/Vienna', + 'Europe/Gibraltar' : 'Europe/Vienna', + 'Europe/Ljubljana' : 'Europe/Vienna', + 'Europe/Luxembourg' : 'Europe/Vienna', + 'Europe/Madrid' : 'Europe/Vienna', + 'Europe/Malta' : 'Europe/Vienna', + 'Europe/Monaco' : 'Europe/Vienna', + 'Europe/Oslo' : 'Europe/Vienna', + 'Europe/Paris' : 'Europe/Vienna', + 'Europe/Podgorica' : 'Europe/Vienna', + 'Europe/Prague' : 'Europe/Vienna', + 'Europe/Rome' : 'Europe/Vienna', + 'Europe/San_Marino' : 'Europe/Vienna', + 'Europe/Sarajevo' : 'Europe/Vienna', + 'Europe/Skopje' : 'Europe/Vienna', + 'Europe/Stockholm' : 'Europe/Vienna', + 'Europe/Tirane' : 'Europe/Vienna', + 'Europe/Vaduz' : 'Europe/Vienna', + 'Europe/Vatican' : 'Europe/Vienna', + 'Europe/Vienna' : 'Europe/Vienna', + 'Europe/Warsaw' : 'Europe/Vienna', + 'Europe/Zagreb' : 'Europe/Vienna', + 'Europe/Zurich' : 'Europe/Vienna', + 'MET' : 'Europe/Vienna', + 'Poland' : 'Europe/Vienna', /*+2:00*/ - 'ART' : 'Asia/Jerusalem', - 'Africa/Blantyre' : 'Asia/Jerusalem', - 'Africa/Bujumbura' : 'Asia/Jerusalem', - 'Africa/Cairo' : 'Asia/Jerusalem', - 'Africa/Gaborone' : 'Asia/Jerusalem', - 'Africa/Harare' : 'Asia/Jerusalem', - 'Africa/Johannesburg' : 'Asia/Jerusalem', - 'Africa/Kigali' : 'Asia/Jerusalem', - 'Africa/Lubumbashi' : 'Asia/Jerusalem', - 'Africa/Lusaka' : 'Asia/Jerusalem', - 'Africa/Maputo' : 'Asia/Jerusalem', - 'Africa/Maseru' : 'Asia/Jerusalem', - 'Africa/Mbabane' : 'Asia/Jerusalem', - 'Africa/Tripoli' : 'Asia/Jerusalem', - 'Asia/Amman' : 'Asia/Jerusalem', - 'Asia/Beirut' : 'Asia/Jerusalem', - 'Asia/Damascus' : 'Asia/Jerusalem', - 'Asia/Gaza' : 'Asia/Jerusalem', - 'Asia/Hebron' : 'Asia/Jerusalem', - 'Asia/Istanbul' : 'Asia/Jerusalem', - 'Asia/Jerusalem' : 'Asia/Jerusalem', - 'Asia/Nicosia' : 'Asia/Jerusalem', - 'Asia/Tel_Aviv' : 'Asia/Jerusalem', - 'CAT' : 'Asia/Jerusalem', - 'EET' : 'Asia/Jerusalem', - 'Egypt' : 'Asia/Jerusalem', - 'Etc/GMT-2' : 'Asia/Jerusalem', - 'Europe/Athens' : 'Asia/Jerusalem', - 'Europe/Bucharest' : 'Asia/Jerusalem', - 'Europe/Chisinau' : 'Asia/Jerusalem', - 'Europe/Helsinki' : 'Asia/Jerusalem', - 'Europe/Istanbul' : 'Asia/Jerusalem', - 'Europe/Kiev' : 'Asia/Jerusalem', - 'Europe/Mariehamn' : 'Asia/Jerusalem', - 'Europe/Nicosia' : 'Asia/Jerusalem', - 'Europe/Riga' : 'Asia/Jerusalem', - 'Europe/Simferopol' : 'Asia/Jerusalem', - 'Europe/Sofia' : 'Asia/Jerusalem', - 'Europe/Tallinn' : 'Asia/Jerusalem', - 'Europe/Tiraspol' : 'Asia/Jerusalem', - 'Europe/Uzhgorod' : 'Asia/Jerusalem', - 'Europe/Vilnius' : 'Asia/Jerusalem', - 'Europe/Zaporozhye' : 'Asia/Jerusalem', - 'Israel' : 'Asia/Jerusalem', - 'Libya' : 'Asia/Jerusalem', - 'Turkey' : 'Asia/Jerusalem', + 'ART' : 'Asia/Jerusalem', + 'Africa/Blantyre' : 'Asia/Jerusalem', + 'Africa/Bujumbura' : 'Asia/Jerusalem', + 'Africa/Cairo' : 'Asia/Jerusalem', + 'Africa/Gaborone' : 'Asia/Jerusalem', + 'Africa/Harare' : 'Asia/Jerusalem', + 'Africa/Johannesburg' : 'Asia/Jerusalem', + 'Africa/Kigali' : 'Asia/Jerusalem', + 'Africa/Lubumbashi' : 'Asia/Jerusalem', + 'Africa/Lusaka' : 'Asia/Jerusalem', + 'Africa/Maputo' : 'Asia/Jerusalem', + 'Africa/Maseru' : 'Asia/Jerusalem', + 'Africa/Mbabane' : 'Asia/Jerusalem', + 'Africa/Tripoli' : 'Asia/Jerusalem', + 'Asia/Amman' : 'Asia/Jerusalem', + 'Asia/Beirut' : 'Asia/Jerusalem', + 'Asia/Damascus' : 'Asia/Jerusalem', + 'Asia/Gaza' : 'Asia/Jerusalem', + 'Asia/Hebron' : 'Asia/Jerusalem', + 'Asia/Istanbul' : 'Asia/Jerusalem', + 'Asia/Jerusalem' : 'Asia/Jerusalem', + 'Asia/Nicosia' : 'Asia/Jerusalem', + 'Asia/Tel_Aviv' : 'Asia/Jerusalem', + 'CAT' : 'Asia/Jerusalem', + 'EET' : 'Asia/Jerusalem', + 'Egypt' : 'Asia/Jerusalem', + 'Etc/GMT-2' : 'Asia/Jerusalem', + 'Europe/Athens' : 'Asia/Jerusalem', + 'Europe/Bucharest' : 'Asia/Jerusalem', + 'Europe/Chisinau' : 'Asia/Jerusalem', + 'Europe/Helsinki' : 'Asia/Jerusalem', + 'Europe/Istanbul' : 'Asia/Jerusalem', + 'Europe/Kiev' : 'Asia/Jerusalem', + 'Europe/Mariehamn' : 'Asia/Jerusalem', + 'Europe/Nicosia' : 'Asia/Jerusalem', + 'Europe/Riga' : 'Asia/Jerusalem', + 'Europe/Simferopol' : 'Asia/Jerusalem', + 'Europe/Sofia' : 'Asia/Jerusalem', + 'Europe/Tallinn' : 'Asia/Jerusalem', + 'Europe/Tiraspol' : 'Asia/Jerusalem', + 'Europe/Uzhgorod' : 'Asia/Jerusalem', + 'Europe/Vilnius' : 'Asia/Jerusalem', + 'Europe/Zaporozhye' : 'Asia/Jerusalem', + 'Israel' : 'Asia/Jerusalem', + 'Libya' : 'Asia/Jerusalem', + 'Turkey' : 'Asia/Jerusalem', /*+3:00*/ - 'Africa/Addis_Ababa' : 'Africa/Addis_Ababa', - 'Africa/Asmara' : 'Africa/Addis_Ababa', - 'Africa/Asmera' : 'Africa/Addis_Ababa', - 'Africa/Dar_es_Salaam' : 'Africa/Addis_Ababa', - 'Africa/Djibouti' : 'Africa/Addis_Ababa', - 'Africa/Juba' : 'Africa/Addis_Ababa', - 'Africa/Kampala' : 'Africa/Addis_Ababa', - 'Africa/Khartoum' : 'Africa/Addis_Ababa', - 'Africa/Mogadishu' : 'Africa/Addis_Ababa', - 'Africa/Nairobi' : 'Africa/Addis_Ababa', - 'Antarctica/Syowa' : 'Africa/Addis_Ababa', - 'Asia/Aden' : 'Africa/Addis_Ababa', - 'Asia/Baghdad' : 'Africa/Addis_Ababa', - 'Asia/Bahrain' : 'Africa/Addis_Ababa', - 'Asia/Kuwait' : 'Africa/Addis_Ababa', - 'Asia/Qatar' : 'Africa/Addis_Ababa', - 'Asia/Riyadh' : 'Africa/Addis_Ababa', - 'EAT' : 'Africa/Addis_Ababa', - 'Etc/GMT-3' : 'Africa/Addis_Ababa', - 'Europe/Kaliningrad' : 'Africa/Addis_Ababa', - 'Europe/Minsk' : 'Africa/Addis_Ababa', - 'Indian/Antananarivo' : 'Africa/Addis_Ababa', - 'Indian/Comoro' : 'Africa/Addis_Ababa', - 'Indian/Mayotte' : 'Africa/Addis_Ababa', + 'Africa/Addis_Ababa' : 'Africa/Addis_Ababa', + 'Africa/Asmara' : 'Africa/Addis_Ababa', + 'Africa/Asmera' : 'Africa/Addis_Ababa', + 'Africa/Dar_es_Salaam' : 'Africa/Addis_Ababa', + 'Africa/Djibouti' : 'Africa/Addis_Ababa', + 'Africa/Juba' : 'Africa/Addis_Ababa', + 'Africa/Kampala' : 'Africa/Addis_Ababa', + 'Africa/Khartoum' : 'Africa/Addis_Ababa', + 'Africa/Mogadishu' : 'Africa/Addis_Ababa', + 'Africa/Nairobi' : 'Africa/Addis_Ababa', + 'Antarctica/Syowa' : 'Africa/Addis_Ababa', + 'Asia/Aden' : 'Africa/Addis_Ababa', + 'Asia/Baghdad' : 'Africa/Addis_Ababa', + 'Asia/Bahrain' : 'Africa/Addis_Ababa', + 'Asia/Kuwait' : 'Africa/Addis_Ababa', + 'Asia/Qatar' : 'Africa/Addis_Ababa', + 'Asia/Riyadh' : 'Africa/Addis_Ababa', + 'EAT' : 'Africa/Addis_Ababa', + 'Etc/GMT-3' : 'Africa/Addis_Ababa', + 'Europe/Kaliningrad' : 'Africa/Addis_Ababa', + 'Europe/Minsk' : 'Africa/Addis_Ababa', + 'Indian/Antananarivo' : 'Africa/Addis_Ababa', + 'Indian/Comoro' : 'Africa/Addis_Ababa', + 'Indian/Mayotte' : 'Africa/Addis_Ababa', /*+3:30*/ - 'Asia/Tehran' : 'Asia/Tehran', - 'Iran' : 'Asia/Tehran', + 'Asia/Tehran' : 'Asia/Tehran', + 'Iran' : 'Asia/Tehran', /*+4:00*/ - 'Asia/Baku' : 'Asia/Dubai', - 'Asia/Dubai' : 'Asia/Dubai', - 'Asia/Muscat' : 'Asia/Dubai', - 'Asia/Tbilisi' : 'Asia/Dubai', - 'Asia/Yerevan' : 'Asia/Dubai', - 'Etc/GMT-4' : 'Asia/Dubai', - 'Europe/Moscow' : 'Asia/Dubai', - 'Europe/Samara' : 'Asia/Dubai', - 'Europe/Volgograd' : 'Asia/Dubai', - 'Indian/Mahe' : 'Asia/Dubai', - 'Indian/Mauritius' : 'Asia/Dubai', - 'Indian/Reunion' : 'Asia/Dubai', - 'NET' : 'Asia/Dubai', - 'W-SU' : 'Asia/Dubai', + 'Asia/Baku' : 'Asia/Dubai', + 'Asia/Dubai' : 'Asia/Dubai', + 'Asia/Muscat' : 'Asia/Dubai', + 'Asia/Tbilisi' : 'Asia/Dubai', + 'Asia/Yerevan' : 'Asia/Dubai', + 'Etc/GMT-4' : 'Asia/Dubai', + 'Europe/Moscow' : 'Asia/Dubai', + 'Europe/Samara' : 'Asia/Dubai', + 'Europe/Volgograd' : 'Asia/Dubai', + 'Indian/Mahe' : 'Asia/Dubai', + 'Indian/Mauritius' : 'Asia/Dubai', + 'Indian/Reunion' : 'Asia/Dubai', + 'NET' : 'Asia/Dubai', + 'W-SU' : 'Asia/Dubai', /*+4:30*/ - 'Asia/Kabul' : 'Asia/Kabul', + 'Asia/Kabul' : 'Asia/Kabul', /*+5:00*/ - 'Antarctica/Mawson' : 'Antarctica/Mawson', - 'Asia/Aqtau' : 'Antarctica/Mawson', - 'Asia/Aqtobe' : 'Antarctica/Mawson', - 'Asia/Ashgabat' : 'Antarctica/Mawson', - 'Asia/Ashkhabad' : 'Antarctica/Mawson', - 'Asia/Dushanbe' : 'Antarctica/Mawson', - 'Asia/Karachi' : 'Antarctica/Mawson', - 'Asia/Oral' : 'Antarctica/Mawson', - 'Asia/Samarkand' : 'Antarctica/Mawson', - 'Asia/Tashkent' : 'Antarctica/Mawson', - 'Etc/GMT-5' : 'Antarctica/Mawson', - 'Indian/Kerguelen' : 'Antarctica/Mawson', - 'Indian/Maldives' : 'Antarctica/Mawson', - 'PLT' : 'Antarctica/Mawson', + 'Antarctica/Mawson' : 'Antarctica/Mawson', + 'Asia/Aqtau' : 'Antarctica/Mawson', + 'Asia/Aqtobe' : 'Antarctica/Mawson', + 'Asia/Ashgabat' : 'Antarctica/Mawson', + 'Asia/Ashkhabad' : 'Antarctica/Mawson', + 'Asia/Dushanbe' : 'Antarctica/Mawson', + 'Asia/Karachi' : 'Antarctica/Mawson', + 'Asia/Oral' : 'Antarctica/Mawson', + 'Asia/Samarkand' : 'Antarctica/Mawson', + 'Asia/Tashkent' : 'Antarctica/Mawson', + 'Etc/GMT-5' : 'Antarctica/Mawson', + 'Indian/Kerguelen' : 'Antarctica/Mawson', + 'Indian/Maldives' : 'Antarctica/Mawson', + 'PLT' : 'Antarctica/Mawson', /*+5:30*/ - 'Asia/Calcutta' : 'Asia/Colombo', - 'Asia/Colombo' : 'Asia/Colombo', - 'Asia/Kolkata' : 'Asia/Colombo', - 'IST' : 'Asia/Colombo', + 'Asia/Calcutta' : 'Asia/Colombo', + 'Asia/Colombo' : 'Asia/Colombo', + 'Asia/Kolkata' : 'Asia/Colombo', + 'IST' : 'Asia/Colombo', /*+6:00*/ - 'Antarctica/Vostok' : 'Antarctica/Vostok', - 'Asia/Almaty' : 'Antarctica/Vostok', - 'Asia/Bishkek' : 'Antarctica/Vostok', - 'Asia/Dacca' : 'Antarctica/Vostok', - 'Asia/Dhaka' : 'Antarctica/Vostok', - 'Asia/Qyzylorda' : 'Antarctica/Vostok', - 'Asia/Thimbu' : 'Antarctica/Vostok', - 'Asia/Thimphu' : 'Antarctica/Vostok', - 'Asia/Yekaterinburg' : 'Antarctica/Vostok', - 'BST' : 'Antarctica/Vostok', - 'Etc/GMT-6' : 'Antarctica/Vostok', - 'Indian/Chagos' : 'Antarctica/Vostok', + 'Antarctica/Vostok' : 'Antarctica/Vostok', + 'Asia/Almaty' : 'Antarctica/Vostok', + 'Asia/Bishkek' : 'Antarctica/Vostok', + 'Asia/Dacca' : 'Antarctica/Vostok', + 'Asia/Dhaka' : 'Antarctica/Vostok', + 'Asia/Qyzylorda' : 'Antarctica/Vostok', + 'Asia/Thimbu' : 'Antarctica/Vostok', + 'Asia/Thimphu' : 'Antarctica/Vostok', + 'Asia/Yekaterinburg' : 'Antarctica/Vostok', + 'BST' : 'Antarctica/Vostok', + 'Etc/GMT-6' : 'Antarctica/Vostok', + 'Indian/Chagos' : 'Antarctica/Vostok', /*+6:30*/ - 'Asia/Rangoon' : 'Asia/Rangoon', - 'Indian/Cocos' : 'Asia/Rangoon', + 'Asia/Rangoon' : 'Asia/Rangoon', + 'Indian/Cocos' : 'Asia/Rangoon', /*+7:00*/ - 'Antarctica/Davis' : 'Antarctica/Davis', - 'Asia/Bangkok' : 'Antarctica/Davis', - 'Asia/Ho_Chi_Minh' : 'Antarctica/Davis', - 'Asia/Hovd' : 'Antarctica/Davis', - 'Asia/Jakarta' : 'Antarctica/Davis', - 'Asia/Novokuznetsk' : 'Antarctica/Davis', - 'Asia/Novosibirsk' : 'Antarctica/Davis', - 'Asia/Omsk' : 'Antarctica/Davis', - 'Asia/Phnom_Penh' : 'Antarctica/Davis', - 'Asia/Pontianak' : 'Antarctica/Davis', - 'Asia/Saigon' : 'Antarctica/Davis', - 'Asia/Vientiane' : 'Antarctica/Davis', - 'Etc/GMT-7' : 'Antarctica/Davis', - 'Indian/Christmas' : 'Antarctica/Davis', - 'VST' : 'Antarctica/Davis', + 'Antarctica/Davis' : 'Antarctica/Davis', + 'Asia/Bangkok' : 'Antarctica/Davis', + 'Asia/Ho_Chi_Minh' : 'Antarctica/Davis', + 'Asia/Hovd' : 'Antarctica/Davis', + 'Asia/Jakarta' : 'Antarctica/Davis', + 'Asia/Novokuznetsk' : 'Antarctica/Davis', + 'Asia/Novosibirsk' : 'Antarctica/Davis', + 'Asia/Omsk' : 'Antarctica/Davis', + 'Asia/Phnom_Penh' : 'Antarctica/Davis', + 'Asia/Pontianak' : 'Antarctica/Davis', + 'Asia/Saigon' : 'Antarctica/Davis', + 'Asia/Vientiane' : 'Antarctica/Davis', + 'Etc/GMT-7' : 'Antarctica/Davis', + 'Indian/Christmas' : 'Antarctica/Davis', + 'VST' : 'Antarctica/Davis', /*+8:00*/ - 'Antarctica/Casey' : 'Antarctica/Casey', - 'Asia/Brunei' : 'Antarctica/Casey', - 'Asia/Choibalsan' : 'Antarctica/Casey', - 'Asia/Chongqing' : 'Antarctica/Casey', - 'Asia/Chungking' : 'Antarctica/Casey', - 'Asia/Harbin' : 'Antarctica/Casey', - 'Asia/Hong_Kong' : 'Antarctica/Casey', - 'Asia/Kashgar' : 'Antarctica/Casey', - 'Asia/Krasnoyarsk' : 'Antarctica/Casey', - 'Asia/Kuala_Lumpur' : 'Antarctica/Casey', - 'Asia/Kuching' : 'Antarctica/Casey', - 'Asia/Macao' : 'Antarctica/Casey', - 'Asia/Macau' : 'Antarctica/Casey', - 'Asia/Makassar' : 'Antarctica/Casey', - 'Asia/Manila' : 'Antarctica/Casey', - 'Asia/Shanghai' : 'Antarctica/Casey', - 'Asia/Singapore' : 'Antarctica/Casey', - 'Asia/Taipei' : 'Antarctica/Casey', - 'Asia/Ujung_Pandang' : 'Antarctica/Casey', - 'Asia/Ulaanbaatar' : 'Antarctica/Casey', - 'Asia/Ulan_Bator' : 'Antarctica/Casey', - 'Asia/Urumqi' : 'Antarctica/Casey', - 'Australia/Perth' : 'Antarctica/Casey', - 'Australia/West' : 'Antarctica/Casey', - 'CTT' : 'Antarctica/Casey', - 'Etc/GMT-8' : 'Antarctica/Casey', - 'Hongkong' : 'Antarctica/Casey', - 'PRC' : 'Antarctica/Casey', - 'Singapore' : 'Antarctica/Casey', + 'Antarctica/Casey' : 'Antarctica/Casey', + 'Asia/Brunei' : 'Antarctica/Casey', + 'Asia/Choibalsan' : 'Antarctica/Casey', + 'Asia/Chongqing' : 'Antarctica/Casey', + 'Asia/Chungking' : 'Antarctica/Casey', + 'Asia/Harbin' : 'Antarctica/Casey', + 'Asia/Hong_Kong' : 'Antarctica/Casey', + 'Asia/Kashgar' : 'Antarctica/Casey', + 'Asia/Krasnoyarsk' : 'Antarctica/Casey', + 'Asia/Kuala_Lumpur' : 'Antarctica/Casey', + 'Asia/Kuching' : 'Antarctica/Casey', + 'Asia/Macao' : 'Antarctica/Casey', + 'Asia/Macau' : 'Antarctica/Casey', + 'Asia/Makassar' : 'Antarctica/Casey', + 'Asia/Manila' : 'Antarctica/Casey', + 'Asia/Shanghai' : 'Antarctica/Casey', + 'Asia/Singapore' : 'Antarctica/Casey', + 'Asia/Taipei' : 'Antarctica/Casey', + 'Asia/Ujung_Pandang' : 'Antarctica/Casey', + 'Asia/Ulaanbaatar' : 'Antarctica/Casey', + 'Asia/Ulan_Bator' : 'Antarctica/Casey', + 'Asia/Urumqi' : 'Antarctica/Casey', + 'Australia/Perth' : 'Antarctica/Casey', + 'Australia/West' : 'Antarctica/Casey', + 'CTT' : 'Antarctica/Casey', + 'Etc/GMT-8' : 'Antarctica/Casey', + 'Hongkong' : 'Antarctica/Casey', + 'PRC' : 'Antarctica/Casey', + 'Singapore' : 'Antarctica/Casey', /*+9:00*/ - 'Asia/Dili' : 'Asia/Dili', - 'Asia/Irkutsk' : 'Asia/Dili', - 'Asia/Jayapura' : 'Asia/Dili', - 'Asia/Pyongyang' : 'Asia/Dili', - 'Asia/Seoul' : 'Asia/Dili', - 'Asia/Tokyo' : 'Asia/Dili', - 'Etc/GMT-9' : 'Asia/Dili', - 'JST' : 'Asia/Dili', - 'Japan' : 'Asia/Dili', - 'Pacific/Palau' : 'Asia/Dili', - 'ROK' : 'Asia/Dili', + 'Asia/Dili' : 'Asia/Dili', + 'Asia/Irkutsk' : 'Asia/Dili', + 'Asia/Jayapura' : 'Asia/Dili', + 'Asia/Pyongyang' : 'Asia/Dili', + 'Asia/Seoul' : 'Asia/Dili', + 'Asia/Tokyo' : 'Asia/Dili', + 'Etc/GMT-9' : 'Asia/Dili', + 'JST' : 'Asia/Dili', + 'Japan' : 'Asia/Dili', + 'Pacific/Palau' : 'Asia/Dili', + 'ROK' : 'Asia/Dili', /*+9:30*/ - 'ACT' : 'Australia/Darwin', - 'Australia/Adelaide' : 'Australia/Darwin', - 'Australia/Broken_Hill' : 'Australia/Darwin', - 'Australia/Darwin' : 'Australia/Darwin', - 'Australia/North' : 'Australia/Darwin', - 'Australia/South' : 'Australia/Darwin', - 'Australia/Yancowinna' : 'Australia/Darwin', + 'ACT' : 'Australia/Darwin', + 'Australia/Adelaide' : 'Australia/Darwin', + 'Australia/Broken_Hill' : 'Australia/Darwin', + 'Australia/Darwin' : 'Australia/Darwin', + 'Australia/North' : 'Australia/Darwin', + 'Australia/South' : 'Australia/Darwin', + 'Australia/Yancowinna' : 'Australia/Darwin', /*+10:00*/ - 'AET' : 'Australia/Currie', - 'Antarctica/DumontDUrville' : 'Australia/Currie', - 'Asia/Yakutsk' : 'Australia/Currie', - 'Australia/ACT' : 'Australia/Currie', - 'Australia/Brisbane' : 'Australia/Currie', - 'Australia/Canberra' : 'Australia/Currie', - 'Australia/Currie' : 'Australia/Currie', - 'Australia/Hobart' : 'Australia/Currie', - 'Australia/Lindeman' : 'Australia/Currie', - 'Australia/Melbourne' : 'Australia/Currie', - 'Australia/NSW' : 'Australia/Currie', - 'Australia/Queensland' : 'Australia/Currie', - 'Australia/Sydney' : 'Australia/Currie', - 'Australia/Tasmania' : 'Australia/Currie', - 'Australia/Victoria' : 'Australia/Currie', - 'Etc/GMT-10' : 'Australia/Currie', - 'Pacific/Chuuk' : 'Australia/Currie', - 'Pacific/Guam' : 'Australia/Currie', - 'Pacific/Port_Moresby' : 'Australia/Currie', - 'Pacific/Saipan' : 'Australia/Currie', - 'Pacific/Truk' : 'Australia/Currie', - 'Pacific/Yap' : 'Australia/Currie', + 'AET' : 'Australia/Currie', + 'Antarctica/DumontDUrville' : 'Australia/Currie', + 'Asia/Yakutsk' : 'Australia/Currie', + 'Australia/ACT' : 'Australia/Currie', + 'Australia/Brisbane' : 'Australia/Currie', + 'Australia/Canberra' : 'Australia/Currie', + 'Australia/Currie' : 'Australia/Currie', + 'Australia/Hobart' : 'Australia/Currie', + 'Australia/Lindeman' : 'Australia/Currie', + 'Australia/Melbourne' : 'Australia/Currie', + 'Australia/NSW' : 'Australia/Currie', + 'Australia/Queensland' : 'Australia/Currie', + 'Australia/Sydney' : 'Australia/Currie', + 'Australia/Tasmania' : 'Australia/Currie', + 'Australia/Victoria' : 'Australia/Currie', + 'Etc/GMT-10' : 'Australia/Currie', + 'Pacific/Chuuk' : 'Australia/Currie', + 'Pacific/Guam' : 'Australia/Currie', + 'Pacific/Port_Moresby' : 'Australia/Currie', + 'Pacific/Saipan' : 'Australia/Currie', + 'Pacific/Truk' : 'Australia/Currie', + 'Pacific/Yap' : 'Australia/Currie', /*+10:30*/ - 'Australia/LHI' : 'Australia/Lord_Howe', - 'Australia/Lord_Howe' : 'Australia/Lord_Howe', + 'Australia/LHI' : 'Australia/Lord_Howe', + 'Australia/Lord_Howe' : 'Australia/Lord_Howe', /*+11:00*/ - 'Antarctica/Macquarie' : 'Antarctica/Macquarie', - 'Asia/Sakhalin' : 'Antarctica/Macquarie', - 'Asia/Vladivostok' : 'Antarctica/Macquarie', - 'Etc/GMT-11' : 'Antarctica/Macquarie', - 'Pacific/Efate' : 'Antarctica/Macquarie', - 'Pacific/Guadalcanal' : 'Antarctica/Macquarie', - 'Pacific/Kosrae' : 'Antarctica/Macquarie', - 'Pacific/Noumea' : 'Antarctica/Macquarie', - 'Pacific/Pohnpei' : 'Antarctica/Macquarie', - 'Pacific/Ponape' : 'Antarctica/Macquarie', - 'SST' : 'Antarctica/Macquarie', + 'Antarctica/Macquarie' : 'Antarctica/Macquarie', + 'Asia/Sakhalin' : 'Antarctica/Macquarie', + 'Asia/Vladivostok' : 'Antarctica/Macquarie', + 'Etc/GMT-11' : 'Antarctica/Macquarie', + 'Pacific/Efate' : 'Antarctica/Macquarie', + 'Pacific/Guadalcanal' : 'Antarctica/Macquarie', + 'Pacific/Kosrae' : 'Antarctica/Macquarie', + 'Pacific/Noumea' : 'Antarctica/Macquarie', + 'Pacific/Pohnpei' : 'Antarctica/Macquarie', + 'Pacific/Ponape' : 'Antarctica/Macquarie', + 'SST' : 'Antarctica/Macquarie', /*+11:30*/ - 'Pacific/Norfolk' : 'Pacific/Norfolk', + 'Pacific/Norfolk' : 'Pacific/Norfolk', /*+12:00*/ - 'Antarctica/McMurdo' : 'Antarctica/McMurdo', - 'Antarctica/South_Pole' : 'Antarctica/McMurdo', - 'Asia/Anadyr' : 'Antarctica/McMurdo', - 'Asia/Kamchatka' : 'Antarctica/McMurdo', - 'Asia/Magadan' : 'Antarctica/McMurdo', - 'Etc/GMT-12' : 'Antarctica/McMurdo', - 'Kwajalein' : 'Antarctica/McMurdo', - 'NST' : 'Antarctica/McMurdo', - 'NZ' : 'Antarctica/McMurdo', - 'Pacific/Auckland' : 'Antarctica/McMurdo', - 'Pacific/Fiji' : 'Antarctica/McMurdo', - 'Pacific/Funafuti' : 'Antarctica/McMurdo', - 'Pacific/Kwajalein' : 'Antarctica/McMurdo', - 'Pacific/Majuro' : 'Antarctica/McMurdo', - 'Pacific/Nauru' : 'Antarctica/McMurdo', - 'Pacific/Tarawa' : 'Antarctica/McMurdo', - 'Pacific/Wake' : 'Antarctica/McMurdo', - 'Pacific/Wallis' : 'Antarctica/McMurdo', + 'Antarctica/McMurdo' : 'Antarctica/McMurdo', + 'Antarctica/South_Pole' : 'Antarctica/McMurdo', + 'Asia/Anadyr' : 'Antarctica/McMurdo', + 'Asia/Kamchatka' : 'Antarctica/McMurdo', + 'Asia/Magadan' : 'Antarctica/McMurdo', + 'Etc/GMT-12' : 'Antarctica/McMurdo', + 'Kwajalein' : 'Antarctica/McMurdo', + 'NST' : 'Antarctica/McMurdo', + 'NZ' : 'Antarctica/McMurdo', + 'Pacific/Auckland' : 'Antarctica/McMurdo', + 'Pacific/Fiji' : 'Antarctica/McMurdo', + 'Pacific/Funafuti' : 'Antarctica/McMurdo', + 'Pacific/Kwajalein' : 'Antarctica/McMurdo', + 'Pacific/Majuro' : 'Antarctica/McMurdo', + 'Pacific/Nauru' : 'Antarctica/McMurdo', + 'Pacific/Tarawa' : 'Antarctica/McMurdo', + 'Pacific/Wake' : 'Antarctica/McMurdo', + 'Pacific/Wallis' : 'Antarctica/McMurdo', /*+13:00*/ - 'Etc/GMT-13' : 'Pacific/Enderbury', - 'MIT' : 'Pacific/Enderbury', - 'Pacific/Apia' : 'Pacific/Enderbury', - 'Pacific/Enderbury' : 'Pacific/Enderbury', - 'Pacific/Tongatapu' : 'Pacific/Enderbury', + 'Etc/GMT-13' : 'Pacific/Enderbury', + 'MIT' : 'Pacific/Enderbury', + 'Pacific/Apia' : 'Pacific/Enderbury', + 'Pacific/Enderbury' : 'Pacific/Enderbury', + 'Pacific/Tongatapu' : 'Pacific/Enderbury', /*+14:00*/ - 'Etc/GMT-14' : 'Pacific/Kiritimati', - 'Pacific/Fakaofo' : 'Pacific/Kiritimati', - 'Pacific/Kiritimati' : 'Pacific/Kiritimati' + 'Etc/GMT-14' : 'Pacific/Kiritimati', + 'Pacific/Fakaofo' : 'Pacific/Kiritimati', + 'Pacific/Kiritimati' : 'Pacific/Kiritimati' }, - + /* return unmapped timezone... */ - unMap: function(timezone) { + unMap: function (timezone) { return this.map[timezone] !== undefined ? this.map[timezone] : timezone; }, - - getOffset: function(timezone) { + + getOffset: function (timezone) { /* find timezone, this needs to be optimized ;) */ timezone = this.unMap(timezone); var i = 0; - for(i = 0; i < this.store.length; i++) { - if(this.store[i][0] == timezone) { + for (i = 0; i < this.store.length; i++) { + if (this.store[i][0] == timezone) { return (this.store[i][2] * 60000); } } - + return 0; // no offset found... } }); diff --git a/js/dialogs/ImportContentPanel.js b/js/dialogs/ImportContentPanel.js index e325d63..dbd2e17 100644 --- a/js/dialogs/ImportContentPanel.js +++ b/js/dialogs/ImportContentPanel.js @@ -2,7 +2,7 @@ * ImportContentPanel.js zarafa calender to ics im/exporter * * Author: Christoph Haas - * Copyright (C) 2012-2013 Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -42,13 +42,9 @@ Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.c */ constructor : function(config) { config = config || {}; - var title = _('Import Calendar File'); - if(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/enable_export")){ - title = _('Import/Export Calendar File'); - } Ext.applyIf(config, { layout : 'fit', - title : title, + title : _('Import Calendar File'), closeOnSave : true, width : 800, height : 700, @@ -56,7 +52,8 @@ Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.c items : [ { xtype : 'calendarimporter.importpanel', - filename : config.filename + filename : config.filename, + folder : config.folder } ] }); diff --git a/js/dialogs/ImportPanel.js b/js/dialogs/ImportPanel.js index fcea308..190d7a8 100644 --- a/js/dialogs/ImportPanel.js +++ b/js/dialogs/ImportPanel.js @@ -2,7 +2,7 @@ * ImportPanel.js zarafa calender to ics im/exporter * * Author: Christoph Haas - * Copyright (C) 2012-2013 Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,7 +29,7 @@ Ext.namespace("Zarafa.plugins.calendarimporter.dialogs"); /** * @class Zarafa.plugins.calendarimporter.dialogs.ImportPanel - * @extends Ext.form.FormPanel + * @extends Ext.Panel */ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { @@ -46,21 +46,16 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { loadMask: null, /* export event buffer */ - exportResponse: new Array(), + exportResponse: [], /* how many requests are still running? */ runningRequests: null, /* The store for the selection grid */ store: null, - - /** - * The internal 'iframe' which is hidden from the user, which is used for downloading - * attachments. See {@link #doOpen}. - * @property - * @type Ext.Element - */ - downloadFrame : undefined, + + /* selected folder */ + folder : null, /** * @constructor @@ -70,10 +65,14 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { config = config || {}; var self = this; this.timezone = container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_timezone"); - - if(typeof config.filename !== "undefined") { + + if (!Ext.isEmpty(config.filename)) { this.icsfile = config.filename; } + + if (!Ext.isEmpty(config.folder)) { + this.folder = config.folder; + } // create the data store this.store = new Ext.data.ArrayStore({ @@ -114,29 +113,19 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { this.createGrid() ], buttons: [ - this.createExportAllButton(), this.createSubmitAllButton(), this.createSubmitButton(), this.createCancelButton() ], listeners: { afterrender: function (cmp) { - Ext.getCmp('importbutton').disable(); - this.loadMask = new Ext.LoadMask(Ext.getCmp("importpanel").getEl(), {msg:'Loading...'}); + this.loadMask = new Ext.LoadMask(this.getEl(), {msg:'Loading...'}); if(this.icsfile != null) { // if we have got the filename from an attachment this.parseCalendar(this.icsfile, this.timezone, this.ignoredst); } }, - close: function (cmp) { - Ext.getCmp('importbutton').enable(); - }, - hide: function (cmp) { - Ext.getCmp('importbutton').enable(); - }, - destroy: function (cmp) { - Ext.getCmp('importbutton').enable(); - } + scope: this } }); @@ -167,6 +156,119 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { }; }, + /** + * Get all calendar folders. + * @param {boolean} asDropdownStore If true, a simple array store will be returned. + * @returns {*} + */ + getAllCalendarFolders: function (asDropdownStore) { + asDropdownStore = Ext.isEmpty(asDropdownStore) ? false : asDropdownStore; + + var allFolders = []; + + var inbox = container.getHierarchyStore().getDefaultStore(); + var pub = container.getHierarchyStore().getPublicStore(); + + if (!Ext.isEmpty(inbox.subStores) && inbox.subStores.folders.totalLength > 0) { + for (var i = 0; i < inbox.subStores.folders.totalLength; i++) { + var folder = inbox.subStores.folders.getAt(i); + if (folder.get("container_class") == "IPF.Appointment") { + if (asDropdownStore) { + allFolders.push([ + folder.get("entryid"), + folder.get("display_name") + ]); + } else { + allFolders.push({ + display_name : folder.get("display_name"), + entryid : folder.get("entryid"), + store_entryid: folder.get("store_entryid"), + is_public : false + }); + } + } + } + } + + if (!Ext.isEmpty(pub.subStores) && pub.subStores.folders.totalLength > 0) { + for (var j = 0; j < pub.subStores.folders.totalLength; j++) { + var folder = pub.subStores.folders.getAt(j); + if (folder.get("container_class") == "IPF.Appointment") { + if (asDropdownStore) { + allFolders.push([ + folder.get("entryid"), + folder.get("display_name") + " (Public)" + ]); + } else { + allFolders.push({ + display_name : folder.get("display_name"), + entryid : folder.get("entryid"), + store_entryid: folder.get("store_entryid"), + is_public : true + }); + } + } + } + } + + if (asDropdownStore) { + return allFolders.sort(this.dynamicSort(1)); + } else { + return allFolders; + } + }, + + /** + * Dynamic sort function, sorts by property name. + * @param {string|int} property + * @returns {Function} + */ + dynamicSort: function (property) { + var sortOrder = 1; + if (property[0] === "-") { + sortOrder = -1; + property = property.substr(1); + } + return function (a, b) { + var result = (a[property].toLowerCase() < b[property].toLowerCase()) ? -1 : (a[property].toLowerCase() > b[property].toLowerCase()) ? 1 : 0; + return result * sortOrder; + } + }, + + /** + * Return a calendar folder element by name. + * @param {string} name + * @returns {*} + */ + getCalendarFolderByName: function (name) { + var folders = this.getAllCalendarFolders(false); + + for (var i = 0; i < folders.length; i++) { + if (folders[i].display_name == name) { + return folders[i]; + } + } + + return container.getHierarchyStore().getDefaultFolder('calendar'); + }, + + /** + * Return a calendar folder element by entryid. + * @param {string} entryid + * @returns {*} + */ + getCalendarFolderByEntryid: function (entryid) { + var folders = this.getAllCalendarFolders(false); + + for (var i = 0; i < folders.length; i++) { + if (folders[i].entryid == entryid) { + return folders[i]; + } + } + + return container.getHierarchyStore().getDefaultFolder('calendar'); + }, + /** * Reloads the data of the grid * @private @@ -241,52 +343,23 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { }, createSelectBox: function() { - var defaultFolder = container.getHierarchyStore().getDefaultFolder('calendar'); // @type: Zarafa.hierarchy.data.MAPIFolderRecord - var subFolders = defaultFolder.getChildren(); - var myStore = []; - - /* add all local calendar folders */ - var i = 0; - myStore.push(new Array(defaultFolder.getDefaultFolderKey(), defaultFolder.getDisplayName())); - for(i = 0; i < subFolders.length; i++) { - /* Store all subfolders */ - myStore.push(new Array(subFolders[i].getDisplayName(), subFolders[i].getDisplayName(), false)); // 3rd field = isPublicfolder - } - - /* add all shared calendar folders */ - var pubStore = container.getHierarchyStore().getPublicStore(); - - if(typeof pubStore !== "undefined") { - try { - var pubFolder = pubStore.getDefaultFolder("publicfolders"); - var pubSubFolders = pubFolder.getChildren(); - - for(i = 0; i < pubSubFolders.length; i++) { - if(pubSubFolders[i].isContainerClass("IPF.Appointment")){ - myStore.push(new Array(pubSubFolders[i].getDisplayName(), pubSubFolders[i].getDisplayName() + " [Shared]", true)); // 3rd field = isPublicfolder - } - } - } catch (e) { - console.log("Error opening the shared folder..."); - console.log(e); - } - } - + var myStore = this.getAllCalendarFolders(true); + return { xtype: "selectbox", ref: 'calendarselector', - id: 'calendarselector', editable: false, name: "choosen_calendar", - value: container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar"), + value: Ext.isEmpty(this.folder) ? this.getCalendarFolderByName(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar")).entryid : this.folder, width: 100, - fieldLabel: "Select a calender", + fieldLabel: "Select folder", store: myStore, mode: 'local', labelSeperator: ":", border: false, anchor: "100%", scope: this, + hidden : Ext.isEmpty(this.folder) ? false : true, allowBlank: false } }, @@ -295,7 +368,6 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { return { xtype: "selectbox", ref: 'timezoneselector', - id: 'timezoneselector', editable: false, name: "choosen_timezone", value: Zarafa.plugins.calendarimporter.data.Timezones.unMap(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_timezone")), @@ -309,7 +381,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { scope: this, allowBlank: true, listeners: { - 'select': this.onTimezoneSelected, + select: this.onTimezoneSelected, scope: this } } @@ -319,7 +391,6 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { return { xtype: "checkbox", ref: 'dstcheck', - id: 'dstcheck', name: "dst_check", width: 100, fieldLabel: "Ignore DST (optional)", @@ -330,7 +401,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { scope: this, allowBlank: true, listeners: { - 'check': this.onDstChecked, + check: this.onDstChecked, scope: this } } @@ -346,10 +417,11 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { emptyText: 'Select an .ics calendar', border: false, anchor: "100%", + height : "30", scope: this, allowBlank: false, listeners: { - 'fileselected': this.onFileSelected, + fileselected: this.onFileSelected, scope: this } } @@ -359,15 +431,13 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { return { xtype: "button", ref: "submitButton", - id: "submitButton", disabled: true, width: 100, border: false, text: _("Import"), anchor: "100%", handler: this.importCheckedEvents, - scope: this, - allowBlank: false + scope: this } }, @@ -375,31 +445,13 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { return { xtype: "button", ref: "submitAllButton", - id: "submitAllButton", disabled: true, width: 100, border: false, text: _("Import All"), anchor: "100%", handler: this.importAllEvents, - scope: this, - allowBlank: false - } - }, - - createExportAllButton: function() { - return { - xtype: "button", - ref: "exportAllButton", - id: "exportAllButton", - hidden: !container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/enable_export"), - width: 100, - border: false, - text: _("Export All"), - anchor: "100%", - handler: this.exportAllEvents, - scope: this, - allowBlank: false + scope: this } }, @@ -411,8 +463,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { text: _("Cancel"), anchor: "100%", handler: this.close, - scope: this, - allowBlank: false + scope: this } }, @@ -432,7 +483,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { /** * This is called when the dst checkbox has been selected - * @param {Ext.form.CheckBox} combo + * @param {Ext.form.CheckBox} checkbox * @param {boolean} checked */ onDstChecked : function(checkbox, checked) { diff --git a/js/plugin.calendarimporter.js b/js/plugin.calendarimporter.js index 34fe6ef..e4a3e98 100644 --- a/js/plugin.calendarimporter.js +++ b/js/plugin.calendarimporter.js @@ -2,7 +2,7 @@ * plugin.calendarimporter.js zarafa calender to ics im/exporter * * Author: Christoph Haas - * Copyright (C) 2012-2013 Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/js/ui/ContextMenu.js b/js/ui/ContextMenu.js new file mode 100644 index 0000000..d1c0f53 --- /dev/null +++ b/js/ui/ContextMenu.js @@ -0,0 +1,158 @@ +/** + * ContectMenu.js zarafa calender to ics im/exporter + * + * Author: Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +Ext.namespace('Zarafa.plugins.calendarimporter.ui'); + +/** + * @class Zarafa.plugins.calendarimporter.ui.ContextMenu + * @extends Zarafa.hierarchy.ui.ContextMenu + * @xtype calendarimporter.hierarchycontextmenu + */ +Zarafa.plugins.calendarimporter.ui.ContextMenu = Ext.extend(Zarafa.hierarchy.ui.ContextMenu, { + + /** + * @constructor + * @param {Object} config Configuration object + */ + constructor: function (config) { + config = config || {}; + + if (config.contextNode) { + config.contextTree = config.contextNode.getOwnerTree(); + } + + Zarafa.plugins.calendarimporter.ui.ContextMenu.superclass.constructor.call(this, config); + + // add item to menu + var additionalItems = this.createAdditionalContextMenuItems(config); + for (var i = 0; i < additionalItems.length; i++) { + config.items[0].push(additionalItems[i]); + } + + Zarafa.plugins.calendarimporter.ui.ContextMenu.superclass.constructor.call(this, config); // redo ... otherwise menu does not get published + }, + + /** + * Create the Action context menu items. + * @param {Object} config Configuration object for the {@link Zarafa.plugins.calendarimporter.ui.ContextMenu ContextMenu} + * @return {Zarafa.core.ui.menu.ConditionalItem[]} The list of Action context menu items + * @private + * + * Note: All handlers are called within the scope of {@link Zarafa.plugins.calendarimporter.ui.ContextMenu HierarchyContextMenu} + */ + createAdditionalContextMenuItems: function (config) { + return [{ + xtype: 'menuseparator' + }, { + text : _('Import Calendar'), + iconCls : 'icon_calendarimporter_import', + handler : this.onContextItemImport, + beforeShow: function (item, record) { + var access = record.get('access') & Zarafa.core.mapi.Access.ACCESS_MODIFY; + if (!access || (record.isIPMSubTree() && !record.getMAPIStore().isDefaultStore())) { + item.setDisabled(true); + } else { + item.setDisabled(false); + } + } + }, { + text : _('Export Calendar'), + iconCls : 'icon_calendarimporter_export', + handler : this.onContextItemExport, + beforeShow: function (item, record) { + var access = record.get('access') & Zarafa.core.mapi.Access.ACCESS_READ; + if (!access || (record.isIPMSubTree() && !record.getMAPIStore().isDefaultStore())) { + item.setDisabled(true); + } else { + item.setDisabled(false); + } + } + }]; + }, + + /** + * Fires on selecting 'Open' menu option from {@link Zarafa.plugins.calendarimporter.ui.ContextMenu ContextMenu} + * @private + */ + onContextItemExport: function () { + var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({ + successCallback: this.downloadICS, + scope : this + }); + + // request attachment preperation + container.getRequest().singleRequest( + 'calendarmodule', + 'export', + { + storeid: this.records.get("store_entryid"), + folder : this.records.get("entryid") + }, + responseHandler + ); + }, + + /** + * Fires on selecting 'Open' menu option from {@link Zarafa.plugins.calendarimporter.ui.ContextMenu ContextMenu} + * @private + */ + onContextItemImport: function () { + var componentType = Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importcontacts']; + var config = { + modal : true, + folder: this.records.get("entryid") + }; + + Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config); + }, + + /** + * Callback for the export request. + * @param {Object} response + */ + downloadICS: function (response) { + if (response.status == false) { + Zarafa.common.dialogs.MessageBox.show({ + title : dgettext('plugin_files', 'Warning'), + msg : dgettext('plugin_files', response.message), + icon : Zarafa.common.dialogs.MessageBox.WARNING, + buttons: Zarafa.common.dialogs.MessageBox.OK + }); + } else { + var downloadFrame = Ext.getBody().createChild({ + tag: 'iframe', + cls: 'x-hidden' + }); + + var url = document.URL; + var link = url.substring(0, url.lastIndexOf('/') + 1); + + link += "index.php?sessionid=" + container.getUser().getSessionId() + "&load=custom&name=download_ics"; + link = Ext.urlAppend(link, "token=" + encodeURIComponent(response.download_token)); + link = Ext.urlAppend(link, "filename=" + encodeURIComponent(response.filename)); + + downloadFrame.dom.contentWindow.location = link; + } + } +}); + +Ext.reg('calendarimporter.hierarchycontextmenu', Zarafa.plugins.calendarimporter.ui.ContextMenu); \ No newline at end of file diff --git a/manifest.xml b/manifest.xml index d6303bc..6ced6d0 100644 --- a/manifest.xml +++ b/manifest.xml @@ -2,7 +2,7 @@ - @_@PLUGIN_VERSION@_@ + 2.2.0 calendarimporter ICS Calendar Importer/Exporter Christoph Haas diff --git a/php/ical/class.icalcreator.php b/php/ical/class.icalcreator.php index 4c457fd..80cd8f3 100644 --- a/php/ical/class.icalcreator.php +++ b/php/ical/class.icalcreator.php @@ -1,10178 +1,16 @@ - * @since 2.9.6 - 2011-05-14 - */ -class vcalendar { - // calendar property variables - var $calscale; - var $method; - var $prodid; - var $version; - var $xprop; - // container for calendar components - var $components; - // component config variables - var $allowEmpty; - var $unique_id; - var $language; - var $directory; - var $filename; - var $url; - var $delimiter; - var $nl; - var $format; - var $dtzid; - // component internal variables - var $attributeDelimiter; - var $valueInit; - // component xCal declaration container - var $xcaldecl; -/** - * constructor for calendar object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.6 - 2011-05-14 - * @param array $config - * @return void - */ - function vcalendar ( $config = array()) { - $this->_makeVersion(); - $this->calscale = null; - $this->method = null; - $this->_makeUnique_id(); - $this->prodid = null; - $this->xprop = array(); - $this->language = null; - $this->directory = null; - $this->filename = null; - $this->url = null; - $this->dtzid = null; -/** - * language = - */ - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - - $this->xcaldecl = array(); - $this->components = array(); - } -/*********************************************************************************/ -/** - * Property Name: CALSCALE - */ -/** - * creates formatted output for calendar property calscale - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.16 - 2011-10-28 - * @return string - */ - function createCalscale() { - if( empty( $this->calscale )) return FALSE; - switch( $this->format ) { - case 'xcal': - return $this->nl.' calscale="'.$this->calscale.'"'; - break; - default: - return 'CALSCALE:'.$this->calscale.$this->nl; - break; - } - } -/** - * set calendar property calscale - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @param string $value - * @return void - */ - function setCalscale( $value ) { - if( empty( $value )) return FALSE; - $this->calscale = $value; - } -/*********************************************************************************/ -/** - * Property Name: METHOD - */ -/** - * creates formatted output for calendar property method - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.16 - 2011-10-28 - * @return string - */ - function createMethod() { - if( empty( $this->method )) return FALSE; - switch( $this->format ) { - case 'xcal': - return $this->nl.' method="'.$this->method.'"'; - break; - default: - return 'METHOD:'.$this->method.$this->nl; - break; - } - } -/** - * set calendar property method - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-20-23 - * @param string $value - * @return bool - */ - function setMethod( $value ) { - if( empty( $value )) return FALSE; - $this->method = $value; - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: PRODID - * - * The identifier is RECOMMENDED to be the identical syntax to the - * [RFC 822] addr-spec. A good method to assure uniqueness is to put the - * domain name or a domain literal IP address of the host on which.. . - */ -/** - * creates formatted output for calendar property prodid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.12.11 - 2012-05-13 - * @return string - */ - function createProdid() { - if( !isset( $this->prodid )) - $this->_makeProdid(); - switch( $this->format ) { - case 'xcal': - return $this->nl.' prodid="'.$this->prodid.'"'; - break; - default: - $toolbox = new calendarComponent(); - $toolbox->setConfig( $this->getConfig()); - return $toolbox->_createElement( 'PRODID', '', $this->prodid ); - break; - } - } -/** - * make default value for calendar prodid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.8 - 2009-12-30 - * @return void - */ - function _makeProdid() { - $this->prodid = '-//'.$this->unique_id.'//'.ICALCREATOR_VERSION.'//'.strtoupper( $this->language ); - } -/** - * Conformance: The property MUST be specified once in an iCalendar object. - * Description: The vendor of the implementation SHOULD assure that this - * is a globally unique identifier; using some technique such as an FPI - * value, as defined in [ISO 9070]. - */ -/** - * make default unique_id for calendar prodid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 0.3.0 - 2006-08-10 - * @return void - */ - function _makeUnique_id() { - $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost'; - } -/*********************************************************************************/ -/** - * Property Name: VERSION - * - * Description: A value of "2.0" corresponds to this memo. - */ -/** - * creates formatted output for calendar property version - - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.16 - 2011-10-28 - * @return string - */ - function createVersion() { - if( empty( $this->version )) - $this->_makeVersion(); - switch( $this->format ) { - case 'xcal': - return $this->nl.' version="'.$this->version.'"'; - break; - default: - return 'VERSION:'.$this->version.$this->nl; - break; - } - } -/** - * set default calendar version - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 0.3.0 - 2006-08-10 - * @return void - */ - function _makeVersion() { - $this->version = '2.0'; - } -/** - * set calendar version - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-23 - * @param string $value - * @return void - */ - function setVersion( $value ) { - if( empty( $value )) return FALSE; - $this->version = $value; - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: x-prop - */ -/** - * creates formatted output for calendar property x-prop, iCal format only - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createXprop() { - if( empty( $this->xprop ) || !is_array( $this->xprop )) return FALSE; - $output = null; - $toolbox = new calendarComponent(); - $toolbox->setConfig( $this->getConfig()); - foreach( $this->xprop as $label => $xpropPart ) { - if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) { - $output .= $toolbox->_createElement( $label ); - continue; - } - $attributes = $toolbox->_createParams( $xpropPart['params'], array( 'LANGUAGE' )); - if( is_array( $xpropPart['value'] )) { - foreach( $xpropPart['value'] as $pix => $theXpart ) - $xpropPart['value'][$pix] = iCalUtilityFunctions::_strrep( $theXpart, $this->format, $this->nl ); - $xpropPart['value'] = implode( ',', $xpropPart['value'] ); - } - else - $xpropPart['value'] = iCalUtilityFunctions::_strrep( $xpropPart['value'], $this->format, $this->nl ); - $output .= $toolbox->_createElement( $label, $attributes, $xpropPart['value'] ); - if( is_array( $toolbox->xcaldecl ) && ( 0 < count( $toolbox->xcaldecl ))) { - foreach( $toolbox->xcaldecl as $localxcaldecl ) - $this->xcaldecl[] = $localxcaldecl; - } - } - return $output; - } -/** - * set calendar property x-prop - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.9 - 2012-01-16 - * @param string $label - * @param string $value - * @param array $params optional - * @return bool - */ - function setXprop( $label, $value, $params=FALSE ) { - if( empty( $label )) - return FALSE; - if( 'X-' != strtoupper( substr( $label, 0, 2 ))) - return FALSE; - if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $xprop = array( 'value' => $value ); - $xprop['params'] = iCalUtilityFunctions::_setParams( $params ); - if( !is_array( $this->xprop )) $this->xprop = array(); - $this->xprop[strtoupper( $label )] = $xprop; - return TRUE; - } -/*********************************************************************************/ -/** - * delete calendar property value - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $propName, bool FALSE => X-property - * @param int $propix, optional, if specific property is wanted in case of multiply occurences - * @return bool, if successfull delete - */ - function deleteProperty( $propName=FALSE, $propix=FALSE ) { - $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; - if( !$propix ) - $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1; - $this->propdelix[$propName] = --$propix; - $return = FALSE; - switch( $propName ) { - case 'CALSCALE': - if( isset( $this->calscale )) { - $this->calscale = null; - $return = TRUE; - } - break; - case 'METHOD': - if( isset( $this->method )) { - $this->method = null; - $return = TRUE; - } - break; - default: - $reduced = array(); - if( $propName != 'X-PROP' ) { - if( !isset( $this->xprop[$propName] )) { unset( $this->propdelix[$propName] ); return FALSE; } - foreach( $this->xprop as $k => $a ) { - if(( $k != $propName ) && !empty( $a )) - $reduced[$k] = $a; - } - } - else { - if( count( $this->xprop ) <= $propix ) return FALSE; - $xpropno = 0; - foreach( $this->xprop as $xpropkey => $xpropvalue ) { - if( $propix != $xpropno ) - $reduced[$xpropkey] = $xpropvalue; - $xpropno++; - } - } - $this->xprop = $reduced; - if( empty( $this->xprop )) { - unset( $this->propdelix[$propName] ); - return FALSE; - } - return TRUE; - } - return $return; - } -/** - * get calendar property value/params - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.13.4 - 2012-08-08 - * @param string $propName, optional - * @param int $propix, optional, if specific property is wanted in case of multiply occurences - * @param bool $inclParam=FALSE - * @return mixed - */ - function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) { - $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; - if( 'X-PROP' == $propName ) { - if( !$propix ) - $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1; - $this->propix[$propName] = --$propix; - } - else - $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' ); - switch( $propName ) { - case 'ATTENDEE': - case 'CATEGORIES': - case 'CONTACT': - case 'DTSTART': - case 'GEOLOCATION': - case 'LOCATION': - case 'ORGANIZER': - case 'PRIORITY': - case 'RESOURCES': - case 'STATUS': - case 'SUMMARY': - case 'RECURRENCE-ID-UID': - case 'RELATED-TO': - case 'R-UID': - case 'UID': - case 'URL': - $output = array(); - foreach ( $this->components as $cix => $component) { - if( !in_array( $component->objName, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' ))) - continue; - if( in_array( strtoupper( $propName ), $mProps )) { - $component->_getProperties( $propName, $output ); - continue; - } - elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) { - if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' ))) - $content = $component->getProperty( 'UID' ); - } - elseif( 'GEOLOCATION' == $propName ) { - $content = $component->getProperty( 'LOCATION' ); - $content = ( !empty( $content )) ? $content.' ' : ''; - if(( FALSE === ( $geo = $component->getProperty( 'GEO' ))) || empty( $geo )) - continue; - if( 0.0 < $geo['latitude'] ) - $sign = '+'; - else - $sign = ( 0.0 > $geo['latitude'] ) ? '-' : ''; - $content .= ' '.$sign.sprintf( "%09.6f", abs( $geo['latitude'] )); - $content = rtrim( rtrim( $content, '0' ), '.' ); - if( 0.0 < $geo['longitude'] ) - $sign = '+'; - else - $sign = ( 0.0 > $geo['longitude'] ) ? '-' : ''; - $content .= $sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/'; - } - elseif( FALSE === ( $content = $component->getProperty( $propName ))) - continue; - if(( FALSE === $content ) || empty( $content )) - continue; - elseif( is_array( $content )) { - if( isset( $content['year'] )) { - $key = sprintf( '%04d%02d%02d', $content['year'], $content['month'], $content['day'] ); - if( !isset( $output[$key] )) - $output[$key] = 1; - else - $output[$key] += 1; - } - else { - foreach( $content as $partValue => $partCount ) { - if( !isset( $output[$partValue] )) - $output[$partValue] = $partCount; - else - $output[$partValue] += $partCount; - } - } - } // end elseif( is_array( $content )) { - elseif( !isset( $output[$content] )) - $output[$content] = 1; - else - $output[$content] += 1; - } // end foreach ( $this->components as $cix => $component) - if( !empty( $output )) - ksort( $output ); - return $output; - break; - case 'CALSCALE': - return ( !empty( $this->calscale )) ? $this->calscale : FALSE; - break; - case 'METHOD': - return ( !empty( $this->method )) ? $this->method : FALSE; - break; - case 'PRODID': - if( empty( $this->prodid )) - $this->_makeProdid(); - return $this->prodid; - break; - case 'VERSION': - return ( !empty( $this->version )) ? $this->version : FALSE; - break; - default: - if( $propName != 'X-PROP' ) { - if( !isset( $this->xprop[$propName] )) return FALSE; - return ( $inclParam ) ? array( $propName, $this->xprop[$propName] ) - : array( $propName, $this->xprop[$propName]['value'] ); - } - else { - if( empty( $this->xprop )) return FALSE; - $xpropno = 0; - foreach( $this->xprop as $xpropkey => $xpropvalue ) { - if( $propix == $xpropno ) - return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] ) - : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); - else - $xpropno++; - } - unset( $this->propix[$propName] ); - return FALSE; // not found ?? - } - } - return FALSE; - } -/** - * general vcalendar property setting - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.2.13 - 2007-11-04 - * @param mixed $args variable number of function arguments, - * first argument is ALWAYS component name, - * second ALWAYS component value! - * @return bool - */ - function setProperty () { - $numargs = func_num_args(); - if( 1 > $numargs ) - return FALSE; - $arglist = func_get_args(); - $arglist[0] = strtoupper( $arglist[0] ); - switch( $arglist[0] ) { - case 'CALSCALE': - return $this->setCalscale( $arglist[1] ); - case 'METHOD': - return $this->setMethod( $arglist[1] ); - case 'VERSION': - return $this->setVersion( $arglist[1] ); - default: - if( !isset( $arglist[1] )) $arglist[1] = null; - if( !isset( $arglist[2] )) $arglist[2] = null; - return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] ); - } - return FALSE; - } -/*********************************************************************************/ -/** - * get vcalendar config values or * calendar components - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.7 - 2012-01-12 - * @param mixed $config - * @return value - */ - function getConfig( $config = FALSE ) { - if( !$config ) { - $return = array(); - $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' ); - $return['DELIMITER'] = $this->getConfig( 'DELIMITER' ); - $return['DIRECTORY'] = $this->getConfig( 'DIRECTORY' ); - $return['FILENAME'] = $this->getConfig( 'FILENAME' ); - $return['DIRFILE'] = $this->getConfig( 'DIRFILE' ); - $return['FILESIZE'] = $this->getConfig( 'FILESIZE' ); - $return['FORMAT'] = $this->getConfig( 'FORMAT' ); - if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' ))) - $return['LANGUAGE'] = $lang; - $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' ); - $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' ); - if( FALSE !== ( $url = $this->getConfig( 'URL' ))) - $return['URL'] = $url; - $return['TZID'] = $this->getConfig( 'TZID' ); - return $return; - } - switch( strtoupper( $config )) { - case 'ALLOWEMPTY': - return $this->allowEmpty; - break; - case 'COMPSINFO': - unset( $this->compix ); - $info = array(); - foreach( $this->components as $cix => $component ) { - if( empty( $component )) continue; - $info[$cix]['ordno'] = $cix + 1; - $info[$cix]['type'] = $component->objName; - $info[$cix]['uid'] = $component->getProperty( 'uid' ); - $info[$cix]['props'] = $component->getConfig( 'propinfo' ); - $info[$cix]['sub'] = $component->getConfig( 'compsinfo' ); - } - return $info; - break; - case 'DELIMITER': - return $this->delimiter; - break; - case 'DIRECTORY': - if( empty( $this->directory ) && ( '0' != $this->directory )) - $this->directory = '.'; - return $this->directory; - break; - case 'DIRFILE': - return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' ); - break; - case 'FILEINFO': - return array( $this->getConfig( 'directory' ) - , $this->getConfig( 'filename' ) - , $this->getConfig( 'filesize' )); - break; - case 'FILENAME': - if( empty( $this->filename ) && ( '0' != $this->filename )) { - if( 'xcal' == $this->format ) - $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. . - else - $this->filename = date( 'YmdHis' ).'.ics'; - } - return $this->filename; - break; - case 'FILESIZE': - $size = 0; - if( empty( $this->url )) { - $dirfile = $this->getConfig( 'dirfile' ); - if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile )))) - $size = 0; - clearstatcache(); - } - return $size; - break; - case 'FORMAT': - return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal'; - break; - case 'LANGUAGE': - /* get language for calendar component as defined in [RFC 1766] */ - return $this->language; - break; - case 'NL': - case 'NEWLINECHAR': - return $this->nl; - break; - case 'TZID': - return $this->dtzid; - break; - case 'UNIQUE_ID': - return $this->unique_id; - break; - case 'URL': - if( !empty( $this->url )) - return $this->url; - else - return FALSE; - break; - } - } -/** - * general vcalendar config setting - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.7 - 2013-01-11 - * @param mixed $config - * @param string $value - * @return void - */ - function setConfig( $config, $value = FALSE) { - if( is_array( $config )) { - $ak = array_keys( $config ); - foreach( $ak as $k ) { - if( 'DIRECTORY' == strtoupper( $k )) { - if( FALSE === $this->setConfig( 'DIRECTORY', $config[$k] )) - return FALSE; - unset( $config[$k] ); - } - elseif( 'NEWLINECHAR' == strtoupper( $k )) { - if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] )) - return FALSE; - unset( $config[$k] ); - } - } - foreach( $config as $cKey => $cValue ) { - if( FALSE === $this->setConfig( $cKey, $cValue )) - return FALSE; - } - return TRUE; - } - $res = FALSE; - switch( strtoupper( $config )) { - case 'ALLOWEMPTY': - $this->allowEmpty = $value; - $subcfg = array( 'ALLOWEMPTY' => $value ); - $res = TRUE; - break; - case 'DELIMITER': - $this->delimiter = $value; - return TRUE; - break; - case 'DIRECTORY': - $value = trim( $value ); - $del = $this->getConfig('delimiter'); - if( $del == substr( $value, ( 0 - strlen( $del )))) - $value = substr( $value, 0, ( strlen( $value ) - strlen( $del ))); - if( is_dir( $value )) { - /* local directory */ - clearstatcache(); - $this->directory = $value; - $this->url = null; - return TRUE; - } - else - return FALSE; - break; - case 'FILENAME': - $value = trim( $value ); - if( !empty( $this->url )) { - /* remote directory+file -> URL */ - $this->filename = $value; - return TRUE; - } - $dirfile = $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$value; - if( file_exists( $dirfile )) { - /* local file exists */ - if( is_readable( $dirfile ) || is_writable( $dirfile )) { - clearstatcache(); - $this->filename = $value; - return TRUE; - } - else - return FALSE; - } - elseif( is_readable($this->getConfig( 'directory' ) ) || is_writable( $this->getConfig( 'directory' ) )) { - /* read- or writable directory */ - $this->filename = $value; - return TRUE; - } - else - return FALSE; - break; - case 'FORMAT': - $value = trim( strtolower( $value )); - if( 'xcal' == $value ) { - $this->format = 'xcal'; - $this->attributeDelimiter = $this->nl; - $this->valueInit = null; - } - else { - $this->format = null; - $this->attributeDelimiter = ';'; - $this->valueInit = ':'; - } - $subcfg = array( 'FORMAT' => $value ); - $res = TRUE; - break; - case 'LANGUAGE': // set language for calendar component as defined in [RFC 1766] - $value = trim( $value ); - $this->language = $value; - $this->_makeProdid(); - $subcfg = array( 'LANGUAGE' => $value ); - $res = TRUE; - break; - case 'NL': - case 'NEWLINECHAR': - $this->nl = $value; - if( 'xcal' == $value ) { - $this->attributeDelimiter = $this->nl; - $this->valueInit = null; - } - else { - $this->attributeDelimiter = ';'; - $this->valueInit = ':'; - } - $subcfg = array( 'NL' => $value ); - $res = TRUE; - break; - case 'TZID': - $this->dtzid = $value; - $subcfg = array( 'TZID' => $value ); - $res = TRUE; - break; - case 'UNIQUE_ID': - $value = trim( $value ); - $this->unique_id = $value; - $this->_makeProdid(); - $subcfg = array( 'UNIQUE_ID' => $value ); - $res = TRUE; - break; - case 'URL': - /* remote file - URL */ - $value = str_replace( array( 'HTTP://', 'WEBCAL://', 'webcal://' ), 'http://', trim( $value )); - if( 'http://' != substr( $value, 0, 7 )) - return FALSE; - $s1 = $this->url; - $this->url = $value; - $s2 = $this->directory; - $this->directory = null; - $parts = pathinfo( $value ); - if( FALSE === $this->setConfig( 'filename', $parts['basename'] )) { - $this->url = $s1; - $this->directory = $s2; - return FALSE; - } - else - return TRUE; - break; - default: // any unvalid config key.. . - return TRUE; - } - if( !$res ) return FALSE; - if( isset( $subcfg ) && !empty( $this->components )) { - foreach( $subcfg as $cfgkey => $cfgvalue ) { - foreach( $this->components as $cix => $component ) { - $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE ); - if( !$res ) - break 2; - $this->components[$cix] = $component->copy(); // PHP4 compliant - } - } - } - return $res; - } -/*********************************************************************************/ -/** - * add calendar component to container - * - * alias to setComponent - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 1.x.x - 2007-04-24 - * @param object $component calendar component - * @return void - */ - function addComponent( $component ) { - $this->setComponent( $component ); - } -/** - * delete calendar component from container - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $arg1 ordno / component type / component uid - * @param mixed $arg2 optional, ordno if arg1 = component type - * @return void - */ - function deleteComponent( $arg1, $arg2=FALSE ) { - $argType = $index = null; - if ( ctype_digit( (string) $arg1 )) { - $argType = 'INDEX'; - $index = (int) $arg1 - 1; - } - elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { - $argType = strtolower( $arg1 ); - $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0; - } - $cix1dC = 0; - foreach ( $this->components as $cix => $component) { - if( empty( $component )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) { - unset( $this->components[$cix] ); - return TRUE; - } - elseif( $argType == $component->objName ) { - if( $index == $cix1dC ) { - unset( $this->components[$cix] ); - return TRUE; - } - $cix1dC++; - } - elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { - unset( $this->components[$cix] ); - return TRUE; - } - } - return FALSE; - } -/** - * get calendar component from container - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.13.5 - 2012-08-08 - * @param mixed $arg1 optional, ordno/component type/ component uid - * @param mixed $arg2 optional, ordno if arg1 = component type - * @return object - */ - function getComponent( $arg1=FALSE, $arg2=FALSE ) { - $index = $argType = null; - if ( !$arg1 ) { // first or next in component chain - $argType = 'INDEX'; - $index = $this->compix['INDEX'] = ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1; - } - elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain - $argType = 'INDEX'; - $index = (int) $arg1; - unset( $this->compix ); - } - elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) - $arg2 = implode( '-', array_keys( $arg1 )); - $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1; - $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' ); - $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' ); - $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' ); - } - elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name - unset( $this->compix['INDEX'] ); - $argType = strtolower( $arg1 ); - if( !$arg2 ) - $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1; - elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 )) - $index = (int) $arg2; - } - elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument - if( !$arg2 ) - $index = $this->compix[$arg1] = ( isset( $this->compix[$arg1] )) ? $this->compix[$arg1] + 1 : 1; - elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 )) - $index = (int) $arg2; - } - if( isset( $index )) - $index -= 1; - $ckeys = array_keys( $this->components ); - if( !empty( $index) && ( $index > end( $ckeys ))) - return FALSE; - $cix1gC = 0; - foreach ( $this->components as $cix => $component) { - if( empty( $component )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) - return $component->copy(); - elseif( $argType == $component->objName ) { - if( $index == $cix1gC ) - return $component->copy(); - $cix1gC++; - } - elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) - $hit = array(); - foreach( $arg1 as $pName => $pValue ) { - $pName = strtoupper( $pName ); - if( !in_array( $pName, $dateProps ) && !in_array( $pName, $otherProps )) - continue; - if( in_array( $pName, $mProps )) { // multiple occurrence - $propValues = array(); - $component->_getProperties( $pName, $propValues ); - $propValues = array_keys( $propValues ); - $hit[] = ( in_array( $pValue, $propValues )) ? TRUE : FALSE; - continue; - } // end if(.. .// multiple occurrence - if( FALSE === ( $value = $component->getProperty( $pName ))) { // single occurrence - $hit[] = FALSE; // missing property - continue; - } - if( 'SUMMARY' == $pName ) { // exists within (any case) - $hit[] = ( FALSE !== stripos( $value, $pValue )) ? TRUE : FALSE; - continue; - } - if( in_array( strtoupper( $pName ), $dateProps )) { - $valuedate = sprintf( '%04d%02d%02d', $value['year'], $value['month'], $value['day'] ); - if( 8 < strlen( $pValue )) { - if( isset( $value['hour'] )) { - if( 'T' == substr( $pValue, 8, 1 )) - $pValue = str_replace( 'T', '', $pValue ); - $valuedate .= sprintf( '%02d%02d%02d', $value['hour'], $value['min'], $value['sec'] ); - } - else - $pValue = substr( $pValue, 0, 8 ); - } - $hit[] = ( $pValue == $valuedate ) ? TRUE : FALSE; - continue; - } - elseif( !is_array( $value )) - $value = array( $value ); - foreach( $value as $part ) { - $part = ( FALSE !== strpos( $part, ',' )) ? explode( ',', $part ) : array( $part ); - foreach( $part as $subPart ) { - if( $pValue == $subPart ) { - $hit[] = TRUE; - continue 3; - } - } - } // end foreach( $value as $part ) - $hit[] = FALSE; // no hit in property - } // end foreach( $arg1 as $pName => $pValue ) - if( in_array( TRUE, $hit )) { - if( $index == $cix1gC ) - return $component->copy(); - $cix1gC++; - } - } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) - elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { // UID - if( $index == $cix1gC ) - return $component->copy(); - $cix1gC++; - } - } // end foreach ( $this->components.. . - /* not found.. . */ - unset( $this->compix ); - return FALSE; - } -/** - * create new calendar component, already included within calendar - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.33 - 2011-01-03 - * @param string $compType component type - * @return object (reference) - */ - function & newComponent( $compType ) { - $config = $this->getConfig(); - $keys = array_keys( $this->components ); - $ix = end( $keys) + 1; - switch( strtoupper( $compType )) { - case 'EVENT': - case 'VEVENT': - $this->components[$ix] = new vevent( $config ); - break; - case 'TODO': - case 'VTODO': - $this->components[$ix] = new vtodo( $config ); - break; - case 'JOURNAL': - case 'VJOURNAL': - $this->components[$ix] = new vjournal( $config ); - break; - case 'FREEBUSY': - case 'VFREEBUSY': - $this->components[$ix] = new vfreebusy( $config ); - break; - case 'TIMEZONE': - case 'VTIMEZONE': - array_unshift( $this->components, new vtimezone( $config )); - $ix = 0; - break; - default: - return FALSE; - } - return $this->components[$ix]; - } -/** - * select components from calendar on date or selectOption basis - * - * Ensure DTSTART is set for every component. - * No date controls occurs. - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.12 - 2013-02-10 - * @param mixed $startY optional, start Year, default current Year ALT. array selecOptions ( *[ => ] ) - * @param int $startM optional, start Month, default current Month - * @param int $startD optional, start Day, default current Day - * @param int $endY optional, end Year, default $startY - * @param int $endY optional, end Month, default $startM - * @param int $endY optional, end Day, default $startD - * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s) - * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][] - * TRUE => output : array[] (ignores split) - * @param bool $any optional, TRUE (default) - select component(-s) that occurs within period - * FALSE - only component(-s) that starts within period - * @param bool $split optional, TRUE (default) - one component copy every DAY it occurs during the - * period (implies flat=FALSE) - * FALSE - one occurance of component only in output array - * @return array or FALSE - */ - function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) { - /* check if empty calendar */ - if( 0 >= count( $this->components )) return FALSE; - if( is_array( $startY )) - return $this->selectComponents2( $startY ); - /* check default dates */ - if( !$startY ) $startY = date( 'Y' ); - if( !$startM ) $startM = date( 'm' ); - if( !$startD ) $startD = date( 'd' ); - $startDate = mktime( 0, 0, 0, $startM, $startD, $startY ); - if( !$endY ) $endY = $startY; - if( !$endM ) $endM = $startM; - if( !$endD ) $endD = $startD; - $endDate = mktime( 23, 59, 59, $endM, $endD, $endY ); -// echo 'selectComp arg='.date( 'Y-m-d H:i:s', $startDate).' -- '.date( 'Y-m-d H:i:s', $endDate)."
\n"; $tcnt = 0;// test ### - /* check component types */ - $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' ); - if( is_array( $cType )) { - foreach( $cType as $cix => $theType ) { - $cType[$cix] = $theType = strtolower( $theType ); - if( !in_array( $theType, $validTypes )) - $cType[$cix] = 'vevent'; - } - $cType = array_unique( $cType ); - } - elseif( !empty( $cType )) { - $cType = strtolower( $cType ); - if( !in_array( $cType, $validTypes )) - $cType = array( 'vevent' ); - else - $cType = array( $cType ); - } - else - $cType = $validTypes; - if( 0 >= count( $cType )) - $cType = $validTypes; - if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination - $split = FALSE; - if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination - $split = FALSE; - /* iterate components */ - $result = array(); - $this->sort( 'UID' ); - $compUIDcmp = null; - $recurridList = array(); - foreach ( $this->components as $cix => $component ) { - if( empty( $component )) continue; - unset( $start ); - /* deselect unvalid type components */ - if( !in_array( $component->objName, $cType )) - continue; - $start = $component->getProperty( 'dtstart' ); - /* select due when dtstart is missing */ - if( empty( $start ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due' )))) - continue; - if( empty( $start )) - continue; - $compUID = $component->getProperty( 'UID' ); - if( $compUIDcmp != $compUID ) { - $compUIDcmp = $compUID; - unset( $exdatelist, $recurridList ); - } - $dtendExist = $dueExist = $durationExist = $endAllDayEvent = $recurrid = FALSE; - unset( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $workstart, $workend, $endDateFormat ); // clean up - $startWdate = iCalUtilityFunctions::_date2timestamp( $start ); - $startDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d'; - /* get end date from dtend/due/duration properties */ - $end = $component->getProperty( 'dtend' ); - if( !empty( $end )) { - $dtendExist = TRUE; - $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d'; - } - if( empty( $end ) && ( $component->objName == 'vtodo' )) { - $end = $component->getProperty( 'due' ); - if( !empty( $end )) { - $dueExist = TRUE; - $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d'; - } - } - if( !empty( $end ) && !isset( $end['hour'] )) { - /* a DTEND without time part regards an event that ends the day before, - for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */ - $endAllDayEvent = TRUE; - $endWdate = mktime( 23, 59, 59, $end['month'], ($end['day'] - 1), $end['year'] ); - $end['year'] = date( 'Y', $endWdate ); - $end['month'] = date( 'm', $endWdate ); - $end['day'] = date( 'd', $endWdate ); - $end['hour'] = 23; - $end['min'] = $end['sec'] = 59; - } - if( empty( $end )) { - $end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format - if( !empty( $end )) - $durationExist = TRUE; - $endDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d'; -// if( !empty($end)) echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."
\n"; // test ### - } - if( empty( $end )) { // assume one day duration if missing end date - $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 ); - } -// if( isset($end)) echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."
\n"; // test ### - $endWdate = iCalUtilityFunctions::_date2timestamp( $end ); - if( $endWdate < $startWdate ) { // MUST be after start date!! - $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 ); - $endWdate = iCalUtilityFunctions::_date2timestamp( $end ); - } - $rdurWsecs = $endWdate - $startWdate; // compute event (component) duration in seconds - /* make a list of optional exclude dates for component occurence from exrule and exdate */ - $exdatelist = array(); - $workstart = iCalUtilityFunctions::_timestamp2date(( $startDate - $rdurWsecs ), 6); - $workend = iCalUtilityFunctions::_timestamp2date(( $endDate + $rdurWsecs ), 6); - while( FALSE !== ( $exrule = $component->getProperty( 'exrule' ))) // check exrule - iCalUtilityFunctions::_recur2date( $exdatelist, $exrule, $start, $workstart, $workend ); - while( FALSE !== ( $exdate = $component->getProperty( 'exdate' ))) { // check exdate - foreach( $exdate as $theExdate ) { - $exWdate = iCalUtilityFunctions::_date2timestamp( $theExdate ); - $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate )); // on a day-basis !!! - if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate )) - $exdatelist[$exWdate] = TRUE; - } // end - foreach( $exdate as $theExdate ) - } // end - check exdate - /* check recurrence-id (note, a missing sequence=0, don't test foer sequence), remove hit with reccurr-id date */ - if( FALSE !== ( $recurrid = $component->getProperty( 'recurrence-id' ))) { -// echo "adding ".$recurrid['year'].'-'.$recurrid['month'].'-'.$recurrid['day']." to recurridList
\n"; // test ### - $recurrid = iCalUtilityFunctions::_date2timestamp( $recurrid ); - $recurrid = mktime( 0, 0, 0, date( 'm', $recurrid ), date( 'd', $recurrid ), date( 'Y', $recurrid )); // on a day-basis !!! - $recurridList[$recurrid] = TRUE; // no recurring to start this day - } // end recurrence-id/sequence test - /* select only components with.. . */ - if(( !$any && ( $startWdate >= $startDate ) && ( $startWdate <= $endDate )) || // (dt)start within the period - ( $any && ( $startWdate < $endDate ) && ( $endWdate >= $startDate ))) { // occurs within the period - /* add the selected component (WITHIN valid dates) to output array */ - if( $flat ) { // any=true/false, ignores split - if( !$recurrid ) - $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id) - } - elseif( $split ) { // split the original component - if( $endWdate > $endDate ) - $endWdate = $endDate; // use period end date - $rstart = $startWdate; - if( $rstart < $startDate ) - $rstart = $startDate; // use period start date - $startYMD = $rstartYMD = date( 'Ymd', $rstart ); - $endYMD = date( 'Ymd', $endWdate ); - $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! -// echo "start org comp = $rstartYMD, endYMD=$endYMD
\n"; // test ### - if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist - while( $rstartYMD <= $endYMD ) { // iterate - if( isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist - $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day - $rstartYMD = date( 'Ymd', $rstart ); - continue; - } - if( $rstartYMD > $startYMD ) // date after dtstart - $datestring = date( $startDateFormat, $checkDate ); // mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ))); - else - $datestring = date( $startDateFormat, $rstart ); - if( isset( $start['tz'] )) - $datestring .= ' '.$start['tz']; -// echo "split org comp rstartYMD=$rstartYMD (datestring=$datestring)
\n"; // test ### - $component->setProperty( 'X-CURRENT-DTSTART', $datestring ); - if( $dtendExist || $dueExist || $durationExist ) { - if( $rstartYMD < $endYMD ) // not the last day - $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart )); - else - $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! - if( $endAllDayEvent && $dtendExist ) - $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day - $datestring = date( $endDateFormat, $tend ); - if( isset( $end['tz'] )) - $datestring .= ' '.$end['tz']; - $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE'; - $component->setProperty( $propName, $datestring ); - } // end if( $dtendExist || $dueExist || $durationExist ) - $wd = getdate( $rstart ); - $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output - $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day - $rstartYMD = date( 'Ymd', $rstart ); - $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! - } // end while( $rstart <= $endWdate ) - } - } // end elseif( $split ) - else use component date - elseif( $recurrid && !$flat && !$any && !$split ) - $continue = TRUE; - else { // !$flat && !$split, i.e. no flat array and DTSTART within period - $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!! - if( !$any || !isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist - $wd = getdate( $startWdate ); - $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output - } - } - } // end if(( $startWdate >= $startDate ) && ( $startWdate <= $endDate )) - /* if 'any' components, check components with reccurrence rules, removing all excluding dates */ - if( TRUE === $any ) { - /* make a list of optional repeating dates for component occurence, rrule, rdate */ - $recurlist = array(); - while( FALSE !== ( $rrule = $component->getProperty( 'rrule' ))) // check rrule - iCalUtilityFunctions::_recur2date( $recurlist, $rrule, $start, $workstart, $workend ); - foreach( $recurlist as $recurkey => $recurvalue ) // key=match date as timestamp - $recurlist[$recurkey] = $rdurWsecs; // add duration in seconds - while( FALSE !== ( $rdate = $component->getProperty( 'rdate' ))) { // check rdate - foreach( $rdate as $theRdate ) { - if( is_array( $theRdate ) && ( 2 == count( $theRdate )) && // all days within PERIOD - array_key_exists( '0', $theRdate ) && array_key_exists( '1', $theRdate )) { - $rstart = iCalUtilityFunctions::_date2timestamp( $theRdate[0] ); - if(( $rstart < ( $startDate - $rdurWsecs )) || ( $rstart > $endDate )) - continue; - if( isset( $theRdate[1]['year'] )) // date-date period - $rend = iCalUtilityFunctions::_date2timestamp( $theRdate[1] ); - else { // date-duration period - $rend = iCalUtilityFunctions::_duration2date( $theRdate[0], $theRdate[1] ); - $rend = iCalUtilityFunctions::_date2timestamp( $rend ); - } - while( $rstart < $rend ) { - $recurlist[$rstart] = $rdurWsecs; // set start date for recurrence instance + rdate duration in seconds - $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day - } - } // PERIOD end - else { // single date - $theRdate = iCalUtilityFunctions::_date2timestamp( $theRdate ); - if((( $startDate - $rdurWsecs ) <= $theRdate ) && ( $endDate >= $theRdate )) - $recurlist[$theRdate] = $rdurWsecs; // set start date for recurrence instance + event duration in seconds - } - } - } // end - check rdate - foreach( $recurlist as $recurkey => $durvalue ) { // remove all recurrence START dates found in the exdatelist - $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!! - if( isset( $exdatelist[$checkDate] )) // no recurring to start this day - unset( $recurlist[$recurkey] ); - } - if( 0 < count( $recurlist )) { - ksort( $recurlist ); - $xRecurrence = 1; - $component2 = $component->copy(); - $compUID = $component2->getProperty( 'UID' ); - foreach( $recurlist as $recurkey => $durvalue ) { -// echo "recurKey=".date( 'Y-m-d H:i:s', $recurkey ).' dur='.iCalUtilityFunctions::offsetSec2His( $durvalue )."
\n"; // test ###; - if((( $startDate - $rdurWsecs ) > $recurkey ) || ( $endDate < $recurkey )) // not within period - continue; - $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!! - if( isset( $recurridList[$checkDate] )) // no recurring to start this day - continue; - if( isset( $exdatelist[$checkDate] )) // check excluded dates - continue; - if( $startWdate >= $recurkey ) // exclude component start date - continue; - $rstart = $recurkey; - $rend = $recurkey + $durvalue; - /* add repeating components within valid dates to output array, only start date set */ - if( $flat ) { - if( !isset( $result[$compUID] )) // only one comp - $result[$compUID] = $component2->copy(); // copy to output - } - /* add repeating components within valid dates to output array, one each day */ - elseif( $split ) { - $xRecurrence += 1; - if( $rend > $endDate ) - $rend = $endDate; - $startYMD = $rstartYMD = date( 'Ymd', $rstart ); - $endYMD = date( 'Ymd', $rend ); -// echo "splitStart=".date( 'Y-m-d H:i:s', $rstart ).' end='.date( 'Y-m-d H:i:s', $rend )."
\n"; // test ###; - while( $rstart <= $rend ) { // iterate.. . - $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! - if( isset( $recurridList[$checkDate] )) // no recurring to start this day - break; - if( isset( $exdatelist[$checkDate] )) // exclude any recurrence START date, found in exdatelist - break; -// echo "checking date after startdate=".date( 'Y-m-d H:i:s', $rstart ).' mot '.date( 'Y-m-d H:i:s', $startDate )."
"; // test ###; - if( $rstart >= $startDate ) { // date after dtstart - if( $rstartYMD > $startYMD ) // date after dtstart - $datestring = date( $startDateFormat, $checkDate ); - else - $datestring = date( $startDateFormat, $rstart ); - if( isset( $start['tz'] )) - $datestring .= ' '.$start['tz']; -// echo "spliting = $datestring
\n"; // test ### - $component2->setProperty( 'X-CURRENT-DTSTART', $datestring ); - if( $dtendExist || $dueExist || $durationExist ) { - if( $rstartYMD < $endYMD ) // not the last day - $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart )); - else - $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! - if( $endAllDayEvent && $dtendExist ) - $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day - $datestring = date( $endDateFormat, $tend ); - if( isset( $end['tz'] )) - $datestring .= ' '.$end['tz']; - $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE'; - $component2->setProperty( $propName, $datestring ); - } // end if( $dtendExist || $dueExist || $durationExist ) - $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); - $wd = getdate( $rstart ); - $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output - } // end if( $checkDate > $startYMD ) { // date after dtstart - $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day - $rstartYMD = date( 'Ymd', $rstart ); - } // end while( $rstart <= $rend ) - } // end elseif( $split ) - elseif( $rstart >= $startDate ) { // date within period //* flat=FALSE && split=FALSE => one comp every recur startdate *// - $xRecurrence += 1; - $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!! - if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist - $datestring = date( $startDateFormat, $rstart ); - if( isset( $start['tz'] )) - $datestring .= ' '.$start['tz']; -//echo "X-CURRENT-DTSTART 2 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."
";$component2->setProperty( 'X-CNT', $tcnt ); // test ### - $component2->setProperty( 'X-CURRENT-DTSTART', $datestring ); - if( $dtendExist || $dueExist || $durationExist ) { - $tend = $rstart + $rdurWsecs; - if( date( 'Ymd', $tend ) < date( 'Ymd', $endWdate )) - $tend = mktime( 23, 59, 59, date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend )); - else - $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend ) ); // on a day-basis !!! - if( $endAllDayEvent && $dtendExist ) - $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day - $datestring = date( $endDateFormat, $tend ); - if( isset( $end['tz'] )) - $datestring .= ' '.$end['tz']; - $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE'; - $component2->setProperty( $propName, $datestring ); - } // end if( $dtendExist || $dueExist || $durationExist ) - $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); - $wd = getdate( $rstart ); - $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output - } // end if( !isset( $exdatelist[$checkDate] )) - } // end elseif( $rstart >= $startDate ) - } // end foreach( $recurlist as $recurkey => $durvalue ) - unset( $component2 ); - } // end if( 0 < count( $recurlist )) - /* deselect components with startdate/enddate not within period */ - if(( $endWdate < $startDate ) || ( $startWdate > $endDate )) - continue; - } // end if( TRUE === $any ) - } // end foreach ( $this->components as $cix => $component ) - unset( $dtendExist, $dueExist, $durationExist, $endAllDayEvent, $recurrid, $recurridList, - $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $recurlist, $workstart, $workend, $endDateFormat ); // clean up - if( 0 >= count( $result )) return FALSE; - elseif( !$flat ) { - foreach( $result as $y => $yeararr ) { - foreach( $yeararr as $m => $montharr ) { - foreach( $montharr as $d => $dayarr ) { - if( empty( $result[$y][$m][$d] )) - unset( $result[$y][$m][$d] ); - else - $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index, hoping they are in hour order.. . - } - if( empty( $result[$y][$m] )) - unset( $result[$y][$m] ); - else - ksort( $result[$y][$m] ); - } - if( empty( $result[$y] )) - unset( $result[$y] ); - else - ksort( $result[$y] ); - } - if( empty( $result )) - unset( $result ); - else - ksort( $result ); - } // end elseif( !$flat ) - if( 0 >= count( $result )) - return FALSE; - return $result; - } -/** - * select components from calendar on based on specific property value(-s) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.6 - 2012-12-26 - * @param array $selectOptions, (string) key => (mixed) value, (key=propertyName) - * @return array - */ - function selectComponents2( $selectOptions ) { - $output = array(); - $allowedComps = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' ); - $allowedProperties = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' ); - foreach( $this->components as $cix => $component3 ) { - if( !in_array( $component3->objName, $allowedComps )) - continue; - $uid = $component3->getProperty( 'UID' ); - foreach( $selectOptions as $propName => $pvalue ) { - $propName = strtoupper( $propName ); - if( !in_array( $propName, $allowedProperties )) - continue; - if( !is_array( $pvalue )) - $pvalue = array( $pvalue ); - if(( 'UID' == $propName ) && in_array( $uid, $pvalue )) { - $output[$uid][] = $component3->copy(); - continue; - } - elseif(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'CONTACT' == $propName ) || ( 'RELATED-TO' == $propName ) || ( 'RESOURCES' == $propName )) { // multiple occurrence? - $propValues = array(); - $component3->_getProperties( $propName, $propValues ); - $propValues = array_keys( $propValues ); - foreach( $pvalue as $theValue ) { - if( in_array( $theValue, $propValues )) { // && !isset( $output[$uid] )) { - $output[$uid][] = $component3->copy(); - break; - } - } - continue; - } // end elseif( // multiple occurrence? - elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single occurrence - continue; - if( is_array( $d )) { - foreach( $d as $part ) { - if( in_array( $part, $pvalue ) && !isset( $output[$uid] )) - $output[$uid][] = $component3->copy(); - } - } - elseif(( 'SUMMARY' == $propName ) && !isset( $output[$uid] )) { - foreach( $pvalue as $pval ) { - if( FALSE !== stripos( $d, $pval )) { - $output[$uid][] = $component3->copy(); - break; - } - } - } - elseif( in_array( $d, $pvalue ) && !isset( $output[$uid] )) - $output[$uid][] = $component3->copy(); - } // end foreach( $selectOptions as $propName => $pvalue ) { - } // end foreach( $this->components as $cix => $component3 ) { - if( !empty( $output )) { - ksort( $output ); // uid order - $output2 = array(); - foreach( $output as $uid => $components ) { - foreach( $components as $component ) - $output2[] = $component; - } - $output = $output2; - } - return $output; - } -/** - * add calendar component to container - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param object $component calendar component - * @param mixed $arg1 optional, ordno/component type/ component uid - * @param mixed $arg2 optional, ordno if arg1 = component type - * @return void - */ - function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) { - $component->setConfig( $this->getConfig(), FALSE, TRUE ); - if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) { - /* make sure dtstamp and uid is set */ - $dummy1 = $component->getProperty( 'dtstamp' ); - $dummy2 = $component->getProperty( 'uid' ); - } - if( !$arg1 ) { // plain insert, last in chain - $this->components[] = $component->copy(); - return TRUE; - } - $argType = $index = null; - if ( ctype_digit( (string) $arg1 )) { // index insert/replace - $argType = 'INDEX'; - $index = (int) $arg1 - 1; - } - elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) { - $argType = strtolower( $arg1 ); - $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0; - } - // else if arg1 is set, arg1 must be an UID - $cix1sC = 0; - foreach ( $this->components as $cix => $component2) { - if( empty( $component2 )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace - $this->components[$cix] = $component->copy(); - return TRUE; - } - elseif( $argType == $component2->objName ) { // component Type index insert/replace - if( $index == $cix1sC ) { - $this->components[$cix] = $component->copy(); - return TRUE; - } - $cix1sC++; - } - elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace - $this->components[$cix] = $component->copy(); - return TRUE; - } - } - /* arg1=index and not found.. . insert at index .. .*/ - if( 'INDEX' == $argType ) { - $this->components[$index] = $component->copy(); - ksort( $this->components, SORT_NUMERIC ); - } - else /* not found.. . insert last in chain anyway .. .*/ - $this->components[] = $component->copy(); - return TRUE; - } -/** - * sort iCal compoments - * - * ascending sort on properties (if exist) x-current-dtstart, dtstart, - * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid if called without arguments, - * otherwise sorting on specific (argument) property values - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.4 - 2012-12-17 - * @param string $sortArg, optional - * @return void - * - */ - function sort( $sortArg=FALSE ) { - if( is_array( $this->components )) { - if( $sortArg ) { - $sortArg = strtoupper( $sortArg ); - if( !in_array( $sortArg, array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'DTSTAMP', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' ))) - $sortArg = FALSE; - } - /* set sort parameters for each component */ - foreach( $this->components as $cix => & $c ) { - $c->srtk = array( '0', '0', '0', '0' ); - if( 'vtimezone' == $c->objName ) { - if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' ))) - $c->srtk[0] = 0; - continue; - } - elseif( $sortArg ) { - if(( 'ATTENDEE' == $sortArg ) || ( 'CATEGORIES' == $sortArg ) || ( 'CONTACT' == $sortArg ) || ( 'RELATED-TO' == $sortArg ) || ( 'RESOURCES' == $sortArg )) { - $propValues = array(); - $c->_getProperties( $sortArg, $propValues ); - if( !empty( $propValues )) { - $sk = array_keys( $propValues ); - $c->srtk[0] = $sk[0]; - if( 'RELATED-TO' == $sortArg ) - $c->srtk[0] .= $c->getProperty( 'uid' ); - } - elseif( 'RELATED-TO' == $sortArg ) - $c->srtk[0] = $c->getProperty( 'uid' ); - } - elseif( FALSE !== ( $d = $c->getProperty( $sortArg ))) { - $c->srtk[0] = $d; - if( 'UID' == $sortArg ) { - if( FALSE !== ( $d = $c->getProperty( 'recurrence-id' ))) { - $c->srtk[1] = iCalUtilityFunctions::_date2strdate( $d ); - if( FALSE === ( $c->srtk[2] = $c->getProperty( 'sequence' ))) - $c->srtk[2] = PHP_INT_MAX; - } - else - $c->srtk[1] = $c->srtk[2] = PHP_INT_MAX; - } - } - continue; - } - if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) { - $c->srtk[0] = iCalUtilityFunctions::_strdate2date( $d[1] ); - unset( $c->srtk[0]['unparsedtext'] ); - } - elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' ))) - $c->srtk[1] = 0; // sortkey 0 : dtstart - if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' ))) { - $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); // sortkey 1 : dtend/due(/dtstart+duration) - unset( $c->srtk[1]['unparsedtext'] ); - } - elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) { - if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' ))) { - $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); - unset( $c->srtk[1]['unparsedtext'] ); - } - elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' ))) - if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE ))) - $c->srtk[1] = 0; - } - if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' ))) // sortkey 2 : created/dtstamp - if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' ))) - $c->srtk[2] = 0; - if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' ))) // sortkey 3 : uid - $c->srtk[3] = 0; - } // end foreach( $this->components as & $c - /* sort */ - usort( $this->components, array( 'iCalUtilityFunctions', '_cmpfcn' )); - } - } -/** - * parse iCal text/file into vcalendar, components, properties and parameters - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings - * @return bool FALSE if error occurs during parsing - * - */ - function parse( $unparsedtext=FALSE ) { - $nl = $this->getConfig( 'nl' ); - if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) { - /* directory+filename is set previously via setConfig directory+filename or url */ - if( FALSE === ( $filename = $this->getConfig( 'url' ))) - $filename = $this->getConfig( 'dirfile' ); - /* READ FILE */ - if( FALSE === ( $rows = file_get_contents( $filename ))) - return FALSE; /* err 1 */ - } - elseif( is_array( $unparsedtext )) - $rows = implode( '\n'.$nl, $unparsedtext ); - else - $rows = & $unparsedtext; - /* fix line folding */ - $rows = explode( $nl, iCalUtilityFunctions::convEolChar( $rows, $nl )); - /* skip leading (empty/invalid) lines */ - foreach( $rows as $lix => $line ) { - if( FALSE !== stripos( $line, 'BEGIN:VCALENDAR' )) - break; - unset( $rows[$lix] ); - } - $rcnt = count( $rows ); - if( 3 > $rcnt ) /* err 10 */ - return FALSE; - /* skip trailing empty lines and ensure an end row */ - $lix = array_keys( $rows ); - $lix = end( $lix ); - while( 3 < $lix ) { - $tst = trim( $rows[$lix] ); - if(( '\n' == $tst ) || empty( $tst )) { - unset( $rows[$lix] ); - $lix--; - continue; - } - if( FALSE === stripos( $rows[$lix], 'END:VCALENDAR' )) - $rows[] = 'END:VCALENDAR'; - break; - } - $comp = & $this; - $calsync = $compsync = 0; - /* identify components and update unparsed data within component */ - $config = $this->getConfig(); - $endtxt = array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' ); - foreach( $rows as $lix => $line ) { - if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) { - $calsync++; - continue; - } - elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) { - if( 0 < $compsync ) - $this->components[] = $comp->copy(); - $compsync--; - $calsync--; - break; - } - elseif( 1 != $calsync ) - return FALSE; /* err 20 */ - elseif( in_array( strtoupper( substr( $line, 0, 6 )), $endtxt )) { - $this->components[] = $comp->copy(); - $compsync--; - continue; - } - if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) { - $comp = new vevent( $config ); - $compsync++; - } - elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) { - $comp = new vfreebusy( $config ); - $compsync++; - } - elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) { - $comp = new vjournal( $config ); - $compsync++; - } - elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) { - $comp = new vtodo( $config ); - $compsync++; - } - elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) { - $comp = new vtimezone( $config ); - $compsync++; - } - else { /* update component with unparsed data */ - $comp->unparsed[] = $line; - } - } // end foreach( $rows as $line ) - unset( $config, $endtxt ); - /* parse data for calendar (this) object */ - if( isset( $this->unparsed ) && is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) { - /* concatenate property values spread over several lines */ - $propnames = array( 'calscale','method','prodid','version','x-' ); - $proprows = array(); - for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines - $line = rtrim( $this->unparsed[$i], $nl ); - while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} )) - $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl ); - $proprows[] = $line; - } - $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' ); - $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ); - $paramProto4 = array( 'crid:', 'news:', 'pres:' ); - foreach( $proprows as $line ) { - if( '\n' == substr( $line, -2 )) - $line = substr( $line, 0, -2 ); - /* get property name */ - $propname = ''; - $cix = 0; - while( FALSE !== ( $char = substr( $line, $cix, 1 ))) { - if( in_array( $char, array( ':', ';' ))) - break; - else - $propname .= $char; - $cix++; - } - /* skip non standard property names */ - if(( 'x-' != strtolower( substr( $propname, 0, 2 ))) && !in_array( strtolower( $propname ), $propnames )) - continue; - /* ignore version/prodid properties */ - if( in_array( strtolower( $propname ), array( 'version', 'prodid' ))) - continue; - /* rest of the line is opt.params and value */ - $line = substr( $line, $cix); - /* separate attributes from value */ - $attr = array(); - $attrix = -1; - $strlen = strlen( $line ); - $WithinQuotes = FALSE; - $cix = 0; - while( FALSE !== substr( $line, $cix, 1 )) { - if( ( ':' == $line[$cix] ) && - ( substr( $line,$cix, 3 ) != '://' ) && - ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) && - ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) && - ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) && - ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) && - !$WithinQuotes ) { - $attrEnd = TRUE; - if(( $cix < ( $strlen - 4 )) && - ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr?? - for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) { - if( '://' == substr( $line, $c2ix - 2, 3 )) { - $attrEnd = FALSE; - break; // an URI with a portnr!! - } - } - } - if( $attrEnd) { - $line = substr( $line, ( $cix + 1 )); - break; - } - } - if( '"' == $line[$cix] ) - $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE; - if( ';' == $line[$cix] ) - $attr[++$attrix] = null; - else - $attr[$attrix] .= $line[$cix]; - $cix++; - } - /* make attributes in array format */ - $propattr = array(); - foreach( $attr as $attribute ) { - $attrsplit = explode( '=', $attribute, 2 ); - if( 1 < count( $attrsplit )) - $propattr[$attrsplit[0]] = $attrsplit[1]; - else - $propattr[] = $attribute; - } - /* update Property */ - if( FALSE !== strpos( $line, ',' )) { - $content = array( 0 => '' ); - $cix = $lix = 0; - while( FALSE !== substr( $line, $lix, 1 )) { - if(( 0 < $lix ) && ( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { - $cix++; - $content[$cix] = ''; - } - else - $content[$cix] .= $line[$lix]; - $lix++; - } - if( 1 < count( $content )) { - foreach( $content as $cix => $contentPart ) - $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart ); - $this->setProperty( $propname, $content, $propattr ); - continue; - } - else - $line = reset( $content ); - $line = iCalUtilityFunctions::_strunrep( $line ); - } - $this->setProperty( $propname, rtrim( $line, "\x00..\x1F" ), $propattr ); - } // end - foreach( $this->unparsed.. . - } // end - if( is_array( $this->unparsed.. . - unset( $unparsedtext, $rows, $this->unparsed, $proprows ); - /* parse Components */ - if( is_array( $this->components ) && ( 0 < count( $this->components ))) { - $ckeys = array_keys( $this->components ); - foreach( $ckeys as $ckey ) { - if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) { - $this->components[$ckey]->parse(); - } - } - } - else - return FALSE; /* err 91 or something.. . */ - return TRUE; - } -/*********************************************************************************/ -/** - * creates formatted output for calendar object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.16 - 2011-10-28 - * @return string - */ - function createCalendar() { - $calendarInit = $calendarxCaldecl = $calendarStart = $calendar = ''; - switch( $this->format ) { - case 'xcal': - $calendarInit = ''.$this->nl. - 'nl. - '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"'; - $calendarStart = '>'.$this->nl.'nl; - break; - } - $calendarStart .= $this->createVersion(); - $calendarStart .= $this->createProdid(); - $calendarStart .= $this->createCalscale(); - $calendarStart .= $this->createMethod(); - if( 'xcal' == $this->format ) - $calendarStart .= '>'.$this->nl; - $calendar .= $this->createXprop(); - - foreach( $this->components as $component ) { - if( empty( $component )) continue; - $component->setConfig( $this->getConfig(), FALSE, TRUE ); - $calendar .= $component->createComponent( $this->xcaldecl ); - } - if(( 'xcal' == $this->format ) && ( 0 < count( $this->xcaldecl ))) { // xCal only - $calendarInit .= ' ['; - $old_xcaldecl = array(); - foreach( $this->xcaldecl as $declix => $declPart ) { - if(( 0 < count( $old_xcaldecl)) && - isset( $declPart['uri'] ) && isset( $declPart['external'] ) && - isset( $old_xcaldecl['uri'] ) && isset( $old_xcaldecl['external'] ) && - ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) && - ( in_array( $declPart['external'], $old_xcaldecl['external'] ))) - continue; // no duplicate uri and ext. references - if(( 0 < count( $old_xcaldecl)) && - !isset( $declPart['uri'] ) && !isset( $declPart['uri'] ) && - isset( $declPart['ref'] ) && isset( $old_xcaldecl['ref'] ) && - ( in_array( $declPart['ref'], $old_xcaldecl['ref'] ))) - continue; // no duplicate element declarations - $calendarxCaldecl .= $this->nl.' $declValue ) { - switch( $declKey ) { // index - case 'xmldecl': // no 1 - $calendarxCaldecl .= $declValue.' '; - break; - case 'uri': // no 2 - $calendarxCaldecl .= $declValue.' '; - $old_xcaldecl['uri'][] = $declValue; - break; - case 'ref': // no 3 - $calendarxCaldecl .= $declValue.' '; - $old_xcaldecl['ref'][] = $declValue; - break; - case 'external': // no 4 - $calendarxCaldecl .= '"'.$declValue.'" '; - $old_xcaldecl['external'][] = $declValue; - break; - case 'type': // no 5 - $calendarxCaldecl .= $declValue.' '; - break; - case 'type2': // no 6 - $calendarxCaldecl .= $declValue; - break; - } - } - $calendarxCaldecl .= '>'; - } - $calendarxCaldecl .= $this->nl.']'; - } - switch( $this->format ) { - case 'xcal': - $calendar .= ''.$this->nl; - break; - default: - $calendar .= 'END:VCALENDAR'.$this->nl; - break; - } - return $calendarInit.$calendarxCaldecl.$calendarStart.$calendar; - } -/** - * a HTTP redirect header is sent with created, updated and/or parsed calendar - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.24 - 2011-12-23 - * @param bool $utf8Encode - * @param bool $gzip - * @return redirect - */ - function returnCalendar( $utf8Encode=FALSE, $gzip=FALSE ) { - $filename = $this->getConfig( 'filename' ); - $output = $this->createCalendar(); - if( $utf8Encode ) - $output = utf8_encode( $output ); - if( $gzip ) { - $output = gzencode( $output, 9 ); - header( 'Content-Encoding: gzip' ); - header( 'Vary: *' ); - header( 'Content-Length: '.strlen( $output )); - } - if( 'xcal' == $this->format ) - header( 'Content-Type: application/calendar+xml; charset=utf-8' ); - else - header( 'Content-Type: text/calendar; charset=utf-8' ); - header( 'Content-Disposition: attachment; filename="'.$filename.'"' ); - header( 'Cache-Control: max-age=10' ); - die( $output ); - } -/** - * save content in a file - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.2.12 - 2007-12-30 - * @param string $directory optional - * @param string $filename optional - * @param string $delimiter optional - * @return bool - */ - function saveCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE ) { - if( $directory ) - $this->setConfig( 'directory', $directory ); - if( $filename ) - $this->setConfig( 'filename', $filename ); - if( $delimiter && ($delimiter != DIRECTORY_SEPARATOR )) - $this->setConfig( 'delimiter', $delimiter ); - if( FALSE === ( $dirfile = $this->getConfig( 'url' ))) - $dirfile = $this->getConfig( 'dirfile' ); - $iCalFile = @fopen( $dirfile, 'w' ); - if( $iCalFile ) { - if( FALSE === fwrite( $iCalFile, $this->createCalendar() )) - return FALSE; - fclose( $iCalFile ); - return TRUE; - } - else - return FALSE; - } -/** - * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent - * else FALSE is returned - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.2.12 - 2007-10-28 - * @param string $directory optional alt. int timeout - * @param string $filename optional - * @param string $delimiter optional - * @param int timeout optional, default 3600 sec - * @return redirect/FALSE - */ - function useCachedCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE, $timeout=3600) { - if ( $directory && ctype_digit( (string) $directory ) && !$filename ) { - $timeout = (int) $directory; - $directory = FALSE; - } - if( $directory ) - $this->setConfig( 'directory', $directory ); - if( $filename ) - $this->setConfig( 'filename', $filename ); - if( $delimiter && ( $delimiter != DIRECTORY_SEPARATOR )) - $this->setConfig( 'delimiter', $delimiter ); - $filesize = $this->getConfig( 'filesize' ); - if( 0 >= $filesize ) - return FALSE; - $dirfile = $this->getConfig( 'dirfile' ); - if( time() - filemtime( $dirfile ) < $timeout) { - clearstatcache(); - $dirfile = $this->getConfig( 'dirfile' ); - $filename = $this->getConfig( 'filename' ); -// if( headers_sent( $filename, $linenum )) -// die( "Headers already sent in $filename on line $linenum\n" ); - if( 'xcal' == $this->format ) - header( 'Content-Type: application/calendar+xml; charset=utf-8' ); - else - header( 'Content-Type: text/calendar; charset=utf-8' ); - header( 'Content-Length: '.$filesize ); - header( 'Content-Disposition: attachment; filename="'.$filename.'"' ); - header( 'Cache-Control: max-age=10' ); - $fp = @fopen( $dirfile, 'r' ); - if( $fp ) { - fpassthru( $fp ); - fclose( $fp ); - } - die(); - } - else - return FALSE; - } -} -/*********************************************************************************/ -/*********************************************************************************/ -/** - * abstract class for calendar components - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.6 - 2011-05-14 - */ -class calendarComponent { - // component property variables - var $uid; - var $dtstamp; - - // component config variables - var $allowEmpty; - var $language; - var $nl; - var $unique_id; - var $format; - var $objName; // created automatically at instance creation - var $dtzid; // default (local) timezone - // component internal variables - var $componentStart1; - var $componentStart2; - var $componentEnd1; - var $componentEnd2; - var $elementStart1; - var $elementStart2; - var $elementEnd1; - var $elementEnd2; - var $intAttrDelimiter; - var $attributeDelimiter; - var $valueInit; - // component xCal declaration container - var $xcaldecl; -/** - * constructor for calendar component object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.6 - 2011-05-17 - */ - function calendarComponent() { - $this->objName = ( isset( $this->timezonetype )) ? - strtolower( $this->timezonetype ) : get_class ( $this ); - $this->uid = array(); - $this->dtstamp = array(); - - $this->language = null; - $this->nl = null; - $this->unique_id = null; - $this->format = null; - $this->dtzid = null; - $this->allowEmpty = TRUE; - $this->xcaldecl = array(); - - $this->_createFormat(); - $this->_makeDtstamp(); - } -/*********************************************************************************/ -/** - * Property Name: ACTION - */ -/** - * creates formatted output for calendar component property action - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-22 - * @return string - */ - function createAction() { - if( empty( $this->action )) return FALSE; - if( empty( $this->action['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE; - $attributes = $this->_createParams( $this->action['params'] ); - return $this->_createElement( 'ACTION', $attributes, $this->action['value'] ); - } -/** - * set calendar component property action - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE" - * @param mixed $params - * @return bool - */ - function setAction( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: ATTACH - */ -/** - * creates formatted output for calendar component property attach - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.16 - 2012-02-04 - * @return string - */ - function createAttach() { - if( empty( $this->attach )) return FALSE; - $output = null; - foreach( $this->attach as $attachPart ) { - if( !empty( $attachPart['value'] )) { - $attributes = $this->_createParams( $attachPart['params'] ); - if(( 'xcal' != $this->format ) && isset( $attachPart['params']['VALUE'] ) && ( 'BINARY' == $attachPart['params']['VALUE'] )) { - $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes ); - $str = 'ATTACH'.$attributes.$this->valueInit.$attachPart['value']; - $output = substr( $str, 0, 75 ).$this->nl; - $str = substr( $str, 75 ); - $output .= ' '.chunk_split( $str, 74, $this->nl.' ' ); - if( ' ' == substr( $output, -1 )) - $output = rtrim( $output ); - if( $this->nl != substr( $output, ( 0 - strlen( $this->nl )))) - $output .= $this->nl; - return $output; - } - $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] ); - } - elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' ); - } - return $output; - } -/** - * set calendar component property attach - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-06 - * @param string $value - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setAttach( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: ATTENDEE - */ -/** - * creates formatted output for calendar component property attendee - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.12 - 2012-01-31 - * @return string - */ - function createAttendee() { - if( empty( $this->attendee )) return FALSE; - $output = null; - foreach( $this->attendee as $attendeePart ) { // start foreach 1 - if( empty( $attendeePart['value'] )) { - if( $this->getConfig( 'allowEmpty' )) - $output .= $this->_createElement( 'ATTENDEE' ); - continue; - } - $attendee1 = $attendee2 = null; - foreach( $attendeePart as $paramlabel => $paramvalue ) { // start foreach 2 - if( 'value' == $paramlabel ) - $attendee2 .= $paramvalue; - elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif - $mParams = array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ); - foreach( $paramvalue as $pKey => $pValue ) { // fix (opt) quotes - if( is_array( $pValue ) || in_array( $pKey, $mParams )) - continue; - if(( FALSE !== strpos( $pValue, ':' )) || - ( FALSE !== strpos( $pValue, ';' )) || - ( FALSE !== strpos( $pValue, ',' ))) - $paramvalue[$pKey] = '"'.$pValue.'"'; - } - // set attenddee parameters in rfc2445 order - if( isset( $paramvalue['CUTYPE'] )) - $attendee1 .= $this->intAttrDelimiter.'CUTYPE='.$paramvalue['CUTYPE']; - if( isset( $paramvalue['MEMBER'] )) { - $attendee1 .= $this->intAttrDelimiter.'MEMBER='; - foreach( $paramvalue['MEMBER'] as $cix => $opv ) - $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; - } - if( isset( $paramvalue['ROLE'] )) - $attendee1 .= $this->intAttrDelimiter.'ROLE='.$paramvalue['ROLE']; - if( isset( $paramvalue['PARTSTAT'] )) - $attendee1 .= $this->intAttrDelimiter.'PARTSTAT='.$paramvalue['PARTSTAT']; - if( isset( $paramvalue['RSVP'] )) - $attendee1 .= $this->intAttrDelimiter.'RSVP='.$paramvalue['RSVP']; - if( isset( $paramvalue['DELEGATED-TO'] )) { - $attendee1 .= $this->intAttrDelimiter.'DELEGATED-TO='; - foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv ) - $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; - } - if( isset( $paramvalue['DELEGATED-FROM'] )) { - $attendee1 .= $this->intAttrDelimiter.'DELEGATED-FROM='; - foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv ) - $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; - } - if( isset( $paramvalue['SENT-BY'] )) - $attendee1 .= $this->intAttrDelimiter.'SENT-BY='.$paramvalue['SENT-BY']; - if( isset( $paramvalue['CN'] )) - $attendee1 .= $this->intAttrDelimiter.'CN='.$paramvalue['CN']; - if( isset( $paramvalue['DIR'] )) { - $delim = ( FALSE === strpos( $paramvalue['DIR'], '"' )) ? '"' : ''; - $attendee1 .= $this->intAttrDelimiter.'DIR='.$delim.$paramvalue['DIR'].$delim; - } - if( isset( $paramvalue['LANGUAGE'] )) - $attendee1 .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE']; - $xparams = array(); - foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3 - if( ctype_digit( (string) $optparamlabel )) { - $xparams[] = $optparamvalue; - continue; - } - if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' ))) - $xparams[$optparamlabel] = $optparamvalue; - } // end foreach 3 - ksort( $xparams, SORT_STRING ); - foreach( $xparams as $paramKey => $paramValue ) { - if( ctype_digit( (string) $paramKey )) - $attendee1 .= $this->intAttrDelimiter.$paramValue; - else - $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue"; - } // end foreach 3 - } // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) - } // end foreach 2 - $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 ); - } // end foreach 1 - return $output; - } -/** - * set calendar component property attach - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.12.18 - 2012-07-13 - * @param string $value - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setAttendee( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// may exist.. . also in params - if( !empty( $value )) { - if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) - $value = 'MAILTO:'.$value; - elseif( !empty( $value )) - $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); - $value = str_replace( 'mailto:', 'MAILTO:', $value ); - } - $params2 = array(); - if( is_array($params )) { - $optarrays = array(); - foreach( $params as $optparamlabel => $optparamvalue ) { - $optparamlabel = strtoupper( $optparamlabel ); - switch( $optparamlabel ) { - case 'MEMBER': - case 'DELEGATED-TO': - case 'DELEGATED-FROM': - if( !is_array( $optparamvalue )) - $optparamvalue = array( $optparamvalue ); - foreach( $optparamvalue as $part ) { - $part = trim( $part ); - if(( '"' == substr( $part, 0, 1 )) && - ( '"' == substr( $part, -1 ))) - $part = substr( $part, 1, ( strlen( $part ) - 2 )); - if( 'mailto:' != strtolower( substr( $part, 0, 7 ))) - $part = "MAILTO:$part"; - else - $part = 'MAILTO:'.substr( $part, 7 ); - $optarrays[$optparamlabel][] = $part; - } - break; - default: - if(( '"' == substr( $optparamvalue, 0, 1 )) && - ( '"' == substr( $optparamvalue, -1 ))) - $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 )); - if( 'SENT-BY' == $optparamlabel ) { - if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 ))) - $optparamvalue = "MAILTO:$optparamvalue"; - else - $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 ); - } - $params2[$optparamlabel] = $optparamvalue; - break; - } // end switch( $optparamlabel.. . - } // end foreach( $optparam.. . - foreach( $optarrays as $optparamlabel => $optparams ) - $params2[$optparamlabel] = $optparams; - } - // remove defaults - iCalUtilityFunctions::_existRem( $params2, 'CUTYPE', 'INDIVIDUAL' ); - iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' ); - iCalUtilityFunctions::_existRem( $params2, 'ROLE', 'REQ-PARTICIPANT' ); - iCalUtilityFunctions::_existRem( $params2, 'RSVP', 'FALSE' ); - // check language setting - if( isset( $params2['CN' ] )) { - $lang = $this->getConfig( 'language' ); - if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang )) - $params2['LANGUAGE' ] = $lang; - } - iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: CATEGORIES - */ -/** - * creates formatted output for calendar component property categories - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createCategories() { - if( empty( $this->categories )) return FALSE; - $output = null; - foreach( $this->categories as $category ) { - if( empty( $category['value'] )) { - if ( $this->getConfig( 'allowEmpty' )) - $output .= $this->_createElement( 'CATEGORIES' ); - continue; - } - $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' )); - if( is_array( $category['value'] )) { - foreach( $category['value'] as $cix => $categoryPart ) - $category['value'][$cix] = iCalUtilityFunctions::_strrep( $categoryPart, $this->format, $this->nl ); - $content = implode( ',', $category['value'] ); - } - else - $content = iCalUtilityFunctions::_strrep( $category['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'CATEGORIES', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property categories - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-06 - * @param mixed $value - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setCategories( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: CLASS - */ -/** - * creates formatted output for calendar component property class - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 0.9.7 - 2006-11-20 - * @return string - */ - function createClass() { - if( empty( $this->class )) return FALSE; - if( empty( $this->class['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE; - $attributes = $this->_createParams( $this->class['params'] ); - return $this->_createElement( 'CLASS', $attributes, $this->class['value'] ); - } -/** - * set calendar component property class - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name - * @param array $params optional - * @return bool - */ - function setClass( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: COMMENT - */ -/** - * creates formatted output for calendar component property comment - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createComment() { - if( empty( $this->comment )) return FALSE; - $output = null; - foreach( $this->comment as $commentPart ) { - if( empty( $commentPart['value'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' ); - continue; - } - $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $commentPart['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'COMMENT', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property comment - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-06 - * @param string $value - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setComment( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: COMPLETED - */ -/** - * creates formatted output for calendar component property completed - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-22 - * @return string - */ - function createCompleted( ) { - if( empty( $this->completed )) return FALSE; - if( !isset( $this->completed['value']['year'] ) && - !isset( $this->completed['value']['month'] ) && - !isset( $this->completed['value']['day'] ) && - !isset( $this->completed['value']['hour'] ) && - !isset( $this->completed['value']['min'] ) && - !isset( $this->completed['value']['sec'] )) - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'COMPLETED' ); - else return FALSE; - $formatted = iCalUtilityFunctions::_date2strdate( $this->completed['value'], 7 ); - $attributes = $this->_createParams( $this->completed['params'] ); - return $this->_createElement( 'COMPLETED', $attributes, $formatted ); - } -/** - * set calendar component property completed - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-23 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param array $params optional - * @return bool - */ - function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->completed = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } - else - return FALSE; - } - $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: CONTACT - */ -/** - * creates formatted output for calendar component property contact - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createContact() { - if( empty( $this->contact )) return FALSE; - $output = null; - foreach( $this->contact as $contact ) { - if( !empty( $contact['value'] )) { - $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $contact['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'CONTACT', $attributes, $content ); - } - elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' ); - } - return $output; - } -/** - * set calendar component property contact - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-05 - * @param string $value - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setContact( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: CREATED - */ -/** - * creates formatted output for calendar component property created - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createCreated() { - if( empty( $this->created )) return FALSE; - $formatted = iCalUtilityFunctions::_date2strdate( $this->created['value'], 7 ); - $attributes = $this->_createParams( $this->created['params'] ); - return $this->_createElement( 'CREATED', $attributes, $formatted ); - } -/** - * set calendar component property created - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-23 - * @param mixed $year optional - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param mixed $params optional - * @return bool - */ - function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( !isset( $year )) { - $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' ))); - } - $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DESCRIPTION - */ -/** - * creates formatted output for calendar component property description - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createDescription() { - if( empty( $this->description )) return FALSE; - $output = null; - foreach( $this->description as $description ) { - if( !empty( $description['value'] )) { - $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $description['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'DESCRIPTION', $attributes, $content ); - } - elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' ); - } - return $output; - } -/** - * set calendar component property description - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.24 - 2010-11-06 - * @param string $value - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setDescription( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; } - if( 'vjournal' != $this->objName ) - $index = 1; - iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DTEND - */ -/** - * creates formatted output for calendar component property dtend - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.4 - 2012-09-26 - * @return string - */ - function createDtend() { - if( empty( $this->dtend )) return FALSE; - if( !isset( $this->dtend['value']['year'] ) && - !isset( $this->dtend['value']['month'] ) && - !isset( $this->dtend['value']['day'] ) && - !isset( $this->dtend['value']['hour'] ) && - !isset( $this->dtend['value']['min'] ) && - !isset( $this->dtend['value']['sec'] )) - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'DTEND' ); - else return FALSE; - $parno = ( isset( $this->dtend['params']['VALUE'] ) && ( 'DATE' == $this->dtend['params']['VALUE'] )) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $this->dtend['value'], $parno ); - $attributes = $this->_createParams( $this->dtend['params'] ); - return $this->_createElement( 'DTEND', $attributes, $formatted ); - } -/** - * set calendar component property dtend - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.6 - 2011-05-14 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param string $tz optional - * @param array params optional - * @return bool - */ - function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->dtend = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } - else - return FALSE; - } - $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DTSTAMP - */ -/** - * creates formatted output for calendar component property dtstamp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.4 - 2008-03-07 - * @return string - */ - function createDtstamp() { - if( !isset( $this->dtstamp['value']['year'] ) && - !isset( $this->dtstamp['value']['month'] ) && - !isset( $this->dtstamp['value']['day'] ) && - !isset( $this->dtstamp['value']['hour'] ) && - !isset( $this->dtstamp['value']['min'] ) && - !isset( $this->dtstamp['value']['sec'] )) - $this->_makeDtstamp(); - $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstamp['value'], 7 ); - $attributes = $this->_createParams( $this->dtstamp['params'] ); - return $this->_createElement( 'DTSTAMP', $attributes, $formatted ); - } -/** - * computes datestamp for calendar component object instance dtstamp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-29 - * @return void - */ - function _makeDtstamp() { - $d = date( 'Y-m-d-H-i-s', mktime( date('H'), date('i'), (date('s') - date( 'Z' )), date('m'), date('d'), date('Y'))); - $date = explode( '-', $d ); - $this->dtstamp['value'] = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2], 'hour' => $date[3], 'min' => $date[4], 'sec' => $date[5], 'tz' => 'Z' ); - $this->dtstamp['params'] = null; - } -/** - * set calendar component property dtstamp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-23 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param array $params optional - * @return TRUE - */ - function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( empty( $year )) - $this->_makeDtstamp(); - else - $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DTSTART - */ -/** - * creates formatted output for calendar component property dtstart - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.4 - 2012-09-26 - * @return string - */ - function createDtstart() { - if( empty( $this->dtstart )) return FALSE; - if( !isset( $this->dtstart['value']['year'] ) && - !isset( $this->dtstart['value']['month'] ) && - !isset( $this->dtstart['value']['day'] ) && - !isset( $this->dtstart['value']['hour'] ) && - !isset( $this->dtstart['value']['min'] ) && - !isset( $this->dtstart['value']['sec'] )) { - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'DTSTART' ); - else return FALSE; - } - if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) - unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] ); - $parno = ( isset( $this->dtstart['params']['VALUE'] ) && ( 'DATE' == $this->dtstart['params']['VALUE'] )) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstart['value'], $parno ); - $attributes = $this->_createParams( $this->dtstart['params'] ); - return $this->_createElement( 'DTSTART', $attributes, $formatted ); - } -/** - * set calendar component property dtstart - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.22 - 2010-09-22 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param string $tz optional - * @param array $params optional - * @return bool - */ - function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->dtstart = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } - else - return FALSE; - } - $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName, $this->getConfig( 'TZID' )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DUE - */ -/** - * creates formatted output for calendar component property due - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.4 - 2012-09-26 - * @return string - */ - function createDue() { - if( empty( $this->due )) return FALSE; - if( !isset( $this->due['value']['year'] ) && - !isset( $this->due['value']['month'] ) && - !isset( $this->due['value']['day'] ) && - !isset( $this->due['value']['hour'] ) && - !isset( $this->due['value']['min'] ) && - !isset( $this->due['value']['sec'] )) { - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'DUE' ); - else - return FALSE; - } - $parno = ( isset( $this->due['params']['VALUE'] ) && ( 'DATE' == $this->due['params']['VALUE'] )) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $this->due['value'], $parno ); - $attributes = $this->_createParams( $this->due['params'] ); - return $this->_createElement( 'DUE', $attributes, $formatted ); - } -/** - * set calendar component property due - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param array $params optional - * @return bool - */ - function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->due = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } - else - return FALSE; - } - $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DURATION - */ -/** - * creates formatted output for calendar component property duration - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createDuration() { - if( empty( $this->duration )) return FALSE; - if( !isset( $this->duration['value']['week'] ) && - !isset( $this->duration['value']['day'] ) && - !isset( $this->duration['value']['hour'] ) && - !isset( $this->duration['value']['min'] ) && - !isset( $this->duration['value']['sec'] )) - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'DURATION', array(), null ); - else return FALSE; - $attributes = $this->_createParams( $this->duration['params'] ); - return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_duration2str( $this->duration['value'] )); - } -/** - * set calendar component property duration - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param mixed $week - * @param mixed $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param array $params optional - * @return bool - */ - function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( empty( $week )) if( $this->getConfig( 'allowEmpty' )) $week = null; else return FALSE; - if( is_array( $week ) && ( 1 <= count( $week ))) - $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); - elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) { - $week = trim( $week ); - if( in_array( substr( $week, 0, 1 ), array( '+', '-' ))) - $week = substr( $week, 1 ); - $this->duration = array( 'value' => iCalUtilityFunctions::_durationStr2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); - } - elseif( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec )) - return FALSE; - else - $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: EXDATE - */ -/** - * creates formatted output for calendar component property exdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.5 - 2012-12-28 - * @return string - */ - function createExdate() { - if( empty( $this->exdate )) return FALSE; - $output = null; - $exdates = array(); - foreach( $this->exdate as $theExdate ) { - if( empty( $theExdate['value'] )) { - if( $this->getConfig( 'allowEmpty' )) - $output .= $this->_createElement( 'EXDATE' ); - continue; - } - if( 1 < count( $theExdate['value'] )) - usort( $theExdate['value'], array( 'iCalUtilityFunctions', '_sortExdate1' )); - $exdates[] = $theExdate; - } - if( 1 < count( $exdates )) - usort( $exdates, array( 'iCalUtilityFunctions', '_sortExdate2' )); - foreach( $exdates as $theExdate ) { - $content = $attributes = null; - foreach( $theExdate['value'] as $eix => $exdatePart ) { - $parno = count( $exdatePart ); - $formatted = iCalUtilityFunctions::_date2strdate( $exdatePart, $parno ); - if( isset( $theExdate['params']['TZID'] )) - $formatted = str_replace( 'Z', '', $formatted); - if( 0 < $eix ) { - if( isset( $theExdate['value'][0]['tz'] )) { - if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) || - ( 'Z' == $theExdate['value'][0]['tz'] )) { - if( 'Z' != substr( $formatted, -1 )) - $formatted .= 'Z'; - } - else - $formatted = str_replace( 'Z', '', $formatted ); - } - else - $formatted = str_replace( 'Z', '', $formatted ); - } // end if( 0 < $eix ) - $content .= ( 0 < $eix ) ? ','.$formatted : $formatted; - } // end foreach( $theExdate['value'] as $eix => $exdatePart ) - $attributes .= $this->_createParams( $theExdate['params'] ); - $output .= $this->_createElement( 'EXDATE', $attributes, $content ); - } // end foreach( $exdates as $theExdate ) - return $output; - } -/** - * set calendar component property exdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-10-02 - * @param array exdates - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setExdate( $exdates, $params=FALSE, $index=FALSE ) { - if( empty( $exdates )) { - if( $this->getConfig( 'allowEmpty' )) { - iCalUtilityFunctions::_setMval( $this->exdate, null, $params, FALSE, $index ); - return TRUE; - } - else - return FALSE; - } - $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ))); - $toZ = ( isset( $input['params']['TZID'] ) && in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) ? TRUE : FALSE; - /* ev. check 1:st date and save ev. timezone **/ - iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] ); - iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter - foreach( $exdates as $eix => $theExdate ) { - iCalUtilityFunctions::_strDate2arr( $theExdate ); - if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate )) { - if( isset( $theExdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theExdate['tz'] )) { - if( isset( $input['params']['TZID'] )) - $theExdate['tz'] = $input['params']['TZID']; - else - $input['params']['TZID'] = $theExdate['tz']; - } - $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno ); - } - elseif( is_array( $theExdate )) { - $d = iCalUtilityFunctions::_chkDateArr( $theExdate, $parno ); - if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); - $exdatea = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $exdatea['unparsedtext'] ); - } - else - $exdatea = $d; - } - elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18 - $exdatea = iCalUtilityFunctions::_strdate2date( $theExdate, $parno ); - unset( $exdatea['unparsedtext'] ); - } - if( 3 == $parno ) - unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] ); - elseif( isset( $exdatea['tz'] )) - $exdatea['tz'] = (string) $exdatea['tz']; - if( isset( $input['params']['TZID'] ) || - ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) || - ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) || - ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] ))) - unset( $exdatea['tz'] ); - if( $toZ ) // time zone Z - $exdatea['tz'] = 'Z'; - $input['value'][] = $exdatea; - } - if( 0 >= count( $input['value'] )) - return FALSE; - if( 3 == $parno ) { - $input['params']['VALUE'] = 'DATE'; - unset( $input['params']['TZID'] ); - } - if( $toZ ) // time zone Z - unset( $input['params']['TZID'] ); - iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: EXRULE - */ -/** - * creates formatted output for calendar component property exrule - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-22 - * @return string - */ - function createExrule() { - if( empty( $this->exrule )) return FALSE; - return $this->_format_recur( 'EXRULE', $this->exrule ); - } -/** - * set calendar component property exdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-05 - * @param array $exruleset - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setExrule( $exruleset, $params=FALSE, $index=FALSE ) { - if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = null; else return FALSE; - iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: FREEBUSY - */ -/** - * creates formatted output for calendar component property freebusy - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.1.23 - 2012-02-16 - * @return string - */ - function createFreebusy() { - if( empty( $this->freebusy )) return FALSE; - $output = null; - foreach( $this->freebusy as $freebusyPart ) { - if( empty( $freebusyPart['value'] ) || (( 1 == count( $freebusyPart['value'] )) && isset( $freebusyPart['value']['fbtype'] ))) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' ); - continue; - } - $attributes = $content = null; - if( isset( $freebusyPart['value']['fbtype'] )) { - $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype']; - unset( $freebusyPart['value']['fbtype'] ); - $freebusyPart['value'] = array_values( $freebusyPart['value'] ); - } - else - $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY'; - $attributes .= $this->_createParams( $freebusyPart['params'] ); - $fno = 1; - $cnt = count( $freebusyPart['value']); - foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) { - $formatted = iCalUtilityFunctions::_date2strdate( $freebusyPeriod[0] ); - $content .= $formatted; - $content .= '/'; - $cnt2 = count( $freebusyPeriod[1]); - if( array_key_exists( 'year', $freebusyPeriod[1] )) // date-time - $cnt2 = 7; - elseif( array_key_exists( 'week', $freebusyPeriod[1] )) // duration - $cnt2 = 5; - if(( 7 == $cnt2 ) && // period= -> date-time - isset( $freebusyPeriod[1]['year'] ) && - isset( $freebusyPeriod[1]['month'] ) && - isset( $freebusyPeriod[1]['day'] )) { - $content .= iCalUtilityFunctions::_date2strdate( $freebusyPeriod[1] ); - } - else { // period= -> dur-time - $content .= iCalUtilityFunctions::_duration2str( $freebusyPeriod[1] ); - } - if( $fno < $cnt ) - $content .= ','; - $fno++; - } - $output .= $this->_createElement( 'FREEBUSY', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property freebusy - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.30 - 2012-01-16 - * @param string $fbType - * @param array $fbValues - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) { - if( empty( $fbValues )) { - if( $this->getConfig( 'allowEmpty' )) { - iCalUtilityFunctions::_setMval( $this->freebusy, null, $params, FALSE, $index ); - return TRUE; - } - else - return FALSE; - } - $fbType = strtoupper( $fbType ); - if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) && - ( 'X-' != substr( $fbType, 0, 2 ))) - $fbType = 'BUSY'; - $input = array( 'fbtype' => $fbType ); - foreach( $fbValues as $fbPeriod ) { // periods => period - if( empty( $fbPeriod )) - continue; - $freebusyPeriod = array(); - foreach( $fbPeriod as $fbMember ) { // pairs => singlepart - $freebusyPairMember = array(); - if( is_array( $fbMember )) { - if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value - $freebusyPairMember = iCalUtilityFunctions::_chkDateArr( $fbMember, 7 ); - $freebusyPairMember['tz'] = 'Z'; - } - elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value - $freebusyPairMember = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 ); - $freebusyPairMember['tz'] = 'Z'; - } - else { // array format duration - $freebusyPairMember = iCalUtilityFunctions::_duration2arr( $fbMember ); - } - } - elseif(( 3 <= strlen( trim( $fbMember ))) && // string format duration - ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) { - if( 'P' != $fbMember{0} ) - $fbmember = substr( $fbMember, 1 ); - $freebusyPairMember = iCalUtilityFunctions::_durationStr2arr( $fbMember ); - } - elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18 - $freebusyPairMember = iCalUtilityFunctions::_strdate2date( $fbMember, 7 ); - unset( $freebusyPairMember['unparsedtext'] ); - $freebusyPairMember['tz'] = 'Z'; - } - $freebusyPeriod[] = $freebusyPairMember; - } - $input[] = $freebusyPeriod; - } - iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: GEO - */ -/** - * creates formatted output for calendar component property geo - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.12.6 - 2012-04-21 - * @return string - */ - function createGeo() { - if( empty( $this->geo )) return FALSE; - if( empty( $this->geo['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE; - $attributes = $this->_createParams( $this->geo['params'] ); - if( 0.0 < $this->geo['value']['latitude'] ) - $sign = '+'; - else - $sign = ( 0.0 > $this->geo['value']['latitude'] ) ? '-' : ''; - $content = $sign.sprintf( "%09.6f", abs( $this->geo['value']['latitude'] )); // sprintf && lpad && float && sign !"#¤%&/( - $content = rtrim( rtrim( $content, '0' ), '.' ); - if( 0.0 < $this->geo['value']['longitude'] ) - $sign = '+'; - else - $sign = ( 0.0 > $this->geo['value']['longitude'] ) ? '-' : ''; - $content .= ';'.$sign.sprintf( '%8.6f', abs( $this->geo['value']['longitude'] )); // sprintf && lpad && float && sign !"#¤%&/( - $content = rtrim( rtrim( $content, '0' ), '.' ); - return $this->_createElement( 'GEO', $attributes, $content ); - } -/** - * set calendar component property geo - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.12.5 - 2012-04-21 - * @param float $latitude - * @param float $longitude - * @param array $params optional - * @return bool - */ - function setGeo( $latitude, $longitude, $params=FALSE ) { - if(( !empty( $latitude ) || ( 0 == $latitude )) && - ( !empty( $longitude ) || ( 0 == $longitude ))) { - if( !is_array( $this->geo )) $this->geo = array(); - $this->geo['value']['latitude'] = (float) $latitude; - $this->geo['value']['longitude'] = (float) $longitude; - $this->geo['params'] = iCalUtilityFunctions::_setParams( $params ); - } - elseif( $this->getConfig( 'allowEmpty' )) - $this->geo = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) ); - else - return FALSE; - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: LAST-MODIFIED - */ -/** - * creates formatted output for calendar component property last-modified - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createLastModified() { - if( empty( $this->lastmodified )) return FALSE; - $attributes = $this->_createParams( $this->lastmodified['params'] ); - $formatted = iCalUtilityFunctions::_date2strdate( $this->lastmodified['value'], 7 ); - return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted ); - } -/** - * set calendar component property completed - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-23 - * @param mixed $year optional - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param array $params optional - * @return boll - */ - function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( empty( $year )) - $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' ))); - $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: LOCATION - */ -/** - * creates formatted output for calendar component property location - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createLocation() { - if( empty( $this->location )) return FALSE; - if( empty( $this->location['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE; - $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $this->location['value'], $this->format, $this->nl ); - return $this->_createElement( 'LOCATION', $attributes, $content ); - } -/** - * set calendar component property location - ' - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value - * @param array params optional - * @return bool - */ - function setLocation( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: ORGANIZER - */ -/** - * creates formatted output for calendar component property organizer - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.33 - 2010-12-17 - * @return string - */ - function createOrganizer() { - if( empty( $this->organizer )) return FALSE; - if( empty( $this->organizer['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE; - $attributes = $this->_createParams( $this->organizer['params'] - , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' )); - return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] ); - } -/** - * set calendar component property organizer - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.12.18 - 2012-07-13 - * @param string $value - * @param array params optional - * @return bool - */ - function setOrganizer( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - if( !empty( $value )) { - if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) - $value = 'MAILTO:'.$value; - elseif( !empty( $value )) - $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); - $value = str_replace( 'mailto:', 'MAILTO:', $value ); - } - $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - if( isset( $this->organizer['params']['SENT-BY'] )){ - if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 ))) - $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY']; - else - $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 ); - } - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: PERCENT-COMPLETE - */ -/** - * creates formatted output for calendar component property percent-complete - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @return string - */ - function createPercentComplete() { - if( !isset($this->percentcomplete) || ( empty( $this->percentcomplete ) && !is_numeric( $this->percentcomplete ))) return FALSE; - if( !isset( $this->percentcomplete['value'] ) || ( empty( $this->percentcomplete['value'] ) && !is_numeric( $this->percentcomplete['value'] ))) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE; - $attributes = $this->_createParams( $this->percentcomplete['params'] ); - return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] ); - } -/** - * set calendar component property percent-complete - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @param int $value - * @param array $params optional - * @return bool - */ - function setPercentComplete( $value, $params=FALSE ) { - if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: PRIORITY - */ -/** - * creates formatted output for calendar component property priority - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @return string - */ - function createPriority() { - if( !isset($this->priority) || ( empty( $this->priority ) && !is_numeric( $this->priority ))) return FALSE; - if( !isset( $this->priority['value'] ) || ( empty( $this->priority['value'] ) && !is_numeric( $this->priority['value'] ))) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE; - $attributes = $this->_createParams( $this->priority['params'] ); - return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] ); - } -/** - * set calendar component property priority - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @param int $value - * @param array $params optional - * @return bool - */ - function setPriority( $value, $params=FALSE ) { - if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RDATE - */ -/** - * creates formatted output for calendar component property rdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.9 - 2013-01-09 - * @return string - */ - function createRdate() { - if( empty( $this->rdate )) return FALSE; - $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE; - $output = null; - $rdates = array(); - foreach( $this->rdate as $rpix => $theRdate ) { - if( empty( $theRdate['value'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' ); - continue; - } - if( $utctime ) - unset( $theRdate['params']['TZID'] ); - if( 1 < count( $theRdate['value'] )) - usort( $theRdate['value'], array( 'iCalUtilityFunctions', '_sortRdate1' )); - $rdates[] = $theRdate; - } - if( 1 < count( $rdates )) - usort( $rdates, array( 'iCalUtilityFunctions', '_sortRdate2' )); - foreach( $rdates as $rpix => $theRdate ) { - $attributes = $this->_createParams( $theRdate['params'] ); - $cnt = count( $theRdate['value'] ); - $content = null; - $rno = 1; - foreach( $theRdate['value'] as $rix => $rdatePart ) { - $contentPart = null; - if( is_array( $rdatePart ) && - isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD - if( $utctime ) - unset( $rdatePart[0]['tz'] ); - $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[0] ); // PERIOD part 1 - if( $utctime || !empty( $theRdate['params']['TZID'] )) - $formatted = str_replace( 'Z', '', $formatted); - $contentPart .= $formatted; - $contentPart .= '/'; - $cnt2 = count( $rdatePart[1]); - if( array_key_exists( 'year', $rdatePart[1] )) { - if( array_key_exists( 'hour', $rdatePart[1] )) - $cnt2 = 7; // date-time - else - $cnt2 = 3; // date - } - elseif( array_key_exists( 'week', $rdatePart[1] )) // duration - $cnt2 = 5; - if(( 7 == $cnt2 ) && // period= -> date-time - isset( $rdatePart[1]['year'] ) && - isset( $rdatePart[1]['month'] ) && - isset( $rdatePart[1]['day'] )) { - if( $utctime ) - unset( $rdatePart[1]['tz'] ); - $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[1] ); // PERIOD part 2 - if( $utctime || !empty( $theRdate['params']['TZID'] )) - $formatted = str_replace( 'Z', '', $formatted ); - $contentPart .= $formatted; - } - else { // period= -> dur-time - $contentPart .= iCalUtilityFunctions::_duration2str( $rdatePart[1] ); - } - } // PERIOD end - else { // SINGLE date start - if( $utctime ) - unset( $rdatePart['tz'] ); - $parno = ( isset( $theRdate['params']['VALUE'] ) && ( 'DATE' == isset( $theRdate['params']['VALUE'] ))) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart, $parno ); - if( $utctime || !empty( $theRdate['params']['TZID'] )) - $formatted = str_replace( 'Z', '', $formatted); - $contentPart .= $formatted; - } - $content .= $contentPart; - if( $rno < $cnt ) - $content .= ','; - $rno++; - } - $output .= $this->_createElement( 'RDATE', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property rdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-10-04 - * @param array $rdates - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setRdate( $rdates, $params=FALSE, $index=FALSE ) { - if( empty( $rdates )) { - if( $this->getConfig( 'allowEmpty' )) { - iCalUtilityFunctions::_setMval( $this->rdate, null, $params, FALSE, $index ); - return TRUE; - } - else - return FALSE; - } - $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ))); - if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) { - unset( $input['params']['TZID'] ); - $input['params']['VALUE'] = 'DATE-TIME'; - } - $zArr = array( 'GMT', 'UTC', 'Z' ); - $toZ = ( isset( $params['TZID'] ) && in_array( strtoupper( $params['TZID'] ), $zArr )) ? TRUE : FALSE; - /* check if PERIOD, if not set */ - if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) && - isset( $rdates[0] ) && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) && - isset( $rdates[0][0] ) && isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) && - (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) || - iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) || - ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] ))))) && - ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] )))))) - $input['params']['VALUE'] = 'PERIOD'; - /* check 1:st date, upd. $parno (opt) and save ev. timezone **/ - $date = reset( $rdates ); - if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD - $date = reset( $date ); - iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] ); - iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default - foreach( $rdates as $rpix => $theRdate ) { - $inputa = null; - iCalUtilityFunctions::_strDate2arr( $theRdate ); - if( is_array( $theRdate )) { - if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD - foreach( $theRdate as $rix => $rPeriod ) { - iCalUtilityFunctions::_strDate2arr( $theRdate ); - if( is_array( $rPeriod )) { - if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) { // timestamp - if( isset( $rPeriod['tz'] ) && !iCalUtilityFunctions::_isOffset( $rPeriod['tz'] )) { - if( isset( $input['params']['TZID'] )) - $rPeriod['tz'] = $input['params']['TZID']; - else - $input['params']['TZID'] = $rPeriod['tz']; - } - $inputab = iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno ); - } - elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod )) { - $d = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_chkDateArr( $rPeriod, $parno ) : iCalUtilityFunctions::_chkDateArr( $rPeriod, 6 ); - if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); - $inputab = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $inputab['unparsedtext'] ); - } - else - $inputab = $d; - } - elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) { // text-date - $inputab = iCalUtilityFunctions::_strdate2date( reset( $rPeriod ), $parno ); - unset( $inputab['unparsedtext'] ); - } - else // array format duration - $inputab = iCalUtilityFunctions::_duration2arr( $rPeriod ); - } - elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration - ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) { - if( 'P' != $rPeriod[0] ) - $rPeriod = substr( $rPeriod, 1 ); - $inputab = iCalUtilityFunctions::_durationStr2arr( $rPeriod ); - } - elseif( 8 <= strlen( trim( $rPeriod ))) { // text date ex. 2006-08-03 10:12:18 - $inputab = iCalUtilityFunctions::_strdate2date( $rPeriod, $parno ); - unset( $inputab['unparsedtext'] ); - } - if(( 0 == $rpix ) && ( 0 == $rix )) { - if( isset( $inputab['tz'] ) && in_array( strtoupper( $inputab['tz'] ), $zArr )) { - $inputab['tz'] = 'Z'; - $toZ = TRUE; - } - } - else { - if( isset( $inputa[0]['tz'] ) && ( 'Z' == $inputa[0]['tz'] ) && isset( $inputab['year'] )) - $inputab['tz'] = 'Z'; - else - unset( $inputab['tz'] ); - } - if( $toZ && isset( $inputab['year'] ) ) - $inputab['tz'] = 'Z'; - $inputa[] = $inputab; - } - } // PERIOD end - elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) { // timestamp - if( isset( $theRdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theRdate['tz'] )) { - if( isset( $input['params']['TZID'] )) - $theRdate['tz'] = $input['params']['TZID']; - else - $input['params']['TZID'] = $theRdate['tz']; - } - $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno ); - } - else { // date[-time] - $inputa = iCalUtilityFunctions::_chkDateArr( $theRdate, $parno ); - if( isset( $inputa['tz'] ) && ( 'Z' != $inputa['tz'] ) && iCalUtilityFunctions::_isOffset( $inputa['tz'] )) { - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $inputa['year'], $inputa['month'], $inputa['day'], $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] ); - $inputa = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $inputa['unparsedtext'] ); - } - } - } - elseif( 8 <= strlen( trim( $theRdate ))) { // text date ex. 2006-08-03 10:12:18 - $inputa = iCalUtilityFunctions::_strdate2date( $theRdate, $parno ); - unset( $inputa['unparsedtext'] ); - if( $toZ ) - $inputa['tz'] = 'Z'; - } - if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD - if(( 0 == $rpix ) && !$toZ ) - $toZ = ( isset( $inputa['tz'] ) && in_array( strtoupper( $inputa['tz'] ), $zArr )) ? TRUE : FALSE; - if( $toZ ) - $inputa['tz'] = 'Z'; - if( 3 == $parno ) - unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] ); - elseif( isset( $inputa['tz'] )) - $inputa['tz'] = (string) $inputa['tz']; - if( isset( $input['params']['TZID'] ) || ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] )))) - if( !$toZ ) - unset( $inputa['tz'] ); - } - $input['value'][] = $inputa; - } - if( 3 == $parno ) { - $input['params']['VALUE'] = 'DATE'; - unset( $input['params']['TZID'] ); - } - if( $toZ ) - unset( $input['params']['TZID'] ); - iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RECURRENCE-ID - */ -/** - * creates formatted output for calendar component property recurrence-id - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.4 - 2012-09-26 - * @return string - */ - function createRecurrenceid() { - if( empty( $this->recurrenceid )) return FALSE; - if( empty( $this->recurrenceid['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE; - $parno = ( isset( $this->recurrenceid['params']['VALUE'] ) && ( 'DATE' == $this->recurrenceid['params']['VALUE'] )) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $this->recurrenceid['value'], $parno ); - $attributes = $this->_createParams( $this->recurrenceid['params'] ); - return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted ); - } -/** - * set calendar component property recurrence-id - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.6 - 2011-05-15 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param array $params optional - * @return bool - */ - function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->recurrenceid = array( 'value' => null, 'params' => null ); - return TRUE; - } - else - return FALSE; - } - $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RELATED-TO - */ -/** - * creates formatted output for calendar component property related-to - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createRelatedTo() { - if( empty( $this->relatedto )) return FALSE; - $output = null; - foreach( $this->relatedto as $relation ) { - if( !empty( $relation['value'] )) - $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ), iCalUtilityFunctions::_strrep( $relation['value'], $this->format, $this->nl )); - elseif( $this->getConfig( 'allowEmpty' )) - $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] )); - } - return $output; - } -/** - * set calendar component property related-to - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.24 - 2012-02-23 - * @param float $relid - * @param array $params, optional - * @param index $index, optional - * @return bool - */ - function setRelatedTo( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default - iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: REPEAT - */ -/** - * creates formatted output for calendar component property repeat - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @return string - */ - function createRepeat() { - if( !isset( $this->repeat ) || ( empty( $this->repeat ) && !is_numeric( $this->repeat ))) return FALSE; - if( !isset( $this->repeat['value']) || ( empty( $this->repeat['value'] ) && !is_numeric( $this->repeat['value'] ))) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE; - $attributes = $this->_createParams( $this->repeat['params'] ); - return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] ); - } -/** - * set calendar component property repeat - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @param string $value - * @param array $params optional - * @return void - */ - function setRepeat( $value, $params=FALSE ) { - if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: REQUEST-STATUS - */ -/** - * creates formatted output for calendar component property request-status - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createRequestStatus() { - if( empty( $this->requeststatus )) return FALSE; - $output = null; - foreach( $this->requeststatus as $rstat ) { - if( empty( $rstat['value']['statcode'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' ); - continue; - } - $attributes = $this->_createParams( $rstat['params'], array( 'LANGUAGE' )); - $content = number_format( (float) $rstat['value']['statcode'], 2, '.', ''); - $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['text'], $this->format, $this->nl ); - if( isset( $rstat['value']['extdata'] )) - $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['extdata'], $this->format, $this->nl ); - $output .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property request-status - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-05 - * @param float $statcode - * @param string $text - * @param string $extdata, optional - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) { - if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = null; else return FALSE; - $input = array( 'statcode' => $statcode, 'text' => $text ); - if( $extdata ) - $input['extdata'] = $extdata; - iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RESOURCES - */ -/** - * creates formatted output for calendar component property resources - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createResources() { - if( empty( $this->resources )) return FALSE; - $output = null; - foreach( $this->resources as $resource ) { - if( empty( $resource['value'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' ); - continue; - } - $attributes = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' )); - if( is_array( $resource['value'] )) { - foreach( $resource['value'] as $rix => $resourcePart ) - $resource['value'][$rix] = iCalUtilityFunctions::_strrep( $resourcePart, $this->format, $this->nl ); - $content = implode( ',', $resource['value'] ); - } - else - $content = iCalUtilityFunctions::_strrep( $resource['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'RESOURCES', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property recources - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-05 - * @param mixed $value - * @param array $params, optional - * @param integer $index, optional - * @return bool - */ - function setResources( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RRULE - */ -/** - * creates formatted output for calendar component property rrule - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createRrule() { - if( empty( $this->rrule )) return FALSE; - return $this->_format_recur( 'RRULE', $this->rrule ); - } -/** - * set calendar component property rrule - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-05 - * @param array $rruleset - * @param array $params, optional - * @param integer $index, optional - * @return void - */ - function setRrule( $rruleset, $params=FALSE, $index=FALSE ) { - if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = null; else return FALSE; - iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: SEQUENCE - */ -/** - * creates formatted output for calendar component property sequence - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @return string - */ - function createSequence() { - if( !isset( $this->sequence ) || ( empty( $this->sequence ) && !is_numeric( $this->sequence ))) return FALSE; - if(( !isset($this->sequence['value'] ) || ( empty( $this->sequence['value'] ) && !is_numeric( $this->sequence['value'] ))) && - ( '0' != $this->sequence['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE; - $attributes = $this->_createParams( $this->sequence['params'] ); - return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] ); - } -/** - * set calendar component property sequence - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.8 - 2011-09-19 - * @param int $value optional - * @param array $params optional - * @return bool - */ - function setSequence( $value=FALSE, $params=FALSE ) { - if(( empty( $value ) && !is_numeric( $value )) && ( '0' != $value )) - $value = ( isset( $this->sequence['value'] ) && ( -1 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : '0'; - $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: STATUS - */ -/** - * creates formatted output for calendar component property status - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createStatus() { - if( empty( $this->status )) return FALSE; - if( empty( $this->status['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE; - $attributes = $this->_createParams( $this->status['params'] ); - return $this->_createElement( 'STATUS', $attributes, $this->status['value'] ); - } -/** - * set calendar component property status - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value - * @param array $params optional - * @return bool - */ - function setStatus( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: SUMMARY - */ -/** - * creates formatted output for calendar component property summary - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createSummary() { - if( empty( $this->summary )) return FALSE; - if( empty( $this->summary['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE; - $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $this->summary['value'], $this->format, $this->nl ); - return $this->_createElement( 'SUMMARY', $attributes, $content ); - } -/** - * set calendar component property summary - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value - * @param string $params optional - * @return bool - */ - function setSummary( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TRANSP - */ -/** - * creates formatted output for calendar component property transp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createTransp() { - if( empty( $this->transp )) return FALSE; - if( empty( $this->transp['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE; - $attributes = $this->_createParams( $this->transp['params'] ); - return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] ); - } -/** - * set calendar component property transp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value - * @param string $params optional - * @return bool - */ - function setTransp( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TRIGGER - */ -/** - * creates formatted output for calendar component property trigger - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.16 - 2008-10-21 - * @return string - */ - function createTrigger() { - if( empty( $this->trigger )) return FALSE; - if( empty( $this->trigger['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE; - $content = $attributes = null; - if( isset( $this->trigger['value']['year'] ) && - isset( $this->trigger['value']['month'] ) && - isset( $this->trigger['value']['day'] )) - $content .= iCalUtilityFunctions::_date2strdate( $this->trigger['value'] ); - else { - if( TRUE !== $this->trigger['value']['relatedStart'] ) - $attributes .= $this->intAttrDelimiter.'RELATED=END'; - if( $this->trigger['value']['before'] ) - $content .= '-'; - $content .= iCalUtilityFunctions::_duration2str( $this->trigger['value'] ); - } - $attributes .= $this->_createParams( $this->trigger['params'] ); - return $this->_createElement( 'TRIGGER', $attributes, $content ); - } -/** - * set calendar component property trigger - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-20 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $week optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param bool $relatedStart optional - * @param bool $before optional - * @param array $params optional - * @return bool - */ - function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) { - if( empty( $year ) && empty( $month ) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec )) - if( $this->getConfig( 'allowEmpty' )) { - $this->trigger = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) ); - return TRUE; - } - else - return FALSE; - if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp UTC - $params = iCalUtilityFunctions::_setParams( $month ); - $date = iCalUtilityFunctions::_timestamp2date( $year, 7 ); - foreach( $date as $k => $v ) - $$k = $v; - } - elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) { - $params = iCalUtilityFunctions::_setParams( $month ); - if(!(array_key_exists( 'year', $year ) && // exclude date-time - array_key_exists( 'month', $year ) && - array_key_exists( 'day', $year ))) { // when this must be a duration - if( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) - $relatedStart = FALSE; - else - $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE; - $before = ( array_key_exists( 'before', $year ) && ( TRUE !== $year['before'] )) ? FALSE : TRUE; - } - $SSYY = ( array_key_exists( 'year', $year )) ? $year['year'] : null; - $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null; - $day = ( array_key_exists( 'day', $year )) ? $year['day'] : null; - $week = ( array_key_exists( 'week', $year )) ? $year['week'] : null; - $hour = ( array_key_exists( 'hour', $year )) ? $year['hour'] : 0; //null; - $min = ( array_key_exists( 'min', $year )) ? $year['min'] : 0; //null; - $sec = ( array_key_exists( 'sec', $year )) ? $year['sec'] : 0; //null; - $year = $SSYY; - } - elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) { // duration or date in a string - $params = iCalUtilityFunctions::_setParams( $month ); - if( in_array( $year[0], array( 'P', '+', '-' ))) { // duration - $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) ? FALSE : TRUE; - $before = ( '-' == $year[0] ) ? TRUE : FALSE; - if( 'P' != $year[0] ) - $year = substr( $year, 1 ); - $date = iCalUtilityFunctions::_durationStr2arr( $year); - } - else // date - $date = iCalUtilityFunctions::_strdate2date( $year, 7 ); - unset( $year, $month, $day, $date['unparsedtext'] ); - if( empty( $date )) - $sec = 0; - else - foreach( $date as $k => $v ) - $$k = $v; - } - else // single values in function input parameters - $params = iCalUtilityFunctions::_setParams( $params ); - if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date - $params['VALUE'] = 'DATE-TIME'; - $hour = ( $hour ) ? $hour : 0; - $min = ( $min ) ? $min : 0; - $sec = ( $sec ) ? $sec : 0; - $this->trigger = array( 'params' => $params ); - $this->trigger['value'] = array( 'year' => $year - , 'month' => $month - , 'day' => $day - , 'hour' => $hour - , 'min' => $min - , 'sec' => $sec - , 'tz' => 'Z' ); - return TRUE; - } - elseif(( empty( $year ) && empty( $month )) && // duration - (( !empty( $week ) || ( 0 == $week )) || - ( !empty( $day ) || ( 0 == $day )) || - ( !empty( $hour ) || ( 0 == $hour )) || - ( !empty( $min ) || ( 0 == $min )) || - ( !empty( $sec ) || ( 0 == $sec )))) { - unset( $params['RELATED'] ); // set at output creation (END only) - unset( $params['VALUE'] ); // 'DURATION' default - $this->trigger = array( 'params' => $params ); - $this->trigger['value'] = array(); - if( !empty( $week )) $this->trigger['value']['week'] = $week; - if( !empty( $day )) $this->trigger['value']['day'] = $day; - if( !empty( $hour )) $this->trigger['value']['hour'] = $hour; - if( !empty( $min )) $this->trigger['value']['min'] = $min; - if( !empty( $sec )) $this->trigger['value']['sec'] = $sec; - if( empty( $this->trigger['value'] )) { - $this->trigger['value']['sec'] = 0; - $before = FALSE; - } - $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE; - $before = ( FALSE !== $before ) ? TRUE : FALSE; - $this->trigger['value']['relatedStart'] = $relatedStart; - $this->trigger['value']['before'] = $before; - return TRUE; - } - return FALSE; - } -/*********************************************************************************/ -/** - * Property Name: TZID - */ -/** - * creates formatted output for calendar component property tzid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createTzid() { - if( empty( $this->tzid )) return FALSE; - if( empty( $this->tzid['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE; - $attributes = $this->_createParams( $this->tzid['params'] ); - return $this->_createElement( 'TZID', $attributes, iCalUtilityFunctions::_strrep( $this->tzid['value'], $this->format, $this->nl )); - } -/** - * set calendar component property tzid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value - * @param array $params optional - * @return bool - */ - function setTzid( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * .. . - * Property Name: TZNAME - */ -/** - * creates formatted output for calendar component property tzname - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createTzname() { - if( empty( $this->tzname )) return FALSE; - $output = null; - foreach( $this->tzname as $theName ) { - if( !empty( $theName['value'] )) { - $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' )); - $output .= $this->_createElement( 'TZNAME', $attributes, iCalUtilityFunctions::_strrep( $theName['value'], $this->format, $this->nl )); - } - elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' ); - } - return $output; - } -/** - * set calendar component property tzname - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-05 - * @param string $value - * @param string $params, optional - * @param integer $index, optional - * @return bool - */ - function setTzname( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TZOFFSETFROM - */ -/** - * creates formatted output for calendar component property tzoffsetfrom - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createTzoffsetfrom() { - if( empty( $this->tzoffsetfrom )) return FALSE; - if( empty( $this->tzoffsetfrom['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE; - $attributes = $this->_createParams( $this->tzoffsetfrom['params'] ); - return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] ); - } -/** - * set calendar component property tzoffsetfrom - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value - * @param string $params optional - * @return bool - */ - function setTzoffsetfrom( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TZOFFSETTO - */ -/** - * creates formatted output for calendar component property tzoffsetto - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createTzoffsetto() { - if( empty( $this->tzoffsetto )) return FALSE; - if( empty( $this->tzoffsetto['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE; - $attributes = $this->_createParams( $this->tzoffsetto['params'] ); - return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] ); - } -/** - * set calendar component property tzoffsetto - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value - * @param string $params optional - * @return bool - */ - function setTzoffsetto( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TZURL - */ -/** - * creates formatted output for calendar component property tzurl - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createTzurl() { - if( empty( $this->tzurl )) return FALSE; - if( empty( $this->tzurl['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE; - $attributes = $this->_createParams( $this->tzurl['params'] ); - return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] ); - } -/** - * set calendar component property tzurl - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value - * @param string $params optional - * @return boll - */ - function setTzurl( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: UID - */ -/** - * creates formatted output for calendar component property uid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 0.9.7 - 2006-11-20 - * @return string - */ - function createUid() { - if( 0 >= count( $this->uid )) - $this->_makeuid(); - $attributes = $this->_createParams( $this->uid['params'] ); - return $this->_createElement( 'UID', $attributes, $this->uid['value'] ); - } -/** - * create an unique id for this calendar component object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.2.7 - 2007-09-04 - * @return void - */ - function _makeUid() { - $date = date('Ymd\THisT'); - $unique = substr(microtime(), 2, 4); - $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890'; - $start = 0; - $end = strlen( $base ) - 1; - $length = 6; - $str = null; - for( $p = 0; $p < $length; $p++ ) - $unique .= $base{mt_rand( $start, $end )}; - $this->uid = array( 'params' => null ); - $this->uid['value'] = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' ); - } -/** - * set calendar component property uid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-11-04 - * @param string $value - * @param string $params optional - * @return bool - */ - function setUid( $value, $params=FALSE ) { - if( empty( $value )) return FALSE; // no allowEmpty check here !!!! - $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: URL - */ -/** - * creates formatted output for calendar component property url - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @return string - */ - function createUrl() { - if( empty( $this->url )) return FALSE; - if( empty( $this->url['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE; - $attributes = $this->_createParams( $this->url['params'] ); - return $this->_createElement( 'URL', $attributes, $this->url['value'] ); - } -/** - * set calendar component property url - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.7 - 2013-01-11 - * @param string $value - * @param string $params optional - * @return bool - */ - function setUrl( $value, $params=FALSE ) { - if( !empty( $value )) { - if( !filter_var( $value, FILTER_VALIDATE_URL ) && ( 'urn' != strtolower( substr( $value, 0, 3 )))) - return FALSE; - } - elseif( $this->getConfig( 'allowEmpty' )) - $value = null; - else - return FALSE; - $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: x-prop - */ -/** - * creates formatted output for calendar component property x-prop - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @return string - */ - function createXprop() { - if( empty( $this->xprop )) return FALSE; - $output = null; - foreach( $this->xprop as $label => $xpropPart ) { - if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $label ); - continue; - } - $attributes = $this->_createParams( $xpropPart['params'], array( 'LANGUAGE' )); - if( is_array( $xpropPart['value'] )) { - foreach( $xpropPart['value'] as $pix => $theXpart ) - $xpropPart['value'][$pix] = iCalUtilityFunctions::_strrep( $theXpart, $this->format, $this->format ); - $xpropPart['value'] = implode( ',', $xpropPart['value'] ); - } - else - $xpropPart['value'] = iCalUtilityFunctions::_strrep( $xpropPart['value'], $this->format, $this->nl ); - $output .= $this->_createElement( $label, $attributes, $xpropPart['value'] ); - } - return $output; - } -/** - * set calendar component property x-prop - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.9 - 2012-01-16 - * @param string $label - * @param mixed $value - * @param array $params optional - * @return bool - */ - function setXprop( $label, $value, $params=FALSE ) { - if( empty( $label )) - return FALSE; - if( 'X-' != strtoupper( substr( $label, 0, 2 ))) - return FALSE; - if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; - $xprop = array( 'value' => $value ); - $xprop['params'] = iCalUtilityFunctions::_setParams( $params ); - if( !is_array( $this->xprop )) $this->xprop = array(); - $this->xprop[strtoupper( $label )] = $xprop; - return TRUE; - } -/*********************************************************************************/ -/*********************************************************************************/ -/** - * create element format parts - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.0.6 - 2006-06-20 - * @return string - */ - function _createFormat() { - $objectname = null; - switch( $this->format ) { - case 'xcal': - $objectname = ( isset( $this->timezonetype )) ? - strtolower( $this->timezonetype ) : strtolower( $this->objName ); - $this->componentStart1 = $this->elementStart1 = '<'; - $this->componentStart2 = $this->elementStart2 = '>'; - $this->componentEnd1 = $this->elementEnd1 = 'componentEnd2 = $this->elementEnd2 = '>'.$this->nl; - $this->intAttrDelimiter = ''; - $this->attributeDelimiter = $this->nl; - $this->valueInit = null; - break; - default: - $objectname = ( isset( $this->timezonetype )) ? - strtoupper( $this->timezonetype ) : strtoupper( $this->objName ); - $this->componentStart1 = 'BEGIN:'; - $this->componentStart2 = null; - $this->componentEnd1 = 'END:'; - $this->componentEnd2 = $this->nl; - $this->elementStart1 = null; - $this->elementStart2 = null; - $this->elementEnd1 = null; - $this->elementEnd2 = $this->nl; - $this->intAttrDelimiter = ''; - $this->attributeDelimiter = ';'; - $this->valueInit = ':'; - break; - } - return $objectname; - } -/** - * creates formatted output for calendar component property - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @param string $label property name - * @param string $attributes property attributes - * @param string $content property content (optional) - * @return string - */ - function _createElement( $label, $attributes=null, $content=FALSE ) { - switch( $this->format ) { - case 'xcal': - $label = strtolower( $label ); - break; - default: - $label = strtoupper( $label ); - break; - } - $output = $this->elementStart1.$label; - $categoriesAttrLang = null; - $attachInlineBinary = FALSE; - $attachfmttype = null; - if (( 'xcal' == $this->format) && ( 'x-' == substr( $label, 0, 2 ))) { - $this->xcaldecl[] = array( 'xmldecl' => 'ELEMENT' - , 'ref' => $label - , 'type2' => '(#PCDATA)' ); - } - if( !empty( $attributes )) { - $attributes = trim( $attributes ); - if ( 'xcal' == $this->format ) { - $attributes2 = explode( $this->intAttrDelimiter, $attributes ); - $attributes = null; - foreach( $attributes2 as $aix => $attribute ) { - $attrKVarr = explode( '=', $attribute ); - if( empty( $attrKVarr[0] )) - continue; - if( !isset( $attrKVarr[1] )) { - $attrValue = $attrKVarr[0]; - $attrKey = $aix; - } - elseif( 2 == count( $attrKVarr)) { - $attrKey = strtolower( $attrKVarr[0] ); - $attrValue = $attrKVarr[1]; - } - else { - $attrKey = strtolower( $attrKVarr[0] ); - unset( $attrKVarr[0] ); - $attrValue = implode( '=', $attrKVarr ); - } - if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) { - $attachInlineBinary = TRUE; - if( 'fmttype' == $attrKey ) - $attachfmttype = $attrKey.'='.$attrValue; - continue; - } - elseif(( 'categories' == $label ) && ( 'language' == $attrKey )) - $categoriesAttrLang = $attrKey.'='.$attrValue; - else { - $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' '; - $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null; - if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) { - $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 )); - $attrValue = str_replace( '"', '', $attrValue ); - } - $attributes .= '"'.htmlspecialchars( $attrValue ).'"'; - } - } - } - else { - $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes ); - } - } - if(( 'xcal' == $this->format) && - ((( 'attach' == $label ) && !$attachInlineBinary ) || ( in_array( $label, array( 'tzurl', 'url' ))))) { - $pos = strrpos($content, "/"); - $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content; - $this->xcaldecl[] = array( 'xmldecl' => 'ENTITY' - , 'uri' => $docname - , 'ref' => 'SYSTEM' - , 'external' => $content - , 'type' => 'NDATA' - , 'type2' => 'BINERY' ); - $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' '; - $attributes .= 'uri="'.$docname.'"'; - $content = null; - if( 'attach' == $label ) { - $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes ); - $content = $this->nl.$this->_createElement( 'extref', $attributes, null ); - $attributes = null; - } - } - elseif(( 'xcal' == $this->format) && ( 'attach' == $label ) && $attachInlineBinary ) { - $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute - } - $output .= $attributes; - if( !$content && ( '0' != $content )) { - switch( $this->format ) { - case 'xcal': - $output .= ' /'; - $output .= $this->elementStart2.$this->nl; - return $output; - break; - default: - $output .= $this->elementStart2.$this->valueInit; - return iCalUtilityFunctions::_size75( $output, $this->nl ); - break; - } - } - $output .= $this->elementStart2; - $output .= $this->valueInit.$content; - switch( $this->format ) { - case 'xcal': - return $output.$this->elementEnd1.$label.$this->elementEnd2; - break; - default: - return iCalUtilityFunctions::_size75( $output, $this->nl ); - break; - } - } -/** - * creates formatted output for calendar component property parameters - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.27 - 2012-01-16 - * @param array $params optional - * @param array $ctrKeys optional - * @return string - */ - function _createParams( $params=array(), $ctrKeys=array() ) { - if( !is_array( $params ) || empty( $params )) - $params = array(); - $attrLANG = $attr1 = $attr2 = $lang = null; - $CNattrKey = ( in_array( 'CN', $ctrKeys )) ? TRUE : FALSE ; - $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ; - $CNattrExist = $LANGattrExist = FALSE; - $xparams = array(); - foreach( $params as $paramKey => $paramValue ) { - if(( FALSE !== strpos( $paramValue, ':' )) || - ( FALSE !== strpos( $paramValue, ';' )) || - ( FALSE !== strpos( $paramValue, ',' ))) - $paramValue = '"'.$paramValue.'"'; - if( ctype_digit( (string) $paramKey )) { - $xparams[] = $paramValue; - continue; - } - $paramKey = strtoupper( $paramKey ); - if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' ))) - $xparams[$paramKey] = $paramValue; - else - $params[$paramKey] = $paramValue; - } - ksort( $xparams, SORT_STRING ); - foreach( $xparams as $paramKey => $paramValue ) { - if( ctype_digit( (string) $paramKey )) - $attr2 .= $this->intAttrDelimiter.$paramValue; - else - $attr2 .= $this->intAttrDelimiter."$paramKey=$paramValue"; - } - if( isset( $params['FMTTYPE'] ) && !in_array( 'FMTTYPE', $ctrKeys )) { - $attr1 .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2; - $attr2 = null; - } - if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING', $ctrKeys )) { - if( !empty( $attr2 )) { - $attr1 .= $attr2; - $attr2 = null; - } - $attr1 .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING']; - } - if( isset( $params['VALUE'] ) && !in_array( 'VALUE', $ctrKeys )) - $attr1 .= $this->intAttrDelimiter.'VALUE='.$params['VALUE']; - if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys )) { - $attr1 .= $this->intAttrDelimiter.'TZID='.$params['TZID']; - } - if( isset( $params['RANGE'] ) && !in_array( 'RANGE', $ctrKeys )) - $attr1 .= $this->intAttrDelimiter.'RANGE='.$params['RANGE']; - if( isset( $params['RELTYPE'] ) && !in_array( 'RELTYPE', $ctrKeys )) - $attr1 .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE']; - if( isset( $params['CN'] ) && $CNattrKey ) { - $attr1 = $this->intAttrDelimiter.'CN='.$params['CN']; - $CNattrExist = TRUE; - } - if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) { - $delim = ( FALSE !== strpos( $params['DIR'], '"' )) ? '' : '"'; - $attr1 .= $this->intAttrDelimiter.'DIR='.$delim.$params['DIR'].$delim; - } - if( isset( $params['SENT-BY'] ) && in_array( 'SENT-BY', $ctrKeys )) - $attr1 .= $this->intAttrDelimiter.'SENT-BY='.$params['SENT-BY']; - if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys )) { - $delim = ( FALSE !== strpos( $params['ALTREP'], '"' )) ? '' : '"'; - $attr1 .= $this->intAttrDelimiter.'ALTREP='.$delim.$params['ALTREP'].$delim; - } - if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) { - $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE']; - $LANGattrExist = TRUE; - } - if( !$LANGattrExist ) { - $lang = $this->getConfig( 'language' ); - if(( $CNattrExist || $LANGattrKey ) && $lang ) - $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang; - } - return $attr1.$attrLANG.$attr2; - } -/** - * creates formatted output for calendar component property data value type recur - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-10-06 - * @param array $recurlabel - * @param array $recurdata - * @return string - */ - function _format_recur( $recurlabel, $recurdata ) { - $output = null; - foreach( $recurdata as $therule ) { - if( empty( $therule['value'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel ); - continue; - } - $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null; - $content1 = $content2 = null; - foreach( $therule['value'] as $rulelabel => $rulevalue ) { - switch( $rulelabel ) { - case 'FREQ': { - $content1 .= "FREQ=$rulevalue"; - break; - } - case 'UNTIL': { - $parno = ( isset( $rulevalue['hour'] )) ? 7 : 3; - $content2 .= ';UNTIL='.iCalUtilityFunctions::_date2strdate( $rulevalue, $parno ); - break; - } - case 'COUNT': - case 'INTERVAL': - case 'WKST': { - $content2 .= ";$rulelabel=$rulevalue"; - break; - } - case 'BYSECOND': - case 'BYMINUTE': - case 'BYHOUR': - case 'BYMONTHDAY': - case 'BYYEARDAY': - case 'BYWEEKNO': - case 'BYMONTH': - case 'BYSETPOS': { - $content2 .= ";$rulelabel="; - if( is_array( $rulevalue )) { - foreach( $rulevalue as $vix => $valuePart ) { - $content2 .= ( $vix ) ? ',' : null; - $content2 .= $valuePart; - } - } - else - $content2 .= $rulevalue; - break; - } - case 'BYDAY': { - $content2 .= ";$rulelabel="; - $bydaycnt = 0; - foreach( $rulevalue as $vix => $valuePart ) { - $content21 = $content22 = null; - if( is_array( $valuePart )) { - $content2 .= ( $bydaycnt ) ? ',' : null; - foreach( $valuePart as $vix2 => $valuePart2 ) { - if( 'DAY' != strtoupper( $vix2 )) - $content21 .= $valuePart2; - else - $content22 .= $valuePart2; - } - $content2 .= $content21.$content22; - $bydaycnt++; - } - else { - $content2 .= ( $bydaycnt ) ? ',' : null; - if( 'DAY' != strtoupper( $vix )) - $content21 .= $valuePart; - else { - $content22 .= $valuePart; - $bydaycnt++; - } - $content2 .= $content21.$content22; - } - } - break; - } - default: { - $content2 .= ";$rulelabel=$rulevalue"; - break; - } - } - } - $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 ); - } - return $output; - } -/** - * check if property not exists within component - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-15 - * @param string $propName - * @return bool - */ - function _notExistProp( $propName ) { - if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed - $propName = strtolower( $propName ); - if( 'last-modified' == $propName ) { if( !isset( $this->lastmodified )) return TRUE; } - elseif( 'percent-complete' == $propName ) { if( !isset( $this->percentcomplete )) return TRUE; } - elseif( 'recurrence-id' == $propName ) { if( !isset( $this->recurrenceid )) return TRUE; } - elseif( 'related-to' == $propName ) { if( !isset( $this->relatedto )) return TRUE; } - elseif( 'request-status' == $propName ) { if( !isset( $this->requeststatus )) return TRUE; } - elseif(( 'x-' != substr($propName,0,2)) && !isset( $this->$propName )) return TRUE; - return FALSE; - } -/*********************************************************************************/ -/*********************************************************************************/ -/** - * get general component config variables or info about subcomponents - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.6 - 2011-05-14 - * @param mixed $config - * @return value - */ - function getConfig( $config = FALSE) { - if( !$config ) { - $return = array(); - $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' ); - $return['FORMAT'] = $this->getConfig( 'FORMAT' ); - if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' ))) - $return['LANGUAGE'] = $lang; - $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' ); - $return['TZTD'] = $this->getConfig( 'TZID' ); - $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' ); - return $return; - } - switch( strtoupper( $config )) { - case 'ALLOWEMPTY': - return $this->allowEmpty; - break; - case 'COMPSINFO': - unset( $this->compix ); - $info = array(); - if( isset( $this->components )) { - foreach( $this->components as $cix => $component ) { - if( empty( $component )) continue; - $info[$cix]['ordno'] = $cix + 1; - $info[$cix]['type'] = $component->objName; - $info[$cix]['uid'] = $component->getProperty( 'uid' ); - $info[$cix]['props'] = $component->getConfig( 'propinfo' ); - $info[$cix]['sub'] = $component->getConfig( 'compsinfo' ); - } - } - return $info; - break; - case 'FORMAT': - return $this->format; - break; - case 'LANGUAGE': - // get language for calendar component as defined in [RFC 1766] - return $this->language; - break; - case 'NL': - case 'NEWLINECHAR': - return $this->nl; - break; - case 'PROPINFO': - $output = array(); - if( !in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) { - if( empty( $this->uid['value'] )) $this->_makeuid(); - $output['UID'] = 1; - if( empty( $this->dtstamp )) $this->_makeDtstamp(); - $output['DTSTAMP'] = 1; - } - if( !empty( $this->summary )) $output['SUMMARY'] = 1; - if( !empty( $this->description )) $output['DESCRIPTION'] = count( $this->description ); - if( !empty( $this->dtstart )) $output['DTSTART'] = 1; - if( !empty( $this->dtend )) $output['DTEND'] = 1; - if( !empty( $this->due )) $output['DUE'] = 1; - if( !empty( $this->duration )) $output['DURATION'] = 1; - if( !empty( $this->rrule )) $output['RRULE'] = count( $this->rrule ); - if( !empty( $this->rdate )) $output['RDATE'] = count( $this->rdate ); - if( !empty( $this->exdate )) $output['EXDATE'] = count( $this->exdate ); - if( !empty( $this->exrule )) $output['EXRULE'] = count( $this->exrule ); - if( !empty( $this->action )) $output['ACTION'] = 1; - if( !empty( $this->attach )) $output['ATTACH'] = count( $this->attach ); - if( !empty( $this->attendee )) $output['ATTENDEE'] = count( $this->attendee ); - if( !empty( $this->categories )) $output['CATEGORIES'] = count( $this->categories ); - if( !empty( $this->class )) $output['CLASS'] = 1; - if( !empty( $this->comment )) $output['COMMENT'] = count( $this->comment ); - if( !empty( $this->completed )) $output['COMPLETED'] = 1; - if( !empty( $this->contact )) $output['CONTACT'] = count( $this->contact ); - if( !empty( $this->created )) $output['CREATED'] = 1; - if( !empty( $this->freebusy )) $output['FREEBUSY'] = count( $this->freebusy ); - if( !empty( $this->geo )) $output['GEO'] = 1; - if( !empty( $this->lastmodified )) $output['LAST-MODIFIED'] = 1; - if( !empty( $this->location )) $output['LOCATION'] = 1; - if( !empty( $this->organizer )) $output['ORGANIZER'] = 1; - if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1; - if( !empty( $this->priority )) $output['PRIORITY'] = 1; - if( !empty( $this->recurrenceid )) $output['RECURRENCE-ID'] = 1; - if( !empty( $this->relatedto )) $output['RELATED-TO'] = count( $this->relatedto ); - if( !empty( $this->repeat )) $output['REPEAT'] = 1; - if( !empty( $this->requeststatus )) $output['REQUEST-STATUS'] = count( $this->requeststatus ); - if( !empty( $this->resources )) $output['RESOURCES'] = count( $this->resources ); - if( !empty( $this->sequence )) $output['SEQUENCE'] = 1; - if( !empty( $this->sequence )) $output['SEQUENCE'] = 1; - if( !empty( $this->status )) $output['STATUS'] = 1; - if( !empty( $this->transp )) $output['TRANSP'] = 1; - if( !empty( $this->trigger )) $output['TRIGGER'] = 1; - if( !empty( $this->tzid )) $output['TZID'] = 1; - if( !empty( $this->tzname )) $output['TZNAME'] = count( $this->tzname ); - if( !empty( $this->tzoffsetfrom )) $output['TZOFFSETFROM'] = 1; - if( !empty( $this->tzoffsetto )) $output['TZOFFSETTO'] = 1; - if( !empty( $this->tzurl )) $output['TZURL'] = 1; - if( !empty( $this->url )) $output['URL'] = 1; - if( !empty( $this->xprop )) $output['X-PROP'] = count( $this->xprop ); - return $output; - break; - case 'SETPROPERTYNAMES': - return array_keys( $this->getConfig( 'propinfo' )); - break; - case 'TZID': - return $this->dtzid; - break; - case 'UNIQUE_ID': - if( empty( $this->unique_id )) - $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost'; - return $this->unique_id; - break; - } - } -/** - * general component config setting - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.18 - 2011-10-28 - * @param mixed $config - * @param string $value - * @param bool $softUpdate - * @return void - */ - function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) { - if( is_array( $config )) { - $ak = array_keys( $config ); - foreach( $ak as $k ) { - if( 'NEWLINECHAR' == strtoupper( $k )) { - if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] )) - return FALSE; - unset( $config[$k] ); - break; - } - } - foreach( $config as $cKey => $cValue ) { - if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate )) - return FALSE; - } - return TRUE; - } - $res = FALSE; - switch( strtoupper( $config )) { - case 'ALLOWEMPTY': - $this->allowEmpty = $value; - $subcfg = array( 'ALLOWEMPTY' => $value ); - $res = TRUE; - break; - case 'FORMAT': - $value = trim( strtolower( $value )); - $this->format = $value; - $this->_createFormat(); - $subcfg = array( 'FORMAT' => $value ); - $res = TRUE; - break; - case 'LANGUAGE': - // set language for calendar component as defined in [RFC 1766] - $value = trim( $value ); - if( empty( $this->language ) || !$softUpdate ) - $this->language = $value; - $subcfg = array( 'LANGUAGE' => $value ); - $res = TRUE; - break; - case 'NL': - case 'NEWLINECHAR': - $this->nl = $value; - $this->_createFormat(); - $subcfg = array( 'NL' => $value ); - $res = TRUE; - break; - case 'TZID': - $this->dtzid = $value; - $subcfg = array( 'TZID' => $value ); - $res = TRUE; - break; - case 'UNIQUE_ID': - $value = trim( $value ); - $this->unique_id = $value; - $subcfg = array( 'UNIQUE_ID' => $value ); - $res = TRUE; - break; - default: // any unvalid config key.. . - return TRUE; - } - if( !$res ) return FALSE; - if( isset( $subcfg ) && !empty( $this->components )) { - foreach( $subcfg as $cfgkey => $cfgvalue ) { - foreach( $this->components as $cix => $component ) { - $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate ); - if( !$res ) - break 2; - $this->components[$cix] = $component->copy(); // PHP4 compliant - } - } - } - return $res; - } -/*********************************************************************************/ -/** - * delete component property value - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $propName, bool FALSE => X-property - * @param int $propix, optional, if specific property is wanted in case of multiply occurences - * @return bool, if successfull delete TRUE - */ - function deleteProperty( $propName=FALSE, $propix=FALSE ) { - if( $this->_notExistProp( $propName )) return FALSE; - $propName = strtoupper( $propName ); - if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE', - 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) { - if( !$propix ) - $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1; - $this->propdelix[$propName] = --$propix; - } - $return = FALSE; - switch( $propName ) { - case 'ACTION': - if( !empty( $this->action )) { - $this->action = ''; - $return = TRUE; - } - break; - case 'ATTACH': - return $this->deletePropertyM( $this->attach, $this->propdelix[$propName] ); - break; - case 'ATTENDEE': - return $this->deletePropertyM( $this->attendee, $this->propdelix[$propName] ); - break; - case 'CATEGORIES': - return $this->deletePropertyM( $this->categories, $this->propdelix[$propName] ); - break; - case 'CLASS': - if( !empty( $this->class )) { - $this->class = ''; - $return = TRUE; - } - break; - case 'COMMENT': - return $this->deletePropertyM( $this->comment, $this->propdelix[$propName] ); - break; - case 'COMPLETED': - if( !empty( $this->completed )) { - $this->completed = ''; - $return = TRUE; - } - break; - case 'CONTACT': - return $this->deletePropertyM( $this->contact, $this->propdelix[$propName] ); - break; - case 'CREATED': - if( !empty( $this->created )) { - $this->created = ''; - $return = TRUE; - } - break; - case 'DESCRIPTION': - return $this->deletePropertyM( $this->description, $this->propdelix[$propName] ); - break; - case 'DTEND': - if( !empty( $this->dtend )) { - $this->dtend = ''; - $return = TRUE; - } - break; - case 'DTSTAMP': - if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) - return FALSE; - if( !empty( $this->dtstamp )) { - $this->dtstamp = ''; - $return = TRUE; - } - break; - case 'DTSTART': - if( !empty( $this->dtstart )) { - $this->dtstart = ''; - $return = TRUE; - } - break; - case 'DUE': - if( !empty( $this->due )) { - $this->due = ''; - $return = TRUE; - } - break; - case 'DURATION': - if( !empty( $this->duration )) { - $this->duration = ''; - $return = TRUE; - } - break; - case 'EXDATE': - return $this->deletePropertyM( $this->exdate, $this->propdelix[$propName] ); - break; - case 'EXRULE': - return $this->deletePropertyM( $this->exrule, $this->propdelix[$propName] ); - break; - case 'FREEBUSY': - return $this->deletePropertyM( $this->freebusy, $this->propdelix[$propName] ); - break; - case 'GEO': - if( !empty( $this->geo )) { - $this->geo = ''; - $return = TRUE; - } - break; - case 'LAST-MODIFIED': - if( !empty( $this->lastmodified )) { - $this->lastmodified = ''; - $return = TRUE; - } - break; - case 'LOCATION': - if( !empty( $this->location )) { - $this->location = ''; - $return = TRUE; - } - break; - case 'ORGANIZER': - if( !empty( $this->organizer )) { - $this->organizer = ''; - $return = TRUE; - } - break; - case 'PERCENT-COMPLETE': - if( !empty( $this->percentcomplete )) { - $this->percentcomplete = ''; - $return = TRUE; - } - break; - case 'PRIORITY': - if( !empty( $this->priority )) { - $this->priority = ''; - $return = TRUE; - } - break; - case 'RDATE': - return $this->deletePropertyM( $this->rdate, $this->propdelix[$propName] ); - break; - case 'RECURRENCE-ID': - if( !empty( $this->recurrenceid )) { - $this->recurrenceid = ''; - $return = TRUE; - } - break; - case 'RELATED-TO': - return $this->deletePropertyM( $this->relatedto, $this->propdelix[$propName] ); - break; - case 'REPEAT': - if( !empty( $this->repeat )) { - $this->repeat = ''; - $return = TRUE; - } - break; - case 'REQUEST-STATUS': - return $this->deletePropertyM( $this->requeststatus, $this->propdelix[$propName] ); - break; - case 'RESOURCES': - return $this->deletePropertyM( $this->resources, $this->propdelix[$propName] ); - break; - case 'RRULE': - return $this->deletePropertyM( $this->rrule, $this->propdelix[$propName] ); - break; - case 'SEQUENCE': - if( !empty( $this->sequence )) { - $this->sequence = ''; - $return = TRUE; - } - break; - case 'STATUS': - if( !empty( $this->status )) { - $this->status = ''; - $return = TRUE; - } - break; - case 'SUMMARY': - if( !empty( $this->summary )) { - $this->summary = ''; - $return = TRUE; - } - break; - case 'TRANSP': - if( !empty( $this->transp )) { - $this->transp = ''; - $return = TRUE; - } - break; - case 'TRIGGER': - if( !empty( $this->trigger )) { - $this->trigger = ''; - $return = TRUE; - } - break; - case 'TZID': - if( !empty( $this->tzid )) { - $this->tzid = ''; - $return = TRUE; - } - break; - case 'TZNAME': - return $this->deletePropertyM( $this->tzname, $this->propdelix[$propName] ); - break; - case 'TZOFFSETFROM': - if( !empty( $this->tzoffsetfrom )) { - $this->tzoffsetfrom = ''; - $return = TRUE; - } - break; - case 'TZOFFSETTO': - if( !empty( $this->tzoffsetto )) { - $this->tzoffsetto = ''; - $return = TRUE; - } - break; - case 'TZURL': - if( !empty( $this->tzurl )) { - $this->tzurl = ''; - $return = TRUE; - } - break; - case 'UID': - if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) - return FALSE; - if( !empty( $this->uid )) { - $this->uid = ''; - $return = TRUE; - } - break; - case 'URL': - if( !empty( $this->url )) { - $this->url = ''; - $return = TRUE; - } - break; - default: - $reduced = ''; - if( $propName != 'X-PROP' ) { - if( !isset( $this->xprop[$propName] )) return FALSE; - foreach( $this->xprop as $k => $a ) { - if(( $k != $propName ) && !empty( $a )) - $reduced[$k] = $a; - } - } - else { - if( count( $this->xprop ) <= $propix ) { unset( $this->propdelix[$propName] ); return FALSE; } - $xpropno = 0; - foreach( $this->xprop as $xpropkey => $xpropvalue ) { - if( $propix != $xpropno ) - $reduced[$xpropkey] = $xpropvalue; - $xpropno++; - } - } - $this->xprop = $reduced; - if( empty( $this->xprop )) { - unset( $this->propdelix[$propName] ); - return FALSE; - } - return TRUE; - } - return $return; - } -/*********************************************************************************/ -/** - * delete component property value, fixing components with multiple occurencies - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param array $multiprop, reference to a component property - * @param int $propix, reference to removal counter - * @return bool TRUE - */ - function deletePropertyM( & $multiprop, & $propix ) { - if( isset( $multiprop[$propix] )) - unset( $multiprop[$propix] ); - if( empty( $multiprop )) { - $multiprop = ''; - unset( $propix ); - return FALSE; - } - else - return TRUE; - } -/** - * get component property value/params - * - * if property has multiply values, consequtive function calls are needed - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.12.4 - 2012-04-22 - * @param string $propName, optional - * @param int @propix, optional, if specific property is wanted in case of multiply occurences - * @param bool $inclParam=FALSE - * @param bool $specform=FALSE - * @return mixed - */ - function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) { - if( 'GEOLOCATION' == strtoupper( $propName )) { - $content = $this->getProperty( 'LOCATION' ); - $content = ( !empty( $content )) ? $content.' ' : ''; - if(( FALSE === ( $geo = $this->getProperty( 'GEO' ))) || empty( $geo )) - return FALSE; - if( 0.0 < $geo['latitude'] ) - $sign = '+'; - else - $sign = ( 0.0 > $geo['latitude'] ) ? '-' : ''; - $content .= $sign.sprintf( "%09.6f", abs( $geo['latitude'] )); // sprintf && lpad && float && sign !"#¤%&/( - $content = rtrim( rtrim( $content, '0' ), '.' ); - if( 0.0 < $geo['longitude'] ) - $sign = '+'; - else - $sign = ( 0.0 > $geo['longitude'] ) ? '-' : ''; - return $content.$sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/'; // sprintf && lpad && float && sign !"#¤%&/( - } - if( $this->_notExistProp( $propName )) return FALSE; - $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; - if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE', - 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) { - if( !$propix ) - $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1; - $this->propix[$propName] = --$propix; - } - switch( $propName ) { - case 'ACTION': - if( !empty( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value']; - break; - case 'ATTACH': - $ak = ( is_array( $this->attach )) ? array_keys( $this->attach ) : array(); - while( is_array( $this->attach ) && !isset( $this->attach[$propix] ) && ( 0 < count( $this->attach )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->attach[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value']; - break; - case 'ATTENDEE': - $ak = ( is_array( $this->attendee )) ? array_keys( $this->attendee ) : array(); - while( is_array( $this->attendee ) && !isset( $this->attendee[$propix] ) && ( 0 < count( $this->attendee )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->attendee[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value']; - break; - case 'CATEGORIES': - $ak = ( is_array( $this->categories )) ? array_keys( $this->categories ) : array(); - while( is_array( $this->categories ) && !isset( $this->categories[$propix] ) && ( 0 < count( $this->categories )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->categories[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value']; - break; - case 'CLASS': - if( !empty( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value']; - break; - case 'COMMENT': - $ak = ( is_array( $this->comment )) ? array_keys( $this->comment ) : array(); - while( is_array( $this->comment ) && !isset( $this->comment[$propix] ) && ( 0 < count( $this->comment )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->comment[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value']; - break; - case 'COMPLETED': - if( !empty( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value']; - break; - case 'CONTACT': - $ak = ( is_array( $this->contact )) ? array_keys( $this->contact ) : array(); - while( is_array( $this->contact ) && !isset( $this->contact[$propix] ) && ( 0 < count( $this->contact )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->contact[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value']; - break; - case 'CREATED': - if( !empty( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value']; - break; - case 'DESCRIPTION': - $ak = ( is_array( $this->description )) ? array_keys( $this->description ) : array(); - while( is_array( $this->description ) && !isset( $this->description[$propix] ) && ( 0 < count( $this->description )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->description[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value']; - break; - case 'DTEND': - if( !empty( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value']; - break; - case 'DTSTAMP': - if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) - return; - if( !isset( $this->dtstamp['value'] )) - $this->_makeDtstamp(); - return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value']; - break; - case 'DTSTART': - if( !empty( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value']; - break; - case 'DUE': - if( !empty( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value']; - break; - case 'DURATION': - if( !isset( $this->duration['value'] )) return FALSE; - $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value']; - return ( $inclParam ) ? array( 'value' => $value, 'params' => $this->duration['params'] ) : $value; - break; - case 'EXDATE': - $ak = ( is_array( $this->exdate )) ? array_keys( $this->exdate ) : array(); - while( is_array( $this->exdate ) && !isset( $this->exdate[$propix] ) && ( 0 < count( $this->exdate )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->exdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value']; - break; - case 'EXRULE': - $ak = ( is_array( $this->exrule )) ? array_keys( $this->exrule ) : array(); - while( is_array( $this->exrule ) && !isset( $this->exrule[$propix] ) && ( 0 < count( $this->exrule )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->exrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value']; - break; - case 'FREEBUSY': - $ak = ( is_array( $this->freebusy )) ? array_keys( $this->freebusy ) : array(); - while( is_array( $this->freebusy ) && !isset( $this->freebusy[$propix] ) && ( 0 < count( $this->freebusy )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->freebusy[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value']; - break; - case 'GEO': - if( !empty( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value']; - break; - case 'LAST-MODIFIED': - if( !empty( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value']; - break; - case 'LOCATION': - if( !empty( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value']; - break; - case 'ORGANIZER': - if( !empty( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value']; - break; - case 'PERCENT-COMPLETE': - if( !empty( $this->percentcomplete['value'] ) || ( isset( $this->percentcomplete['value'] ) && ( '0' == $this->percentcomplete['value'] ))) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value']; - break; - case 'PRIORITY': - if( !empty( $this->priority['value'] ) || ( isset( $this->priority['value'] ) && ('0' == $this->priority['value'] ))) return ( $inclParam ) ? $this->priority : $this->priority['value']; - break; - case 'RDATE': - $ak = ( is_array( $this->rdate )) ? array_keys( $this->rdate ) : array(); - while( is_array( $this->rdate ) && !isset( $this->rdate[$propix] ) && ( 0 < count( $this->rdate )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->rdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value']; - break; - case 'RECURRENCE-ID': - if( !empty( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value']; - break; - case 'RELATED-TO': - $ak = ( is_array( $this->relatedto )) ? array_keys( $this->relatedto ) : array(); - while( is_array( $this->relatedto ) && !isset( $this->relatedto[$propix] ) && ( 0 < count( $this->relatedto )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->relatedto[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value']; - break; - case 'REPEAT': - if( !empty( $this->repeat['value'] ) || ( isset( $this->repeat['value'] ) && ( '0' == $this->repeat['value'] ))) return ( $inclParam ) ? $this->repeat : $this->repeat['value']; - break; - case 'REQUEST-STATUS': - $ak = ( is_array( $this->requeststatus )) ? array_keys( $this->requeststatus ) : array(); - while( is_array( $this->requeststatus ) && !isset( $this->requeststatus[$propix] ) && ( 0 < count( $this->requeststatus )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->requeststatus[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value']; - break; - case 'RESOURCES': - $ak = ( is_array( $this->resources )) ? array_keys( $this->resources ) : array(); - while( is_array( $this->resources ) && !isset( $this->resources[$propix] ) && ( 0 < count( $this->resources )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->resources[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value']; - break; - case 'RRULE': - $ak = ( is_array( $this->rrule )) ? array_keys( $this->rrule ) : array(); - while( is_array( $this->rrule ) && !isset( $this->rrule[$propix] ) && ( 0 < count( $this->rrule )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->rrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value']; - break; - case 'SEQUENCE': - if( isset( $this->sequence['value'] ) && ( isset( $this->sequence['value'] ) && ( '0' <= $this->sequence['value'] ))) return ( $inclParam ) ? $this->sequence : $this->sequence['value']; - break; - case 'STATUS': - if( !empty( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value']; - break; - case 'SUMMARY': - if( !empty( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value']; - break; - case 'TRANSP': - if( !empty( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value']; - break; - case 'TRIGGER': - if( !empty( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value']; - break; - case 'TZID': - if( !empty( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value']; - break; - case 'TZNAME': - $ak = ( is_array( $this->tzname )) ? array_keys( $this->tzname ) : array(); - while( is_array( $this->tzname ) && !isset( $this->tzname[$propix] ) && ( 0 < count( $this->tzname )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->tzname[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value']; - break; - case 'TZOFFSETFROM': - if( !empty( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value']; - break; - case 'TZOFFSETTO': - if( !empty( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value']; - break; - case 'TZURL': - if( !empty( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value']; - break; - case 'UID': - if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) - return FALSE; - if( empty( $this->uid['value'] )) - $this->_makeuid(); - return ( $inclParam ) ? $this->uid : $this->uid['value']; - break; - case 'URL': - if( !empty( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value']; - break; - default: - if( $propName != 'X-PROP' ) { - if( !isset( $this->xprop[$propName] )) return FALSE; - return ( $inclParam ) ? array( $propName, $this->xprop[$propName] ) - : array( $propName, $this->xprop[$propName]['value'] ); - } - else { - if( empty( $this->xprop )) return FALSE; - $xpropno = 0; - foreach( $this->xprop as $xpropkey => $xpropvalue ) { - if( $propix == $xpropno ) - return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] ) - : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); - else - $xpropno++; - } - return FALSE; // not found ?? - } - } - return FALSE; - } -/** - * returns calendar property unique values for 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO' or 'RESOURCES' and for each, number of occurrence - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.13.4 - 2012-08-07 - * @param string $propName - * @param array $output, incremented result array - */ - function _getProperties( $propName, & $output ) { - if( empty( $output )) - $output = array(); - if( !in_array( strtoupper( $propName ), array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' ))) - return $output; - while( FALSE !== ( $content = $this->getProperty( $propName ))) { - if( empty( $content )) - continue; - if( is_array( $content )) { - foreach( $content as $part ) { - if( FALSE !== strpos( $part, ',' )) { - $part = explode( ',', $part ); - foreach( $part as $thePart ) { - $thePart = trim( $thePart ); - if( !empty( $thePart )) { - if( !isset( $output[$thePart] )) - $output[$thePart] = 1; - else - $output[$thePart] += 1; - } - } - } - else { - $part = trim( $part ); - if( !isset( $output[$part] )) - $output[$part] = 1; - else - $output[$part] += 1; - } - } - } // end if( is_array( $content )) - elseif( FALSE !== strpos( $content, ',' )) { - $content = explode( ',', $content ); - foreach( $content as $thePart ) { - $thePart = trim( $thePart ); - if( !empty( $thePart )) { - if( !isset( $output[$thePart] )) - $output[$thePart] = 1; - else - $output[$thePart] += 1; - } - } - } // end elseif( FALSE !== strpos( $content, ',' )) - else { - $content = trim( $content ); - if( !empty( $content )) { - if( !isset( $output[$content] )) - $output[$content] = 1; - else - $output[$content] += 1; - } - } - } - ksort( $output ); - } -/** - * general component property setting - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-05 - * @param mixed $args variable number of function arguments, - * first argument is ALWAYS component name, - * second ALWAYS component value! - * @return void - */ - function setProperty() { - $numargs = func_num_args(); - if( 1 > $numargs ) return FALSE; - $arglist = func_get_args(); - if( $this->_notExistProp( $arglist[0] )) return FALSE; - if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] ))) - return FALSE; - $arglist[0] = strtoupper( $arglist[0] ); - for( $argix=$numargs; $argix < 12; $argix++ ) { - if( !isset( $arglist[$argix] )) - $arglist[$argix] = null; - } - switch( $arglist[0] ) { - case 'ACTION': - return $this->setAction( $arglist[1], $arglist[2] ); - case 'ATTACH': - return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] ); - case 'ATTENDEE': - return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] ); - case 'CATEGORIES': - return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] ); - case 'CLASS': - return $this->setClass( $arglist[1], $arglist[2] ); - case 'COMMENT': - return $this->setComment( $arglist[1], $arglist[2], $arglist[3] ); - case 'COMPLETED': - return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); - case 'CONTACT': - return $this->setContact( $arglist[1], $arglist[2], $arglist[3] ); - case 'CREATED': - return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); - case 'DESCRIPTION': - return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] ); - case 'DTEND': - return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); - case 'DTSTAMP': - return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); - case 'DTSTART': - return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); - case 'DUE': - return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); - case 'DURATION': - return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] ); - case 'EXDATE': - return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] ); - case 'EXRULE': - return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] ); - case 'FREEBUSY': - return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] ); - case 'GEO': - return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] ); - case 'LAST-MODIFIED': - return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); - case 'LOCATION': - return $this->setLocation( $arglist[1], $arglist[2] ); - case 'ORGANIZER': - return $this->setOrganizer( $arglist[1], $arglist[2] ); - case 'PERCENT-COMPLETE': - return $this->setPercentComplete( $arglist[1], $arglist[2] ); - case 'PRIORITY': - return $this->setPriority( $arglist[1], $arglist[2] ); - case 'RDATE': - return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] ); - case 'RECURRENCE-ID': - return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); - case 'RELATED-TO': - return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] ); - case 'REPEAT': - return $this->setRepeat( $arglist[1], $arglist[2] ); - case 'REQUEST-STATUS': - return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] ); - case 'RESOURCES': - return $this->setResources( $arglist[1], $arglist[2], $arglist[3] ); - case 'RRULE': - return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] ); - case 'SEQUENCE': - return $this->setSequence( $arglist[1], $arglist[2] ); - case 'STATUS': - return $this->setStatus( $arglist[1], $arglist[2] ); - case 'SUMMARY': - return $this->setSummary( $arglist[1], $arglist[2] ); - case 'TRANSP': - return $this->setTransp( $arglist[1], $arglist[2] ); - case 'TRIGGER': - return $this->setTrigger( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8], $arglist[9], $arglist[10], $arglist[11] ); - case 'TZID': - return $this->setTzid( $arglist[1], $arglist[2] ); - case 'TZNAME': - return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] ); - case 'TZOFFSETFROM': - return $this->setTzoffsetfrom( $arglist[1], $arglist[2] ); - case 'TZOFFSETTO': - return $this->setTzoffsetto( $arglist[1], $arglist[2] ); - case 'TZURL': - return $this->setTzurl( $arglist[1], $arglist[2] ); - case 'UID': - return $this->setUid( $arglist[1], $arglist[2] ); - case 'URL': - return $this->setUrl( $arglist[1], $arglist[2] ); - default: - return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] ); - } - return FALSE; - } -/*********************************************************************************/ -/** - * parse component unparsed data into properties - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of strings - * @return bool FALSE if error occurs during parsing - * - */ - function parse( $unparsedtext=null ) { - $nl = $this->getConfig( 'nl' ); - if( !empty( $unparsedtext )) { - if( is_array( $unparsedtext )) - $unparsedtext = implode( '\n'.$nl, $unparsedtext ); - $unparsedtext = explode( $nl, iCalUtilityFunctions::convEolChar( $unparsedtext, $nl )); - } - elseif( !isset( $this->unparsed )) - $unparsedtext = array(); - else - $unparsedtext = $this->unparsed; - /* skip leading (empty/invalid) lines */ - foreach( $unparsedtext as $lix => $line ) { - $tst = trim( $line ); - if(( '\n' == $tst ) || empty( $tst )) - unset( $unparsedtext[$lix] ); - else - break; - } - $this->unparsed = array(); - $comp = & $this; - $config = $this->getConfig(); - $compsync = $subsync = 0; - foreach ( $unparsedtext as $lix => $line ) { - if( 'END:VALARM' == strtoupper( substr( $line, 0, 10 ))) { - if( 1 != $subsync ) return FALSE; - $this->components[] = $comp->copy(); - $subsync--; - } - elseif( 'END:DAYLIGHT' == strtoupper( substr( $line, 0, 12 ))) { - if( 1 != $subsync ) return FALSE; - $this->components[] = $comp->copy(); - $subsync--; - } - elseif( 'END:STANDARD' == strtoupper( substr( $line, 0, 12 ))) { - if( 1 != $subsync ) return FALSE; - array_unshift( $this->components, $comp->copy()); - $subsync--; - } - elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) { // end: - if( 1 != $compsync ) return FALSE; - if( 0 < $subsync ) - $this->components[] = $comp->copy(); - $compsync--; - break; /* skip trailing empty lines */ - } - elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) { - $comp = new valarm( $config); - $subsync++; - } - elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) { - $comp = new vtimezone( 'standard', $config ); - $subsync++; - } - elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) { - $comp = new vtimezone( 'daylight', $config ); - $subsync++; - } - elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) // begin: - $compsync++; - else - $comp->unparsed[] = $line; - } - if( 0 < $subsync ) - $this->components[] = $comp->copy(); - unset( $config ); - /* concatenate property values spread over several lines */ - $lastix = -1; - $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed' - , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart' - , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo' - , 'last-modified', 'location', 'organizer', 'percent-complete' - , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat' - , 'request-status', 'resources', 'rrule', 'sequence', 'status' - , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom' - , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' ); - $proprows = array(); - for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines - $line = rtrim( $this->unparsed[$i], $nl ); - while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} )) - $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl ); - $proprows[] = $line; - } - /* parse each property 'line' */ - $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' ); - $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ); - $paramProto4 = array( 'crid:', 'news:', 'pres:' ); - foreach( $proprows as $line ) { - if( '\n' == substr( $line, -2 )) - $line = substr( $line, 0, -2 ); - /* get propname */ - $propname = null; - $cix = 0; - while( isset( $line[$cix] )) { - if( in_array( $line[$cix], array( ':', ';' ))) - break; - else - $propname .= $line[$cix]; - $cix++; - } - if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) { - $propname2 = $propname; - $propname = 'X-'; - } - if( !in_array( strtolower( $propname ), $propnames )) // skip non standard property names - continue; - /* rest of the line is opt.params and value */ - $line = substr( $line, $cix ); - /* separate attributes from value */ - $attr = array(); - $attrix = -1; - $clen = strlen( $line ); - $WithinQuotes = FALSE; - $cix = 0; - while( FALSE !== substr( $line, $cix, 1 )) { - if( ( ':' == $line[$cix] ) && - ( substr( $line,$cix, 3 ) != '://' ) && - ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) && - ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) && - ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) && - ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) && - !$WithinQuotes ) { - $attrEnd = TRUE; - if(( $cix < ( $clen - 4 )) && - ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr?? - for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) { - if( '://' == substr( $line, $c2ix - 2, 3 )) { - $attrEnd = FALSE; - break; // an URI with a portnr!! - } - } - } - if( $attrEnd) { - $line = substr( $line, ( $cix + 1 )); - break; - } - $cix++; - } - if( '"' == $line[$cix] ) - $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE; - if( ';' == $line[$cix] ) - $attr[++$attrix] = null; - else - $attr[$attrix] .= $line[$cix]; - $cix++; - } - /* make attributes in array format */ - $propattr = array(); - foreach( $attr as $attribute ) { - $attrsplit = explode( '=', $attribute, 2 ); - if( 1 < count( $attrsplit )) - $propattr[$attrsplit[0]] = $attrsplit[1]; - else - $propattr[] = $attribute; - } - /* call setProperty( $propname.. . */ - switch( strtoupper( $propname )) { - case 'ATTENDEE': - foreach( $propattr as $pix => $attr ) { - if( !in_array( strtoupper( $pix ), array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ))) - continue; - $attr2 = explode( ',', $attr ); - if( 1 < count( $attr2 )) - $propattr[$pix] = $attr2; - } - $this->setProperty( $propname, $line, $propattr ); - break; - case 'X-': - $propname = ( isset( $propname2 )) ? $propname2 : $propname; - unset( $propname2 ); - case 'CATEGORIES': - case 'RESOURCES': - if( FALSE !== strpos( $line, ',' )) { - $content = array( 0 => '' ); - $cix = $lix = 0; - while( FALSE !== substr( $line, $lix, 1 )) { - if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { - $cix++; - $content[$cix] = ''; - } - else - $content[$cix] .= $line[$lix]; - $lix++; - } - if( 1 < count( $content )) { - $content = array_values( $content ); - foreach( $content as $cix => $contentPart ) - $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart ); - $this->setProperty( $propname, $content, $propattr ); - break; - } - else - $line = reset( $content ); - } - case 'COMMENT': - case 'CONTACT': - case 'DESCRIPTION': - case 'LOCATION': - case 'SUMMARY': - if( empty( $line )) - $propattr = null; - $this->setProperty( $propname, iCalUtilityFunctions::_strunrep( $line ), $propattr ); - break; - case 'REQUEST-STATUS': - $values = explode( ';', $line, 3 ); - $values[1] = ( !isset( $values[1] )) ? null : iCalUtilityFunctions::_strunrep( $values[1] ); - $values[2] = ( !isset( $values[2] )) ? null : iCalUtilityFunctions::_strunrep( $values[2] ); - $this->setProperty( $propname - , $values[0] // statcode - , $values[1] // statdesc - , $values[2] // extdata - , $propattr ); - break; - case 'FREEBUSY': - $fbtype = ( isset( $propattr['FBTYPE'] )) ? $propattr['FBTYPE'] : ''; // force setting default, if missing - unset( $propattr['FBTYPE'] ); - $values = explode( ',', $line ); - foreach( $values as $vix => $value ) { - $value2 = explode( '/', $value ); - if( 1 < count( $value2 )) - $values[$vix] = $value2; - } - $this->setProperty( $propname, $fbtype, $values, $propattr ); - break; - case 'GEO': - $value = explode( ';', $line, 2 ); - if( 2 > count( $value )) - $value[1] = null; - $this->setProperty( $propname, $value[0], $value[1], $propattr ); - break; - case 'EXDATE': - $values = ( !empty( $line )) ? explode( ',', $line ) : null; - $this->setProperty( $propname, $values, $propattr ); - break; - case 'RDATE': - if( empty( $line )) { - $this->setProperty( $propname, $line, $propattr ); - break; - } - $values = explode( ',', $line ); - foreach( $values as $vix => $value ) { - $value2 = explode( '/', $value ); - if( 1 < count( $value2 )) - $values[$vix] = $value2; - } - $this->setProperty( $propname, $values, $propattr ); - break; - case 'EXRULE': - case 'RRULE': - $values = explode( ';', $line ); - $recur = array(); - foreach( $values as $value2 ) { - if( empty( $value2 )) - continue; // ;-char in ending position ??? - $value3 = explode( '=', $value2, 2 ); - $rulelabel = strtoupper( $value3[0] ); - switch( $rulelabel ) { - case 'BYDAY': { - $value4 = explode( ',', $value3[1] ); - if( 1 < count( $value4 )) { - foreach( $value4 as $v5ix => $value5 ) { - $value6 = array(); - $dayno = $dayname = null; - $value5 = trim( (string) $value5 ); - if(( ctype_alpha( substr( $value5, -1 ))) && - ( ctype_alpha( substr( $value5, -2, 1 )))) { - $dayname = substr( $value5, -2, 2 ); - if( 2 < strlen( $value5 )) - $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 )); - } - if( $dayno ) - $value6[] = $dayno; - if( $dayname ) - $value6['DAY'] = $dayname; - $value4[$v5ix] = $value6; - } - } - else { - $value4 = array(); - $dayno = $dayname = null; - $value5 = trim( (string) $value3[1] ); - if(( ctype_alpha( substr( $value5, -1 ))) && - ( ctype_alpha( substr( $value5, -2, 1 )))) { - $dayname = substr( $value5, -2, 2 ); - if( 2 < strlen( $value5 )) - $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 )); - } - if( $dayno ) - $value4[] = $dayno; - if( $dayname ) - $value4['DAY'] = $dayname; - } - $recur[$rulelabel] = $value4; - break; - } - default: { - $value4 = explode( ',', $value3[1] ); - if( 1 < count( $value4 )) - $value3[1] = $value4; - $recur[$rulelabel] = $value3[1]; - break; - } - } // end - switch $rulelabel - } // end - foreach( $values.. . - $this->setProperty( $propname, $recur, $propattr ); - break; - case 'ACTION': - case 'CLASSIFICATION': - case 'STATUS': - case 'TRANSP': - case 'UID': - case 'TZID': - case 'RELATED-TO': - case 'TZNAME': - $line = iCalUtilityFunctions::_strunrep( $line ); - default: - $this->setProperty( $propname, $line, $propattr ); - break; - } // end switch( $propname.. . - } // end - foreach( $proprows.. . - unset( $unparsedtext, $this->unparsed, $proprows ); - if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) { - $ckeys = array_keys( $this->components ); - foreach( $ckeys as $ckey ) { - if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) { - $this->components[$ckey]->parse(); - } - } - } - return TRUE; - } -/*********************************************************************************/ -/*********************************************************************************/ -/** - * return a copy of this component - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.4 - 2012-10-18 - * @return object - */ - function copy() { - return unserialize( serialize( $this )); - } -/*********************************************************************************/ -/*********************************************************************************/ -/** - * delete calendar subcomponent from component container - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $arg1 ordno / component type / component uid - * @param mixed $arg2 optional, ordno if arg1 = component type - * @return void - */ - function deleteComponent( $arg1, $arg2=FALSE ) { - if( !isset( $this->components )) return FALSE; - $argType = $index = null; - if ( ctype_digit( (string) $arg1 )) { - $argType = 'INDEX'; - $index = (int) $arg1 - 1; - } - elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { - $argType = strtolower( $arg1 ); - $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0; - } - $cix2dC = 0; - foreach ( $this->components as $cix => $component) { - if( empty( $component )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) { - unset( $this->components[$cix] ); - return TRUE; - } - elseif( $argType == $component->objName ) { - if( $index == $cix2dC ) { - unset( $this->components[$cix] ); - return TRUE; - } - $cix2dC++; - } - elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { - unset( $this->components[$cix] ); - return TRUE; - } - } - return FALSE; - } -/** - * get calendar component subcomponent from component container - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $arg1 optional, ordno/component type/ component uid - * @param mixed $arg2 optional, ordno if arg1 = component type - * @return object - */ - function getComponent ( $arg1=FALSE, $arg2=FALSE ) { - if( !isset( $this->components )) return FALSE; - $index = $argType = null; - if ( !$arg1 ) { - $argType = 'INDEX'; - $index = $this->compix['INDEX'] = - ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1; - } - elseif ( ctype_digit( (string) $arg1 )) { - $argType = 'INDEX'; - $index = (int) $arg1; - unset( $this->compix ); - } - elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { - unset( $this->compix['INDEX'] ); - $argType = strtolower( $arg1 ); - if( !$arg2 ) - $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1; - else - $index = (int) $arg2; - } - $index -= 1; - $ckeys = array_keys( $this->components ); - if( !empty( $index) && ( $index > end( $ckeys ))) - return FALSE; - $cix2gC = 0; - foreach( $this->components as $cix => $component ) { - if( empty( $component )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) - return $component->copy(); - elseif( $argType == $component->objName ) { - if( $index == $cix2gC ) - return $component->copy(); - $cix2gC++; - } - elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' ))) - return $component->copy(); - } - /* not found.. . */ - unset( $this->compix ); - return false; - } -/** - * add calendar component as subcomponent to container for subcomponents - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 1.x.x - 2007-04-24 - * @param object $component calendar component - * @return void - */ - function addSubComponent ( $component ) { - $this->setComponent( $component ); - } -/** - * create new calendar component subcomponent, already included within component - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.33 - 2011-01-03 - * @param string $compType subcomponent type - * @return object (reference) - */ - function & newComponent( $compType ) { - $config = $this->getConfig(); - $keys = array_keys( $this->components ); - $ix = end( $keys) + 1; - switch( strtoupper( $compType )) { - case 'ALARM': - case 'VALARM': - $this->components[$ix] = new valarm( $config ); - break; - case 'STANDARD': - array_unshift( $this->components, new vtimezone( 'STANDARD', $config )); - $ix = 0; - break; - case 'DAYLIGHT': - $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config ); - break; - default: - return FALSE; - } - return $this->components[$ix]; - } -/** - * add calendar component as subcomponent to container for subcomponents - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param object $component calendar component - * @param mixed $arg1 optional, ordno/component type/ component uid - * @param mixed $arg2 optional, ordno if arg1 = component type - * @return bool - */ - function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) { - if( !isset( $this->components )) return FALSE; - $component->setConfig( $this->getConfig(), FALSE, TRUE ); - if( !in_array( $component->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) { - /* make sure dtstamp and uid is set */ - $dummy = $component->getProperty( 'dtstamp' ); - $dummy = $component->getProperty( 'uid' ); - } - if( !$arg1 ) { // plain insert, last in chain - $this->components[] = $component->copy(); - return TRUE; - } - $argType = $index = null; - if ( ctype_digit( (string) $arg1 )) { // index insert/replace - $argType = 'INDEX'; - $index = (int) $arg1 - 1; - } - elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) { - $argType = strtolower( $arg1 ); - $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0; - } - // else if arg1 is set, arg1 must be an UID - $cix2sC = 0; - foreach ( $this->components as $cix => $component2 ) { - if( empty( $component2 )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace - $this->components[$cix] = $component->copy(); - return TRUE; - } - elseif( $argType == $component2->objName ) { // component Type index insert/replace - if( $index == $cix2sC ) { - $this->components[$cix] = $component->copy(); - return TRUE; - } - $cix2sC++; - } - elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace - $this->components[$cix] = $component->copy(); - return TRUE; - } - } - /* arg1=index and not found.. . insert at index .. .*/ - if( 'INDEX' == $argType ) { - $this->components[$index] = $component->copy(); - ksort( $this->components, SORT_NUMERIC ); - } - else /* not found.. . insert last in chain anyway .. .*/ - $this->components[] = $component->copy(); - return TRUE; - } -/** - * creates formatted output for subcomponents - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.20 - 2012-02-06 - * @param array $xcaldecl - * @return string - */ - function createSubComponent() { - $output = null; - if( 'vtimezone' == $this->objName ) { // sort subComponents, first standard, then daylight, in dtstart order - $stdarr = $dlarr = array(); - foreach( $this->components as $component ) { - if( empty( $component )) - continue; - $dt = $component->getProperty( 'dtstart' ); - $key = sprintf( '%04d%02d%02d%02d%02d%02d000', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] ); - if( 'standard' == $component->objName ) { - while( isset( $stdarr[$key] )) - $key += 1; - $stdarr[$key] = $component->copy(); - } - elseif( 'daylight' == $component->objName ) { - while( isset( $dlarr[$key] )) - $key += 1; - $dlarr[$key] = $component->copy(); - } - } // end foreach( $this->components as $component ) - $this->components = array(); - ksort( $stdarr, SORT_NUMERIC ); - foreach( $stdarr as $std ) - $this->components[] = $std->copy(); - unset( $stdarr ); - ksort( $dlarr, SORT_NUMERIC ); - foreach( $dlarr as $dl ) - $this->components[] = $dl->copy(); - unset( $dlarr ); - } // end if( 'vtimezone' == $this->objName ) - foreach( $this->components as $component ) { - $component->setConfig( $this->getConfig(), FALSE, TRUE ); - $output .= $component->createComponent( $this->xcaldecl ); - } - return $output; - } -} -/*********************************************************************************/ -/*********************************************************************************/ -/** - * class for calendar component VEVENT - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-12 - */ -class vevent extends calendarComponent { - var $attach; - var $attendee; - var $categories; - var $comment; - var $contact; - var $class; - var $created; - var $description; - var $dtend; - var $dtstart; - var $duration; - var $exdate; - var $exrule; - var $geo; - var $lastmodified; - var $location; - var $organizer; - var $priority; - var $rdate; - var $recurrenceid; - var $relatedto; - var $requeststatus; - var $resources; - var $rrule; - var $sequence; - var $status; - var $summary; - var $transp; - var $url; - var $xprop; - // component subcomponents container - var $components; -/** - * constructor for calendar component VEVENT object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @return void - */ - function vevent( $config = array()) { - $this->calendarComponent(); - - $this->attach = ''; - $this->attendee = ''; - $this->categories = ''; - $this->class = ''; - $this->comment = ''; - $this->contact = ''; - $this->created = ''; - $this->description = ''; - $this->dtstart = ''; - $this->dtend = ''; - $this->duration = ''; - $this->exdate = ''; - $this->exrule = ''; - $this->geo = ''; - $this->lastmodified = ''; - $this->location = ''; - $this->organizer = ''; - $this->priority = ''; - $this->rdate = ''; - $this->recurrenceid = ''; - $this->relatedto = ''; - $this->requeststatus = ''; - $this->resources = ''; - $this->rrule = ''; - $this->sequence = ''; - $this->status = ''; - $this->summary = ''; - $this->transp = ''; - $this->url = ''; - $this->xprop = ''; - - $this->components = array(); - - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - - } -/** - * create formatted output for calendar component VEVENT object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.16 - 2011-10-28 - * @param array $xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createUid(); - $component .= $this->createDtstamp(); - $component .= $this->createAttach(); - $component .= $this->createAttendee(); - $component .= $this->createCategories(); - $component .= $this->createComment(); - $component .= $this->createContact(); - $component .= $this->createClass(); - $component .= $this->createCreated(); - $component .= $this->createDescription(); - $component .= $this->createDtstart(); - $component .= $this->createDtend(); - $component .= $this->createDuration(); - $component .= $this->createExdate(); - $component .= $this->createExrule(); - $component .= $this->createGeo(); - $component .= $this->createLastModified(); - $component .= $this->createLocation(); - $component .= $this->createOrganizer(); - $component .= $this->createPriority(); - $component .= $this->createRdate(); - $component .= $this->createRrule(); - $component .= $this->createRelatedTo(); - $component .= $this->createRequestStatus(); - $component .= $this->createRecurrenceid(); - $component .= $this->createResources(); - $component .= $this->createSequence(); - $component .= $this->createStatus(); - $component .= $this->createSummary(); - $component .= $this->createTransp(); - $component .= $this->createUrl(); - $component .= $this->createXprop(); - $component .= $this->createSubComponent(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} -/*********************************************************************************/ -/*********************************************************************************/ -/** - * class for calendar component VTODO - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-12 - */ -class vtodo extends calendarComponent { - var $attach; - var $attendee; - var $categories; - var $comment; - var $completed; - var $contact; - var $class; - var $created; - var $description; - var $dtstart; - var $due; - var $duration; - var $exdate; - var $exrule; - var $geo; - var $lastmodified; - var $location; - var $organizer; - var $percentcomplete; - var $priority; - var $rdate; - var $recurrenceid; - var $relatedto; - var $requeststatus; - var $resources; - var $rrule; - var $sequence; - var $status; - var $summary; - var $url; - var $xprop; - // component subcomponents container - var $components; -/** - * constructor for calendar component VTODO object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @return void - */ - function vtodo( $config = array()) { - $this->calendarComponent(); - - $this->attach = ''; - $this->attendee = ''; - $this->categories = ''; - $this->class = ''; - $this->comment = ''; - $this->completed = ''; - $this->contact = ''; - $this->created = ''; - $this->description = ''; - $this->dtstart = ''; - $this->due = ''; - $this->duration = ''; - $this->exdate = ''; - $this->exrule = ''; - $this->geo = ''; - $this->lastmodified = ''; - $this->location = ''; - $this->organizer = ''; - $this->percentcomplete = ''; - $this->priority = ''; - $this->rdate = ''; - $this->recurrenceid = ''; - $this->relatedto = ''; - $this->requeststatus = ''; - $this->resources = ''; - $this->rrule = ''; - $this->sequence = ''; - $this->status = ''; - $this->summary = ''; - $this->url = ''; - $this->xprop = ''; - - $this->components = array(); - - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - - } -/** - * create formatted output for calendar component VTODO object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-07 - * @param array $xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createUid(); - $component .= $this->createDtstamp(); - $component .= $this->createAttach(); - $component .= $this->createAttendee(); - $component .= $this->createCategories(); - $component .= $this->createClass(); - $component .= $this->createComment(); - $component .= $this->createCompleted(); - $component .= $this->createContact(); - $component .= $this->createCreated(); - $component .= $this->createDescription(); - $component .= $this->createDtstart(); - $component .= $this->createDue(); - $component .= $this->createDuration(); - $component .= $this->createExdate(); - $component .= $this->createExrule(); - $component .= $this->createGeo(); - $component .= $this->createLastModified(); - $component .= $this->createLocation(); - $component .= $this->createOrganizer(); - $component .= $this->createPercentComplete(); - $component .= $this->createPriority(); - $component .= $this->createRdate(); - $component .= $this->createRelatedTo(); - $component .= $this->createRequestStatus(); - $component .= $this->createRecurrenceid(); - $component .= $this->createResources(); - $component .= $this->createRrule(); - $component .= $this->createSequence(); - $component .= $this->createStatus(); - $component .= $this->createSummary(); - $component .= $this->createUrl(); - $component .= $this->createXprop(); - $component .= $this->createSubComponent(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} -/*********************************************************************************/ -/*********************************************************************************/ -/** - * class for calendar component VJOURNAL - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-12 - */ -class vjournal extends calendarComponent { - var $attach; - var $attendee; - var $categories; - var $comment; - var $contact; - var $class; - var $created; - var $description; - var $dtstart; - var $exdate; - var $exrule; - var $lastmodified; - var $organizer; - var $rdate; - var $recurrenceid; - var $relatedto; - var $requeststatus; - var $rrule; - var $sequence; - var $status; - var $summary; - var $url; - var $xprop; -/** - * constructor for calendar component VJOURNAL object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @return void - */ - function vjournal( $config = array()) { - $this->calendarComponent(); - - $this->attach = ''; - $this->attendee = ''; - $this->categories = ''; - $this->class = ''; - $this->comment = ''; - $this->contact = ''; - $this->created = ''; - $this->description = ''; - $this->dtstart = ''; - $this->exdate = ''; - $this->exrule = ''; - $this->lastmodified = ''; - $this->organizer = ''; - $this->rdate = ''; - $this->recurrenceid = ''; - $this->relatedto = ''; - $this->requeststatus = ''; - $this->rrule = ''; - $this->sequence = ''; - $this->status = ''; - $this->summary = ''; - $this->url = ''; - $this->xprop = ''; - - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - - } -/** - * create formatted output for calendar component VJOURNAL object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-12 - * @param array $xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createUid(); - $component .= $this->createDtstamp(); - $component .= $this->createAttach(); - $component .= $this->createAttendee(); - $component .= $this->createCategories(); - $component .= $this->createClass(); - $component .= $this->createComment(); - $component .= $this->createContact(); - $component .= $this->createCreated(); - $component .= $this->createDescription(); - $component .= $this->createDtstart(); - $component .= $this->createExdate(); - $component .= $this->createExrule(); - $component .= $this->createLastModified(); - $component .= $this->createOrganizer(); - $component .= $this->createRdate(); - $component .= $this->createRequestStatus(); - $component .= $this->createRecurrenceid(); - $component .= $this->createRelatedTo(); - $component .= $this->createRrule(); - $component .= $this->createSequence(); - $component .= $this->createStatus(); - $component .= $this->createSummary(); - $component .= $this->createUrl(); - $component .= $this->createXprop(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} -/*********************************************************************************/ -/*********************************************************************************/ -/** - * class for calendar component VFREEBUSY - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-12 - */ -class vfreebusy extends calendarComponent { - var $attendee; - var $comment; - var $contact; - var $dtend; - var $dtstart; - var $duration; - var $freebusy; - var $organizer; - var $requeststatus; - var $url; - var $xprop; - // component subcomponents container - var $components; -/** - * constructor for calendar component VFREEBUSY object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @return void - */ - function vfreebusy( $config = array()) { - $this->calendarComponent(); - - $this->attendee = ''; - $this->comment = ''; - $this->contact = ''; - $this->dtend = ''; - $this->dtstart = ''; - $this->duration = ''; - $this->freebusy = ''; - $this->organizer = ''; - $this->requeststatus = ''; - $this->url = ''; - $this->xprop = ''; - - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - - } -/** - * create formatted output for calendar component VFREEBUSY object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.3.1 - 2007-11-19 - * @param array $xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createUid(); - $component .= $this->createDtstamp(); - $component .= $this->createAttendee(); - $component .= $this->createComment(); - $component .= $this->createContact(); - $component .= $this->createDtstart(); - $component .= $this->createDtend(); - $component .= $this->createDuration(); - $component .= $this->createFreebusy(); - $component .= $this->createOrganizer(); - $component .= $this->createRequestStatus(); - $component .= $this->createUrl(); - $component .= $this->createXprop(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} -/*********************************************************************************/ -/*********************************************************************************/ -/** - * class for calendar component VALARM - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-12 - */ -class valarm extends calendarComponent { - var $action; - var $attach; - var $attendee; - var $description; - var $duration; - var $repeat; - var $summary; - var $trigger; - var $xprop; -/** - * constructor for calendar component VALARM object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @return void - */ - function valarm( $config = array()) { - $this->calendarComponent(); - - $this->action = ''; - $this->attach = ''; - $this->attendee = ''; - $this->description = ''; - $this->duration = ''; - $this->repeat = ''; - $this->summary = ''; - $this->trigger = ''; - $this->xprop = ''; - - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - - } -/** - * create formatted output for calendar component VALARM object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-22 - * @param array $xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createAction(); - $component .= $this->createAttach(); - $component .= $this->createAttendee(); - $component .= $this->createDescription(); - $component .= $this->createDuration(); - $component .= $this->createRepeat(); - $component .= $this->createSummary(); - $component .= $this->createTrigger(); - $component .= $this->createXprop(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} -/********************************************************************************** -/*********************************************************************************/ -/** - * class for calendar component VTIMEZONE - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-12 - */ -class vtimezone extends calendarComponent { - var $timezonetype; - - var $comment; - var $dtstart; - var $lastmodified; - var $rdate; - var $rrule; - var $tzid; - var $tzname; - var $tzoffsetfrom; - var $tzoffsetto; - var $tzurl; - var $xprop; - // component subcomponents container - var $components; -/** - * constructor for calendar component VTIMEZONE object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param mixed $timezonetype optional, default FALSE ( STANDARD / DAYLIGHT ) - * @param array $config - * @return void - */ - function vtimezone( $timezonetype=FALSE, $config = array()) { - if( is_array( $timezonetype )) { - $config = $timezonetype; - $timezonetype = FALSE; - } - if( !$timezonetype ) - $this->timezonetype = 'VTIMEZONE'; - else - $this->timezonetype = strtoupper( $timezonetype ); - $this->calendarComponent(); - - $this->comment = ''; - $this->dtstart = ''; - $this->lastmodified = ''; - $this->rdate = ''; - $this->rrule = ''; - $this->tzid = ''; - $this->tzname = ''; - $this->tzoffsetfrom = ''; - $this->tzoffsetto = ''; - $this->tzurl = ''; - $this->xprop = ''; - - $this->components = array(); - - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - - } -/** - * create formatted output for calendar component VTIMEZONE object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-25 - * @param array $xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createTzid(); - $component .= $this->createLastModified(); - $component .= $this->createTzurl(); - $component .= $this->createDtstart(); - $component .= $this->createTzoffsetfrom(); - $component .= $this->createTzoffsetto(); - $component .= $this->createComment(); - $component .= $this->createRdate(); - $component .= $this->createRrule(); - $component .= $this->createTzname(); - $component .= $this->createXprop(); - $component .= $this->createSubComponent(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} -/*********************************************************************************/ -/*********************************************************************************/ -/** - * moving all utility (static) functions to a utility class - * 20111223 - move iCalUtilityFunctions class to the end of the iCalcreator class file - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.1 - 2011-07-16 - * - */ -class iCalUtilityFunctions { - // Store the single instance of iCalUtilityFunctions - private static $m_pInstance; - - // Private constructor to limit object instantiation to within the class - private function __construct() { - $m_pInstance = FALSE; - } - - // Getter method for creating/returning the single instance of this class - public static function getInstance() { - if (!self::$m_pInstance) - self::$m_pInstance = new iCalUtilityFunctions(); - - return self::$m_pInstance; - } -/** - * ensures internal date-time/date format (keyed array) for an input date-time/date array (keyed or unkeyed) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-27 - * @param array $datetime - * @param int $parno optional, default FALSE - * @return array - */ - public static function _date_time_array( $datetime, $parno=FALSE ) { - return iCalUtilityFunctions::_chkDateArr( $datetime, $parno ); - } - public static function _chkDateArr( $datetime, $parno=FALSE ) { - $output = array(); - foreach( $datetime as $dateKey => $datePart ) { - switch ( $dateKey ) { - case '0': case 'year': $output['year'] = $datePart; break; - case '1': case 'month': $output['month'] = $datePart; break; - case '2': case 'day': $output['day'] = $datePart; break; - } - if( 3 != $parno ) { - switch ( $dateKey ) { - case '0': - case '1': - case '2': break; - case '3': case 'hour': $output['hour'] = $datePart; break; - case '4': case 'min' : $output['min'] = $datePart; break; - case '5': case 'sec' : $output['sec'] = $datePart; break; - case '6': case 'tz' : $output['tz'] = $datePart; break; - } - } - } - if( 3 != $parno ) { - if( !isset( $output['hour'] )) $output['hour'] = 0; - if( !isset( $output['min'] )) $output['min'] = 0; - if( !isset( $output['sec'] )) $output['sec'] = 0; - if( isset( $output['tz'] ) && - (( '+0000' == $output['tz'] ) || ( '-0000' == $output['tz'] ) || ( '+000000' == $output['tz'] ) || ( '-000000' == $output['tz'] ))) - $output['tz'] = 'Z'; - } - return $output; - } -/** - * check date(-time) and params arrays for an opt. timezone and if it is a DATE-TIME or DATE (updates $parno and params) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.30 - 2012-01-16 - * @param array $date, date to check - * @param int $parno, no of date parts (i.e. year, month.. .) - * @param array $params, property parameters - * @return void - */ - public static function _chkdatecfg( $theDate, & $parno, & $params ) { - if( isset( $params['TZID'] )) - $parno = 6; - elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )) - $parno = 3; - else { - if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] )) - $parno = 7; - if( is_array( $theDate )) { - if( isset( $theDate['timestamp'] )) - $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null; - else - $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null; - if( !empty( $tzid )) { - $parno = 7; - if( !iCalUtilityFunctions::_isOffset( $tzid )) - $params['TZID'] = $tzid; // save only timezone - } - elseif( !$parno && ( 3 == count( $theDate )) && - ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))) - $parno = 3; - else - $parno = 6; - } - else { // string - $date = trim( $theDate ); - if( 'Z' == substr( $date, -1 )) - $parno = 7; // UTC DATE-TIME - elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) && - ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' )))) - $parno = 3; // DATE - $date = iCalUtilityFunctions::_strdate2date( $date, $parno ); - unset( $date['unparsedtext'] ); - if( !empty( $date['tz'] )) { - $parno = 7; - if( !iCalUtilityFunctions::_isOffset( $date['tz'] )) - $params['TZID'] = $date['tz']; // save only timezone - } - elseif( empty( $parno )) - $parno = 6; - } - if( isset( $params['TZID'] )) - $parno = 6; - } - } -/** - * vcalendar sort callback function - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-17 - * @param array $a - * @param array $b - * @return int - */ - public static function _cmpfcn( $a, $b ) { - if( empty( $a )) return -1; - if( empty( $b )) return 1; - if( 'vtimezone' == $a->objName ) { - if( 'vtimezone' != $b->objName ) return -1; - elseif( $a->srtk[0] <= $b->srtk[0] ) return -1; - else return 1; - } - elseif( 'vtimezone' == $b->objName ) return 1; - $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' ); - for( $k = 0; $k < 4 ; $k++ ) { - if( empty( $a->srtk[$k] )) return -1; - elseif( empty( $b->srtk[$k] )) return 1; - if( is_array( $a->srtk[$k] )) { - if( is_array( $b->srtk[$k] )) { - foreach( $sortkeys as $key ) { - if ( !isset( $a->srtk[$k][$key] )) return -1; - elseif( !isset( $b->srtk[$k][$key] )) return 1; - if ( empty( $a->srtk[$k][$key] )) return -1; - elseif( empty( $b->srtk[$k][$key] )) return 1; - if ( $a->srtk[$k][$key] == $b->srtk[$k][$key]) - continue; - if (( (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] )) - return -1; - elseif(( (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] )) - return 1; - } - } - else return -1; - } - elseif( is_array( $b->srtk[$k] )) return 1; - elseif( $a->srtk[$k] < $b->srtk[$k] ) return -1; - elseif( $a->srtk[$k] > $b->srtk[$k] ) return 1; - } - return 0; - } -/** - * byte oriented line folding fix - * - * remove any line-endings that may include spaces or tabs - * and convert all line endings (iCal default '\r\n'), - * takes care of '\r\n', '\r' and '\n' and mixed '\r\n'+'\r', '\r\n'+'\n' - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.12.17 - 2012-07-12 - * @param string $text - * @param string $nl - * @return string - */ - public static function convEolChar( & $text, $nl ) { - $outp = ''; - $cix = 0; - while( isset( $text[$cix] )) { - if( isset( $text[$cix + 2] ) && ( "\r" == $text[$cix] ) && ( "\n" == $text[$cix + 1] ) && - (( " " == $text[$cix + 2] ) || ( "\t" == $text[$cix + 2] ))) // 2 pos eolchar + ' ' or '\t' - $cix += 2; // skip 3 - elseif( isset( $text[$cix + 1] ) && ( "\r" == $text[$cix] ) && ( "\n" == $text[$cix + 1] )) { - $outp .= $nl; // 2 pos eolchar - $cix += 1; // replace with $nl - } - elseif( isset( $text[$cix + 1] ) && (( "\r" == $text[$cix] ) || ( "\n" == $text[$cix] )) && - (( " " == $text[$cix + 1] ) || ( "\t" == $text[$cix + 1] ))) // 1 pos eolchar + ' ' or '\t' - $cix += 1; // skip 2 - elseif(( "\r" == $text[$cix] ) || ( "\n" == $text[$cix] )) // 1 pos eolchar - $outp .= $nl; // replace with $nl - else - $outp .= $text[$cix]; // add any other byte - $cix += 1; - } - return $outp; - } -/** - * create a calendar timezone and standard/daylight components - * - * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone: - * - * BEGIN:VTIMEZONE - * TZID:Europe/Stockholm - * BEGIN:STANDARD - * DTSTART:20101031T020000 - * TZOFFSETFROM:+0200 - * TZOFFSETTO:+0100 - * TZNAME:CET - * END:STANDARD - * BEGIN:DAYLIGHT - * DTSTART:20100328T030000 - * TZOFFSETFROM:+0100 - * TZOFFSETTO:+0200 - * TZNAME:CEST - * END:DAYLIGHT - * END:VTIMEZONE - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.1 - 2012-11-26 - * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi - * Additional changes jpirkey - * @param object $calendar, reference to an iCalcreator calendar instance - * @param string $timezone, a PHP5 (DateTimeZone) valid timezone - * @param array $xProp, *[x-propName => x-propValue], optional - * @param int $from a unix timestamp - * @param int $to a unix timestamp - * @return bool - */ - public static function createTimezone( & $calendar, $timezone, $xProp=array(), $from=null, $to=null ) { - if( empty( $timezone )) - return FALSE; - if( !empty( $from ) && !is_int( $from )) - return FALSE; - if( !empty( $to ) && !is_int( $to )) - return FALSE; - try { - $dtz = new DateTimeZone( $timezone ); - $transitions = $dtz->getTransitions(); - $utcTz = new DateTimeZone( 'UTC' ); - } - catch( Exception $e ) { return FALSE; } - if( empty( $to )) { - $dates = array_keys( $calendar->getProperty( 'dtstart' )); - if( empty( $dates )) - $dates = array( date( 'Ymd' )); - } - if( !empty( $from )) - $dateFrom = new DateTime( "@$from" ); // set lowest date (UTC) - else { - $from = reset( $dates ); // set lowest date to the lowest dtstart date - $dateFrom = new DateTime( $from.'T000000', $dtz ); - $dateFrom->modify( '-1 month' ); // set $dateFrom to one month before the lowest date - $dateFrom->setTimezone( $utcTz ); // convert local date to UTC - } - $dateFromYmd = $dateFrom->format('Y-m-d' ); - if( !empty( $to )) - $dateTo = new DateTime( "@$to" ); // set end date (UTC) - else { - $to = end( $dates ); // set highest date to the highest dtstart date - $dateTo = new DateTime( $to.'T235959', $dtz ); - $dateTo->modify( '+1 year' ); // set $dateTo to one year after the highest date - $dateTo->setTimezone( $utcTz ); // convert local date to UTC - } - $dateToYmd = $dateTo->format('Y-m-d' ); - unset( $dtz ); - $transTemp = array(); - $prevOffsetfrom = 0; - $stdIx = $dlghtIx = null; - $prevTrans = FALSE; - foreach( $transitions as $tix => $trans ) { // all transitions in date-time order!! - $date = new DateTime( "@{$trans['ts']}" ); // set transition date (UTC) - $transDateYmd = $date->format('Y-m-d' ); - if ( $transDateYmd < $dateFromYmd ) { - $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom - $prevTrans = $trans; // save it in case we don't find any that match - $prevTrans['offsetfrom'] = ( 0 < $tix ) ? $transitions[$tix-1]['offset'] : 0; - continue; - } - if( $transDateYmd > $dateToYmd ) - break; // loop always (?) breaks here - if( !empty( $prevOffsetfrom ) || ( 0 == $prevOffsetfrom )) { - $trans['offsetfrom'] = $prevOffsetfrom; // i.e. set previous offsetto as offsetFrom - $date->modify( $trans['offsetfrom'].'seconds' ); // convert utc date to local date - $d = $date->format( 'Y-n-j-G-i-s' ); // set date to array to ease up dtstart and (opt) rdate setting - $d = explode( '-', $d ); - $trans['time'] = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); - } - $prevOffsetfrom = $trans['offset']; - if( TRUE !== $trans['isdst'] ) { // standard timezone - if( !empty( $stdIx ) && isset( $transTemp[$stdIx]['offsetfrom'] ) && // check for any repeating rdate's (in order) - ( $transTemp[$stdIx]['abbr'] == $trans['abbr'] ) && - ( $transTemp[$stdIx]['offsetfrom'] == $trans['offsetfrom'] ) && - ( $transTemp[$stdIx]['offset'] == $trans['offset'] )) { - $transTemp[$stdIx]['rdate'][] = $trans['time']; - continue; - } - $stdIx = $tix; - } // end standard timezone - else { // daylight timezone - if( !empty( $dlghtIx ) && isset( $transTemp[$dlghtIx]['offsetfrom'] ) && // check for any repeating rdate's (in order) - ( $transTemp[$dlghtIx]['abbr'] == $trans['abbr'] ) && - ( $transTemp[$dlghtIx]['offsetfrom'] == $trans['offsetfrom'] ) && - ( $transTemp[$dlghtIx]['offset'] == $trans['offset'] )) { - $transTemp[$dlghtIx]['rdate'][] = $trans['time']; - continue; - } - $dlghtIx = $tix; - } // end daylight timezone - $transTemp[$tix] = $trans; - } // end foreach( $transitions as $tix => $trans ) - $tz = & $calendar->newComponent( 'vtimezone' ); - $tz->setproperty( 'tzid', $timezone ); - if( !empty( $xProp )) { - foreach( $xProp as $xPropName => $xPropValue ) - if( 'x-' == strtolower( substr( $xPropName, 0, 2 ))) - $tz->setproperty( $xPropName, $xPropValue ); - } - if( empty( $transTemp )) { // if no match found - if( $prevTrans ) { // then we use the last transition (before startdate) for the tz info - $date = new DateTime( "@{$prevTrans['ts']}" ); // set transition date (UTC) - $date->modify( $prevTrans['offsetfrom'].'seconds' ); // convert utc date to local date - $d = $date->format( 'Y-n-j-G-i-s' ); // set date to array to ease up dtstart setting - $d = explode( '-', $d ); - $prevTrans['time'] = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); - $transTemp[0] = $prevTrans; - } - else { // or we use the timezone identifier to BUILD the standard tz info (?) - $date = new DateTime( 'now', new DateTimeZone( $timezone )); - $transTemp[0] = array( 'time' => $date->format( 'Y-m-d\TH:i:s O' ) - , 'offset' => $date->format( 'Z' ) - , 'offsetfrom' => $date->format( 'Z' ) - , 'isdst' => FALSE ); - } - } - unset( $transitions, $date, $prevTrans ); - foreach( $transTemp as $tix => $trans ) { - $type = ( TRUE !== $trans['isdst'] ) ? 'standard' : 'daylight'; - $scomp = & $tz->newComponent( $type ); - $scomp->setProperty( 'dtstart', $trans['time'] ); -// $scomp->setProperty( 'x-utc-timestamp', $tix.' : '.$trans['ts'] ); // test ### - if( !empty( $trans['abbr'] )) - $scomp->setProperty( 'tzname', $trans['abbr'] ); - if( isset( $trans['offsetfrom'] )) - $scomp->setProperty( 'tzoffsetfrom', iCalUtilityFunctions::offsetSec2His( $trans['offsetfrom'] )); - $scomp->setProperty( 'tzoffsetto', iCalUtilityFunctions::offsetSec2His( $trans['offset'] )); - if( isset( $trans['rdate'] )) - $scomp->setProperty( 'RDATE', $trans['rdate'] ); - } - return TRUE; - } -/** - * creates formatted output for calendar component property data value type date/date-time - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-17 - * @param array $datetime - * @param int $parno, optional, default 6 - * @return string - */ - public static function _format_date_time( $datetime, $parno=6 ) { - return iCalUtilityFunctions::_date2strdate( $datetime, $parno ); - } - public static function _date2strdate( $datetime, $parno=6 ) { - if( !isset( $datetime['year'] ) && - !isset( $datetime['month'] ) && - !isset( $datetime['day'] ) && - !isset( $datetime['hour'] ) && - !isset( $datetime['min'] ) && - !isset( $datetime['sec'] )) - return; - $output = null; - foreach( $datetime as $dkey => & $dvalue ) - if( 'tz' != $dkey ) $dvalue = (integer) $dvalue; - $output = sprintf( '%04d%02d%02d', $datetime['year'], $datetime['month'], $datetime['day'] ); - if( 3 == $parno ) - return $output; - if( !isset( $datetime['hour'] )) $datetime['hour'] = 0; - if( !isset( $datetime['min'] )) $datetime['min'] = 0; - if( !isset( $datetime['sec'] )) $datetime['sec'] = 0; - $output .= sprintf( 'T%02d%02d%02d', $datetime['hour'], $datetime['min'], $datetime['sec'] ); - if( isset( $datetime['tz'] ) && ( '' < trim( $datetime['tz'] ))) { - $datetime['tz'] = trim( $datetime['tz'] ); - if( 'Z' == $datetime['tz'] ) - $parno = 7; - elseif( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) { - $parno = 7; - $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] ); - try { - $d = new DateTime( $output, new DateTimeZone( 'UTC' )); - if( 0 != $offset ) // adjust för offset - $d->modify( "$offset seconds" ); - $output = $d->format( 'Ymd\THis' ); - } - catch( Exception $e ) { - $output = date( 'Ymd\THis', mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] - $offset), $datetime['month'], $datetime['day'], $datetime['year'] )); - } - } - if( 7 == $parno ) - $output .= 'Z'; - } - return $output; - } -/** - * convert a date/datetime (array) to timestamp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-29 - * @param array $datetime datetime(/date) - * @param string $wtz timezone - * @return int - */ - public static function _date2timestamp( $datetime, $wtz=null ) { - if( !isset( $datetime['hour'] )) $datetime['hour'] = 0; - if( !isset( $datetime['min'] )) $datetime['min'] = 0; - if( !isset( $datetime['sec'] )) $datetime['sec'] = 0; - if( empty( $wtz ) && ( !isset( $datetime['tz'] ) || empty( $datetime['tz'] ))) - return mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year'] ); - $output = $offset = 0; - if( empty( $wtz )) { - if( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) { - $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] ) * -1; - $wtz = 'UTC'; - } - else - $wtz = $datetime['tz']; - } - if(( 'Z' == $wtz ) || ( 'GMT' == strtoupper( $wtz ))) - $wtz = 'UTC'; - try { - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $datetime['year'], $datetime['month'], $datetime['day'], $datetime['hour'], $datetime['min'], $datetime['sec'] ); - $d = new DateTime( $strdate, new DateTimeZone( $wtz )); - if( 0 != $offset ) // adjust for offset - $d->modify( $offset.' seconds' ); - $output = $d->format( 'U' ); - unset( $d ); - } - catch( Exception $e ) { - $output = mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year'] ); - } - return $output; - } -/** - * ensures internal duration format for input in array format - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-25 - * @param array $duration - * @return array - */ - public static function _duration_array( $duration ) { - return iCalUtilityFunctions::_duration2arr( $duration ); - } - public static function _duration2arr( $duration ) { - $output = array(); - if( is_array( $duration ) && - ( 1 == count( $duration )) && - isset( $duration['sec'] ) && - ( 60 < $duration['sec'] )) { - $durseconds = $duration['sec']; - $output['week'] = (int) floor( $durseconds / ( 60 * 60 * 24 * 7 )); - $durseconds = $durseconds % ( 60 * 60 * 24 * 7 ); - $output['day'] = (int) floor( $durseconds / ( 60 * 60 * 24 )); - $durseconds = $durseconds % ( 60 * 60 * 24 ); - $output['hour'] = (int) floor( $durseconds / ( 60 * 60 )); - $durseconds = $durseconds % ( 60 * 60 ); - $output['min'] = (int) floor( $durseconds / ( 60 )); - $output['sec'] = ( $durseconds % ( 60 )); - } - else { - foreach( $duration as $durKey => $durValue ) { - if( empty( $durValue )) continue; - switch ( $durKey ) { - case '0': case 'week': $output['week'] = $durValue; break; - case '1': case 'day': $output['day'] = $durValue; break; - case '2': case 'hour': $output['hour'] = $durValue; break; - case '3': case 'min': $output['min'] = $durValue; break; - case '4': case 'sec': $output['sec'] = $durValue; break; - } - } - } - if( isset( $output['week'] ) && ( 0 < $output['week'] )) { - unset( $output['day'], $output['hour'], $output['min'], $output['sec'] ); - return $output; - } - unset( $output['week'] ); - if( empty( $output['day'] )) - unset( $output['day'] ); - if ( isset( $output['hour'] ) || isset( $output['min'] ) || isset( $output['sec'] )) { - if( !isset( $output['hour'] )) $output['hour'] = 0; - if( !isset( $output['min'] )) $output['min'] = 0; - if( !isset( $output['sec'] )) $output['sec'] = 0; - if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] )) - unset( $output['hour'], $output['min'], $output['sec'] ); - } - return $output; - } -/** - * convert startdate+duration to a array format datetime - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.12 - 2012-10-31 - * @param array $startdate - * @param array $duration - * @return array, date format - */ - public static function _duration2date( $startdate, $duration ) { - $dateOnly = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE; - $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0; - $startdate['min'] = ( isset( $startdate['min'] )) ? $startdate['min'] : 0; - $startdate['sec'] = ( isset( $startdate['sec'] )) ? $startdate['sec'] : 0; - $dtend = 0; - if( isset( $duration['week'] )) $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 ); - if( isset( $duration['day'] )) $dtend += ( $duration['day'] * 24 * 60 * 60 ); - if( isset( $duration['hour'] )) $dtend += ( $duration['hour'] * 60 *60 ); - if( isset( $duration['min'] )) $dtend += ( $duration['min'] * 60 ); - if( isset( $duration['sec'] )) $dtend += $duration['sec']; - $date = date( 'Y-m-d-H-i-s', mktime((int) $startdate['hour'], (int) $startdate['min'], (int) ( $startdate['sec'] + $dtend ), (int) $startdate['month'], (int) $startdate['day'], (int) $startdate['year'] )); - $d = explode( '-', $date ); - $dtend2 = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); - if( isset( $startdate['tz'] )) - $dtend2['tz'] = $startdate['tz']; - if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] ))) - unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] ); - return $dtend2; - } -/** - * ensures internal duration format for an input string (iCal) formatted duration - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-25 - * @param string $duration - * @return array - */ - public static function _duration_string( $duration ) { - return iCalUtilityFunctions::_durationStr2arr( $duration ); - } - public static function _durationStr2arr( $duration ) { - $duration = (string) trim( $duration ); - while( 'P' != strtoupper( substr( $duration, 0, 1 ))) { - if( 0 < strlen( $duration )) - $duration = substr( $duration, 1 ); - else - return false; // no leading P !?!? - } - $duration = substr( $duration, 1 ); // skip P - $duration = str_replace ( 't', 'T', $duration ); - $duration = str_replace ( 'T', '', $duration ); - $output = array(); - $val = null; - for( $ix=0; $ix < strlen( $duration ); $ix++ ) { - switch( strtoupper( substr( $duration, $ix, 1 ))) { - case 'W': - $output['week'] = $val; - $val = null; - break; - case 'D': - $output['day'] = $val; - $val = null; - break; - case 'H': - $output['hour'] = $val; - $val = null; - break; - case 'M': - $output['min'] = $val; - $val = null; - break; - case 'S': - $output['sec'] = $val; - $val = null; - break; - default: - if( !ctype_digit( substr( $duration, $ix, 1 ))) - return false; // unknown duration control character !?!? - else - $val .= substr( $duration, $ix, 1 ); - } - } - return iCalUtilityFunctions::_duration2arr( $output ); - } -/** - * creates formatted output for calendar component property data value type duration - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.8 - 2012-10-30 - * @param array $duration, array( week, day, hour, min, sec ) - * @return string - */ - public static function _format_duration( $duration ) { - return iCalUtilityFunctions::_duration2str( $duration ); - } - public static function _duration2str( $duration ) { - if( isset( $duration['week'] ) || - isset( $duration['day'] ) || - isset( $duration['hour'] ) || - isset( $duration['min'] ) || - isset( $duration['sec'] )) - $ok = TRUE; - else - return; - if( isset( $duration['week'] ) && ( 0 < $duration['week'] )) - return 'P'.$duration['week'].'W'; - $output = 'P'; - if( isset($duration['day'] ) && ( 0 < $duration['day'] )) - $output .= $duration['day'].'D'; - if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) || - ( isset( $duration['min']) && ( 0 < $duration['min'] )) || - ( isset( $duration['sec']) && ( 0 < $duration['sec'] ))) { - $output .= 'T'; - $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '0H'; - $output .= ( isset( $duration['min']) && ( 0 < $duration['min'] )) ? $duration['min']. 'M' : '0M'; - $output .= ( isset( $duration['sec']) && ( 0 < $duration['sec'] )) ? $duration['sec']. 'S' : '0S'; - } - if( 'P' == $output ) - $output = 'PT0H0M0S'; - return $output; - } -/** - * removes expkey+expvalue from array and returns hitval (if found) else returns elseval - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.16 - 2008-11-08 - * @param array $array - * @param string $expkey, expected key - * @param string $expval, expected value - * @param int $hitVal optional, return value if found - * @param int $elseVal optional, return value if not found - * @param int $preSet optional, return value if already preset - * @return int - */ - public static function _existRem( &$array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) { - if( $preSet ) - return $preSet; - if( !is_array( $array ) || ( 0 == count( $array ))) - return $elseVal; - foreach( $array as $key => $value ) { - if( strtoupper( $expkey ) == strtoupper( $key )) { - if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) { - unset( $array[$key] ); - return $hitVal; - } - } - } - return $elseVal; - } -/** - * checks if input contains a (array formatted) date/time - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.8 - 2012-01-20 - * @param array $input - * @return bool - */ - public static function _isArrayDate( $input ) { - if( !is_array( $input )) - return FALSE; - if( isset( $input['week'] ) || ( !in_array( count( $input ), array( 3, 6, 7 )))) - return FALSE; - if( 7 == count( $input )) - return TRUE; - if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) - return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); - if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] )) - return FALSE; - if( in_array( 0, $input )) - return FALSE; - if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] )) - return FALSE; - if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) && - checkdate( (int) $input[1], (int) $input[2], (int) $input[0] )) - return TRUE; - $input = iCalUtilityFunctions::_strdate2date( $input[1].'/'.$input[2].'/'.$input[0], 3 ); // m - d - Y - if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) - return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); - return FALSE; - } -/** - * checks if input array contains a timestamp date - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.16 - 2008-10-18 - * @param array $input - * @return bool - */ - public static function _isArrayTimestampDate( $input ) { - return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ; - } -/** - * controls if input string contains (trailing) UTC/iCal offset - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-21 - * @param string $input - * @return bool - */ - public static function _isOffset( $input ) { - $input = trim( (string) $input ); - if( 'Z' == substr( $input, -1 )) - return TRUE; - elseif(( 5 <= strlen( $input )) && - ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) && - ( '0000' <= substr( $input, -4 )) && ( '9999' >= substr( $input, -4 ))) - return TRUE; - elseif(( 7 <= strlen( $input )) && - ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) && - ( '000000' <= substr( $input, -6 )) && ( '999999' >= substr( $input, -6 ))) - return TRUE; - return FALSE; - } -/** - * (very simple) conversion of a MS timezone to a PHP5 valid (Date-)timezone - * matching (MS) UCT offset and time zone descriptors - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-16 - * @param string $timezone, input/output variable reference - * @return bool - */ - public static function ms2phpTZ( & $timezone ) { - if( empty( $timezone )) - return FALSE; - $search = str_replace( '"', '', $timezone ); - $search = str_replace( array('GMT', 'gmt', 'utc' ), 'UTC', $search ); - if( '(UTC' != substr( $search, 0, 4 )) - return FALSE; - if( FALSE === ( $pos = strpos( $search, ')' ))) - return FALSE; - $pos = strpos( $search, ')' ); - $searchOffset = substr( $search, 4, ( $pos - 4 )); - $searchOffset = iCalUtilityFunctions::_tz2offset( str_replace( ':', '', $searchOffset )); - while( ' ' ==substr( $search, ( $pos + 1 ))) - $pos += 1; - $searchText = trim( str_replace( array( '(', ')', '&', ',', ' ' ), ' ', substr( $search, ( $pos + 1 )) )); - $searchWords = explode( ' ', $searchText ); - $timezone_abbreviations = DateTimeZone::listAbbreviations(); - $hits = array(); - foreach( $timezone_abbreviations as $name => $transitions ) { - foreach( $transitions as $cnt => $transition ) { - if( empty( $transition['offset'] ) || - empty( $transition['timezone_id'] ) || - ( $transition['offset'] != $searchOffset )) - continue; - $cWords = explode( '/', $transition['timezone_id'] ); - $cPrio = $hitCnt = $rank = 0; - foreach( $cWords as $cWord ) { - if( empty( $cWord )) - continue; - $cPrio += 1; - $sPrio = 0; - foreach( $searchWords as $sWord ) { - if( empty( $sWord ) || ( 'time' == strtolower( $sWord ))) - continue; - $sPrio += 1; - if( strtolower( $cWord ) == strtolower( $sWord )) { - $hitCnt += 1; - $rank += ( $cPrio + $sPrio ); - } - else - $rank += 10; - } - } - if( 0 < $hitCnt ) { - $hits[$rank][] = $transition['timezone_id']; - } - } - } - unset( $timezone_abbreviations ); - if( empty( $hits )) - return FALSE; - ksort( $hits ); - foreach( $hits as $rank => $tzs ) { - if( !empty( $tzs )) { - $timezone = reset( $tzs ); - return TRUE; - } - } - return FALSE; - } -/** - * transforms offset in seconds to [-/+]hhmm[ss] - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2011-05-02 - * @param string $seconds - * @return string - */ - public static function offsetSec2His( $seconds ) { - if( '-' == substr( $seconds, 0, 1 )) { - $prefix = '-'; - $seconds = substr( $seconds, 1 ); - } - elseif( '+' == substr( $seconds, 0, 1 )) { - $prefix = '+'; - $seconds = substr( $seconds, 1 ); - } - else - $prefix = '+'; - $output = ''; - $hour = (int) floor( $seconds / 3600 ); - if( 10 > $hour ) - $hour = '0'.$hour; - $seconds = $seconds % 3600; - $min = (int) floor( $seconds / 60 ); - if( 10 > $min ) - $min = '0'.$min; - $output = $hour.$min; - $seconds = $seconds % 60; - if( 0 < $seconds) { - if( 9 < $seconds) - $output .= $seconds; - else - $output .= '0'.$seconds; - } - return $prefix.$output; - } -/** - * updates an array with dates based on a recur pattern - * - * if missing, UNTIL is set 1 year from startdate (emergency break) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.19 - 2011-10-31 - * @param array $result, array to update, array([timestamp] => timestamp) - * @param array $recur, pattern for recurrency (only value part, params ignored) - * @param array $wdate, component start date - * @param array $startdate, start date - * @param array $enddate, optional - * @return void - * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start - */ - public static function _recur2date( & $result, $recur, $wdate, $startdate, $enddate=FALSE ) { - foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v; - $wdateStart = $wdate; - $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate ); - $startdatets = iCalUtilityFunctions::_date2timestamp( $startdate ); - if( !$enddate ) { - $enddate = $startdate; - $enddate['year'] += 1; - } -// echo "recur __in_ comp start ".implode('-',$wdate)." period start ".implode('-',$startdate)." period end ".implode('-',$enddate)."
\n";print_r($recur);echo "
\n";//test### - $endDatets = iCalUtilityFunctions::_date2timestamp( $enddate ); // fix break - if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] )) - $recur['UNTIL'] = $enddate; // create break - if( isset( $recur['UNTIL'] )) { - $tdatets = iCalUtilityFunctions::_date2timestamp( $recur['UNTIL'] ); - if( $endDatets > $tdatets ) { - $endDatets = $tdatets; // emergency break - $enddate = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 ); - } - else - $recur['UNTIL'] = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 ); - } - if( $wdatets > $endDatets ) { -// echo "recur out of date ".date('Y-m-d H:i:s',$wdatets)."
\n";//test - return array(); // nothing to do.. . - } - if( !isset( $recur['FREQ'] )) // "MUST be specified.. ." - $recur['FREQ'] = 'DAILY'; // ?? - $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ?? - $weekStart = (int) date( 'W', ( $wdatets + $wkst )); - if( !isset( $recur['INTERVAL'] )) - $recur['INTERVAL'] = 1; - $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence - /* find out how to step up dates and set index for interval count */ - $step = array(); - if( 'YEARLY' == $recur['FREQ'] ) - $step['year'] = 1; - elseif( 'MONTHLY' == $recur['FREQ'] ) - $step['month'] = 1; - elseif( 'WEEKLY' == $recur['FREQ'] ) - $step['day'] = 7; - else - $step['day'] = 1; - if( isset( $step['year'] ) && isset( $recur['BYMONTH'] )) - $step = array( 'month' => 1 ); - if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ?? - $step = array( 'day' => 7 ); - if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] )) - $step = array( 'day' => 1 ); - $intervalarr = array(); - if( 1 < $recur['INTERVAL'] ) { - $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); - $intervalarr = array( $intervalix => 0 ); - } - if( isset( $recur['BYSETPOS'] )) { // save start date + weekno - $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array(); -// echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold
\n"; // test ### - if( is_array( $recur['BYSETPOS'] )) { - foreach( $recur['BYSETPOS'] as $bix => $bval ) - $recur['BYSETPOS'][$bix] = (int) $bval; - } - else - $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] ); - if( 'YEARLY' == $recur['FREQ'] ) { - $wdate['month'] = $wdate['day'] = 1; // start from beginning of year - $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate ); - iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'year' => 1 )); // make sure to count whole last year - } - elseif( 'MONTHLY' == $recur['FREQ'] ) { - $wdate['day'] = 1; // start from beginning of month - $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate ); - iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'month' => 1 )); // make sure to count whole last month - } - else - iCalUtilityFunctions::_stepdate( $enddate, $endDatets, $step); // make sure to count whole last period -// echo "BYSETPOS endDat++ =".implode('-',$enddate).' step='.var_export($step,TRUE)."
\n";//test### - $bysetposWold = (int) date( 'W', ( $wdatets + $wkst )); - $bysetposYold = $wdate['year']; - $bysetposMold = $wdate['month']; - $bysetposDold = $wdate['day']; - } - else - iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step); - $year_old = null; - $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' ); - /* MAIN LOOP */ -// echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."
\n";//test - while( TRUE ) { - if( isset( $endDatets ) && ( $wdatets > $endDatets )) - break; - if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) - break; - if( $year_old != $wdate['year'] ) { - $year_old = $wdate['year']; - $daycnts = array(); - $yeardays = $weekno = 0; - $yeardaycnt = array(); - foreach( $daynames as $dn ) - $yeardaycnt[$dn] = 0; - for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters - $daycnts[$m] = array(); - $weekdaycnt = array(); - foreach( $daynames as $dn ) - $weekdaycnt[$dn] = 0; - $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); - for( $d = 1; $d <= $mcnt; $d++ ) { - $daycnts[$m][$d] = array(); - if( isset( $recur['BYYEARDAY'] )) { - $yeardays++; - $daycnts[$m][$d]['yearcnt_up'] = $yeardays; - } - if( isset( $recur['BYDAY'] )) { - $day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] )); - $day = $daynames[$day]; - $daycnts[$m][$d]['DAY'] = $day; - $weekdaycnt[$day]++; - $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day]; - $yeardaycnt[$day]++; - $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day]; - } - if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) - $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year'])); - } - } - $daycnt = 0; - $yeardaycnt = array(); - if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) { - $weekno = null; - for( $d=31; $d > 25; $d-- ) { // get last weekno for year - if( !$weekno ) - $weekno = $daycnts[12][$d]['weekno_up']; - elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) { - $weekno = $daycnts[12][$d]['weekno_up']; - break; - } - } - } - for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters - $weekdaycnt = array(); - foreach( $daynames as $dn ) - $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0; - $monthcnt = 0; - $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); - for( $d = $mcnt; $d > 0; $d-- ) { - if( isset( $recur['BYYEARDAY'] )) { - $daycnt -= 1; - $daycnts[$m][$d]['yearcnt_down'] = $daycnt; - } - if( isset( $recur['BYMONTHDAY'] )) { - $monthcnt -= 1; - $daycnts[$m][$d]['monthcnt_down'] = $monthcnt; - } - if( isset( $recur['BYDAY'] )) { - $day = $daycnts[$m][$d]['DAY']; - $weekdaycnt[$day] -= 1; - $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day]; - $yeardaycnt[$day] -= 1; - $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day]; - } - if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) - $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1); - } - } - } - /* check interval */ - if( 1 < $recur['INTERVAL'] ) { - /* create interval index */ - $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); - /* check interval */ - $currentKey = array_keys( $intervalarr ); - $currentKey = end( $currentKey ); // get last index - if( $currentKey != $intervalix ) - $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 )); - if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) && - ( 0 != $intervalarr[$intervalix] )) { - /* step up date */ -// echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."
\n";//test - iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step); - continue; - } - else // continue within the selected interval - $intervalarr[$intervalix] = 0; -// echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."
\n";//test - } - $updateOK = TRUE; - if( $updateOK && isset( $recur['BYMONTH'] )) - $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH'] - , $wdate['month'] - ,($wdate['month'] - 13)); - if( $updateOK && isset( $recur['BYWEEKNO'] )) - $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO'] - , $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] - , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] ); - if( $updateOK && isset( $recur['BYYEARDAY'] )) - $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY'] - , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up'] - , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] ); - if( $updateOK && isset( $recur['BYMONTHDAY'] )) - $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY'] - , $wdate['day'] - , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] ); -// echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "
\n";//test### - if( $updateOK && isset( $recur['BYDAY'] )) { - $updateOK = FALSE; - $m = $wdate['month']; - $d = $wdate['day']; - if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no - $daynoexists = $daynosw = $daynamesw = FALSE; - if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] ) - $daynamesw = TRUE; - if( isset( $recur['BYDAY'][0] )) { - $daynoexists = TRUE; - if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] )) - $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] - , $daycnts[$m][$d]['monthdayno_up'] - , $daycnts[$m][$d]['monthdayno_down'] ); - elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) - $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] - , $daycnts[$m][$d]['yeardayno_up'] - , $daycnts[$m][$d]['yeardayno_down'] ); - } - if(( $daynoexists && $daynosw && $daynamesw ) || - ( !$daynoexists && !$daynosw && $daynamesw )) { - $updateOK = TRUE; -// echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK
\n"; // test ### - } -// echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK
\n"; // test ### - } - else { - foreach( $recur['BYDAY'] as $bydayvalue ) { - $daynoexists = $daynosw = $daynamesw = FALSE; - if( isset( $bydayvalue['DAY'] ) && - ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] )) - $daynamesw = TRUE; - if( isset( $bydayvalue[0] )) { - $daynoexists = TRUE; - if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || - isset( $recur['BYMONTH'] )) - $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] - , $daycnts[$m][$d]['monthdayno_up'] - , $daycnts[$m][$d]['monthdayno_down'] ); - elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) - $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] - , $daycnts[$m][$d]['yeardayno_up'] - , $daycnts[$m][$d]['yeardayno_down'] ); - } -// echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw
\n"; // test ### - if(( $daynoexists && $daynosw && $daynamesw ) || - ( !$daynoexists && !$daynosw && $daynamesw )) { - $updateOK = TRUE; - break; - } - } - } - } -// echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "
\n"; // test ### - /* check BYSETPOS */ - if( $updateOK ) { - if( isset( $recur['BYSETPOS'] ) && - ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) { - if( isset( $recur['WEEKLY'] )) { - if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] ) - $bysetposw1[] = $wdatets; - else - $bysetposw2[] = $wdatets; - } - else { - if(( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && - ( $bysetposYold == $wdate['year'] )) || - ( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) && - (( $bysetposYold == $wdate['year'] ) && - ( $bysetposMold == $wdate['month'] ))) || - ( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && - (( $bysetposYold == $wdate['year'] ) && - ( $bysetposMold == $wdate['month']) && - ( $bysetposDold == $wdate['day'] )))) { -// echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."
\n";//test - $bysetposymd1[] = $wdatets; - } - else { -// echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."
\n";//test - $bysetposymd2[] = $wdatets; - } - } - } - else { - /* update result array if BYSETPOS is set */ - $countcnt++; - if( $startdatets <= $wdatets ) { // only output within period - $result[$wdatets] = TRUE; -// echo "recur ".date('Y-m-d H:i:s',$wdatets)."
\n";//test - } -// echo "recur undate ".date('Y-m-d H:i:s',$wdatets)." okdatstart ".date('Y-m-d H:i:s',$startdatets)."
\n";//test - $updateOK = FALSE; - } - } - /* step up date */ - iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step); - /* check if BYSETPOS is set for updating result array */ - if( $updateOK && isset( $recur['BYSETPOS'] )) { - $bysetpos = FALSE; - if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && - ( $bysetposYold != $wdate['year'] )) { - $bysetpos = TRUE; - $bysetposYold = $wdate['year']; - } - elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] && - (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) { - $bysetpos = TRUE; - $bysetposYold = $wdate['year']; - $bysetposMold = $wdate['month']; - } - elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) { - $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year'])); - if( $bysetposWold != $weekno ) { - $bysetposWold = $weekno; - $bysetpos = TRUE; - } - } - elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && - (( $bysetposYold != $wdate['year'] ) || - ( $bysetposMold != $wdate['month'] ) || - ( $bysetposDold != $wdate['day'] ))) { - $bysetpos = TRUE; - $bysetposYold = $wdate['year']; - $bysetposMold = $wdate['month']; - $bysetposDold = $wdate['day']; - } - if( $bysetpos ) { - if( isset( $recur['BYWEEKNO'] )) { - $bysetposarr1 = & $bysetposw1; - $bysetposarr2 = & $bysetposw2; - } - else { - $bysetposarr1 = & $bysetposymd1; - $bysetposarr2 = & $bysetposymd2; - } -// echo 'test före out startYMD (weekno)='.$wdateStart['year'].':'.$wdateStart['month'].':'.$wdateStart['day']." ($weekStart) "; // test ### - foreach( $recur['BYSETPOS'] as $ix ) { - if( 0 > $ix ) // both positive and negative BYSETPOS allowed - $ix = ( count( $bysetposarr1 ) + $ix + 1); - $ix--; - if( isset( $bysetposarr1[$ix] )) { - if( $startdatets <= $bysetposarr1[$ix] ) { // only output within period -// $testdate = iCalUtilityFunctions::_timestamp2date( $bysetposarr1[$ix], 6 ); // test ### -// $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, $testdate['month'], $testdate['day'], $testdate['year'] )); // test ### -// echo " testYMD (weekno)=".$testdate['year'].':'.$testdate['month'].':'.$testdate['day']." ($testweekno)"; // test ### - $result[$bysetposarr1[$ix]] = TRUE; -// echo " recur ".date('Y-m-d H:i:s',$bysetposarr1[$ix]); // test ### - } - $countcnt++; - } - if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) - break; - } -// echo "
\n"; // test ### - $bysetposarr1 = $bysetposarr2; - $bysetposarr2 = array(); - } - } - } - } - public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) { - if( is_array( $BYvalue ) && - ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue ))) - return TRUE; - elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue )) - return TRUE; - else - return FALSE; - } - public static function _recurIntervalIx( $freq, $date, $wkst ) { - /* create interval index */ - switch( $freq ) { - case 'YEARLY': - $intervalix = $date['year']; - break; - case 'MONTHLY': - $intervalix = $date['year'].'-'.$date['month']; - break; - case 'WEEKLY': - $wdatets = iCalUtilityFunctions::_date2timestamp( $date ); - $intervalix = (int) date( 'W', ( $wdatets + $wkst )); - break; - case 'DAILY': - default: - $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day']; - break; - } - return $intervalix; - } -/** - * convert input format for exrule and rrule to internal format - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-24 - * @param array $rexrule - * @return array - */ - public static function _setRexrule( $rexrule ) { - $input = array(); - if( empty( $rexrule )) - return $input; - foreach( $rexrule as $rexrulelabel => $rexrulevalue ) { - $rexrulelabel = strtoupper( $rexrulelabel ); - if( 'UNTIL' != $rexrulelabel ) - $input[$rexrulelabel] = $rexrulevalue; - else { - iCalUtilityFunctions::_strDate2arr( $rexrulevalue ); - if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp, always date-time UTC - $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 7, 'UTC' ); - elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) { // date or UTC date-time - $parno = ( isset( $rexrulevalue['hour'] ) || isset( $rexrulevalue[4] )) ? 7 : 3; - $d = iCalUtilityFunctions::_chkDateArr( $rexrulevalue, $parno ); - if(( 3 < $parno ) && isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); - $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input[$rexrulelabel]['unparsedtext'] ); - } - else - $input[$rexrulelabel] = $d; - } - elseif( 8 <= strlen( trim( $rexrulevalue ))) { // ex. textual date-time 2006-08-03 10:12:18 => UTC - $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $rexrulevalue ); - unset( $input['$rexrulelabel']['unparsedtext'] ); - } - if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] )) - $input[$rexrulelabel]['tz'] = 'Z'; - } - } - /* set recurrence rule specification in rfc2445 order */ - $input2 = array(); - if( isset( $input['FREQ'] )) - $input2['FREQ'] = $input['FREQ']; - if( isset( $input['UNTIL'] )) - $input2['UNTIL'] = $input['UNTIL']; - elseif( isset( $input['COUNT'] )) - $input2['COUNT'] = $input['COUNT']; - if( isset( $input['INTERVAL'] )) - $input2['INTERVAL'] = $input['INTERVAL']; - if( isset( $input['BYSECOND'] )) - $input2['BYSECOND'] = $input['BYSECOND']; - if( isset( $input['BYMINUTE'] )) - $input2['BYMINUTE'] = $input['BYMINUTE']; - if( isset( $input['BYHOUR'] )) - $input2['BYHOUR'] = $input['BYHOUR']; - if( isset( $input['BYDAY'] )) { - if( !is_array( $input['BYDAY'] )) // ensure upper case.. . - $input2['BYDAY'] = strtoupper( $input['BYDAY'] ); - else { - foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) { - if( 'DAY' == strtoupper( $BYDAYx )) - $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv ); - elseif( !is_array( $BYDAYv )) { - $input2['BYDAY'][$BYDAYx] = $BYDAYv; - } - else { - foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) { - if( 'DAY' == strtoupper( $BYDAYx2 )) - $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 ); - else - $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2; - } - } - } - } - } - if( isset( $input['BYMONTHDAY'] )) - $input2['BYMONTHDAY'] = $input['BYMONTHDAY']; - if( isset( $input['BYYEARDAY'] )) - $input2['BYYEARDAY'] = $input['BYYEARDAY']; - if( isset( $input['BYWEEKNO'] )) - $input2['BYWEEKNO'] = $input['BYWEEKNO']; - if( isset( $input['BYMONTH'] )) - $input2['BYMONTH'] = $input['BYMONTH']; - if( isset( $input['BYSETPOS'] )) - $input2['BYSETPOS'] = $input['BYSETPOS']; - if( isset( $input['WKST'] )) - $input2['WKST'] = $input['WKST']; - return $input2; - } -/** - * convert format for input date to internal date with parameters - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-10-15 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param string $tz optional - * @param array $params optional - * @param string $caller optional - * @param string $objName optional - * @param string $tzid optional - * @return array - */ - public static function _setDate( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE, $caller=null, $objName=null, $tzid=FALSE ) { - $input = $parno = null; - $localtime = (( 'dtstart' == $caller ) && in_array( $objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE; - iCalUtilityFunctions::_strDate2arr( $year ); - if( iCalUtilityFunctions::_isArrayDate( $year )) { - $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, $parno ); - if( 100 > $input['value']['year'] ) - $input['value']['year'] += 2000; - if( $localtime ) - unset( $month['VALUE'], $month['TZID'] ); - elseif( !isset( $month['TZID'] ) && isset( $tzid )) - $month['TZID'] = $tzid; - if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) - unset( $month['TZID'] ); - elseif( isset( $month['TZID'] ) && iCalUtilityFunctions::_isOffset( $month['TZID'] )) { - $input['value']['tz'] = $month['TZID']; - unset( $month['TZID'] ); - } - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - $hitval = ( isset( $input['value']['tz'] )) ? 7 : 6; - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval ); - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $input['value'] ), $parno ); - if(( 3 != $parno ) && isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { - $d = $input['value']; - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - if( isset( $input['value']['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { - $input['params']['TZID'] = $input['value']['tz']; - unset( $input['value']['tz'] ); - } - } // end if( iCalUtilityFunctions::_isArrayDate( $year )) - elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { - if( $localtime ) unset ( $month['VALUE'], $month['TZID'] ); - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); - $hitval = 7; - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno ); - if( !isset( $input['params']['TZID'] ) && !empty( $tzid )) - $input['params']['TZID'] = $tzid; - if( isset( $year['tz'] )) { - $parno = 6; - if( !iCalUtilityFunctions::_isOffset( $year['tz'] )) - $input['params']['TZID'] = $year['tz']; - } - elseif( isset( $input['params']['TZID'] )) { - $year['tz'] = $input['params']['TZID']; - $parno = 6; - if( iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { - unset( $input['params']['TZID'] ); - $parno = 7; - } - } - $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, $parno ); - } // end elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) - elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18 [[[+/-]1234[56]] / timezone] - if( $localtime ) - unset( $month['VALUE'], $month['TZID'] ); - elseif( !isset( $month['TZID'] ) && !empty( $tzid )) - $month['TZID'] = $tzid; - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno ); - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $year, $parno ); - unset( $input['value']['unparsedtext'] ); - if( isset( $input['value']['tz'] )) { - if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { - $d = $input['value']; - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - else { - $input['params']['TZID'] = $input['value']['tz']; - unset( $input['value']['tz'] ); - } - } - elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { - $d = $input['value']; - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $input['params']['TZID'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - } // end elseif( 8 <= strlen( trim( $year ))) - else { - if( is_array( $params )) - $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); - elseif( is_array( $tz )) { - $input['params'] = iCalUtilityFunctions::_setParams( $tz, array( 'VALUE' => 'DATE-TIME' )); - $tz = FALSE; - } - elseif( is_array( $hour )) { - $input['params'] = iCalUtilityFunctions::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' )); - $hour = $min = $sec = $tz = FALSE; - } - if( $localtime ) - unset ( $input['params']['VALUE'], $input['params']['TZID'] ); - elseif( !isset( $tz ) && !isset( $input['params']['TZID'] ) && !empty( $tzid )) - $input['params']['TZID'] = $tzid; - elseif( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) - unset( $input['params']['TZID'] ); - elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { - $tz = $input['params']['TZID']; - unset( $input['params']['TZID'] ); - } - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); - $hitval = ( iCalUtilityFunctions::_isOffset( $tz )) ? 7 : 6; - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno ); - $input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day ); - if( 3 != $parno ) { - $input['value']['hour'] = ( $hour ) ? $hour : '0'; - $input['value']['min'] = ( $min ) ? $min : '0'; - $input['value']['sec'] = ( $sec ) ? $sec : '0'; - if( !empty( $tz )) - $input['value']['tz'] = $tz; - $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], $parno ); - if( !empty( $tz ) && !iCalUtilityFunctions::_isOffset( $tz )) - $strdate .= ( 'Z' == $tz ) ? $tz : ' '.$tz; - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno ); - unset( $input['value']['unparsedtext'] ); - if( isset( $input['value']['tz'] )) { - if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { - $d = $input['value']; - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - else { - $input['params']['TZID'] = $input['value']['tz']; - unset( $input['value']['tz'] ); - } - } - elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { - $d = $input['value']; - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $input['params']['TZID'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - } - } // end else (i.e. using all arguments) - if(( 3 == $parno ) || ( isset( $input['params']['VALUE'] ) && ( 'DATE' == $input['params']['VALUE'] ))) { - $input['params']['VALUE'] = 'DATE'; - unset( $input['value']['hour'], $input['value']['min'], $input['value']['sec'], $input['value']['tz'], $input['params']['TZID'] ); - } - elseif( isset( $input['params']['TZID'] )) { - if(( 'UTC' == strtoupper( $input['params']['TZID'] )) || ( 'GMT' == strtoupper( $input['params']['TZID'] ))) { - $input['value']['tz'] = 'Z'; - unset( $input['params']['TZID'] ); - } - else - unset( $input['value']['tz'] ); - } - elseif( isset( $input['value']['tz'] )) { - if(( 'UTC' == strtoupper( $input['value']['tz'] )) || ( 'GMT' == strtoupper( $input['value']['tz'] ))) - $input['value']['tz'] = 'Z'; - if( 'Z' != $input['value']['tz'] ) { - $input['params']['TZID'] = $input['value']['tz']; - unset( $input['value']['tz'] ); - } - else - unset( $input['params']['TZID'] ); - } - if( $localtime ) - unset( $input['value']['tz'], $input['params']['TZID'] ); - return $input; - } -/** - * convert format for input date (UTC) to internal date with parameters - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.4 - 2012-10-06 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param array $params optional - * @return array - */ - public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - $input = null; - iCalUtilityFunctions::_strDate2arr( $year ); - if( iCalUtilityFunctions::_isArrayDate( $year )) { - $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, 7 ); - if( isset( $input['value']['year'] ) && ( 100 > $input['value']['year'] )) - $input['value']['year'] += 2000; - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - if( isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { - $d = $input['value']; - $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'] ); - } - } - elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { - $year['tz'] = 'UTC'; - $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, 7 ); - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - } - elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18 - $input['value'] = iCalUtilityFunctions::_strdate2date( $year, 7 ); - unset( $input['value']['unparsedtext'] ); - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - } - else { - $input['value'] = array( 'year' => $year - , 'month' => $month - , 'day' => $day - , 'hour' => $hour - , 'min' => $min - , 'sec' => $sec ); - if( isset( $tz )) $input['value']['tz'] = $tz; - if(( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) || - ( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))) { - if( !isset( $tz ) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) - $input['value']['tz'] = $input['params']['TZID']; - unset( $input['params']['TZID'] ); - $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], 7 ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'] ); - } - $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); - } - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default - if( !isset( $input['value']['hour'] )) $input['value']['hour'] = 0; - if( !isset( $input['value']['min'] )) $input['value']['min'] = 0; - if( !isset( $input['value']['sec'] )) $input['value']['sec'] = 0; - $input['value']['tz'] = 'Z'; - return $input; - } -/** - * check index and set (an indexed) content in multiple value array - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.12 - 2011-01-03 - * @param array $valArr - * @param mixed $value - * @param array $params - * @param array $defaults - * @param int $index - * @return void - */ - public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) { - if( !is_array( $valArr )) $valArr = array(); - if( $index ) - $index = $index - 1; - elseif( 0 < count( $valArr )) { - $keys = array_keys( $valArr ); - $index = end( $keys ) + 1; - } - else - $index = 0; - $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults )); - ksort( $valArr ); - } -/** - * set input (formatted) parameters- component property attributes - * - * default parameters can be set, if missing - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 1.x.x - 2007-05-01 - * @param array $params - * @param array $defaults - * @return array - */ - public static function _setParams( $params, $defaults=FALSE ) { - if( !is_array( $params)) - $params = array(); - $input = array(); - foreach( $params as $paramKey => $paramValue ) { - if( is_array( $paramValue )) { - foreach( $paramValue as $pkey => $pValue ) { - if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 ))) - $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 )); - } - } - elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 ))) - $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 )); - if( 'VALUE' == strtoupper( $paramKey )) - $input['VALUE'] = strtoupper( $paramValue ); - else - $input[strtoupper( $paramKey )] = $paramValue; - } - if( is_array( $defaults )) { - foreach( $defaults as $paramKey => $paramValue ) { - if( !isset( $input[$paramKey] )) - $input[$paramKey] = $paramValue; - } - } - return (0 < count( $input )) ? $input : null; - } -/** - * break lines at pos 75 - * - * Lines of text SHOULD NOT be longer than 75 octets, excluding the line - * break. Long content lines SHOULD be split into a multiple line - * representations using a line "folding" technique. That is, a long - * line can be split between any two characters by inserting a CRLF - * immediately followed by a single linear white space character (i.e., - * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence - * of CRLF followed immediately by a single linear white space character - * is ignored (i.e., removed) when processing the content type. - * - * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where - * the reserved expression "\n" in the arg $string could be broken up by the - * folding of lines, causing ambiguity in the return string. - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @param string $value - * @return string + * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved + * @link http://kigkonsult.se/iCalcreator/index.php + * @license http://kigkonsult.se/downloads/dl.php?f=LGPL + * @package iCalcreator + * @version 2.22.1 */ - public static function _size75( $string, $nl ) { - $tmp = $string; - $string = ''; - $cCnt = $x = 0; - while( TRUE ) { - if( !isset( $tmp[$x] )) { - $string .= $nl; // loop breakes here - break; - } - elseif(( 74 <= $cCnt ) && ( '\\' == $tmp[$x] ) && ( 'n' == $tmp[$x+1] )) { - $string .= $nl.' \n'; // don't break lines inside '\n' - $x += 2; - if( !isset( $tmp[$x] )) { - $string .= $nl; - break; - } - $cCnt = 3; - } - elseif( 75 <= $cCnt ) { - $string .= $nl.' '; - $cCnt = 1; - } - $byte = ord( $tmp[$x] ); - $string .= $tmp[$x]; - switch( TRUE ) { // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - case(( $byte >= 0x20 ) && ( $byte <= 0x7F )): // characters U-00000000 - U-0000007F (same as ASCII) - $cCnt += 1; - break; // add a one byte character - case(( $byte & 0xE0) == 0xC0 ): // characters U-00000080 - U-000007FF, mask 110XXXXX - if( isset( $tmp[$x+1] )) { - $cCnt += 1; - $string .= $tmp[$x+1]; - $x += 1; // add a two bytes character - } - break; - case(( $byte & 0xF0 ) == 0xE0 ): // characters U-00000800 - U-0000FFFF, mask 1110XXXX - if( isset( $tmp[$x+2] )) { - $cCnt += 1; - $string .= $tmp[$x+1].$tmp[$x+2]; - $x += 2; // add a three bytes character - } - break; - case(( $byte & 0xF8 ) == 0xF0 ): // characters U-00010000 - U-001FFFFF, mask 11110XXX - if( isset( $tmp[$x+3] )) { - $cCnt += 1; - $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3]; - $x += 3; // add a four bytes character - } - break; - case(( $byte & 0xFC ) == 0xF8 ): // characters U-00200000 - U-03FFFFFF, mask 111110XX - if( isset( $tmp[$x+4] )) { - $cCnt += 1; - $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4]; - $x += 4; // add a five bytes character - } - break; - case(( $byte & 0xFE ) == 0xFC ): // characters U-04000000 - U-7FFFFFFF, mask 1111110X - if( isset( $tmp[$x+5] )) { - $cCnt += 1; - $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4].$tmp[$x+5]; - $x += 5; // add a six bytes character - } - default: // add any other byte without counting up $cCnt - break; - } // end switch( TRUE ) - $x += 1; // next 'byte' to test - } // end while( TRUE ) { - return $string; - } /** - * sort callback functions for exdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.11 - 2013-01-12 - * @param array $a - * @param array $b - * @return int - */ - public static function _sortExdate1( $a, $b ) { - $as = sprintf( '%04d%02d%02d', $a['year'], $a['month'], $a['day'] ); - $as .= ( isset( $a['hour'] )) ? sprintf( '%02d%02d%02d', $a['hour'], $a['min'], $a['sec'] ) : ''; - $bs = sprintf( '%04d%02d%02d', $b['year'], $b['month'], $b['day'] ); - $bs .= ( isset( $b['hour'] )) ? sprintf( '%02d%02d%02d', $b['hour'], $b['min'], $b['sec'] ) : ''; - return strcmp( $as, $bs ); - } - public static function _sortExdate2( $a, $b ) { - $val = reset( $a['value'] ); - $as = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] ); - $as .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : ''; - $val = reset( $b['value'] ); - $bs = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] ); - $bs .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : ''; - return strcmp( $as, $bs ); - } -/** - * sort callback functions for rdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.9 - 2013-01-12 - * @param array $a - * @param array $b - * @return int - */ - public static function _sortRdate1( $a, $b ) { - $val = isset( $a['year'] ) ? $a : $a[0]; - $as = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] ); - $as .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : ''; - $val = isset( $b['year'] ) ? $b : $b[0]; - $bs = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] ); - $bs .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : ''; - return strcmp( $as, $bs ); - } - public static function _sortRdate2( $a, $b ) { - $val = isset( $a['value'][0]['year'] ) ? $a['value'][0] : $a['value'][0][0]; - $as = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] ); - $as .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : ''; - $val = isset( $b['value'][0]['year'] ) ? $b['value'][0] : $b['value'][0][0]; - $bs = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] ); - $bs .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : ''; - return strcmp( $as, $bs ); - } -/** - * step date, return updated date, array and timpstamp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-24 - * @param array $date, date to step - * @param int $timestamp - * @param array $step, default array( 'day' => 1 ) - * @return void - */ - public static function _stepdate( &$date, &$timestamp, $step=array( 'day' => 1 )) { - if( !isset( $date['hour'] )) $date['hour'] = 0; - if( !isset( $date['min'] )) $date['min'] = 0; - if( !isset( $date['sec'] )) $date['sec'] = 0; - foreach( $step as $stepix => $stepvalue ) - $date[$stepix] += $stepvalue; - $timestamp = mktime( $date['hour'], $date['min'], $date['sec'], $date['month'], $date['day'], $date['year'] ); - $d = date( 'Y-m-d-H-i-s', $timestamp); - $d = explode( '-', $d ); - $date = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); - foreach( $date as $k => $v ) - $date[$k] = (int) $v; - } -/** - * convert a date from specific string to array format - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.8 - 2012-01-27 - * @param mixed $input - * @return bool, TRUE on success - */ - public static function _strDate2arr( & $input ) { - if( is_array( $input )) - return FALSE; - if( 5 > strlen( (string) $input )) - return FALSE; - $work = $input; - if( 2 == substr_count( $work, '-' )) - $work = str_replace( '-', '', $work ); - if( 2 == substr_count( $work, '/' )) - $work = str_replace( '/', '', $work ); - if( !ctype_digit( substr( $work, 0, 8 ))) - return FALSE; - $temp = array( 'year' => (int) substr( $work, 0, 4 ) - , 'month' => (int) substr( $work, 4, 2 ) - , 'day' => (int) substr( $work, 6, 2 )); - if( !checkdate( $temp['month'], $temp['day'], $temp['year'] )) - return FALSE; - if( 8 == strlen( $work )) { - $input = $temp; - return TRUE; - } - if(( ' ' == substr( $work, 8, 1 )) || ( 'T' == substr( $work, 8, 1 )) || ( 't' == substr( $work, 8, 1 ))) - $work = substr( $work, 9 ); - elseif( ctype_digit( substr( $work, 8, 1 ))) - $work = substr( $work, 8 ); - else - return FALSE; - if( 2 == substr_count( $work, ':' )) - $work = str_replace( ':', '', $work ); - if( !ctype_digit( substr( $work, 0, 4 ))) - return FALSE; - $temp['hour'] = substr( $work, 0, 2 ); - $temp['min'] = substr( $work, 2, 2 ); - if((( 0 > $temp['hour'] ) || ( $temp['hour'] > 23 )) || - (( 0 > $temp['min'] ) || ( $temp['min'] > 59 ))) - return FALSE; - if( ctype_digit( substr( $work, 4, 2 ))) { - $temp['sec'] = substr( $work, 4, 2 ); - if(( 0 > $temp['sec'] ) || ( $temp['sec'] > 59 )) - return FALSE; - $len = 6; - } - else { - $temp['sec'] = 0; - $len = 4; - } - if( $len < strlen( $work)) - $temp['tz'] = trim( substr( $work, 6 )); - $input = $temp; - return TRUE; - } -/** - * ensures internal date-time/date format for input date-time/date in string fromat - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-10-07 - * Modified to also return original string value by Yitzchok Lavi - * @param array $datetime - * @param int $parno optional, default FALSE - * @param moxed $wtz optional, default null - * @return array - */ - public static function _date_time_string( $datetime, $parno=FALSE ) { - return iCalUtilityFunctions::_strdate2date( $datetime, $parno, null ); - } - public static function _strdate2date( $datetime, $parno=FALSE, $wtz=null ) { - // save original input string to return it later - $unparseddatetime = $datetime; - $datetime = (string) trim( $datetime ); - $tz = null; - $offset = 0; - $tzSts = FALSE; - $len = strlen( $datetime ); - if( 'Z' == substr( $datetime, -1 )) { - $tz = 'Z'; - $datetime = trim( substr( $datetime, 0, ( $len - 1 ))); - $tzSts = TRUE; - $len = 88; - } - if( iCalUtilityFunctions::_isOffset( substr( $datetime, -5, 5 ))) { // [+/-]NNNN offset - $tz = substr( $datetime, -5, 5 ); - $datetime = trim( substr( $datetime, 0, ($len - 5))); - $len = strlen( $datetime ); - } - elseif( iCalUtilityFunctions::_isOffset( substr( $datetime, -7, 7 ))) { // [+/-]NNNNNN offset - $tz = substr( $datetime, -7, 7 ); - $datetime = trim( substr( $datetime, 0, ($len - 7))); - $len = strlen( $datetime ); - } - elseif( empty( $wtz ) && ctype_digit( substr( $datetime, 0, 4 )) && ctype_digit( substr( $datetime, -2, 2 )) && iCalUtilityFunctions::_strDate2arr( $datetime )) { - $output = $datetime; - if( !empty( $tz )) - $output['tz'] = 'Z'; - $output['unparsedtext'] = $unparseddatetime; - return $output; - } - else { - $cx = $tx = 0; // find any trailing timezone or offset - for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) { - $char = substr( $datetime, $cx, 1 ); - if(( ' ' == $char) || ctype_digit( $char )) - break; // if exists, tz ends here.. . ? - else - $tx--; // tz length counter - } - if( 0 > $tx ) { // if any - $tz = substr( $datetime, $tx ); - $datetime = trim( substr( $datetime, 0, $len + $tx )); - $len = strlen( $datetime ); - } - if(( 17 <= $len ) || // long textual datetime - ( ctype_digit( substr( $datetime, 0, 8 )) && ( 'T' == substr( $datetime, 8, 1 )) && ctype_digit( substr( $datetime, -6, 6 ))) || - ( ctype_digit( substr( $datetime, 0, 14 )))) { - $len = 88; - $tzSts = TRUE; - } - else - $tz = null; // no tz for Y-m-d dates - } - if( empty( $tz ) && !empty( $wtz )) - $tz = $wtz; - if( 17 >= $len ) // any Y-m-d textual date - $tz = null; - if( !empty( $tz ) && ( 17 < $len )) { // tz set AND long textual datetime - if(( 'Z' != $tz ) && ( iCalUtilityFunctions::_isOffset( $tz ))) { - $offset = (string) iCalUtilityFunctions::_tz2offset( $tz ) * -1; - $tz = 'UTC'; - $tzSts = TRUE; - } - elseif( !empty( $wtz )) - $tzSts = TRUE; - $tz = trim( $tz ); - if(( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz ))) - $tz = 'UTC'; - if( 0 < substr_count( $datetime, '-' )) - $datetime = str_replace( '-', '/', $datetime ); - try { - $d = new DateTime( $datetime, new DateTimeZone( $tz )); - if( 0 != $offset ) // adjust for offset - $d->modify( $offset.' seconds' ); - $datestring = $d->format( 'Y-m-d-H-i-s' ); - unset( $d ); - } - catch( Exception $e ) { - $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime )); - } - } // end if( !empty( $tz ) && ( 17 < $len )) - else - $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime )); -// echo " _strdate2date input=$datetime, tz=$tz, offset=$offset, wtz=$wtz, len=$len, prepDate=$datestring\n"; - if( 'UTC' == $tz ) - $tz = 'Z'; - $d = explode( '-', $datestring ); - $output = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2] ); - if((( FALSE !== $parno ) && ( 3 != $parno )) || // parno is set to 6 or 7 - (( FALSE === $parno ) && ( 'Z' == $tz )) || // parno is not set and UTC - (( FALSE === $parno ) && ( 'Z' != $tz ) && ( 0 != $d[3] + $d[4] + $d[5] ) && ( 17 < $len ))) { // !parno and !UTC and 0 != hour+min+sec and long input text - $output['hour'] = $d[3]; - $output['min'] = $d[4]; - $output['sec'] = $d[5]; - if(( $tzSts || ( 7 == $parno )) && !empty( $tz )) - $output['tz'] = $tz; - } - // return original string in the array in case strtotime failed to make sense of it - $output['unparsedtext'] = $unparseddatetime; - return $output; - } -/********************************************************************************/ -/** - * special characters management output - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @param string $string - * @param string $format - * @param string $nl - * @return string - */ - public static function _strrep( $string, $format, $nl ) { - switch( $format ) { - case 'xcal': - $string = str_replace( '\n', $nl, $string); - $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string )))); - break; - default: - $pos = 0; - $specChars = array( 'n', 'N', 'r', ',', ';' ); - while( isset( $string[$pos] )) { - if( FALSE === ( $pos = strpos( $string, "\\", $pos ))) - break; - if( !in_array( substr( $string, $pos, 1 ), $specChars )) { - $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 )); - $pos += 1; - } - $pos += 1; - } - if( FALSE !== strpos( $string, '"' )) - $string = str_replace('"', "'", $string); - if( FALSE !== strpos( $string, ',' )) - $string = str_replace(',', '\,', $string); - if( FALSE !== strpos( $string, ';' )) - $string = str_replace(';', '\;', $string); - if( FALSE !== strpos( $string, "\r\n" )) - $string = str_replace( "\r\n", '\n', $string); - elseif( FALSE !== strpos( $string, "\r" )) - $string = str_replace( "\r", '\n', $string); - elseif( FALSE !== strpos( $string, "\n" )) - $string = str_replace( "\n", '\n', $string); - if( FALSE !== strpos( $string, '\N' )) - $string = str_replace( '\N', '\n', $string); -// if( FALSE !== strpos( $string, $nl )) - $string = str_replace( $nl, '\n', $string); - break; - } - return $string; - } -/** - * special characters management input (from iCal file) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @param string $string - * @return string - */ - public static function _strunrep( $string ) { - $string = str_replace( '\\\\', '\\', $string); - $string = str_replace( '\,', ',', $string); - $string = str_replace( '\;', ';', $string); -// $string = str_replace( '\n', $nl, $string); // ?? - return $string; - } -/** - * convert timestamp to date array, default UTC or adjusted for offset/timezone - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.1 - 2012-10-17 - * @param mixed $timestamp - * @param int $parno - * @param string $wtz - * @return array - */ - public static function _timestamp2date( $timestamp, $parno=6, $wtz=null ) { - if( is_array( $timestamp )) { - $tz = ( isset( $timestamp['tz'] )) ? $timestamp['tz'] : $wtz; - $timestamp = $timestamp['timestamp']; - } - $tz = ( isset( $tz )) ? $tz : $wtz; - if( empty( $tz ) || ( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz ))) - $tz = 'UTC'; - elseif( iCalUtilityFunctions::_isOffset( $tz )) { - $offset = iCalUtilityFunctions::_tz2offset( $tz ); - $tz = 'UTC'; - } - try { - $d = new DateTime( "@$timestamp" ); // set UTC date - if( isset( $offset ) && ( 0 != $offset )) // adjust for offset - $d->modify( $offset.' seconds' ); - elseif( 'UTC' != $tz ) - $d->setTimezone( new DateTimeZone( $tz )); // convert to local date - $date = $d->format( 'Y-m-d-H-i-s' ); - unset( $d ); - } - catch( Exception $e ) { - $date = date( 'Y-m-d-H-i-s', $timestamp ); - } - $date = explode( '-', $date ); - $output = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2] ); - if( 3 != $parno ) { - $output['hour'] = $date[3]; - $output['min'] = $date[4]; - $output['sec'] = $date[5]; - if( 'UTC' == $tz && ( !isset( $offset ) || ( 0 == $offset ))) - $output['tz'] = 'Z'; - } - return $output; - } -/** - * convert timestamp (seconds) to duration in array format - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.23 - 2010-10-23 - * @param int $timestamp - * @return array, duration format - */ - public static function _timestamp2duration( $timestamp ) { - $dur = array(); - $dur['week'] = (int) floor( $timestamp / ( 7 * 24 * 60 * 60 )); - $timestamp = $timestamp % ( 7 * 24 * 60 * 60 ); - $dur['day'] = (int) floor( $timestamp / ( 24 * 60 * 60 )); - $timestamp = $timestamp % ( 24 * 60 * 60 ); - $dur['hour'] = (int) floor( $timestamp / ( 60 * 60 )); - $timestamp = $timestamp % ( 60 * 60 ); - $dur['min'] = (int) floor( $timestamp / ( 60 )); - $dur['sec'] = (int) $timestamp % ( 60 ); - return $dur; - } -/** - * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.1 - 2012-10-17 - * @param mixed $date, date to alter - * @param string $tzFrom, PHP valid 'from' timezone - * @param string $tzTo, PHP valid 'to' timezone, default 'UTC' - * @param string $format, date output format, default 'Ymd\THis' - * @return bool - */ - public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) { - if( is_array( $date ) && isset( $date['timestamp'] )) { - try { - $d = new DateTime( "@{$date['timestamp']}" ); // set UTC date - $d->setTimezone(new DateTimeZone( $tzFrom )); // convert to 'from' date - } - catch( Exception $e ) { return FALSE; } - } - else { - if( iCalUtilityFunctions::_isArrayDate( $date )) { - if( isset( $date['tz'] )) - unset( $date['tz'] ); - $date = iCalUtilityFunctions::_date2strdate( iCalUtilityFunctions::_chkDateArr( $date )); - } - if( 'Z' == substr( $date, -1 )) - $date = substr( $date, 0, ( strlen( $date ) - 2 )); - try { $d = new DateTime( $date, new DateTimeZone( $tzFrom )); } - catch( Exception $e ) { return FALSE; } - } - try { $d->setTimezone( new DateTimeZone( $tzTo )); } - catch( Exception $e ) { return FALSE; } - $date = $d->format( $format ); - return TRUE; - } -/** - * convert offset, [+/-]HHmm[ss], to seconds, used when correcting UTC to localtime or v.v. - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.4 - 2012-01-11 - * @param string $offset - * @return integer - */ - public static function _tz2offset( $tz ) { - $tz = trim( (string) $tz ); - $offset = 0; - if((( 5 != strlen( $tz )) && ( 7 != strlen( $tz ))) || - (( '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) || - (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) || - (( 7 == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 )))) - return $offset; - $hours2sec = (int) substr( $tz, 1, 2 ) * 3600; - $min2sec = (int) substr( $tz, 3, 2 ) * 60; - $sec = ( 7 == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00'; - $offset = $hours2sec + $min2sec + $sec; - $offset = ('-' == substr( $tz, 0, 1 )) ? $offset * -1 : $offset; - return $offset; - } -} -/*********************************************************************************/ -/* iCalcreator vCard helper functions */ -/*********************************************************************************/ -/** - * convert single ATTENDEE, CONTACT or ORGANIZER (in email format) to vCard - * returns vCard/TRUE or if directory (if set) or file write is unvalid, FALSE - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.12.2 - 2012-07-11 - * @param object $email - * $param string $version, vCard version (default 2.1) - * $param string $directory, where to save vCards (default FALSE) - * $param string $ext, vCard file extension (default 'vcf') - * @return mixed - */ -function iCal2vCard( $email, $version='2.1', $directory=FALSE, $ext='vcf' ) { - if( FALSE === ( $pos = strpos( $email, '@' ))) - return FALSE; - if( $directory ) { - if( DIRECTORY_SEPARATOR != substr( $directory, ( 0 - strlen( DIRECTORY_SEPARATOR )))) - $directory .= DIRECTORY_SEPARATOR; - if( !is_dir( $directory ) || !is_writable( $directory )) - return FALSE; - } - /* prepare vCard */ - $email = str_replace( 'MAILTO:', '', $email ); - $name = $person = substr( $email, 0, $pos ); - if( ctype_upper( $name ) || ctype_lower( $name )) - $name = array( $name ); - else { - if( FALSE !== ( $pos = strpos( $name, '.' ))) { - $name = explode( '.', $name ); - foreach( $name as $k => $part ) - $name[$k] = ucfirst( $part ); - } - else { // split camelCase - $chars = $name; - $name = array( $chars[0] ); - $k = 0; - $x = 1; - while( FALSE !== ( $char = substr( $chars, $x, 1 ))) { - if( ctype_upper( $char )) { - $k += 1; - $name[$k] = ''; - } - $name[$k] .= $char; - $x++; - } - } - } - $nl = "\r\n"; - $FN = 'FN:'.implode( ' ', $name ).$nl; - $name = array_reverse( $name ); - $N = 'N:'.array_shift( $name ); - $scCnt = 0; - while( NULL != ( $part = array_shift( $name ))) { - if(( '4.0' != $version ) || ( 4 > $scCnt )) - $scCnt += 1; - $N .= ';'.$part; - } - while(( '4.0' == $version ) && ( 4 > $scCnt )) { - $N .= ';'; - $scCnt += 1; - } - $N .= $nl; - $EMAIL = 'EMAIL:'.$email.$nl; - /* create vCard */ - $vCard = 'BEGIN:VCARD'.$nl; - $vCard .= "VERSION:$version$nl"; - $vCard .= 'PRODID:-//kigkonsult.se '.ICALCREATOR_VERSION."//$nl"; - $vCard .= $N; - $vCard .= $FN; - $vCard .= $EMAIL; - $vCard .= 'REV:'.gmdate( 'Ymd\THis\Z' ).$nl; - $vCard .= 'END:VCARD'.$nl; - /* save each vCard as (unique) single file */ - if( $directory ) { - $fname = $directory.preg_replace( '/[^a-z0-9.]/i', '', $email ); - $cnt = 1; - $dbl = ''; - while( is_file ( $fname.$dbl.'.'.$ext )) { - $cnt += 1; - $dbl = "_$cnt"; - } - if( FALSE === file_put_contents( $fname, $fname.$dbl.'.'.$ext )) - return FALSE; - return TRUE; - } - /* return vCard */ - else - return $vCard; -} -/** - * convert ATTENDEEs, CONTACTs and ORGANIZERs (in email format) to vCards - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.12.2 - 2012-05-07 - * @param object $calendar, iCalcreator vcalendar instance reference - * $param string $version, vCard version (default 2.1) - * $param string $directory, where to save vCards (default FALSE) - * $param string $ext, vCard file extension (default 'vcf') - * @return mixed - */ -function iCal2vCards( & $calendar, $version='2.1', $directory=FALSE, $ext='vcf' ) { - $hits = array(); - $vCardP = array( 'ATTENDEE', 'CONTACT', 'ORGANIZER' ); - foreach( $vCardP as $prop ) { - $hits2 = $calendar->getProperty( $prop ); - foreach( $hits2 as $propValue => $occCnt ) { - if( FALSE === ( $pos = strpos( $propValue, '@' ))) - continue; - $propValue = str_replace( 'MAILTO:', '', $propValue ); - if( isset( $hits[$propValue] )) - $hits[$propValue] += $occCnt; - else - $hits[$propValue] = $occCnt; - } - } - if( empty( $hits )) - return FALSE; - ksort( $hits ); - $output = ''; - foreach( $hits as $email => $skip ) { - $res = iCal2vCard( $email, $version, $directory, $ext ); - if( $directory && !$res ) - return FALSE; - elseif( !$res ) - return $res; - else - $output .= $res; - } - if( $directory ) - return TRUE; - if( !empty( $output )) - return $output; - return FALSE; -} -/*********************************************************************************/ -/* iCalcreator XML (rfc6321) helper functions */ -/*********************************************************************************/ -/** - * format iCal XML output, rfc6321, using PHP SimpleXMLElement - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.6 - 2012-10-19 - * @param object $calendar, iCalcreator vcalendar instance reference - * @return string - */ -function iCal2XML( & $calendar ) { - /** fix an SimpleXMLElement instance and create root element */ - $xmlstr = ''; - $xmlstr .= ''; - $xmlstr .= ''; - $xml = new SimpleXMLElement( $xmlstr ); - $vcalendar = $xml->addChild( 'vcalendar' ); - /** fix calendar properties */ - $properties = $vcalendar->addChild( 'properties' ); - $calProps = array( 'prodid', 'version', 'calscale', 'method' ); - foreach( $calProps as $calProp ) { - if( FALSE !== ( $content = $calendar->getProperty( $calProp ))) - _addXMLchild( $properties, $calProp, 'text', $content ); - } - while( FALSE !== ( $content = $calendar->getProperty( FALSE, FALSE, TRUE ))) - _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); - $langCal = $calendar->getConfig( 'language' ); - /** prepare to fix components with properties */ - $components = $vcalendar->addChild( 'components' ); - $comps = array( 'vtimezone', 'vevent', 'vtodo', 'vjournal', 'vfreebusy' ); - foreach( $comps as $compName ) { - switch( $compName ) { - case 'vevent': - case 'vtodo': - $subComps = array( 'valarm' ); - break; - case 'vjournal': - case 'vfreebusy': - $subComps = array(); - break; - case 'vtimezone': - $subComps = array( 'standard', 'daylight' ); - break; - } // end switch( $compName ) - /** fix component properties */ - while( FALSE !== ( $component = $calendar->getComponent( $compName ))) { - $child = $components->addChild( $compName ); - $properties = $child->addChild( 'properties' ); - $langComp = $component->getConfig( 'language' ); - $props = $component->getConfig( 'setPropertyNames' ); - foreach( $props as $prop ) { - switch( strtolower( $prop )) { - case 'attach': // may occur multiple times, below - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'attendee': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); - } - break; - case 'exdate': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time'; - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'freebusy': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if( is_array( $content ) && isset( $content['value']['fbtype'] )) { - $content['params']['FBTYPE'] = $content['value']['fbtype']; - unset( $content['value']['fbtype'] ); - } - _addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] ); - } - break; - case 'request-status': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if( !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] ); - } - break; - case 'rdate': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - $type = 'date-time'; - if( isset( $content['params']['VALUE'] )) { - if( 'DATE' == $content['params']['VALUE'] ) - $type = 'date'; - elseif( 'PERIOD' == $content['params']['VALUE'] ) - $type = 'period'; - } - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'categories': - case 'comment': - case 'contact': - case 'description': - case 'related-to': - case 'resources': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); - } - break; - case 'x-prop': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); - break; - case 'created': // single occurence below, if set - case 'completed': - case 'dtstamp': - case 'last-modified': - $utcDate = TRUE; - case 'dtstart': - case 'dtend': - case 'due': - case 'recurrence-id': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time'; - unset( $content['params']['VALUE'] ); - if(( isset( $content['params']['TZID'] ) && empty( $content['params']['TZID'] )) || @is_null( $content['params']['TZID'] )) - unset( $content['params']['TZID'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - unset( $utcDate ); - break; - case 'duration': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] )) - $content['params']['RELATED'] = 'END'; - _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); - } - break; - case 'rrule': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); - break; - case 'class': - case 'location': - case 'status': - case 'summary': - case 'transp': - case 'tzid': - case 'uid': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if((( 'location' == $prop ) || ( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); - } - break; - case 'geo': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] ); - break; - case 'organizer': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); - } - break; - case 'percent-complete': - case 'priority': - case 'sequence': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); - break; - case 'tzurl': - case 'url': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] ); - break; - } // end switch( $prop ) - } // end foreach( $props as $prop ) - /** fix subComponent properties, if any */ - foreach( $subComps as $subCompName ) { - while( FALSE !== ( $subcomp = $component->getComponent( $subCompName ))) { - $child2 = $child->addChild( $subCompName ); - $properties = $child2->addChild( 'properties' ); - $langComp = $subcomp->getConfig( 'language' ); - $subCompProps = $subcomp->getConfig( 'setPropertyNames' ); - foreach( $subCompProps as $prop ) { - switch( strtolower( $prop )) { - case 'attach': // may occur multiple times, below - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'attendee': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); - } - break; - case 'comment': - case 'tzname': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - if( !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); - } - break; - case 'rdate': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - $type = 'date-time'; - if( isset( $content['params']['VALUE'] )) { - if( 'DATE' == $content['params']['VALUE'] ) - $type = 'date'; - elseif( 'PERIOD' == $content['params']['VALUE'] ) - $type = 'period'; - } - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'x-prop': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); - break; - case 'action': // single occurence below, if set - case 'description': - case 'summary': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); - } - break; - case 'dtstart': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - unset( $content['value']['tz'], $content['params']['VALUE'] ); // always local time - _addXMLchild( $properties, $prop, 'date-time', $content['value'], $content['params'] ); - } - break; - case 'duration': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); - break; - case 'repeat': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); - break; - case 'trigger': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - if( isset( $content['value']['year'] ) && - isset( $content['value']['month'] ) && - isset( $content['value']['day'] )) - $type = 'date-time'; - else { - $type = 'duration'; - if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] )) - $content['params']['RELATED'] = 'END'; - } - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'tzoffsetto': - case 'tzoffsetfrom': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] ); - break; - case 'rrule': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); - break; - } // switch( $prop ) - } // end foreach( $subCompProps as $prop ) - } // end while( FALSE !== ( $subcomp = $component->getComponent( subCompName ))) - } // end foreach( $subCombs as $subCompName ) - } // end while( FALSE !== ( $component = $calendar->getComponent( $compName ))) - } // end foreach( $comps as $compName) - return $xml->asXML(); -} -/** - * Add children to a SimpleXMLelement - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.5 - 2012-10-19 - * @param object $parent, reference to a SimpleXMLelement node - * @param string $name, new element node name - * @param string $type, content type, subelement(-s) name - * @param string $content, new subelement content - * @param array $params, new element 'attributes' - * @return void - */ -function _addXMLchild( & $parent, $name, $type, $content, $params=array()) { - /** create new child node */ - $name = strtolower( $name ); - $child = $parent->addChild( $name ); - if( isset( $params['VALUE'] )) - unset( $params['VALUE'] ); - if( !empty( $params )) { - $parameters = $child->addChild( 'parameters' ); - foreach( $params as $param => $parVal ) { - $param = strtolower( $param ); - if( 'x-' == substr( $param, 0, 2 )) { - $p1 = $parameters->addChild( $param ); - $p2 = $p1->addChild( 'unknown', htmlspecialchars( $parVal )); - } - else { - $p1 = $parameters->addChild( $param ); - switch( $param ) { - case 'altrep': - case 'dir': $ptype = 'uri'; break; - case 'delegated-from': - case 'delegated-to': - case 'member': - case 'sent-by': $ptype = 'cal-address'; break; - case 'rsvp': $ptype = 'boolean'; break ; - default: $ptype = 'text'; break; - } - if( is_array( $parVal )) { - foreach( $parVal as $pV ) - $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV )); - } - else - $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal )); - } - } - } - if( empty( $content ) && ( '0' != $content )) - return; - /** store content */ - switch( $type ) { - case 'binary': - $v = $child->addChild( $type, $content ); - break; - case 'boolean': - break; - case 'cal-address': - $v = $child->addChild( $type, $content ); - break; - case 'date': - if( array_key_exists( 'year', $content )) - $content = array( $content ); - foreach( $content as $date ) { - $str = sprintf( '%04d-%02d-%02d', $date['year'], $date['month'], $date['day'] ); - $v = $child->addChild( $type, $str ); - } - break; - case 'date-time': - if( array_key_exists( 'year', $content )) - $content = array( $content ); - foreach( $content as $dt ) { - if( !isset( $dt['hour'] )) $dt['hour'] = 0; - if( !isset( $dt['min'] )) $dt['min'] = 0; - if( !isset( $dt['sec'] )) $dt['sec'] = 0; - $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] ); - if( isset( $dt['tz'] ) && ( 'Z' == $dt['tz'] )) - $str .= 'Z'; - $v = $child->addChild( $type, $str ); - } - break; - case 'duration': - $output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ? '-' : ''; - $v = $child->addChild( $type, $output.iCalUtilityFunctions::_duration2str( $content ) ); - break; - case 'geo': - $v1 = $child->addChild( 'latitude', number_format( (float) $content['latitude'], 6, '.', '' )); - $v1 = $child->addChild( 'longitude', number_format( (float) $content['longitude'], 6, '.', '' )); - break; - case 'integer': - $v = $child->addChild( $type, $content ); - break; - case 'period': - if( !is_array( $content )) - break; - foreach( $content as $period ) { - $v1 = $child->addChild( $type ); - $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $period[0]['year'], $period[0]['month'], $period[0]['day'], $period[0]['hour'], $period[0]['min'], $period[0]['sec'] ); - if( isset( $period[0]['tz'] ) && ( 'Z' == $period[0]['tz'] )) - $str .= 'Z'; - $v2 = $v1->addChild( 'start', $str ); - if( array_key_exists( 'year', $period[1] )) { - $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $period[1]['year'], $period[1]['month'], $period[1]['day'], $period[1]['hour'], $period[1]['min'], $period[1]['sec'] ); - if( isset($period[1]['tz'] ) && ( 'Z' == $period[1]['tz'] )) - $str .= 'Z'; - $v2 = $v1->addChild( 'end', $str ); - } - else - $v2 = $v1->addChild( 'duration', iCalUtilityFunctions::_duration2str( $period[1] )); - } - break; - case 'recur': - foreach( $content as $rulelabel => $rulevalue ) { - $rulelabel = strtolower( $rulelabel ); - switch( $rulelabel ) { - case 'until': - if( isset( $rulevalue['hour'] )) - $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02dZ', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'], $rulevalue['hour'], $rulevalue['min'], $rulevalue['sec'] ); - else - $str = sprintf( '%04d-%02d-%02d', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'] ); - $v = $child->addChild( $rulelabel, $str ); - break; - case 'bysecond': - case 'byminute': - case 'byhour': - case 'bymonthday': - case 'byyearday': - case 'byweekno': - case 'bymonth': - case 'bysetpos': { - if( is_array( $rulevalue )) { - foreach( $rulevalue as $vix => $valuePart ) - $v = $child->addChild( $rulelabel, $valuePart ); - } - else - $v = $child->addChild( $rulelabel, $rulevalue ); - break; - } - case 'byday': { - if( isset( $rulevalue['DAY'] )) { - $str = ( isset( $rulevalue[0] )) ? $rulevalue[0] : ''; - $str .= $rulevalue['DAY']; - $p = $child->addChild( $rulelabel, $str ); - } - else { - foreach( $rulevalue as $valuePart ) { - if( isset( $valuePart['DAY'] )) { - $str = ( isset( $valuePart[0] )) ? $valuePart[0] : ''; - $str .= $valuePart['DAY']; - $p = $child->addChild( $rulelabel, $str ); - } - else - $p = $child->addChild( $rulelabel, $valuePart ); - } - } - break; - } - case 'freq': - case 'count': - case 'interval': - case 'wkst': - default: - $p = $child->addChild( $rulelabel, $rulevalue ); - break; - } // end switch( $rulelabel ) - } // end foreach( $content as $rulelabel => $rulevalue ) - break; - case 'rstatus': - $v = $child->addChild( 'code', number_format( (float) $content['statcode'], 2, '.', '')); - $v = $child->addChild( 'description', htmlspecialchars( $content['text'] )); - if( isset( $content['extdata'] )) - $v = $child->addChild( 'data', htmlspecialchars( $content['extdata'] )); - break; - case 'text': - if( !is_array( $content )) - $content = array( $content ); - foreach( $content as $part ) - $v = $child->addChild( $type, htmlspecialchars( $part )); - break; - case 'time': - break; - case 'uri': - $v = $child->addChild( $type, $content ); - break; - case 'utc-offset': - if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) { - $str = substr( $content, 0, 1 ); - $content = substr( $content, 1 ); - } - else - $str = '+'; - $str .= substr( $content, 0, 2 ).':'.substr( $content, 2, 2 ); - if( 4 < strlen( $content )) - $str .= ':'.substr( $content, 4 ); - $v = $child->addChild( $type, $str ); - break; - case 'unknown': - default: - if( is_array( $content )) - $content = implode( '', $content ); - $v = $child->addChild( 'unknown', htmlspecialchars( $content )); - break; - } -} -/** - * parse xml string into iCalcreator instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.2 - 2012-01-31 - * @param string $xmlstr - * @param array $iCalcfg iCalcreator config array (opt) - * @return mixed iCalcreator instance or FALSE on error - */ -function & XMLstr2iCal( $xmlstr, $iCalcfg=array()) { - libxml_use_internal_errors( TRUE ); - $xml = simplexml_load_string( $xmlstr ); - if( !$xml ) { - $str = ''; - $return = FALSE; - foreach( libxml_get_errors() as $error ) { - switch ( $error->level ) { - case LIBXML_ERR_FATAL: $str .= ' FATAL '; break; - case LIBXML_ERR_ERROR: $str .= ' ERROR '; break; - case LIBXML_ERR_WARNING: - default: $str .= ' WARNING '; break; - } - $str .= PHP_EOL.'Error when loading XML'; - if( !empty( $error->file )) - $str .= ', file:'.$error->file.', '; - $str .= ', line:'.$error->line; - $str .= ', ('.$error->code.') '.$error->message; - } - error_log( $str ); - if( LIBXML_ERR_WARNING != $error->level ) - return $return; - libxml_clear_errors(); - } - return xml2iCal( $xml, $iCalcfg ); -} -/** - * parse xml file into iCalcreator instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.2 - 2012-01-20 - * @param string $xmlfile - * @param array$iCalcfg iCalcreator config array (opt) - * @return mixediCalcreator instance or FALSE on error - */ -function & XMLfile2iCal( $xmlfile, $iCalcfg=array()) { - libxml_use_internal_errors( TRUE ); - $xml = simplexml_load_file( $xmlfile ); - if( !$xml ) { - $str = ''; - foreach( libxml_get_errors() as $error ) { - switch ( $error->level ) { - case LIBXML_ERR_FATAL: $str .= 'FATAL '; break; - case LIBXML_ERR_ERROR: $str .= 'ERROR '; break; - case LIBXML_ERR_WARNING: - default: $str .= 'WARNING '; break; - } - $str .= 'Failed loading XML'.PHP_EOL; - if( !empty( $error->file )) - $str .= ' file:'.$error->file.', '; - $str .= 'line:'.$error->line.PHP_EOL; - $str .= '('.$error->code.') '.$error->message.PHP_EOL; - } - error_log( $str ); - if( LIBXML_ERR_WARNING != $error->level ) - return FALSE; - libxml_clear_errors(); - } - return xml2iCal( $xml, $iCalcfg ); -} -/** - * parse SimpleXMLElement instance into iCalcreator instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.2 - 2012-01-27 - * @param object $xmlobj SimpleXMLElement - * @param array $iCalcfg iCalcreator config array (opt) - * @return mixed iCalcreator instance or FALSE on error - */ -function & XML2iCal( $xmlobj, $iCalcfg=array()) { - $iCal = new vcalendar( $iCalcfg ); - foreach( $xmlobj->children() as $icalendar ) { // vcalendar - foreach( $icalendar->children() as $calPart ) { // calendar properties and components - if( 'components' == $calPart->getName()) { - foreach( $calPart->children() as $component ) { // single components - if( 0 < $component->count()) - _getXMLComponents( $iCal, $component ); - } - } - elseif(( 'properties' == $calPart->getName()) && ( 0 < $calPart->count())) { - foreach( $calPart->children() as $calProp ) { // calendar properties - $propName = $calProp->getName(); - if(( 'calscale' != $propName ) && ( 'method' != $propName ) && ( 'x-' != substr( $propName,0,2 ))) - continue; - $params = array(); - foreach( $calProp->children() as $calPropElem ) { // single calendar property - if( 'parameters' == $calPropElem->getName()) - $params = _getXMLParams( $calPropElem ); - else - $iCal->setProperty( $propName, reset( $calPropElem ), $params ); - } // end foreach( $calProp->children() as $calPropElem ) - } // end foreach( $calPart->properties->children() as $calProp ) - } // end if( 0 < $calPart->properties->count()) - } // end foreach( $icalendar->children() as $calPart ) - } // end foreach( $xmlobj->children() as $icalendar ) - return $iCal; -} -/** - * parse SimpleXMLElement instance property parameters and return iCalcreator property parameter array - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.2 - 2012-01-15 - * @param object $parameters SimpleXMLElement - * @return array iCalcreator property parameter array - */ -function _getXMLParams( & $parameters ) { - if( 1 > $parameters->count()) - return array(); - $params = array(); - foreach( $parameters->children() as $parameter ) { // single parameter key - $key = strtoupper( $parameter->getName()); - $value = array(); - foreach( $parameter->children() as $paramValue ) // skip parameter value type - $value[] = reset( $paramValue ); - if( 2 > count( $value )) - $params[$key] = html_entity_decode( reset( $value )); - else - $params[$key] = $value; - } - return $params; -} -/** - * parse SimpleXMLElement instance components, create iCalcreator component and update - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.2 - 2012-01-15 - * @param array $iCal iCalcreator calendar instance - * @param object $component SimpleXMLElement - * @return void - */ -function _getXMLComponents( & $iCal, & $component ) { - $compName = $component->getName(); - $comp = & $iCal->newComponent( $compName ); - $subComponents = array( 'valarm', 'standard', 'daylight' ); - foreach( $component->children() as $compPart ) { // properties and (opt) subComponents - if( 1 > $compPart->count()) - continue; - if( in_array( $compPart->getName(), $subComponents )) - _getXMLComponents( $comp, $compPart ); - elseif( 'properties' == $compPart->getName()) { - foreach( $compPart->children() as $property ) // properties as single property - _getXMLProperties( $comp, $property ); - } - } // end foreach( $component->children() as $compPart ) -} -/** - * parse SimpleXMLElement instance property, create iCalcreator component property - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.2 - 2012-01-27 - * @param array $iCal iCalcreator calendar instance - * @param object $component SimpleXMLElement - * @return void - */ -function _getXMLProperties( & $iCal, & $property ) { - $propName = $property->getName(); - $value = $params = array(); - $valueType = ''; - foreach( $property->children() as $propPart ) { // calendar property parameters (opt) and value(-s) - $valueType = $propPart->getName(); - if( 'parameters' == $valueType) { - $params = _getXMLParams( $propPart ); - continue; - } - switch( $valueType ) { - case 'binary': - $value = reset( $propPart ); - break; - case 'boolean': - break; - case 'cal-address': - $value = reset( $propPart ); - break; - case 'date': - $params['VALUE'] = 'DATE'; - case 'date-time': - if(( 'exdate' == $propName ) || ( 'rdate' == $propName )) - $value[] = reset( $propPart ); - else - $value = reset( $propPart ); - break; - case 'duration': - $value = reset( $propPart ); - break; -// case 'geo': - case 'latitude': - case 'longitude': - $value[$valueType] = reset( $propPart ); - break; - case 'integer': - $value = reset( $propPart ); - break; - case 'period': - if( 'rdate' == $propName ) - $params['VALUE'] = 'PERIOD'; - $pData = array(); - foreach( $propPart->children() as $periodPart ) - $pData[] = reset( $periodPart ); - if( !empty( $pData )) - $value[] = $pData; - break; -// case 'rrule': - case 'freq': - case 'count': - case 'until': - case 'interval': - case 'wkst': - $value[$valueType] = reset( $propPart ); - break; - case 'bysecond': - case 'byminute': - case 'byhour': - case 'bymonthday': - case 'byyearday': - case 'byweekno': - case 'bymonth': - case 'bysetpos': - $value[$valueType][] = reset( $propPart ); - break; - case 'byday': - $byday = reset( $propPart ); - if( 2 == strlen( $byday )) - $value[$valueType][] = array( 'DAY' => $byday ); - else { - $day = substr( $byday, -2 ); - $key = substr( $byday, 0, ( strlen( $byday ) - 2 )); - $value[$valueType][] = array( $key, 'DAY' => $day ); - } - break; -// case 'rstatus': - case 'code': - $value[0] = reset( $propPart ); - break; - case 'description': - $value[1] = reset( $propPart ); - break; - case 'data': - $value[2] = reset( $propPart ); - break; - case 'text': - $text = str_replace( array( "\r\n", "\n\r", "\r", "\n"), '\n', reset( $propPart )); - $value['text'][] = html_entity_decode( $text ); - break; - case 'time': - break; - case 'uri': - $value = reset( $propPart ); - break; - case 'utc-offset': - $value = str_replace( ':', '', reset( $propPart )); - break; - case 'unknown': - default: - $value = html_entity_decode( reset( $propPart )); - break; - } // end switch( $valueType ) - } // end foreach( $property->children() as $propPart ) - if( 'freebusy' == $propName ) { - $fbtype = $params['FBTYPE']; - unset( $params['FBTYPE'] ); - $iCal->setProperty( $propName, $fbtype, $value, $params ); - } - elseif( 'geo' == $propName ) - $iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params ); - elseif( 'request-status' == $propName ) { - if( !isset( $value[2] )) - $value[2] = FALSE; - $iCal->setProperty( $propName, $value[0], $value[1], $value[2], $params ); - } - else { - if( isset( $value['text'] ) && is_array( $value['text'] )) { - if(( 'categories' == $propName ) || ( 'resources' == $propName )) - $value = $value['text']; - else - $value = reset( $value['text'] ); - } - $iCal->setProperty( $propName, $value, $params ); - } -} -/*********************************************************************************/ -/* Additional functions to use with vtimezone components */ -/*********************************************************************************/ -/** - * For use with - * iCalcreator (kigkonsult.se/iCalcreator/index.php) - * copyright (c) 2011 Yitzchok Lavi - * icalcreator@onebigsystem.com - * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -10188,271 +26,39 @@ function _getXMLProperties( & $iCal, & $property ) { * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** - * Additional functions to use with vtimezone components + * iCalcreator.php * - * Before calling the functions, set time zone 'GMT' ('date_default_timezone_set')! - * - * @author Yitzchok Lavi - * adjusted for iCalcreator Kjell-Inge Gustafsson, kigkonsult - * @version 1.0.2 - 2011-02-24 + * iCalcreator (class) files includes * + * @package icalcreator + * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved + * @since 2.22.1 - 2015-06-06 */ +/*********************************************************************************/ /** - * Returns array with the offset information from UTC for a (UTC) datetime/timestamp in the - * timezone, according to the VTIMEZONE information in the input array. - * - * $param array $timezonesarray, output from function getTimezonesAsDateArrays (below) - * $param string $tzid, time zone identifier - * $param mixed $timestamp, timestamp or a UTC datetime (in array format) - * @return array, time zone data with keys for 'offsetHis', 'offsetSec' and 'tzname' - * + * Do NOT remove or change version!! */ -function getTzOffsetForDate($timezonesarray, $tzid, $timestamp) { - if( is_array( $timestamp )) { -//$disp = sprintf( '%04d%02d%02d %02d%02d%02d', $timestamp['year'], $timestamp['month'], $timestamp['day'], $timestamp['hour'], $timestamp['min'], $timestamp['sec'] ); // test ### - $timestamp = gmmktime( - $timestamp['hour'], - $timestamp['min'], - $timestamp['sec'], - $timestamp['month'], - $timestamp['day'], - $timestamp['year'] - ) ; -// echo ' '."\n".' '.$timestamp.''.$disp.' '."\n".' '; // test ### - } - $tzoffset = array(); - // something to return if all goes wrong (such as if $tzid doesn't find us an array of dates) - $tzoffset['offsetHis'] = '+0000'; - $tzoffset['offsetSec'] = 0; - $tzoffset['tzname'] = '?'; - if( !isset( $timezonesarray[$tzid] )) - return $tzoffset; - $tzdatearray = $timezonesarray[$tzid]; - if ( is_array($tzdatearray) ) { - sort($tzdatearray); // just in case - if ( $timestamp < $tzdatearray[0]['timestamp'] ) { - // our date is before the first change - $tzoffset['offsetHis'] = $tzdatearray[0]['tzbefore']['offsetHis'] ; - $tzoffset['offsetSec'] = $tzdatearray[0]['tzbefore']['offsetSec'] ; - $tzoffset['tzname'] = $tzdatearray[0]['tzbefore']['offsetHis'] ; // we don't know the tzname in this case - } elseif ( $timestamp >= $tzdatearray[count($tzdatearray)-1]['timestamp'] ) { - // our date is after the last change (we do this so our scan can stop at the last record but one) - $tzoffset['offsetHis'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetHis'] ; - $tzoffset['offsetSec'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetSec'] ; - $tzoffset['tzname'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['tzname'] ; - } else { - // our date somewhere in between - // loop through the list of dates and stop at the one where the timestamp is before our date and the next one is after it - // we don't include the last date in our loop as there isn't one after it to check - for ( $i = 0 ; $i <= count($tzdatearray)-2 ; $i++ ) { - if(( $timestamp >= $tzdatearray[$i]['timestamp'] ) && ( $timestamp < $tzdatearray[$i+1]['timestamp'] )) { - $tzoffset['offsetHis'] = $tzdatearray[$i]['tzafter']['offsetHis'] ; - $tzoffset['offsetSec'] = $tzdatearray[$i]['tzafter']['offsetSec'] ; - $tzoffset['tzname'] = $tzdatearray[$i]['tzafter']['tzname'] ; - break; - } - } - } - } - return $tzoffset; -} +define( 'ICALCREATOR_VERSION', 'iCalcreator 2.22.1' ); +/*********************************************************************************/ +if( !defined( 'ICALCREATOR_LIB_DIR' )) + define( 'ICALCREATOR_LIB_DIR', __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR ); /** - * Returns an array containing all the timezone data in the vcalendar object + * iCalLoader * - * @param object $vcalendar, iCalcreator calendar instance - * @return array, time zone transition timestamp, array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname) - * based on the timezone data in the vcalendar object + * load iCalcreator src and util classes * + * @param string $class + * @return void */ -function getTimezonesAsDateArrays($vcalendar) { - $timezonedata = array(); - while( $vtz = $vcalendar->getComponent( 'vtimezone' )) { - $tzid = $vtz->getProperty('tzid'); - $alltzdates = array(); - while ( $vtzc = $vtz->getComponent( 'standard' )) { - $newtzdates = expandTimezoneDates($vtzc); - $alltzdates = array_merge($alltzdates, $newtzdates); - } - while ( $vtzc = $vtz->getComponent( 'daylight' )) { - $newtzdates = expandTimezoneDates($vtzc); - $alltzdates = array_merge($alltzdates, $newtzdates); - } - sort($alltzdates); - $timezonedata[$tzid] = $alltzdates; - } - return $timezonedata; +function iCalLoader( $class ) { + $file = ICALCREATOR_LIB_DIR . $class . '.class.php'; + if( file_exists( $file )) + include $file; } +spl_autoload_register( 'iCalLoader' ); /** - * Returns an array containing time zone data from vtimezone standard/daylight instances - * - * @param object $vtzc, an iCalcreator calendar standard/daylight instance - * @return array, time zone data; array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname) - * + * iCalcreator add-on functionality functions */ -function expandTimezoneDates($vtzc) { - $tzdates = array(); - // prepare time zone "description" to attach to each change - $tzbefore = array(); - $tzbefore['offsetHis'] = $vtzc->getProperty('tzoffsetfrom') ; - $tzbefore['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzbefore['offsetHis']); - if(( '-' != substr( (string) $tzbefore['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzbefore['offsetSec'], 0, 1 ))) - $tzbefore['offsetSec'] = '+'.$tzbefore['offsetSec']; - $tzafter = array(); - $tzafter['offsetHis'] = $vtzc->getProperty('tzoffsetto') ; - $tzafter['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzafter['offsetHis']); - if(( '-' != substr( (string) $tzafter['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzafter['offsetSec'], 0, 1 ))) - $tzafter['offsetSec'] = '+'.$tzafter['offsetSec']; - if( FALSE === ( $tzafter['tzname'] = $vtzc->getProperty('tzname'))) - $tzafter['tzname'] = $tzafter['offsetHis']; - // find out where to start from - $dtstart = $vtzc->getProperty('dtstart'); - $dtstarttimestamp = mktime( - $dtstart['hour'], - $dtstart['min'], - $dtstart['sec'], - $dtstart['month'], - $dtstart['day'], - $dtstart['year'] - ) ; - if( !isset( $dtstart['unparsedtext'] )) // ?? - $dtstart['unparsedtext'] = sprintf( '%04d%02d%02dT%02d%02d%02d', $dtstart['year'], $dtstart['month'], $dtstart['day'], $dtstart['hour'], $dtstart['min'], $dtstart['sec'] ); - if ( $dtstarttimestamp == 0 ) { - // it seems that the dtstart string may not have parsed correctly - // let's set a timestamp starting from 1902, using the time part of the original string - // so that the time will change at the right time of day - // at worst we'll get midnight again - $origdtstartsplit = explode('T',$dtstart['unparsedtext']) ; - $dtstarttimestamp = strtotime("19020101",0); - $dtstarttimestamp = strtotime($origdtstartsplit[1],$dtstarttimestamp); - } - // the date (in dtstart and opt RDATE/RRULE) is ALWAYS LOCAL (not utc!!), adjust from 'utc' to 'local' timestamp - $diff = -1 * $tzbefore['offsetSec']; - $dtstarttimestamp += $diff; - // add this (start) change to the array of changes - $tzdates[] = array( - 'timestamp' => $dtstarttimestamp, - 'tzbefore' => $tzbefore, - 'tzafter' => $tzafter - ); - $datearray = getdate($dtstarttimestamp); - // save original array to use time parts, because strtotime (used below) apparently loses the time - $changetime = $datearray ; - // generate dates according to an RRULE line - $rrule = $vtzc->getProperty('rrule') ; - if ( is_array($rrule) ) { - if ( $rrule['FREQ'] == 'YEARLY' ) { - // calculate transition dates starting from DTSTART - $offsetchangetimestamp = $dtstarttimestamp; - // calculate transition dates until 10 years in the future - $stoptimestamp = strtotime("+10 year",time()); - // if UNTIL is set, calculate until then (however far ahead) - if ( isset( $rrule['UNTIL'] ) && ( $rrule['UNTIL'] != '' )) { - $stoptimestamp = mktime( - $rrule['UNTIL']['hour'], - $rrule['UNTIL']['min'], - $rrule['UNTIL']['sec'], - $rrule['UNTIL']['month'], - $rrule['UNTIL']['day'], - $rrule['UNTIL']['year'] - ) ; - } - $count = 0 ; - $stopcount = isset( $rrule['COUNT'] ) ? $rrule['COUNT'] : 0 ; - $daynames = array( - 'SU' => 'Sunday', - 'MO' => 'Monday', - 'TU' => 'Tuesday', - 'WE' => 'Wednesday', - 'TH' => 'Thursday', - 'FR' => 'Friday', - 'SA' => 'Saturday' - ); - // repeat so long as we're between DTSTART and UNTIL, or we haven't prepared COUNT dates - while ( $offsetchangetimestamp < $stoptimestamp && ( $stopcount == 0 || $count < $stopcount ) ) { - // break up the timestamp into its parts - $datearray = getdate($offsetchangetimestamp); - if ( isset( $rrule['BYMONTH'] ) && ( $rrule['BYMONTH'] != 0 )) { - // set the month - $datearray['mon'] = $rrule['BYMONTH'] ; - } - if ( isset( $rrule['BYMONTHDAY'] ) && ( $rrule['BYMONTHDAY'] != 0 )) { - // set specific day of month - $datearray['mday'] = $rrule['BYMONTHDAY']; - } elseif ( is_array($rrule['BYDAY']) ) { - // find the Xth WKDAY in the month - // the starting point for this process is the first of the month set above - $datearray['mday'] = 1 ; - // turn $datearray as it is now back into a timestamp - $offsetchangetimestamp = mktime( - $datearray['hours'], - $datearray['minutes'], - $datearray['seconds'], - $datearray['mon'], - $datearray['mday'], - $datearray['year'] - ); - if ($rrule['BYDAY'][0] > 0) { - // to find Xth WKDAY in month, we find last WKDAY in month before - // we do that by finding first WKDAY in this month and going back one week - // then we add X weeks (below) - $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp); - $offsetchangetimestamp = strtotime("-1 week",$offsetchangetimestamp); - } else { - // to find Xth WKDAY before the end of the month, we find the first WKDAY in the following month - // we do that by going forward one month and going to WKDAY there - // then we subtract X weeks (below) - $offsetchangetimestamp = strtotime("+1 month",$offsetchangetimestamp); - $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp); - } - // now move forward or back the appropriate number of weeks, into the month we want - $offsetchangetimestamp = strtotime($rrule['BYDAY'][0] . " week",$offsetchangetimestamp); - $datearray = getdate($offsetchangetimestamp); - } - // convert the date parts back into a timestamp, setting the time parts according to the - // original time data which we stored - $offsetchangetimestamp = mktime( - $changetime['hours'], - $changetime['minutes'], - $changetime['seconds'] + $diff, - $datearray['mon'], - $datearray['mday'], - $datearray['year'] - ); - // add this change to the array of changes - $tzdates[] = array( - 'timestamp' => $offsetchangetimestamp, - 'tzbefore' => $tzbefore, - 'tzafter' => $tzafter - ); - // update counters (timestamp and count) - $offsetchangetimestamp = strtotime("+" . (( isset( $rrule['INTERVAL'] ) && ( $rrule['INTERVAL'] != 0 )) ? $rrule['INTERVAL'] : 1 ) . " year",$offsetchangetimestamp); - $count += 1 ; - } - } - } - // generate dates according to RDATE lines - while ($rdates = $vtzc->getProperty('rdate')) { - if ( is_array($rdates) ) { - - foreach ( $rdates as $rdate ) { - // convert the explicit change date to a timestamp - $offsetchangetimestamp = mktime( - $rdate['hour'], - $rdate['min'], - $rdate['sec'] + $diff, - $rdate['month'], - $rdate['day'], - $rdate['year'] - ) ; - // add this change to the array of changes - $tzdates[] = array( - 'timestamp' => $offsetchangetimestamp, - 'tzbefore' => $tzbefore, - 'tzafter' => $tzafter - ); - } - } - } - return $tzdates; -} -?> +include ICALCREATOR_LIB_DIR . 'iCal.XML.inc.php'; +include ICALCREATOR_LIB_DIR . 'iCal.vCard.inc.php'; +include ICALCREATOR_LIB_DIR . 'iCal.tz.inc.php'; diff --git a/php/ical/class.icalparser.php b/php/ical/class.icalparser.php index a035847..0ff9c32 100644 --- a/php/ical/class.icalparser.php +++ b/php/ical/class.icalparser.php @@ -1,310 +1,323 @@ - * Copyright (C) 2012-2014 Christoph Haas - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * PHP Version 5 * + * @category Parser + * @package ics-parser + * @author Martin Thoma + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @link https://github.com/MartinThoma/ics-parser/ + * @link https://github.com/johngrogg/ics-parser + * @version 1.0.3 */ /** - * This is the iCal-class - * Parse ics file content to array. + * This is the ICal class * * @param {string} filename The name of the file which should be parsed * @constructor */ -class ICal { - /* How many ToDos are in this ical? */ - public /** @type {int} */ $todo_count = 0; +class ICal +{ + /* How many ToDos are in this iCal? */ + public /** @type {int} */ $todo_count = 0; - /* How many events are in this ical? */ - public /** @type {int} */ $event_count = 0; - - /* Currently editing an alarm? */ - private /** @type {boolean} */ $isalarm = false; + /* How many events are in this iCal? */ + public /** @type {int} */ $event_count = 0; + + /* How many freebusy are in this iCal? */ + public /** @type {int} */ $freebusy_count = 0; /* The parsed calendar */ public /** @type {Array} */ $cal; - /* Error message store... null default */ - public /** @type {String} */ $errors; - /* Which keyword has been added to cal at last? */ - private /** @type {string} */ $_lastKeyWord; + private /** @type {string} */ $last_keyword; - /* The default timezone, used to convert UTC Time */ - private /** @type {string} */ $default_timezone = "Europe/Vienna"; - - /* The default timezone, used to convert UTC Time */ - private /** @type {boolean} */ $timezone_set = false; - - /* Ignore Daylight Saving Time */ - private /** @type {boolean} */ $ignore_dst = false; - - /** - * Creates the iCal-Object - * - * @param {string} $filename The path to the iCal-file + /* The value in years to use for indefinite, recurring events */ + public /** @type {int} */ $default_span = 2; + + /** + * Creates the iCal Object * - * @return Object The iCal-Object - */ - public function __construct($filename, $default_timezone, $timezone = false, $igndst = false) { + * @param {mixed} $filename The path to the iCal-file or an array of lines from an iCal file + * + * @return Object The iCal Object + */ + public function __construct($filename=false) + { if (!$filename) { - $this->errors = "No filename specified"; return false; } - - $this->default_timezone = $default_timezone; - - if(isset($timezone) && $timezone != false) { - $this->default_timezone = $timezone; - $this->timezone_set = true; + + if (is_array($filename)) { + $lines = $filename; + } else { + $lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); } - - if(isset($igndst) && $igndst != false) { - $this->ignore_dst = true; - } - - $lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + + return $this->initLines($lines); + } + + + /** + * Initializes lines from a URL + * + * @url {string} $url The url of the ical file to download and initialize. Unless you know what you're doing, it should begin with "http://" + * + * @return Object The iCal Object + */ + public function initURL($url) + { + $contents = file_get_contents($url); + + $lines = explode("\n", $contents); + + return $this->initLines($lines); + } + + + /** + * Initializes lines from a string + * + * @param {string} $contents The contents of the ical file to initialize + * + * @return Object The iCal Object + */ + public function initString($contents) + { + $lines = explode("\n", $contents); + + return $this->initLines($lines); + } + + + /** + * Initializes lines from file + * + * @param {array} $lines The lines to initialize + * + * @return Object The iCal Object + */ + public function initLines($lines) + { if (stristr($lines[0], 'BEGIN:VCALENDAR') === false) { - $this->errors = "Not a valid ical file"; return false; } else { - foreach ($lines as $line) { - $line = trim($line); + $line = rtrim($line); // Trim trailing whitespace $add = $this->keyValueFromString($line); + if ($add === false) { - $this->addCalendarComponentWithKeyAndValue($type, false, $line); + $this->addCalendarComponentWithKeyAndValue($component, false, $line); continue; - } + } - list($keyword, $dummy, $prop, $propvalue, $value) = $add; + $keyword = $add[0]; + $values = $add[1]; // Could be an array containing multiple values - switch ($line) { - // http://www.kanzaki.com/docs/ical/vtodo.html - case "BEGIN:VTODO": - $this->todo_count++; - $type = "VTODO"; - break; - - case "BEGIN:VALARM": - //echo "vevent gematcht"; - $this->isalarm=true; - $type = "VEVENT"; - break; - // http://www.kanzaki.com/docs/ical/vevent.html - case "BEGIN:VEVENT": - //echo "vevent gematcht"; - $this->event_count++; - $type = "VEVENT"; - break; + if (!is_array($values)) { + if (!empty($values)) { + $values = array($values); // Make an array as not already + $blank_array = array(); // Empty placeholder array + array_push($values, $blank_array); + } else { + $values = array(); // Use blank array to ignore this line + } + } else if (empty($values[0])) { + $values = array(); // Use blank array to ignore this line + } - //all other special strings - case "BEGIN:VCALENDAR": - case "BEGIN:DAYLIGHT": - // http://www.kanzaki.com/docs/ical/vtimezone.html - case "BEGIN:VTIMEZONE": - case "BEGIN:STANDARD": - $type = $value; - break; - case "END:VTODO": // end special text - goto VCALENDAR key - case "END:VEVENT": - case "END:VCALENDAR": - case "END:DAYLIGHT": - case "END:VTIMEZONE": - case "END:STANDARD": - $type = "VCALENDAR"; - break; - case "END:VALARM": - $this->isalarm=false; - $type = "VEVENT"; - break; - default: - $this->addCalendarComponentWithKeyAndValue($type, $keyword, $value, $prop, $propvalue); - break; - } + $values = array_reverse($values); // Reverse so that our array of properties is processed first + + foreach ($values as $value) { + switch ($line) { + // http://www.kanzaki.com/docs/ical/vtodo.html + case 'BEGIN:VTODO': + $this->todo_count++; + $component = 'VTODO'; + break; + + // http://www.kanzaki.com/docs/ical/vevent.html + case 'BEGIN:VEVENT': + if (!is_array($value)) { + $this->event_count++; + } + $component = 'VEVENT'; + break; + + // http://www.kanzaki.com/docs/ical/vfreebusy.html + case 'BEGIN:VFREEBUSY': + $this->freebusy_count++; + $component = 'VFREEBUSY'; + break; + + // All other special strings + case 'BEGIN:VCALENDAR': + case 'BEGIN:DAYLIGHT': + // http://www.kanzaki.com/docs/ical/vtimezone.html + case 'BEGIN:VTIMEZONE': + case 'BEGIN:STANDARD': + case 'BEGIN:VALARM': + $component = $value; + break; + case 'END:VALARM': + case 'END:VTODO': // End special text - goto VCALENDAR key + case 'END:VEVENT': + case 'END:VFREEBUSY': + case 'END:VCALENDAR': + case 'END:DAYLIGHT': + case 'END:VTIMEZONE': + case 'END:STANDARD': + $component = 'VCALENDAR'; + break; + default: + $this->addCalendarComponentWithKeyAndValue($component, $keyword, $value); + break; + } + } } - return $this->cal; + $this->process_recurrences(); + return $this->cal; } } - /** + /** * Add to $this->ical array one value and key. - * - * @param {string} $component This could be VTODO, VEVENT, VCALENDAR, ... + * + * @param {string} $component This could be VTODO, VEVENT, VCALENDAR, ... * @param {string} $keyword The keyword, for example DTSTART * @param {string} $value The value, for example 20110105T090000Z * * @return {None} - */ - public function addCalendarComponentWithKeyAndValue($component, $keyword, $value, $prop = false, $propvalue = false) { - if ($keyword == false) { // multiline value - $keyword = $this->last_keyword; - - switch ($component) { - case 'VEVENT': - if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND") or stristr($keyword, "TRIGGER")) { - $ts = $this->iCalDateToUnixTimestamp($value, $prop, $propvalue); - $value = $ts * 1000; - } - $value = str_replace("\\n", "\n", $value); - - $value = $this->customFilters($keyword, $value); - - if(!$this->isalarm) { - $value = $this->cal[$component][$this->event_count - 1][$keyword].$value; - } else { - $value = $this->cal[$component][$this->event_count - 1]["VALARM"][$keyword].$value; - } - break; - case 'VTODO' : - $value = $this->cal[$component][$this->todo_count - 1] - [$keyword].$value; - break; - } - } - - /* This should not be neccesary anymore*/ - //always strip additional content.... - //if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) { - //$keyword = explode(";", $keyword); - //$keyword = $keyword[0]; // remove additional content like VALUE=DATE - //} - - if ((stristr($keyword, "TIMEZONE") || stristr($keyword, "TZID")) && !$this->timezone_set) { // check if timezone already set... - $this->default_timezone = $this->trimTimeZone($value); // store the calendertimezone + */ + public function addCalendarComponentWithKeyAndValue($component, $keyword, $value) + { + if ($keyword == false) { + $keyword = $this->last_keyword; } - switch ($component) { - case "VTODO": + switch ($component) { + case 'VTODO': $this->cal[$component][$this->todo_count - 1][$keyword] = $value; - //$this->cal[$component][$this->todo_count]['Unix'] = $unixtime; - break; - case "VEVENT": - if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND") or stristr($keyword, "TRIGGER")) { - $ts = $this->iCalDateToUnixTimestamp($value, $prop, $propvalue); - $value = $ts * 1000; + break; + case 'VEVENT': + if (!isset($this->cal[$component][$this->event_count - 1][$keyword . '_array'])) { + $this->cal[$component][$this->event_count - 1][$keyword . '_array'] = array(); // Create array() } - $value = str_replace("\\n", "\n", $value); - - $value = $this->customFilters($keyword, $value); - - if(!$this->isalarm) { - $this->cal[$component][$this->event_count - 1][$keyword] = $value; + + if (is_array($value)) { + array_push($this->cal[$component][$this->event_count - 1][$keyword . '_array'], $value); // Add array of properties to the end } else { - $this->cal[$component][$this->event_count - 1]["VALARM"][$keyword] = $value; + if (!isset($this->cal[$component][$this->event_count - 1][$keyword])) { + $this->cal[$component][$this->event_count - 1][$keyword] = $value; + } + + $this->cal[$component][$this->event_count - 1][$keyword . '_array'][] = $value; + + // Glue back together for multi-line content + if ($this->cal[$component][$this->event_count - 1][$keyword] != $value) { + $ord = (isset($value[0])) ? ord($value[0]) : NULL; // First char + + if (in_array($ord, array(9, 32))) { // Is space or tab? + $value = substr($value, 1); // Only trim the first character + } + + if (is_array($this->cal[$component][$this->event_count - 1][$keyword . '_array'][1])) { // Account for multiple definitions of current keyword (e.g. ATTENDEE) + $this->cal[$component][$this->event_count - 1][$keyword] .= ';' . $value; // Concat value *with separator* as content spans multiple lines + } else { + if ($keyword === 'EXDATE') { + // This will give out a comma separated EXDATE string as per RFC2445 + // Example: EXDATE:19960402T010000Z,19960403T010000Z,19960404T010000Z + // Usage: $event['EXDATE'] will print out 19960402T010000Z,19960403T010000Z,19960404T010000Z + $this->cal[$component][$this->event_count - 1][$keyword] .= ',' . $value; + } else { + // Concat value as content spans multiple lines + $this->cal[$component][$this->event_count - 1][$keyword] .= $value; + } + } + } } - break; + break; + case 'VFREEBUSY': + $this->cal[$component][$this->freebusy_count - 1][$keyword] = $value; + break; default: - $this->cal[$component][$keyword] = $value; - break; - } - $this->last_keyword = $keyword; + $this->cal[$component][$keyword] = $value; + break; + } + $this->last_keyword = $keyword; } - /** - * Filter some chars out of the value. - * - * @param {string} $keyword keyword to which the filter is applied - * @param {string} $value to filter - * @return {string} filtered value - */ - private function customFilters($keyword, $value) { - if (stristr($keyword, "SUMMARY")) { - $value = str_replace("\n", " ", $value); // we don't need linebreaks in the summary... - } - - if (stristr($keyword, "SUMMARY")) { - $value = str_replace("\,", ",", $value); // strange escaped comma - } - - return $value; - } - - /** - * Trim a Timezone String - * - * @param {string} $timezone timezone string which should be trimmed - * @return {string} trimmed value - */ - private function trimTimeZone($timezone) { - if(preg_match('~([?<=/]*)([^/]*[/|-][^/]*$)~', $timezone, $matches)) { // detects tzurls in tzids - if ($matches[2] != "") { - return $matches[2]; // 2 = extracted timezone - } else { - return $timezone; - } - } - - return $timezone; - } - /** * Get a key-value pair of a string. * * @param {string} $text which is like "VCALENDAR:Begin" or "LOCATION:" * - * @return {array} array("Argument", "Optional Arg/Val", "Optional Arg", "Optional Value", "Value") + * @return {array} array("VCALENDAR", "Begin") */ - public function keyValueFromString($text) { - - preg_match('/(^[^a-z:;]+)([;]+([a-zA-Z]*)[=]*([^:"]*|"[\w\W]*"))?[:]([\w\W]*)/', $text, $matches); - - // this regex has problems with multiple attributes... ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT:mailto:jsmith@example.com - // TODO: fix this - + public function keyValueFromString($text) + { + // Match colon separator outside of quoted substrings + // Fallback to nearest semicolon outside of quoted substrings, if colon cannot be found + // Do not try and match within the value paired with the keyword + preg_match('/(.*?)(?::(?=(?:[^"]*"[^"]*")*[^"]*$)|;(?=[^:]*$))([\w\W]*)/', htmlspecialchars($text, ENT_QUOTES, 'UTF-8'), $matches); + if (count($matches) == 0) { return false; } - - $matches = array_splice($matches, 1, 5); // 0 = Arg, 1 = Complete Optional Arg/Val, 2 = Optional Arg, 3 = Optional Val, 4 = Value - return $matches; + + if (preg_match('/^([A-Z-]+)([;][\w\W]*)?$/', $matches[1])) { + $matches = array_splice($matches, 1, 2); // Remove first match and re-align ordering + + // Process properties + if (preg_match('/([A-Z-]+)[;]([\w\W]*)/', $matches[0], $properties)) { + array_shift($properties); // Remove first match + $matches[0] = $properties[0]; // Fix to ignore everything in keyword after a ; (e.g. Language, TZID, etc.) + array_shift($properties); // Repeat removing first match + + $formatted = array(); + foreach ($properties as $property) { + preg_match_all('~[^\r\n";]+(?:"[^"\\\]*(?:\\\.[^"\\\]*)*"[^\r\n";]*)*~', $property, $attributes); // Match semicolon separator outside of quoted substrings + $attributes = (sizeof($attributes) == 0) ? array($property) : reset($attributes); // Remove multi-dimensional array and use the first key + + foreach ($attributes as $attribute) { + preg_match_all('~[^\r\n"=]+(?:"[^"\\\]*(?:\\\.[^"\\\]*)*"[^\r\n"=]*)*~', $attribute, $values); // Match equals sign separator outside of quoted substrings + $value = (sizeof($values) == 0) ? NULL : reset($values); // Remove multi-dimensional array and use the first key + + if (is_array($value) && isset($value[1])) { + $formatted[$value[0]] = trim($value[1], '"'); // Remove double quotes from beginning and end only + } + } + } + + $properties[0] = $formatted; // Assign the keyword property information + + array_unshift($properties, $matches[1]); // Add match to beginning of array + $matches[1] = $properties; + } + + return $matches; + } else { + return false; // Ignore this match + } } - /** - * Return UTC Unix timestamp from ical date time format - * + /** + * Return Unix timestamp from iCal date time format + * * @param {string} $icalDate A Date in the format YYYYMMDD[T]HHMMSS[Z] or * YYYYMMDD[T]HHMMSS * - * @return {int} - */ - private function iCalDateToUTCUnixTimestamp($icalDate, $prop, $propvalue) { - - $timezone = false; - $allday = false; - - if($prop) { - $pos = strpos("TZIDtzid", $prop); - if($pos !== false && $propvalue != false) { - $timezone = str_replace('"', '', $propvalue); - $timezone = str_replace('\'', '', $timezone); - $timezone = $this->trimTimeZone($timezone); - } - } - - /* timestring format */ - $utc = strpos("zZ",substr($icalDate, -1)) === false ? false : true; - - $icalDate = str_replace('T', '', $icalDate); - $icalDate = str_replace('Z', '', $icalDate); + * @return {int} + */ + public static function iCalDateToUnixTimestamp($icalDate) + { + $icalDate = str_replace('T', '', $icalDate); + $icalDate = str_replace('Z', '', $icalDate); $pattern = '/([0-9]{4})'; // 1: YYYY $pattern .= '([0-9]{2})'; // 2: MM @@ -318,161 +331,346 @@ class ICal { if ($date[1] <= 1970) { return false; } - - // check if we have a allday event - if((!$date[6] || $date[6] === "") || (!$date[5] || $date[5] === "") || (!$date[4] || $date[4] === "")) { - $date[6] = 0; - $date[5] = 0; - $date[4] = 0; - $allday = true; - - $dtz = date_default_timezone_get(); - date_default_timezone_set('UTC'); - } - // Unix timestamps after 03:14:07 UTC 2038-01-19 might cause an overflow // if 32 bit integers are used. - $timestamp = mktime((int)$date[4], - (int)$date[5], - (int)$date[6], - (int)$date[2], - (int)$date[3], - (int)$date[1]); - - if($allday) { - date_default_timezone_set($dtz); - } - - if(!$utc && !$allday) { - $tz = $this->default_timezone; - if($timezone != false) { - $tz = $timezone; - } - - $error = false; - $this_tz = false; - - try { - $this_tz = new DateTimeZone($tz); - } catch(Exception $e) { - error_log($e->getMessage()); - $error = true; - } - - if($error) { - try { // Try using the default calendar timezone - $this_tz = new DateTimeZone($this->default_timezone); - } catch(Exception $e) { - error_log($e->getMessage()); - $timestamp_utc = $timestamp; // if that fails, we cannot do anymore - } - } - - if($this_tz != false) { - $tz_now = new DateTime("now", $this_tz); - $tz_offset = $this_tz->getOffset($tz_now); - $timestamp_utc = $timestamp - $tz_offset; - } - } else { - $timestamp_utc = $timestamp; - } - - return array($timestamp_utc,$allday); - } - - /** - * Return a timezone specific timestamp - * @param {int} $timestamp_utc UTC Timestamp to convert - * @param {string} $timezone Timezone - * @return {int} - */ - private function UTCTimestampToTZTimestamp($timestamp_utc, $timezone, $ignore_dst = false) { - $this_tz = false; - try { // Try using the default calendar timezone - $this_tz = new DateTimeZone($this->default_timezone); - } catch(Exception $e) { - error_log($e->getMessage()); - $timestamp_utc = $timestamp; // if that fails, we cannot do anymore - } - if($this_tz != false) { - $transition = $this_tz->getTransitions($timestamp_utc,$timestamp_utc); - $trans_offset = $transition[0]['offset']; - $isdst = $transition[0]['isdst']; - - $tz_now = new DateTime("now", $this_tz); - $tz_offset = $this_tz->getOffset($tz_now); - - if(!$ignore_dst) { - $tz_offset = $trans_offset; // normaly use dst - } - - return $timestamp_utc + $tz_offset; - } - return $timestamp_utc; // maybe timezone conversion will fail... - } - - /** - * Return Timezone specific Unix timestamp from ical date time format - * - * @param {string} $icalDate A Date in the format YYYYMMDD[T]HHMMSS[Z] or - * YYYYMMDD[T]HHMMSS - * - * @return {int} - */ - public function iCalDateToUnixTimestamp($icalDate, $prop, $propvalue) { - list($timestamp, $allday) = $this->iCalDateToUTCUnixTimestamp($icalDate, $prop, $propvalue); - - if(!$allday) { - $timestamp = $this->UTCTimestampToTZTimestamp($timestamp, $this->default_timezone, $this->ignore_dst, $allday); - } - + $timestamp = mktime((int)$date[4], (int)$date[5], (int)$date[6], (int)$date[2], (int)$date[3], (int)$date[1]); return $timestamp; } - + + /** + * Processes recurrences + * + * @author John Grogg + * @return {array} + */ + public function process_recurrences() + { + $array = $this->cal; + $events = $array['VEVENT']; + if (empty($events)) + return false; + foreach ($array['VEVENT'] as $anEvent) { + if (isset($anEvent['RRULE']) && $anEvent['RRULE'] != '') { + // Recurring event, parse RRULE and add appropriate duplicate events + $rrules = array(); + $rrule_strings = explode(';', $anEvent['RRULE']); + foreach ($rrule_strings as $s) { + list($k, $v) = explode('=', $s); + $rrules[$k] = $v; + } + // Get frequency + $frequency = $rrules['FREQ']; + // Get Start timestamp + $start_timestamp = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']); + $end_timestamp = $this->iCalDateToUnixTimestamp($anEvent['DTEND']); + $event_timestamp_offset = $end_timestamp - $start_timestamp; + // Get Interval + $interval = (isset($rrules['INTERVAL']) && $rrules['INTERVAL'] != '') ? $rrules['INTERVAL'] : 1; + + if (in_array($frequency, array('MONTHLY', 'YEARLY')) && isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { + // Deal with BYDAY + $day_number = intval($rrules['BYDAY']); + if (empty($day_number)) { // Returns 0 when no number defined in BYDAY + if (!isset($rrules['BYSETPOS'])) { + $day_number = 1; // Set first as default + } else if (is_numeric($rrules['BYSETPOS'])) { + $day_number = $rrules['BYSETPOS']; + } + } + $day_number = ($day_number == -1) ? 6 : $day_number; // Override for our custom key (6 => 'last') + $week_day = substr($rrules['BYDAY'], -2); + $day_ordinals = array(1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth', 6 => 'last'); + $weekdays = array('SU' => 'sunday', 'MO' => 'monday', 'TU' => 'tuesday', 'WE' => 'wednesday', 'TH' => 'thursday', 'FR' => 'friday', 'SA' => 'saturday'); + } + + $until_default = date_create('now'); + $until_default->modify($this->default_span . ' year'); + $until_default->setTime(23, 59, 59); // End of the day + $until_default = date_format($until_default, 'Ymd\THis'); + + if (isset($rrules['UNTIL'])) { + // Get Until + $until = $this->iCalDateToUnixTimestamp($rrules['UNTIL']); + } else if (isset($rrules['COUNT'])) { + $frequency_conversion = array('DAILY' => 'day', 'WEEKLY' => 'week', 'MONTHLY' => 'month', 'YEARLY' => 'year'); + $count_orig = (is_numeric($rrules['COUNT']) && $rrules['COUNT'] > 1) ? $rrules['COUNT'] : 0; + $count = ($count_orig - 1); // Remove one to exclude the occurrence that initialises the rule + $count += ($count > 0) ? $count * ($interval - 1) : 0; + $offset = "+$count " . $frequency_conversion[$frequency]; + $until = strtotime($offset, $start_timestamp); + + if (in_array($frequency, array('MONTHLY', 'YEARLY')) && isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { + $dtstart = date_create($anEvent['DTSTART']); + for ($i = 1; $i <= $count; $i++) { + $dtstart_clone = clone $dtstart; + $dtstart_clone->modify('next ' . $frequency_conversion[$frequency]); + $offset = "{$day_ordinals[$day_number]} {$weekdays[$week_day]} of " . $dtstart_clone->format('F Y H:i:01'); + $dtstart->modify($offset); + } + + // Jumping X months forwards doesn't mean the end date will fall on the same day defined in BYDAY + // Use the largest of these to ensure we are going far enough in the future to capture our final end day + $until = max($until, $dtstart->format('U')); + } + + unset($offset); + } else { + $until = $this->iCalDateToUnixTimestamp($until_default); + } + + if (!isset($anEvent['EXDATE_array'])) { + $anEvent['EXDATE_array'] = array(); + } + + // Decide how often to add events and do so + switch ($frequency) { + case 'DAILY': + // Simply add a new event each interval of days until UNTIL is reached + $offset = "+$interval day"; + $recurring_timestamp = strtotime($offset, $start_timestamp); + + while ($recurring_timestamp <= $until) { + // Add event + $anEvent['DTSTART'] = date('Ymd\THis', $recurring_timestamp); + $anEvent['DTEND'] = date('Ymd\THis', $recurring_timestamp + $event_timestamp_offset); + + $search_date = $anEvent['DTSTART']; + $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { + return is_string($val) && strpos($search_date, $val) === 0; + }); + + if (!$is_excluded) { + $events[] = $anEvent; + } + + // Move forwards + $recurring_timestamp = strtotime($offset, $recurring_timestamp); + } + break; + case 'WEEKLY': + // Create offset + $offset = "+$interval week"; + // Build list of days of week to add events + $weekdays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); + + if (isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { + $bydays = explode(',', $rrules['BYDAY']); + } else { + $weekTemp = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); + $findDay = $weekTemp[date('w', $start_timestamp)]; + $bydays = array($findDay); + } + + // Get timestamp of first day of start week + $week_recurring_timestamp = (date('w', $start_timestamp) == 0) ? $start_timestamp : strtotime('last Sunday ' . date('H:i:s', $start_timestamp), $start_timestamp); + + // Step through weeks + while ($week_recurring_timestamp <= $until) { + // Add events for bydays + $day_recurring_timestamp = $week_recurring_timestamp; + + foreach ($weekdays as $day) { + // Check if day should be added + + if (in_array($day, $bydays) && $day_recurring_timestamp > $start_timestamp && $day_recurring_timestamp <= $until) { + // Add event to day + $anEvent['DTSTART'] = date('Ymd\THis', $day_recurring_timestamp); + $anEvent['DTEND'] = date('Ymd\THis', $day_recurring_timestamp + $event_timestamp_offset); + + $search_date = $anEvent['DTSTART']; + $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); + + if (!$is_excluded) { + $events[] = $anEvent; + } + } + + // Move forwards a day + $day_recurring_timestamp = strtotime('+1 day', $day_recurring_timestamp); + } + + // Move forwards $interval weeks + $week_recurring_timestamp = strtotime($offset, $week_recurring_timestamp); + } + break; + case 'MONTHLY': + // Create offset + $offset = "+$interval month"; + $recurring_timestamp = strtotime($offset, $start_timestamp); + + if (isset($rrules['BYMONTHDAY']) && $rrules['BYMONTHDAY'] != '') { + // Deal with BYMONTHDAY + $monthdays = explode(',', $rrules['BYMONTHDAY']); + + while ($recurring_timestamp <= $until) { + foreach ($monthdays as $monthday) { + // Add event + $anEvent['DTSTART'] = date('Ym' . sprintf('%02d', $monthday) . '\THis', $recurring_timestamp); + $anEvent['DTEND'] = date('Ymd\THis', $this->iCalDateToUnixTimestamp($anEvent['DTSTART']) + $event_timestamp_offset); + + $search_date = $anEvent['DTSTART']; + $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); + + if (!$is_excluded) { + $events[] = $anEvent; + } + } + + // Move forwards + $recurring_timestamp = strtotime($offset, $recurring_timestamp); + } + } else if (isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { + $start_time = date('His', $start_timestamp); + + while ($recurring_timestamp <= $until) { + $event_start_desc = "{$day_ordinals[$day_number]} {$weekdays[$week_day]} of " . date('F Y H:i:s', $recurring_timestamp); + $event_start_timestamp = strtotime($event_start_desc); + + if ($event_start_timestamp > $start_timestamp && $event_start_timestamp < $until) { + $anEvent['DTSTART'] = date('Ymd\T', $event_start_timestamp) . $start_time; + $anEvent['DTEND'] = date('Ymd\THis', $this->iCalDateToUnixTimestamp($anEvent['DTSTART']) + $event_timestamp_offset); + + $search_date = $anEvent['DTSTART']; + $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); + + if (!$is_excluded) { + $events[] = $anEvent; + } + } + + // Move forwards + $recurring_timestamp = strtotime($offset, $recurring_timestamp); + } + } + break; + case 'YEARLY': + // Create offset + $offset = "+$interval year"; + $recurring_timestamp = strtotime($offset, $start_timestamp); + $month_names = array(1 => 'January', 2 => 'February', 3 => 'March', 4 => 'April', 5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August', 9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December'); + + // Check if BYDAY rule exists + if (isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { + $start_time = date('His', $start_timestamp); + + while ($recurring_timestamp <= $until) { + $event_start_desc = "{$day_ordinals[$day_number]} {$weekdays[$week_day]} of {$month_names[$rrules['BYMONTH']]} " . date('Y H:i:s', $recurring_timestamp); + $event_start_timestamp = strtotime($event_start_desc); + + if ($event_start_timestamp > $start_timestamp && $event_start_timestamp < $until) { + $anEvent['DTSTART'] = date('Ymd\T', $event_start_timestamp) . $start_time; + $anEvent['DTEND'] = date('Ymd\THis', $this->iCalDateToUnixTimestamp($anEvent['DTSTART']) + $event_timestamp_offset); + + $search_date = $anEvent['DTSTART']; + $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); + + if (!$is_excluded) { + $events[] = $anEvent; + } + } + + // Move forwards + $recurring_timestamp = strtotime($offset, $recurring_timestamp); + } + } else { + $day = date('d', $start_timestamp); + $start_time = date('His', $start_timestamp); + + // Step through years + while ($recurring_timestamp <= $until) { + // Add specific month dates + if (isset($rrules['BYMONTH']) && $rrules['BYMONTH'] != '') { + $event_start_desc = "$day {$month_names[$rrules['BYMONTH']]} " . date('Y H:i:s', $recurring_timestamp); + } else { + $event_start_desc = $day . date('F Y H:i:s', $recurring_timestamp); + } + + $event_start_timestamp = strtotime($event_start_desc); + + if ($event_start_timestamp > $start_timestamp && $event_start_timestamp < $until) { + $anEvent['DTSTART'] = date('Ymd\T', $event_start_timestamp) . $start_time; + $anEvent['DTEND'] = date('Ymd\THis', $this->iCalDateToUnixTimestamp($anEvent['DTSTART']) + $event_timestamp_offset); + + $search_date = $anEvent['DTSTART']; + $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); + + if (!$is_excluded) { + $events[] = $anEvent; + } + } + + // Move forwards + $recurring_timestamp = strtotime($offset, $recurring_timestamp); + } + } + break; + + $events = (isset($count_orig) && sizeof($events) > $count_orig) ? array_slice($events, 0, $count_orig) : $events; // Ensure we abide by COUNT if defined + } + } + } + $this->cal['VEVENT'] = $events; + } + /** * Returns an array of arrays with all events. Every event is an associative * array and each property is an element it. * * @return {array} */ - public function events() { + public function events() + { $array = $this->cal; return $array['VEVENT']; } /** - * Returns an array of calendar types. + * Returns the calendar name * - * @return {array} + * @return {calendar name} */ - public function calendar() { - $array = $this->cal; - return $array['VCALENDAR']; - } - - /** - * Returns the default or set timezone - * - * @return {string} - */ - public function timezone() { - return $this->default_timezone; + public function calendarName() + { + return $this->cal['VCALENDAR']['X-WR-CALNAME']; } /** - * Returns a boolean value whether thr current calendar has events or not + * Returns the calendar description + * + * @return {calendar description} + */ + public function calendarDescription() + { + return $this->cal['VCALENDAR']['X-WR-CALDESC']; + } + + /** + * Returns an array of arrays with all free/busy events. Every event is + * an associative array and each property is an element it. + * + * @return {array} + */ + public function freeBusyEvents() + { + $array = $this->cal; + return $array['VFREEBUSY']; + } + + /** + * Returns a boolean value whether the current calendar has events or not * * @return {boolean} */ - public function hasEvents() { - return ( count($this->events()) > 0 ? true : false ); + public function hasEvents() + { + return (count($this->events()) > 0) ? true : false; } /** * Returns false when the current calendar has no events in range, else the * events. - * - * Note that this function makes use of a UNIX timestamp. This might be a + * + * Note that this function makes use of a UNIX timestamp. This might be a * problem on January the 29th, 2038. * See http://en.wikipedia.org/wiki/Unix_time#Representing_the_number * @@ -481,7 +679,8 @@ class ICal { * * @return {mixed} */ - public function eventsFromRange($rangeStart = false, $rangeEnd = false) { + public function eventsFromRange($rangeStart = false, $rangeEnd = false) + { $events = $this->sortEventsWithOrder($this->events(), SORT_ASC); if (!$events) { @@ -489,12 +688,14 @@ class ICal { } $extendedEvents = array(); - - if ($rangeStart !== false) { + + if ($rangeStart === false) { $rangeStart = new DateTime(); + } else { + $rangeStart = new DateTime($rangeStart); } - if ($rangeEnd !== false or $rangeEnd <= 0) { + if ($rangeEnd === false or $rangeEnd <= 0) { $rangeEnd = new DateTime('2038/01/18'); } else { $rangeEnd = new DateTime($rangeEnd); @@ -503,9 +704,7 @@ class ICal { $rangeStart = $rangeStart->format('U'); $rangeEnd = $rangeEnd->format('U'); - - - // loop through all events by adding two new elements + // Loop through all events by adding two new elements foreach ($events as $anEvent) { $timestamp = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']); if ($timestamp >= $rangeStart && $timestamp <= $rangeEnd) { @@ -517,30 +716,31 @@ class ICal { } /** - * Returns sorted events + * Returns a boolean value whether the current calendar has events or not * * @param {array} $events An array with events. - * @param {array} $sortOrder Either SORT_ASC, SORT_DESC, SORT_REGULAR, + * @param {array} $sortOrder Either SORT_ASC, SORT_DESC, SORT_REGULAR, * SORT_NUMERIC, SORT_STRING * - * @return {array} + * @return {boolean} */ - public function sortEventsWithOrder($events, $sortOrder = SORT_ASC) { + public function sortEventsWithOrder($events, $sortOrder = SORT_ASC) + { $extendedEvents = array(); - - // loop through all events by adding two new elements + + // Loop through all events by adding two new elements foreach ($events as $anEvent) { if (!array_key_exists('UNIX_TIMESTAMP', $anEvent)) { $anEvent['UNIX_TIMESTAMP'] = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']); } if (!array_key_exists('REAL_DATETIME', $anEvent)) { - $anEvent['REAL_DATETIME'] = date("d.m.Y", $anEvent['UNIX_TIMESTAMP']); + $anEvent['REAL_DATETIME'] = date('d.m.Y', $anEvent['UNIX_TIMESTAMP']); } - + $extendedEvents[] = $anEvent; } - + foreach ($extendedEvents as $key => $value) { $timestamp[$key] = $value['UNIX_TIMESTAMP']; } @@ -548,5 +748,4 @@ class ICal { return $extendedEvents; } -} -?> +} \ No newline at end of file diff --git a/php/ical/lib/calendarComponent.class.php b/php/ical/lib/calendarComponent.class.php new file mode 100644 index 0000000..0bae538 --- /dev/null +++ b/php/ical/lib/calendarComponent.class.php @@ -0,0 +1,4661 @@ + + * @since 2.2.11 - 2015-03-31 + */ +class calendarComponent extends iCalBase { +/** @var array component property UID value */ + protected $uid; +/** @var array component property DTSTAMP value */ + protected $dtstamp; +/** @var string component type */ + public $objName; +/** + * constructor for calendar component object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.6 - 2011-05-17 + * @uses calendarComponent::$objName + * @uses calendarComponent::$timezonetype + * @uses calendarComponent::$uid + * @uses calendarComponent::$dtstamp + * @uses calendarComponent::$language + * @uses calendarComponent::$nl + * @uses calendarComponent::$unique_id + * @uses calendarComponent::$format + * @uses calendarComponent::$dtzid + * @uses calendarComponent::$allowEmpty + * @uses calendarComponent::$xcaldecl + * @uses calendarComponent::_createFormat() + * @uses calendarComponent::_makeDtstamp() + */ + function calendarComponent() { + $this->objName = ( isset( $this->timezonetype )) ? + strtolower( $this->timezonetype ) : get_class ( $this ); + $this->uid = array(); + $this->dtstamp = array(); + + $this->language = null; + $this->nl = null; + $this->unique_id = null; + $this->format = null; + $this->dtzid = null; + $this->allowEmpty = TRUE; + $this->xcaldecl = array(); + + $this->_createFormat(); + $this->_makeDtstamp(); + } +/*********************************************************************************/ +/** + * Property Name: ACTION + */ +/** + * creates formatted output for calendar component property action + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @uses calendarComponent::$action + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses calendarComponent::_createElement() + * @return string + */ + function createAction() { + if( empty( $this->action )) return FALSE; + if( empty( $this->action['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE; + $attributes = $this->_createParams( $this->action['params'] ); + return $this->_createElement( 'ACTION', $attributes, $this->action['value'] ); + } +/** + * set calendar component property action + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE" + * @param mixed $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$action + * @uses calendarComponent::$action + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setAction( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: ATTACH + */ +/** + * creates formatted output for calendar component property attach + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.16 - 2012-02-04 + * @uses calendarComponent::$attach + * @uses calendarComponent::_createParams() + * @uses calendarComponent::$format + * @uses calendarComponent::$intAttrDelimiter + * @uses calendarComponent::$attributeDelimiter + * @uses calendarComponent::$valueInit + * @uses calendarComponent::$nl + * @uses calendarComponent::_createElement() + * @return string + */ + function createAttach() { + if( empty( $this->attach )) return FALSE; + $output = null; + foreach( $this->attach as $attachPart ) { + if( !empty( $attachPart['value'] )) { + $attributes = $this->_createParams( $attachPart['params'] ); + if(( 'xcal' != $this->format ) && isset( $attachPart['params']['VALUE'] ) && ( 'BINARY' == $attachPart['params']['VALUE'] )) { + $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes ); + $str = 'ATTACH'.$attributes.$this->valueInit.$attachPart['value']; + $output = substr( $str, 0, 75 ).$this->nl; + $str = substr( $str, 75 ); + $output .= ' '.chunk_split( $str, 74, $this->nl.' ' ); + if( ' ' == substr( $output, -1 )) + $output = rtrim( $output ); + if( $this->nl != substr( $output, ( 0 - strlen( $this->nl )))) + $output .= $this->nl; + return $output; + } + $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] ); + } + elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' ); + } + return $output; + } +/** + * set calendar component property attach + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @param integer $index + * @uses calendarComponent::$attach + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setMval() + * @return bool + */ + function setAttach( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: ATTENDEE + */ +/** + * creates formatted output for calendar component property attendee + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.12 - 2012-01-31 + * @uses calendarComponent::$attendee + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::$intAttrDelimiter + * @return string + */ + function createAttendee() { + if( empty( $this->attendee )) return FALSE; + $output = null; + foreach( $this->attendee as $attendeePart ) { // start foreach 1 + if( empty( $attendeePart['value'] )) { + if( $this->getConfig( 'allowEmpty' )) + $output .= $this->_createElement( 'ATTENDEE' ); + continue; + } + $attendee1 = $attendee2 = null; + foreach( $attendeePart as $paramlabel => $paramvalue ) { // start foreach 2 + if( 'value' == $paramlabel ) + $attendee2 .= $paramvalue; + elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif + $mParams = array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ); + foreach( $paramvalue as $pKey => $pValue ) { // fix (opt) quotes + if( is_array( $pValue ) || in_array( $pKey, $mParams )) + continue; + if(( FALSE !== strpos( $pValue, ':' )) || + ( FALSE !== strpos( $pValue, ';' )) || + ( FALSE !== strpos( $pValue, ',' ))) + $paramvalue[$pKey] = '"'.$pValue.'"'; + } + // set attenddee parameters in rfc2445 order + if( isset( $paramvalue['CUTYPE'] )) + $attendee1 .= $this->intAttrDelimiter.'CUTYPE='.$paramvalue['CUTYPE']; + if( isset( $paramvalue['MEMBER'] )) { + $attendee1 .= $this->intAttrDelimiter.'MEMBER='; + foreach( $paramvalue['MEMBER'] as $cix => $opv ) + $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; + } + if( isset( $paramvalue['ROLE'] )) + $attendee1 .= $this->intAttrDelimiter.'ROLE='.$paramvalue['ROLE']; + if( isset( $paramvalue['PARTSTAT'] )) + $attendee1 .= $this->intAttrDelimiter.'PARTSTAT='.$paramvalue['PARTSTAT']; + if( isset( $paramvalue['RSVP'] )) + $attendee1 .= $this->intAttrDelimiter.'RSVP='.$paramvalue['RSVP']; + if( isset( $paramvalue['DELEGATED-TO'] )) { + $attendee1 .= $this->intAttrDelimiter.'DELEGATED-TO='; + foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv ) + $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; + } + if( isset( $paramvalue['DELEGATED-FROM'] )) { + $attendee1 .= $this->intAttrDelimiter.'DELEGATED-FROM='; + foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv ) + $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; + } + if( isset( $paramvalue['SENT-BY'] )) + $attendee1 .= $this->intAttrDelimiter.'SENT-BY='.$paramvalue['SENT-BY']; + if( isset( $paramvalue['CN'] )) + $attendee1 .= $this->intAttrDelimiter.'CN='.$paramvalue['CN']; + if( isset( $paramvalue['DIR'] )) { + $delim = ( FALSE === strpos( $paramvalue['DIR'], '"' )) ? '"' : ''; + $attendee1 .= $this->intAttrDelimiter.'DIR='.$delim.$paramvalue['DIR'].$delim; + } + if( isset( $paramvalue['LANGUAGE'] )) + $attendee1 .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE']; + $xparams = array(); + foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3 + if( ctype_digit( (string) $optparamlabel )) { + $xparams[] = $optparamvalue; + continue; + } + if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' ))) + $xparams[$optparamlabel] = $optparamvalue; + } // end foreach 3 + ksort( $xparams, SORT_STRING ); + foreach( $xparams as $paramKey => $paramValue ) { + if( ctype_digit( (string) $paramKey )) + $attendee1 .= $this->intAttrDelimiter.$paramValue; + else + $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue"; + } // end foreach 3 + } // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) + } // end foreach 2 + $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 ); + } // end foreach 1 + return $output; + } +/** + * set calendar component property attach + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.18.13 - 2013-09-22 + * @param string $value + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$objName + * @uses iCalUtilityFunctions::_existRem() + * @uses iCalUtilityFunctions::_setMval() + * @return bool + */ + function setAttendee( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// may exist.. . also in params + if( !empty( $value )) { + if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) + $value = 'MAILTO:'.$value; + elseif( !empty( $value )) + $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); + $value = str_replace( 'mailto:', 'MAILTO:', $value ); + } + $params2 = array(); + if( is_array($params )) { + $optarrays = array(); + $params = array_change_key_case( $params, CASE_UPPER ); + foreach( $params as $optparamlabel => $optparamvalue ) { + if(( 'X-' != substr( $optparamlabel, 0, 2 )) && (( 'vfreebusy' == $this->objName ) || ( 'valarm' == $this->objName ))) + continue; + switch( $optparamlabel ) { + case 'MEMBER': + case 'DELEGATED-TO': + case 'DELEGATED-FROM': + if( !is_array( $optparamvalue )) + $optparamvalue = array( $optparamvalue ); + foreach( $optparamvalue as $part ) { + $part = trim( $part ); + if(( '"' == substr( $part, 0, 1 )) && + ( '"' == substr( $part, -1 ))) + $part = substr( $part, 1, ( strlen( $part ) - 2 )); + if( 'mailto:' != strtolower( substr( $part, 0, 7 ))) + $part = "MAILTO:$part"; + else + $part = 'MAILTO:'.substr( $part, 7 ); + $optarrays[$optparamlabel][] = $part; + } + break; + default: + if(( '"' == substr( $optparamvalue, 0, 1 )) && + ( '"' == substr( $optparamvalue, -1 ))) + $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 )); + if( 'SENT-BY' == $optparamlabel ) { + if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 ))) + $optparamvalue = "MAILTO:$optparamvalue"; + else + $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 ); + } + $params2[$optparamlabel] = $optparamvalue; + break; + } // end switch( $optparamlabel.. . + } // end foreach( $optparam.. . + foreach( $optarrays as $optparamlabel => $optparams ) + $params2[$optparamlabel] = $optparams; + } + // remove defaults + iCalUtilityFunctions::_existRem( $params2, 'CUTYPE', 'INDIVIDUAL' ); + iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' ); + iCalUtilityFunctions::_existRem( $params2, 'ROLE', 'REQ-PARTICIPANT' ); + iCalUtilityFunctions::_existRem( $params2, 'RSVP', 'FALSE' ); + // check language setting + if( isset( $params2['CN' ] )) { + $lang = $this->getConfig( 'language' ); + if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang )) + $params2['LANGUAGE' ] = $lang; + } + iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: CATEGORIES + */ +/** + * creates formatted output for calendar component property categories + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$categories + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @uses calendarComponent::$format + * @uses calendarComponent::$nl + * @return string + */ + function createCategories() { + if( empty( $this->categories )) return FALSE; + $output = null; + foreach( $this->categories as $category ) { + if( empty( $category['value'] )) { + if ( $this->getConfig( 'allowEmpty' )) + $output .= $this->_createElement( 'CATEGORIES' ); + continue; + } + $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' )); + if( is_array( $category['value'] )) { + foreach( $category['value'] as $cix => $categoryPart ) + $category['value'][$cix] = iCalUtilityFunctions::_strrep( $categoryPart, $this->format, $this->nl ); + $content = implode( ',', $category['value'] ); + } + else + $content = iCalUtilityFunctions::_strrep( $category['value'], $this->format, $this->nl ); + $output .= $this->_createElement( 'CATEGORIES', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property categories + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param mixed $value + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$categories + * @uses iCalUtilityFunctions::_setMval() + * @return bool + */ + function setCategories( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: CLASS + */ +/** + * creates formatted output for calendar component property class + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 0.9.7 - 2006-11-20 + * @uses calendarComponent::$class + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createClass() { + if( empty( $this->class )) return FALSE; + if( empty( $this->class['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE; + $attributes = $this->_createParams( $this->class['params'] ); + return $this->_createElement( 'CLASS', $attributes, $this->class['value'] ); + } +/** + * set calendar component property class + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name + * @param array $params optional + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$class + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setClass( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: COMMENT + */ +/** + * creates formatted output for calendar component property comment + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$comment + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @uses calendarComponent::$format + * @uses calendarComponent::$nl + * @return string + */ + function createComment() { + if( empty( $this->comment )) return FALSE; + $output = null; + foreach( $this->comment as $commentPart ) { + if( empty( $commentPart['value'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' ); + continue; + } + $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = iCalUtilityFunctions::_strrep( $commentPart['value'], $this->format, $this->nl ); + $output .= $this->_createElement( 'COMMENT', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property comment + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setMval() + * @uses calendarComponent::$comment + * @return bool + */ + function setComment( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: COMPLETED + */ +/** + * creates formatted output for calendar component property completed + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @return string + * @uses calendarComponent::$completed + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses iCalUtilityFunctions::_date2strdate(); + * @uses calendarComponent::_createParams() + */ + function createCompleted( ) { + if( empty( $this->completed )) return FALSE; + if( !isset( $this->completed['value']['year'] ) && + !isset( $this->completed['value']['month'] ) && + !isset( $this->completed['value']['day'] ) && + !isset( $this->completed['value']['hour'] ) && + !isset( $this->completed['value']['min'] ) && + !isset( $this->completed['value']['sec'] )) + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'COMPLETED' ); + else return FALSE; + $formatted = iCalUtilityFunctions::_date2strdate( $this->completed['value'], 7 ); + $attributes = $this->_createParams( $this->completed['params'] ); + return $this->_createElement( 'COMPLETED', $attributes, $formatted ); + } +/** + * set calendar component property completed + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param mixed $year + * @param mixed $month + * @param int $day + * @param int $hour + * @param int $min + * @param int $sec + * @param array $params + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setParams() + * @uses calendarComponent::$completed + * @uses iCalUtilityFunctions::_setDate2() + * @return bool + */ + function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->completed = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } + else + return FALSE; + } + $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: CONTACT + */ +/** + * creates formatted output for calendar component property contact + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$contact + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @return string + */ + function createContact() { + if( empty( $this->contact )) return FALSE; + $output = null; + foreach( $this->contact as $contact ) { + if( !empty( $contact['value'] )) { + $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = iCalUtilityFunctions::_strrep( $contact['value'], $this->format, $this->nl ); + $output .= $this->_createElement( 'CONTACT', $attributes, $content ); + } + elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' ); + } + return $output; + } +/** + * set calendar component property contact + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setMval() + * @uses calendarComponent::$contact + * @return bool + */ + function setContact( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: CREATED + */ +/** + * creates formatted output for calendar component property created + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @uses calendarComponent::$created + * @uses iCalUtilityFunctions::_date2strdate() + * @uses calendarComponent::_createParams() + * @uses calendarComponent::_createElement() + * @return string + */ + function createCreated() { + if( empty( $this->created )) return FALSE; + $formatted = iCalUtilityFunctions::_date2strdate( $this->created['value'], 7 ); + $attributes = $this->_createParams( $this->created['params'] ); + return $this->_createElement( 'CREATED', $attributes, $formatted ); + } +/** + * set calendar component property created + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.19.5 - 2014-03-29 + * @param mixed $year + * @param mixed $month + * @param int $day + * @param int $hour + * @param int $min + * @param int $sec + * @param mixed $params + * @uses calendarComponent::$created + * @uses iCalUtilityFunctions::_setDate2() + * @return bool + */ + function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( !isset( $year )) + $year = gmdate( 'Ymd\THis' ); + $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DESCRIPTION + */ +/** + * creates formatted output for calendar component property description + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$description + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::getConfig() + * @return string + */ + function createDescription() { + if( empty( $this->description )) return FALSE; + $output = null; + foreach( $this->description as $description ) { + if( !empty( $description['value'] )) { + $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = iCalUtilityFunctions::_strrep( $description['value'], $this->format, $this->nl ); + $output .= $this->_createElement( 'DESCRIPTION', $attributes, $content ); + } + elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' ); + } + return $output; + } +/** + * set calendar component property description + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$objName + * @uses iCalUtilityFunctions::_setMval() + * @uses calendarComponent::$description + * @return bool + */ + function setDescription( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; } + if( 'vjournal' != $this->objName ) + $index = 1; + iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DTEND + */ +/** + * creates formatted output for calendar component property dtend + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.14.4 - 2012-09-26 + * @uses calendarComponent::$dtend + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses calendarComponent::_createParams() + * @uses calendarComponent::_createElement() + * @return string + */ + function createDtend() { + if( empty( $this->dtend )) return FALSE; + if( !isset( $this->dtend['value']['year'] ) && + !isset( $this->dtend['value']['month'] ) && + !isset( $this->dtend['value']['day'] ) && + !isset( $this->dtend['value']['hour'] ) && + !isset( $this->dtend['value']['min'] ) && + !isset( $this->dtend['value']['sec'] )) + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'DTEND' ); + else return FALSE; + $parno = ( isset( $this->dtend['params']['VALUE'] ) && ( 'DATE' == $this->dtend['params']['VALUE'] )) ? 3 : null; + $formatted = iCalUtilityFunctions::_date2strdate( $this->dtend['value'], $parno ); + $attributes = $this->_createParams( $this->dtend['params'] ); + return $this->_createElement( 'DTEND', $attributes, $formatted ); + } +/** + * set calendar component property dtend + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param mixed $year + * @param mixed $month + * @param int $day + * @param int $hour + * @param int $min + * @param int $sec + * @param string $tz + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$dtend + * @uses iCalUtilityFunctions::_setParams() + * @uses iCalUtilityFunctions::_setDate() + * @return bool + */ + function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->dtend = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } + else + return FALSE; + } + $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DTSTAMP + */ +/** + * creates formatted output for calendar component property dtstamp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.4 - 2008-03-07 + * @uses calendarComponent::$dtstamp + * @uses calendarComponent::_makeDtstamp() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses calendarComponent::_createParams() + * @uses calendarComponent::_createElement() + * @return string + */ + function createDtstamp() { + if( !isset( $this->dtstamp['value']['year'] ) && + !isset( $this->dtstamp['value']['month'] ) && + !isset( $this->dtstamp['value']['day'] ) && + !isset( $this->dtstamp['value']['hour'] ) && + !isset( $this->dtstamp['value']['min'] ) && + !isset( $this->dtstamp['value']['sec'] )) + $this->_makeDtstamp(); + $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstamp['value'], 7 ); + $attributes = $this->_createParams( $this->dtstamp['params'] ); + return $this->_createElement( 'DTSTAMP', $attributes, $formatted ); + } +/** + * computes datestamp for calendar component object instance dtstamp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @uses iCalUtilityFunctions::$fmt + * @uses calendarComponent::$dtstamp + * @return void + */ + function _makeDtstamp() { + $d = gmdate( iCalUtilityFunctions::$fmt['YmdHis3'], time()); + $date = explode( '-', $d ); + $this->dtstamp['value'] = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2], 'hour' => $date[3], 'min' => $date[4], 'sec' => $date[5], 'tz' => 'Z' ); + $this->dtstamp['params'] = null; + } +/** + * set calendar component property dtstamp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @param mixed $year + * @param mixed $month + * @param int $day + * @param int $hour + * @param int $min + * @param int $sec + * @param array $params + * @uses calendarComponent::_makeDtstamp() + * @uses calendarComponent::$dtstamp + * @uses iCalUtilityFunctions::_setDate2() + * @return TRUE + */ + function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( empty( $year )) + $this->_makeDtstamp(); + else + $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DTSTART + */ +/** + * creates formatted output for calendar component property dtstart + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.14.4 - 2012-09-26 + * @uses calendarComponent::$dtstart + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses calendarComponent::_createParams() + * @return string + */ + function createDtstart() { + if( empty( $this->dtstart )) return FALSE; + if( !isset( $this->dtstart['value']['year'] ) && + !isset( $this->dtstart['value']['month'] ) && + !isset( $this->dtstart['value']['day'] ) && + !isset( $this->dtstart['value']['hour'] ) && + !isset( $this->dtstart['value']['min'] ) && + !isset( $this->dtstart['value']['sec'] )) { + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'DTSTART' ); + else return FALSE; + } + if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) + unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] ); + $parno = ( isset( $this->dtstart['params']['VALUE'] ) && ( 'DATE' == $this->dtstart['params']['VALUE'] )) ? 3 : null; + $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstart['value'], $parno ); + $attributes = $this->_createParams( $this->dtstart['params'] ); + return $this->_createElement( 'DTSTART', $attributes, $formatted ); + } +/** + * set calendar component property dtstart + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param mixed $year + * @param mixed $month + * @param int $day + * @param int $hour + * @param int $min + * @param int $sec + * @param string $tz + * @param array $params + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setParams() + * @uses calendarComponent::$dtstart + * @uses iCalUtilityFunctions::_setDate() + * @return bool + */ + function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->dtstart = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } + else + return FALSE; + } + $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName, $this->getConfig( 'TZID' )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DUE + */ +/** + * creates formatted output for calendar component property due + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.14.4 - 2012-09-26 + * @uses calendarComponent::$due + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses calendarComponent::_createParams() + * @return string + */ + function createDue() { + if( empty( $this->due )) return FALSE; + if( !isset( $this->due['value']['year'] ) && + !isset( $this->due['value']['month'] ) && + !isset( $this->due['value']['day'] ) && + !isset( $this->due['value']['hour'] ) && + !isset( $this->due['value']['min'] ) && + !isset( $this->due['value']['sec'] )) { + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'DUE' ); + else + return FALSE; + } + $parno = ( isset( $this->due['params']['VALUE'] ) && ( 'DATE' == $this->due['params']['VALUE'] )) ? 3 : null; + $formatted = iCalUtilityFunctions::_date2strdate( $this->due['value'], $parno ); + $attributes = $this->_createParams( $this->due['params'] ); + return $this->_createElement( 'DUE', $attributes, $formatted ); + } +/** + * set calendar component property due + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param mixed $year + * @param mixed $month + * @param int $day + * @param int $hour + * @param int $min + * @param int $sec + * @param string $tz + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$due + * @uses iCalUtilityFunctions::_setParams() + * @uses iCalUtilityFunctions::_setDate() + * @return bool + */ + function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->due = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } + else + return FALSE; + } + $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: DURATION + */ +/** + * creates formatted output for calendar component property duration + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-05-25 + * @uses calendarComponent::$duration + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_duration2str() + * @return string + */ + function createDuration() { + if( empty( $this->duration )) return FALSE; + if( !isset( $this->duration['value']['week'] ) && + !isset( $this->duration['value']['day'] ) && + !isset( $this->duration['value']['hour'] ) && + !isset( $this->duration['value']['min'] ) && + !isset( $this->duration['value']['sec'] )) + if( $this->getConfig( 'allowEmpty' )) + return $this->_createElement( 'DURATION' ); + else return FALSE; + $attributes = $this->_createParams( $this->duration['params'] ); + return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_duration2str( $this->duration['value'] )); + } +/** + * set calendar component property duration + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-05-25 + * @param mixed $week + * @param mixed $day + * @param int $hour + * @param int $min + * @param int $sec + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$duration + * @uses iCalUtilityFunctions::_duration2arr() + * @uses iCalUtilityFunctions::_durationStr2arr() + * @uses iCalUtilityFunctions::_setParams() + * @uses iCalUtilityFunctions::_duration2arr() + * @return bool + */ + function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec )) { + if( $this->getConfig( 'allowEmpty' )) + $week = $day = null; + else + return FALSE; + } + if( is_array( $week ) && ( 1 <= count( $week ))) + $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); + elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) { + $week = trim( $week ); + if( in_array( substr( $week, 0, 1 ), array( '+', '-' ))) + $week = substr( $week, 1 ); + $this->duration = array( 'value' => iCalUtilityFunctions::_durationStr2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); + } + else + $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( array( 'week' => $week, 'day' => $day, 'hour' => $hour, 'min' => $min, 'sec' => $sec )) + , 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: EXDATE + */ +/** + * creates formatted output for calendar component property exdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.5 - 2012-12-28 + * @uses calendarComponent::$exdate + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses iCalUtilityFunctions::_sortExdate1() + * @uses iCalUtilityFunctions::_sortExdate2() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses calendarComponent::_createParams() + * @return string + */ + function createExdate() { + if( empty( $this->exdate )) return FALSE; + $output = null; + $exdates = array(); + foreach( $this->exdate as $theExdate ) { + if( empty( $theExdate['value'] )) { + if( $this->getConfig( 'allowEmpty' )) + $output .= $this->_createElement( 'EXDATE' ); + continue; + } + if( 1 < count( $theExdate['value'] )) + usort( $theExdate['value'], array( 'iCalUtilityFunctions', '_sortExdate1' )); + $exdates[] = $theExdate; + } + if( 1 < count( $exdates )) + usort( $exdates, array( 'iCalUtilityFunctions', '_sortExdate2' )); + foreach( $exdates as $theExdate ) { + $content = $attributes = null; + foreach( $theExdate['value'] as $eix => $exdatePart ) { + $parno = count( $exdatePart ); + $formatted = iCalUtilityFunctions::_date2strdate( $exdatePart, $parno ); + if( isset( $theExdate['params']['TZID'] )) + $formatted = str_replace( 'Z', '', $formatted); + if( 0 < $eix ) { + if( isset( $theExdate['value'][0]['tz'] )) { + if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) || + ( 'Z' == $theExdate['value'][0]['tz'] )) { + if( 'Z' != substr( $formatted, -1 )) + $formatted .= 'Z'; + } + else + $formatted = str_replace( 'Z', '', $formatted ); + } + else + $formatted = str_replace( 'Z', '', $formatted ); + } // end if( 0 < $eix ) + $content .= ( 0 < $eix ) ? ','.$formatted : $formatted; + } // end foreach( $theExdate['value'] as $eix => $exdatePart ) + $attributes .= $this->_createParams( $theExdate['params'] ); + $output .= $this->_createElement( 'EXDATE', $attributes, $content ); + } // end foreach( $exdates as $theExdate ) + return $output; + } +/** + * set calendar component property exdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-10 + * @param array $exdates + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setMval() + * @uses calendarComponent::$exdate + * @uses iCalUtilityFunctions::_setParams() + * @uses iCalUtilityFunctions::_chkdatecfg() + * @uses iCalUtilityFunctions::_existRem() + * @uses iCalUtilityFunctions::_strDate2arr() + * @uses iCalUtilityFunctions::_isArrayTimestampDate() + * @uses iCalUtilityFunctions::_isOffset() + * @uses iCalUtilityFunctions::_timestamp2date() + * @uses iCalUtilityFunctions::_chkDateArr() + * @uses iCalUtilityFunctions::$fmt + * @uses iCalUtilityFunctions::_strdate2date() + * @return bool + */ + function setExdate( $exdates, $params=FALSE, $index=FALSE ) { + if( empty( $exdates )) { + if( $this->getConfig( 'allowEmpty' )) { + iCalUtilityFunctions::_setMval( $this->exdate, '', $params, FALSE, $index ); + return TRUE; + } + else + return FALSE; + } + $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ))); + $toZ = ( isset( $input['params']['TZID'] ) && in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) ? TRUE : FALSE; + /* ev. check 1:st date and save ev. timezone **/ + iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] ); + iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter + foreach( $exdates as $eix => $theExdate ) { + iCalUtilityFunctions::_strDate2arr( $theExdate ); + if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate )) { + if( isset( $theExdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theExdate['tz'] )) { + if( isset( $input['params']['TZID'] )) + $theExdate['tz'] = $input['params']['TZID']; + else + $input['params']['TZID'] = $theExdate['tz']; + } + $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno ); + } + elseif( is_array( $theExdate )) { + $d = iCalUtilityFunctions::_chkDateArr( $theExdate, $parno ); + if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); + $exdatea = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $exdatea['unparsedtext'] ); + } + else + $exdatea = $d; + } + elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18 + $exdatea = iCalUtilityFunctions::_strdate2date( $theExdate, $parno ); + unset( $exdatea['unparsedtext'] ); + } + if( 3 == $parno ) + unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] ); + elseif( isset( $exdatea['tz'] )) + $exdatea['tz'] = (string) $exdatea['tz']; + if( isset( $input['params']['TZID'] ) || + ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) || + ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) || + ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] ))) + unset( $exdatea['tz'] ); + if( $toZ ) // time zone Z + $exdatea['tz'] = 'Z'; + $input['value'][] = $exdatea; + } + if( 0 >= count( $input['value'] )) + return FALSE; + if( 3 == $parno ) { + $input['params']['VALUE'] = 'DATE'; + unset( $input['params']['TZID'] ); + } + if( $toZ ) // time zone Z + unset( $input['params']['TZID'] ); + iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: EXRULE + */ +/** + * creates formatted output for calendar component property exrule + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-22 + * @uses calendarComponent::$exrule + * @uses calendarComponent::_format_recur() + * @return string + */ + function createExrule() { + if( empty( $this->exrule )) return FALSE; + return $this->_format_recur( 'EXRULE', $this->exrule ); + } +/** + * set calendar component property exdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param array $exruleset + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setMval() + * @uses calendarComponent::$exrule + * @uses iCalUtilityFunctions::_setRexrule() + * @return bool + */ + function setExrule( $exruleset, $params=FALSE, $index=FALSE ) { + if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = ''; else return FALSE; + iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: FREEBUSY + */ +/** + * creates formatted output for calendar component property freebusy + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.27 - 2013-07-05 + * @uses calendarComponent::$freebusy + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::$intAttrDelimiter + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_sortRdate1() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses iCalUtilityFunctions::_duration2str() + * @return string + */ + function createFreebusy() { + if( empty( $this->freebusy )) return FALSE; + $output = null; + foreach( $this->freebusy as $freebusyPart ) { + if( empty( $freebusyPart['value'] ) || (( 1 == count( $freebusyPart['value'] )) && isset( $freebusyPart['value']['fbtype'] ))) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' ); + continue; + } + $attributes = $content = null; + if( isset( $freebusyPart['value']['fbtype'] )) { + $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype']; + unset( $freebusyPart['value']['fbtype'] ); + $freebusyPart['value'] = array_values( $freebusyPart['value'] ); + } + else + $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY'; + $attributes .= $this->_createParams( $freebusyPart['params'] ); + $fno = 1; + $cnt = count( $freebusyPart['value']); + if( 1 < $cnt ) + usort( $freebusyPart['value'], array( 'iCalUtilityFunctions', '_sortRdate1' )); + foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) { + $formatted = iCalUtilityFunctions::_date2strdate( $freebusyPeriod[0] ); + $content .= $formatted; + $content .= '/'; + $cnt2 = count( $freebusyPeriod[1]); + if( array_key_exists( 'year', $freebusyPeriod[1] )) // date-time + $cnt2 = 7; + elseif( array_key_exists( 'week', $freebusyPeriod[1] )) // duration + $cnt2 = 5; + if(( 7 == $cnt2 ) && // period= -> date-time + isset( $freebusyPeriod[1]['year'] ) && + isset( $freebusyPeriod[1]['month'] ) && + isset( $freebusyPeriod[1]['day'] )) { + $content .= iCalUtilityFunctions::_date2strdate( $freebusyPeriod[1] ); + } + else { // period= -> dur-time + $content .= iCalUtilityFunctions::_duration2str( $freebusyPeriod[1] ); + } + if( $fno < $cnt ) + $content .= ','; + $fno++; + } + $output .= $this->_createElement( 'FREEBUSY', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property freebusy + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $fbType + * @param array $fbValues + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$freebusy + * @uses iCalUtilityFunctions::_isArrayDate() + * @uses iCalUtilityFunctions::_setMval() + * @uses iCalUtilityFunctions::_chkDateArr() + * @uses iCalUtilityFunctions::_isArrayTimestampDate() + * @uses iCalUtilityFunctions::_timestamp2date() + * @uses iCalUtilityFunctions::_duration2arr() + * @uses iCalUtilityFunctions::_durationStr2arr() + * @uses iCalUtilityFunctions::_strdate2date() + * @return bool + */ + function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) { + if( empty( $fbValues )) { + if( $this->getConfig( 'allowEmpty' )) { + iCalUtilityFunctions::_setMval( $this->freebusy, '', $params, FALSE, $index ); + return TRUE; + } + else + return FALSE; + } + $fbType = strtoupper( $fbType ); + if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) && + ( 'X-' != substr( $fbType, 0, 2 ))) + $fbType = 'BUSY'; + $input = array( 'fbtype' => $fbType ); + foreach( $fbValues as $fbPeriod ) { // periods => period + if( empty( $fbPeriod )) + continue; + $freebusyPeriod = array(); + foreach( $fbPeriod as $fbMember ) { // pairs => singlepart + $freebusyPairMember = array(); + if( is_array( $fbMember )) { + if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value + $freebusyPairMember = iCalUtilityFunctions::_chkDateArr( $fbMember, 7 ); + $freebusyPairMember['tz'] = 'Z'; + } + elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value + $freebusyPairMember = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 ); + $freebusyPairMember['tz'] = 'Z'; + } + else { // array format duration + $freebusyPairMember = iCalUtilityFunctions::_duration2arr( $fbMember ); + } + } + elseif(( 3 <= strlen( trim( $fbMember ))) && // string format duration + ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) { + if( 'P' != $fbMember{0} ) + $fbmember = substr( $fbMember, 1 ); + $freebusyPairMember = iCalUtilityFunctions::_durationStr2arr( $fbMember ); + } + elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18 + $freebusyPairMember = iCalUtilityFunctions::_strdate2date( $fbMember, 7 ); + unset( $freebusyPairMember['unparsedtext'] ); + $freebusyPairMember['tz'] = 'Z'; + } + $freebusyPeriod[] = $freebusyPairMember; + } + $input[] = $freebusyPeriod; + } + iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: GEO + */ +/** + * creates formatted output for calendar component property geo + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.18.10 - 2013-09-03 + * @uses calendarComponent::$geo + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_geo2str2() + * @uses iCalUtilityFunctions::$geoLongFmt + * @uses iCalUtilityFunctions::$geoLatFmt + * @return string + */ + function createGeo() { + if( empty( $this->geo )) return FALSE; + if( empty( $this->geo['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE; + return $this->_createElement( 'GEO', $this->_createParams( $this->geo['params'] ), + iCalUtilityFunctions::_geo2str2( $this->geo['value']['latitude'], iCalUtilityFunctions::$geoLatFmt ). + ';'.iCalUtilityFunctions::_geo2str2( $this->geo['value']['longitude'], iCalUtilityFunctions::$geoLongFmt )); + } +/** + * set calendar component property geo + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.18.10 - 2013-09-03 + * @param mixed $latitude + * @param mixed $longitude + * @param array $params + * @uses calendarComponent::$geo + * @uses iCalUtilityFunctions::_setParams() + * @uses calendarComponent::getConfig() + * @return bool + */ + function setGeo( $latitude, $longitude, $params=FALSE ) { + if( isset( $latitude ) && isset( $longitude )) { + if( !is_array( $this->geo )) $this->geo = array(); + $this->geo['value']['latitude'] = floatval( $latitude ); + $this->geo['value']['longitude'] = floatval( $longitude ); + $this->geo['params'] = iCalUtilityFunctions::_setParams( $params ); + } + elseif( $this->getConfig( 'allowEmpty' )) + $this->geo = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params ) ); + else + return FALSE; + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: LAST-MODIFIED + */ +/** + * creates formatted output for calendar component property last-modified + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @uses calendarComponent::$lastmodified + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses calendarComponent::_createElement() + * @return string + */ + function createLastModified() { + if( empty( $this->lastmodified )) return FALSE; + $attributes = $this->_createParams( $this->lastmodified['params'] ); + $formatted = iCalUtilityFunctions::_date2strdate( $this->lastmodified['value'], 7 ); + return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted ); + } +/** + * set calendar component property completed + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.19.5 - 2014-03-29 + * @param mixed $year optional + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param array $params optional + * @uses calendarComponent::$lastmodified + * @uses iCalUtilityFunctions::_setDate2() + * @return boll + */ + function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + if( empty( $year )) + $year = gmdate( 'Ymd\THis' ); + $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: LOCATION + */ +/** + * creates formatted output for calendar component property location + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$location + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @return string + */ + function createLocation() { + if( empty( $this->location )) return FALSE; + if( empty( $this->location['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE; + $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = iCalUtilityFunctions::_strrep( $this->location['value'], $this->format, $this->nl ); + return $this->_createElement( 'LOCATION', $attributes, $content ); + } +/** + * set calendar component property location + ' + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @uses calendarComponent::$location + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setLocation( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: ORGANIZER + */ +/** + * creates formatted output for calendar component property organizer + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.33 - 2010-12-17 + * @uses calendarComponent::$organizer + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createOrganizer() { + if( empty( $this->organizer )) return FALSE; + if( empty( $this->organizer['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE; + $attributes = $this->_createParams( $this->organizer['params'] + , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' )); + return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] ); + } +/** + * set calendar component property organizer + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$organizer + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setOrganizer( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + if( !empty( $value )) { + if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) + $value = 'MAILTO:'.$value; + elseif( !empty( $value )) + $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); + $value = str_replace( 'mailto:', 'MAILTO:', $value ); + } + $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + if( isset( $this->organizer['params']['SENT-BY'] )){ + if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 ))) + $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY']; + else + $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 ); + } + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: PERCENT-COMPLETE + */ +/** + * creates formatted output for calendar component property percent-complete + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @uses calendarComponent::$percentcomplete + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createPercentComplete() { + if( !isset($this->percentcomplete) || ( empty( $this->percentcomplete ) && !is_numeric( $this->percentcomplete ))) return FALSE; + if( !isset( $this->percentcomplete['value'] ) || ( empty( $this->percentcomplete['value'] ) && !is_numeric( $this->percentcomplete['value'] ))) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE; + $attributes = $this->_createParams( $this->percentcomplete['params'] ); + return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] ); + } +/** + * set calendar component property percent-complete + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param int $value + * @param array $params + * @uses calendarComponent::$percentcomplete + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setPercentComplete( $value, $params=FALSE ) { + if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: PRIORITY + */ +/** + * creates formatted output for calendar component property priority + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @uses calendarComponent::$priority + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createPriority() { + if( !isset($this->priority) || ( empty( $this->priority ) && !is_numeric( $this->priority ))) return FALSE; + if( !isset( $this->priority['value'] ) || ( empty( $this->priority['value'] ) && !is_numeric( $this->priority['value'] ))) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE; + $attributes = $this->_createParams( $this->priority['params'] ); + return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] ); + } +/** + * set calendar component property priority + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param int $value + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$priority + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setPriority( $value, $params=FALSE ) { + if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RDATE + */ +/** + * creates formatted output for calendar component property rdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.9 - 2013-01-09 + * @uses calendarComponent::$rdate + * @uses calendarComponent::$objName + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses iCalUtilityFunctions::_sortRdate1() + * @uses iCalUtilityFunctions::_sortRdate2() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_date2strdate() + * @return string + */ + function createRdate() { + if( empty( $this->rdate )) return FALSE; + $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE; + $output = null; + $rdates = array(); + foreach( $this->rdate as $rpix => $theRdate ) { + if( empty( $theRdate['value'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' ); + continue; + } + if( $utctime ) + unset( $theRdate['params']['TZID'] ); + if( 1 < count( $theRdate['value'] )) + usort( $theRdate['value'], array( 'iCalUtilityFunctions', '_sortRdate1' )); + $rdates[] = $theRdate; + } + if( 1 < count( $rdates )) + usort( $rdates, array( 'iCalUtilityFunctions', '_sortRdate2' )); + foreach( $rdates as $rpix => $theRdate ) { + $attributes = $this->_createParams( $theRdate['params'] ); + $cnt = count( $theRdate['value'] ); + $content = null; + $rno = 1; + foreach( $theRdate['value'] as $rix => $rdatePart ) { + $contentPart = null; + if( is_array( $rdatePart ) && + isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD + if( $utctime ) + unset( $rdatePart[0]['tz'] ); + $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[0] ); // PERIOD part 1 + if( $utctime || !empty( $theRdate['params']['TZID'] )) + $formatted = str_replace( 'Z', '', $formatted); + $contentPart .= $formatted; + $contentPart .= '/'; + $cnt2 = count( $rdatePart[1]); + if( array_key_exists( 'year', $rdatePart[1] )) { + if( array_key_exists( 'hour', $rdatePart[1] )) + $cnt2 = 7; // date-time + else + $cnt2 = 3; // date + } + elseif( array_key_exists( 'week', $rdatePart[1] )) // duration + $cnt2 = 5; + if(( 7 == $cnt2 ) && // period= -> date-time + isset( $rdatePart[1]['year'] ) && + isset( $rdatePart[1]['month'] ) && + isset( $rdatePart[1]['day'] )) { + if( $utctime ) + unset( $rdatePart[1]['tz'] ); + $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[1] ); // PERIOD part 2 + if( $utctime || !empty( $theRdate['params']['TZID'] )) + $formatted = str_replace( 'Z', '', $formatted ); + $contentPart .= $formatted; + } + else { // period= -> dur-time + $contentPart .= iCalUtilityFunctions::_duration2str( $rdatePart[1] ); + } + } // PERIOD end + else { // SINGLE date start + if( $utctime ) + unset( $rdatePart['tz'] ); + $parno = ( isset( $theRdate['params']['VALUE'] ) && ( 'DATE' == isset( $theRdate['params']['VALUE'] ))) ? 3 : null; + $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart, $parno ); + if( $utctime || !empty( $theRdate['params']['TZID'] )) + $formatted = str_replace( 'Z', '', $formatted); + $contentPart .= $formatted; + } + $content .= $contentPart; + if( $rno < $cnt ) + $content .= ','; + $rno++; + } + $output .= $this->_createElement( 'RDATE', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property rdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-10 + * @param array $rdates + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setMval() + * @uses calendarComponent::$rdate + * @uses iCalUtilityFunctions::_setParams() + * @uses calendarComponent::$objName + * @uses iCalUtilityFunctions::_isArrayDate() + * @uses iCalUtilityFunctions::_chkdatecfg() + * @uses iCalUtilityFunctions::_existRem() + * @uses iCalUtilityFunctions::_strDate2arr() + * @uses iCalUtilityFunctions::_isArrayTimestampDate() + * @uses iCalUtilityFunctions::_isOffset() + * @uses iCalUtilityFunctions::_timestamp2date() + * @uses iCalUtilityFunctions::_chkDateArr() + * @uses iCalUtilityFunctions::$fmt + * @uses iCalUtilityFunctions::_strdate2date() + * @uses iCalUtilityFunctions::_duration2arr() + * @uses iCalUtilityFunctions::_durationStr2arr() + * @return bool + */ + function setRdate( $rdates, $params=FALSE, $index=FALSE ) { + if( empty( $rdates )) { + if( $this->getConfig( 'allowEmpty' )) { + iCalUtilityFunctions::_setMval( $this->rdate, '', $params, FALSE, $index ); + return TRUE; + } + else + return FALSE; + } + $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ))); + if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) { + unset( $input['params']['TZID'] ); + $input['params']['VALUE'] = 'DATE-TIME'; + } + $zArr = array( 'GMT', 'UTC', 'Z' ); + $toZ = ( isset( $params['TZID'] ) && in_array( strtoupper( $params['TZID'] ), $zArr )) ? TRUE : FALSE; + /* check if PERIOD, if not set */ + if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) && + isset( $rdates[0] ) && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) && + isset( $rdates[0][0] ) && isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) && + (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) || + iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) || + ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] ))))) && + ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] )))))) + $input['params']['VALUE'] = 'PERIOD'; + /* check 1:st date, upd. $parno (opt) and save ev. timezone **/ + $date = reset( $rdates ); + if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD + $date = reset( $date ); + iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] ); + iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default + foreach( $rdates as $rpix => $theRdate ) { + $inputa = null; + iCalUtilityFunctions::_strDate2arr( $theRdate ); + if( is_array( $theRdate )) { + if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD + foreach( $theRdate as $rix => $rPeriod ) { + iCalUtilityFunctions::_strDate2arr( $theRdate ); + if( is_array( $rPeriod )) { + if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) { // timestamp + if( isset( $rPeriod['tz'] ) && !iCalUtilityFunctions::_isOffset( $rPeriod['tz'] )) { + if( isset( $input['params']['TZID'] )) + $rPeriod['tz'] = $input['params']['TZID']; + else + $input['params']['TZID'] = $rPeriod['tz']; + } + $inputab = iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno ); + } + elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod )) { + $d = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_chkDateArr( $rPeriod, $parno ) : iCalUtilityFunctions::_chkDateArr( $rPeriod, 6 ); + if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); + $inputab = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $inputab['unparsedtext'] ); + } + else + $inputab = $d; + } + elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) { // text-date + $inputab = iCalUtilityFunctions::_strdate2date( reset( $rPeriod ), $parno ); + unset( $inputab['unparsedtext'] ); + } + else // array format duration + $inputab = iCalUtilityFunctions::_duration2arr( $rPeriod ); + } + elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration + ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) { + if( 'P' != $rPeriod[0] ) + $rPeriod = substr( $rPeriod, 1 ); + $inputab = iCalUtilityFunctions::_durationStr2arr( $rPeriod ); + } + elseif( 8 <= strlen( trim( $rPeriod ))) { // text date ex. 2006-08-03 10:12:18 + $inputab = iCalUtilityFunctions::_strdate2date( $rPeriod, $parno ); + unset( $inputab['unparsedtext'] ); + } + if(( 0 == $rpix ) && ( 0 == $rix )) { + if( isset( $inputab['tz'] ) && in_array( strtoupper( $inputab['tz'] ), $zArr )) { + $inputab['tz'] = 'Z'; + $toZ = TRUE; + } + } + else { + if( isset( $inputa[0]['tz'] ) && ( 'Z' == $inputa[0]['tz'] ) && isset( $inputab['year'] )) + $inputab['tz'] = 'Z'; + else + unset( $inputab['tz'] ); + } + if( $toZ && isset( $inputab['year'] ) ) + $inputab['tz'] = 'Z'; + $inputa[] = $inputab; + } + } // PERIOD end + elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) { // timestamp + if( isset( $theRdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theRdate['tz'] )) { + if( isset( $input['params']['TZID'] )) + $theRdate['tz'] = $input['params']['TZID']; + else + $input['params']['TZID'] = $theRdate['tz']; + } + $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno ); + } + else { // date[-time] + $inputa = iCalUtilityFunctions::_chkDateArr( $theRdate, $parno ); + if( isset( $inputa['tz'] ) && ( 'Z' != $inputa['tz'] ) && iCalUtilityFunctions::_isOffset( $inputa['tz'] )) { + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $inputa['year'], (int) $inputa['month'], (int) $inputa['day'], (int) $inputa['hour'], (int) $inputa['min'], (int) $inputa['sec'], $inputa['tz'] ); + $inputa = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $inputa['unparsedtext'] ); + } + } + } + elseif( 8 <= strlen( trim( $theRdate ))) { // text date ex. 2006-08-03 10:12:18 + $inputa = iCalUtilityFunctions::_strdate2date( $theRdate, $parno ); + unset( $inputa['unparsedtext'] ); + if( $toZ ) + $inputa['tz'] = 'Z'; + } + if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD + if(( 0 == $rpix ) && !$toZ ) + $toZ = ( isset( $inputa['tz'] ) && in_array( strtoupper( $inputa['tz'] ), $zArr )) ? TRUE : FALSE; + if( $toZ ) + $inputa['tz'] = 'Z'; + if( 3 == $parno ) + unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] ); + elseif( isset( $inputa['tz'] )) + $inputa['tz'] = (string) $inputa['tz']; + if( isset( $input['params']['TZID'] ) || ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] )))) + if( !$toZ ) + unset( $inputa['tz'] ); + } + $input['value'][] = $inputa; + } + if( 3 == $parno ) { + $input['params']['VALUE'] = 'DATE'; + unset( $input['params']['TZID'] ); + } + if( $toZ ) + unset( $input['params']['TZID'] ); + iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RECURRENCE-ID + */ +/** + * creates formatted output for calendar component property recurrence-id + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.14.4 - 2012-09-26 + * @uses calendarComponent::$recurrenceid + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses calendarComponent::_createParams() + * @return string + */ + function createRecurrenceid() { + if( empty( $this->recurrenceid )) return FALSE; + if( empty( $this->recurrenceid['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE; + $parno = ( isset( $this->recurrenceid['params']['VALUE'] ) && ( 'DATE' == $this->recurrenceid['params']['VALUE'] )) ? 3 : null; + $formatted = iCalUtilityFunctions::_date2strdate( $this->recurrenceid['value'], $parno ); + $attributes = $this->_createParams( $this->recurrenceid['params'] ); + return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted ); + } +/** + * set calendar component property recurrence-id + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param mixed $year + * @param mixed $month + * @param int $day + * @param int $hour + * @param int $min + * @param int $sec + * @param string $tz + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$recurrenceid + * @uses iCalUtilityFunctions::_setDate() + * @return bool + */ + function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { + if( empty( $year )) { + if( $this->getConfig( 'allowEmpty' )) { + $this->recurrenceid = array( 'value' => '', 'params' => null ); + return TRUE; + } + else + return FALSE; + } + $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RELATED-TO + */ +/** + * creates formatted output for calendar component property related-to + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-05-25 + * @uses calendarComponent::$relatedto + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @uses calendarComponent::getConfig() + * @return string + */ + function createRelatedTo() { + if( empty( $this->relatedto )) return FALSE; + $output = null; + foreach( $this->relatedto as $relation ) { + if( !empty( $relation['value'] )) + $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ), iCalUtilityFunctions::_strrep( $relation['value'], $this->format, $this->nl )); + elseif( $this->getConfig( 'allowEmpty' )) + $output .= $this->_createElement( 'RELATED-TO' ); + } + return $output; + } +/** + * set calendar component property related-to + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @param int $index + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_existRem() + * @uses iCalUtilityFunctions::_setMval() + * @return bool + */ + function setRelatedTo( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default + iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: REPEAT + */ +/** + * creates formatted output for calendar component property repeat + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @uses calendarComponent::$repeat + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createRepeat() { + if( !isset( $this->repeat ) || ( empty( $this->repeat ) && !is_numeric( $this->repeat ))) return FALSE; + if( !isset( $this->repeat['value']) || ( empty( $this->repeat['value'] ) && !is_numeric( $this->repeat['value'] ))) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE; + $attributes = $this->_createParams( $this->repeat['params'] ); + return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] ); + } +/** + * set calendar component property repeat + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$repeat + * @uses iCalUtilityFunctions::_setParams() + * @return void + */ + function setRepeat( $value, $params=FALSE ) { + if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: REQUEST-STATUS + */ +/** + * creates formatted output for calendar component property request-status + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$requeststatus + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @return string + */ + function createRequestStatus() { + if( empty( $this->requeststatus )) return FALSE; + $output = null; + foreach( $this->requeststatus as $rstat ) { + if( empty( $rstat['value']['statcode'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' ); + continue; + } + $attributes = $this->_createParams( $rstat['params'], array( 'LANGUAGE' )); + $content = number_format( (float) $rstat['value']['statcode'], 2, '.', ''); + $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['text'], $this->format, $this->nl ); + if( isset( $rstat['value']['extdata'] )) + $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['extdata'], $this->format, $this->nl ); + $output .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property request-status + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param float $statcode + * @param string $text + * @param string $extdata + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$requeststatus + * @uses iCalUtilityFunctions::_setMval() + * @return bool + */ + function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) { + if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = ''; else return FALSE; + $input = array( 'statcode' => $statcode, 'text' => $text ); + if( $extdata ) + $input['extdata'] = $extdata; + iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RESOURCES + */ +/** + * creates formatted output for calendar component property resources + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$resources + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @return string + */ + function createResources() { + if( empty( $this->resources )) return FALSE; + $output = null; + foreach( $this->resources as $resource ) { + if( empty( $resource['value'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' ); + continue; + } + $attributes = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' )); + if( is_array( $resource['value'] )) { + foreach( $resource['value'] as $rix => $resourcePart ) + $resource['value'][$rix] = iCalUtilityFunctions::_strrep( $resourcePart, $this->format, $this->nl ); + $content = implode( ',', $resource['value'] ); + } + else + $content = iCalUtilityFunctions::_strrep( $resource['value'], $this->format, $this->nl ); + $output .= $this->_createElement( 'RESOURCES', $attributes, $content ); + } + return $output; + } +/** + * set calendar component property recources + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param mixed $value + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setMval() + * @uses calendarComponent::$resources + * @return bool + */ + function setResources( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: RRULE + */ +/** + * creates formatted output for calendar component property rrule + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @uses calendarComponent::$rrule + * @uses calendarComponent::_format_recur() + * @return string + */ + function createRrule() { + if( empty( $this->rrule )) return FALSE; + return $this->_format_recur( 'RRULE', $this->rrule ); + } +/** + * set calendar component property rrule + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param array $rruleset + * @param array $params + * @param integer $index + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setMval() + * @uses iCalUtilityFunctions::_setRexrule() + * @return void + */ + function setRrule( $rruleset, $params=FALSE, $index=FALSE ) { + if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = ''; else return FALSE; + iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: SEQUENCE + */ +/** + * creates formatted output for calendar component property sequence + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.9.3 - 2011-05-14 + * @uses calendarComponent::$sequence + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createSequence() { + if( !isset( $this->sequence ) || ( empty( $this->sequence ) && !is_numeric( $this->sequence ))) return FALSE; + if(( !isset($this->sequence['value'] ) || ( empty( $this->sequence['value'] ) && !is_numeric( $this->sequence['value'] ))) && + ( '0' != $this->sequence['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE; + $attributes = $this->_createParams( $this->sequence['params'] ); + return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] ); + } +/** + * set calendar component property sequence + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.8 - 2011-09-19 + * @param int $value + * @param array $params + * @uses calendarComponent::$sequence + * @uses iCalUtilityFunctions::_setParams(); + * @return bool + */ + function setSequence( $value=FALSE, $params=FALSE ) { + if(( empty( $value ) && !is_numeric( $value )) && ( '0' != $value )) + $value = ( isset( $this->sequence['value'] ) && ( -1 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : '0'; + $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: STATUS + */ +/** + * creates formatted output for calendar component property status + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @uses calendarComponent::$status + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createStatus() { + if( empty( $this->status )) return FALSE; + if( empty( $this->status['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE; + $attributes = $this->_createParams( $this->status['params'] ); + return $this->_createElement( 'STATUS', $attributes, $this->status['value'] ); + } +/** + * set calendar component property status + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$status + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setStatus( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: SUMMARY + */ +/** + * creates formatted output for calendar component property summary + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$summary + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @return string + */ + function createSummary() { + if( empty( $this->summary )) return FALSE; + if( empty( $this->summary['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE; + $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' )); + $content = iCalUtilityFunctions::_strrep( $this->summary['value'], $this->format, $this->nl ); + return $this->_createElement( 'SUMMARY', $attributes, $content ); + } +/** + * set calendar component property summary + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param string $params + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setSummary( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TRANSP + */ +/** + * creates formatted output for calendar component property transp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @uses calendarComponent::$transp + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createTransp() { + if( empty( $this->transp )) return FALSE; + if( empty( $this->transp['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE; + $attributes = $this->_createParams( $this->transp['params'] ); + return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] ); + } +/** + * set calendar component property transp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param string $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$transp + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setTransp( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TRIGGER + */ +/** + * creates formatted output for calendar component property trigger + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-10-21 + * @uses calendarComponent::$trigger + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses calendarComponent::$intAttrDelimiter + * @uses iCalUtilityFunctions::_duration2str() + * @uses calendarComponent::_createParams() + * @return string + */ + function createTrigger() { + if( empty( $this->trigger )) return FALSE; + if( empty( $this->trigger['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE; + $content = $attributes = null; + if( isset( $this->trigger['value']['year'] ) && + isset( $this->trigger['value']['month'] ) && + isset( $this->trigger['value']['day'] )) + $content .= iCalUtilityFunctions::_date2strdate( $this->trigger['value'] ); + else { + if( TRUE !== $this->trigger['value']['relatedStart'] ) + $attributes .= $this->intAttrDelimiter.'RELATED=END'; + if( $this->trigger['value']['before'] ) + $content .= '-'; + $content .= iCalUtilityFunctions::_duration2str( $this->trigger['value'] ); + } + $attributes .= $this->_createParams( $this->trigger['params'] ); + return $this->_createElement( 'TRIGGER', $attributes, $content ); + } +/** + * set calendar component property trigger + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param mixed $year + * @param mixed $month + * @param int $day + * @param int $week + * @param int $hour + * @param int $min + * @param int $sec + * @param bool $relatedStart + * @param bool $before + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$trigger + * @uses iCalUtilityFunctions::_setParams() + * @uses iCalUtilityFunctions::_isArrayTimestampDate() + * @uses iCalUtilityFunctions::_timestamp2date() + * @uses iCalUtilityFunctions::_strdate2date() + * @uses iCalUtilityFunctions::_duration2arr() + * @return bool + */ + function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) { + if( empty( $year ) && ( empty( $month ) || is_array( $month )) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec )) + if( $this->getConfig( 'allowEmpty' )) { + $this->trigger = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $month ) ); + return TRUE; + } + else + return FALSE; + if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp UTC + $params = iCalUtilityFunctions::_setParams( $month ); + $date = iCalUtilityFunctions::_timestamp2date( $year, 7 ); + foreach( $date as $k => $v ) + $$k = $v; + } + elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) { + $params = iCalUtilityFunctions::_setParams( $month ); + if(!(array_key_exists( 'year', $year ) && // exclude date-time + array_key_exists( 'month', $year ) && + array_key_exists( 'day', $year ))) { // when this must be a duration + if( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) + $relatedStart = FALSE; + else + $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE; + $before = ( array_key_exists( 'before', $year ) && ( TRUE !== $year['before'] )) ? FALSE : TRUE; + } + $SSYY = ( array_key_exists( 'year', $year )) ? $year['year'] : null; + $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null; + $day = ( array_key_exists( 'day', $year )) ? $year['day'] : null; + $week = ( array_key_exists( 'week', $year )) ? $year['week'] : null; + $hour = ( array_key_exists( 'hour', $year )) ? $year['hour'] : 0; //null; + $min = ( array_key_exists( 'min', $year )) ? $year['min'] : 0; //null; + $sec = ( array_key_exists( 'sec', $year )) ? $year['sec'] : 0; //null; + $year = $SSYY; + } + elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) { // duration or date in a string + $params = iCalUtilityFunctions::_setParams( $month ); + if( in_array( $year{0}, array( 'P', '+', '-' ))) { // duration + $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) ? FALSE : TRUE; + $before = ( '-' == $year[0] ) ? TRUE : FALSE; + if( 'P' != $year[0] ) + $year = substr( $year, 1 ); + $date = iCalUtilityFunctions::_durationStr2arr( $year); + } + else // date + $date = iCalUtilityFunctions::_strdate2date( $year, 7 ); + unset( $year, $month, $day, $date['unparsedtext'] ); + if( empty( $date )) + $sec = 0; + else + foreach( $date as $k => $v ) + $$k = $v; + } + else // single values in function input parameters + $params = iCalUtilityFunctions::_setParams( $params ); + if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date + $params['VALUE'] = 'DATE-TIME'; + $hour = ( $hour ) ? $hour : 0; + $min = ( $min ) ? $min : 0; + $sec = ( $sec ) ? $sec : 0; + $this->trigger = array( 'params' => $params ); + $this->trigger['value'] = array( 'year' => $year + , 'month' => $month + , 'day' => $day + , 'hour' => $hour + , 'min' => $min + , 'sec' => $sec + , 'tz' => 'Z' ); + return TRUE; + } + elseif(( empty( $year ) && empty( $month )) && // duration + (( !empty( $week ) || ( 0 == $week )) || + ( !empty( $day ) || ( 0 == $day )) || + ( !empty( $hour ) || ( 0 == $hour )) || + ( !empty( $min ) || ( 0 == $min )) || + ( !empty( $sec ) || ( 0 == $sec )))) { + unset( $params['RELATED'] ); // set at output creation (END only) + unset( $params['VALUE'] ); // 'DURATION' default + $this->trigger = array( 'params' => $params ); + $this->trigger['value'] = array(); + if( !empty( $week )) $this->trigger['value']['week'] = $week; + if( !empty( $day )) $this->trigger['value']['day'] = $day; + if( !empty( $hour )) $this->trigger['value']['hour'] = $hour; + if( !empty( $min )) $this->trigger['value']['min'] = $min; + if( !empty( $sec )) $this->trigger['value']['sec'] = $sec; + if( empty( $this->trigger['value'] )) { + $this->trigger['value']['sec'] = 0; + $before = FALSE; + } + else + $this->trigger['value'] = iCalUtilityFunctions::_duration2arr( $this->trigger['value'] ); + $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE; + $before = ( FALSE !== $before ) ? TRUE : FALSE; + $this->trigger['value']['relatedStart'] = $relatedStart; + $this->trigger['value']['before'] = $before; + return TRUE; + } + return FALSE; + } +/*********************************************************************************/ +/** + * Property Name: TZID + */ +/** + * creates formatted output for calendar component property tzid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$tzid + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_strrep() + * @uses calendarComponent::$format + * @uses calendarComponent::$nl + * @return string + */ + function createTzid() { + if( empty( $this->tzid )) return FALSE; + if( empty( $this->tzid['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE; + $attributes = $this->_createParams( $this->tzid['params'] ); + return $this->_createElement( 'TZID', $attributes, iCalUtilityFunctions::_strrep( $this->tzid['value'], $this->format, $this->nl )); + } +/** + * set calendar component property tzid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$tzid + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setTzid( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * .. . + * Property Name: TZNAME + */ +/** + * creates formatted output for calendar component property tzname + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @uses calendarComponent::$tzname + * @uses calendarComponent::_createParams() + * @uses calendarComponent::_createElement() + * @uses iCalUtilityFunctions::_strrep( + * @uses calendarComponent::$format + * @uses calendarComponent::$nl + * @uses calendarComponent::getConfig() + * @return string + */ + function createTzname() { + if( empty( $this->tzname )) return FALSE; + $output = null; + foreach( $this->tzname as $theName ) { + if( !empty( $theName['value'] )) { + $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' )); + $output .= $this->_createElement( 'TZNAME', $attributes, iCalUtilityFunctions::_strrep( $theName['value'], $this->format, $this->nl )); + } + elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' ); + } + return $output; + } +/** + * set calendar component property tzname + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @param integer $index + * @uses calendarComponent::_createParams() + * @uses calendarComponent::$tzname + * @uses iCalUtilityFunctions::_setMval() + * @return bool + */ + function setTzname( $value, $params=FALSE, $index=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index ); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TZOFFSETFROM + */ +/** + * creates formatted output for calendar component property tzoffsetfrom + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @uses calendarComponent::$tzoffsetfrom + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createTzoffsetfrom() { + if( empty( $this->tzoffsetfrom )) return FALSE; + if( empty( $this->tzoffsetfrom['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE; + $attributes = $this->_createParams( $this->tzoffsetfrom['params'] ); + return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] ); + } +/** + * set calendar component property tzoffsetfrom + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$tzoffsetfrom + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setTzoffsetfrom( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TZOFFSETTO + */ +/** + * creates formatted output for calendar component property tzoffsetto + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @uses calendarComponent::$tzoffsetto + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createTzoffsetto() { + if( empty( $this->tzoffsetto )) return FALSE; + if( empty( $this->tzoffsetto['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE; + $attributes = $this->_createParams( $this->tzoffsetto['params'] ); + return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] ); + } +/** + * set calendar component property tzoffsetto + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$tzoffsetto + * @uses iCalUtilityFunctions::_setParams() + * @return bool + */ + function setTzoffsetto( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: TZURL + */ +/** + * creates formatted output for calendar component property tzurl + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @uses calendarComponent::$tzurl + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createTzurl() { + if( empty( $this->tzurl )) return FALSE; + if( empty( $this->tzurl['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE; + $attributes = $this->_createParams( $this->tzurl['params'] ); + return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] ); + } +/** + * set calendar component property tzurl + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @uses calendarComponent::$tzurl + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$tzurl + * @uses iCalUtilityFunctions::_setParams() + * @return boll + */ + function setTzurl( $value, $params=FALSE ) { + if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; + $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: UID + */ +/** + * creates formatted output for calendar component property uid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.19.1 - 2014-02-21 + * @uses calendarComponent::$uid + * @uses calendarComponent::_makeuid(); + * @uses calendarComponent::_createParams() + * @uses calendarComponent::_createElement() + * @return string + */ + function createUid() { + if( empty( $this->uid )) + $this->_makeuid(); + $attributes = $this->_createParams( $this->uid['params'] ); + return $this->_createElement( 'UID', $attributes, $this->uid['value'] ); + } +/** + * create an unique id for this calendar component object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.2.7 - 2007-09-04 + * @uses calendarComponent::$uid + * @uses calendarComponent::getConfig() + * @return void + */ + function _makeUid() { + $date = date('Ymd\THisT'); + $unique = substr(microtime(), 2, 4); + $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890'; + $start = 0; + $end = strlen( $base ) - 1; + $length = 6; + $str = null; + for( $p = 0; $p < $length; $p++ ) + $unique .= $base{mt_rand( $start, $end )}; + $this->uid = array( 'params' => null ); + $this->uid['value'] = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' ); + } +/** + * set calendar component property uid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.19.1 - 2014-02-21 + * @param string $value + * @param array $params + * @uses calendarComponent::$uid + * @uses calendarComponent::_setParams() + * @return bool + */ + function setUid( $value, $params=FALSE ) { + if( empty( $value ) && ( '0' != $value )) return FALSE; // no allowEmpty check here !!!! + $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: URL + */ +/** + * creates formatted output for calendar component property url + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @uses calendarComponent::$url + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @return string + */ + function createUrl() { + if( empty( $this->url )) return FALSE; + if( empty( $this->url['value'] )) + return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE; + $attributes = $this->_createParams( $this->url['params'] ); + return $this->_createElement( 'URL', $attributes, $this->url['value'] ); + } +/** + * set calendar component property url + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.21 - 2013-06-23 + * @param string $value + * @param array $params + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$url + * @uses calendarComponent::_setParams() + * @return bool + */ + function setUrl( $value, $params=FALSE ) { + if( !empty( $value )) { + if( !filter_var( $value, FILTER_VALIDATE_URL ) && ( 'urn' != strtolower( substr( $value, 0, 3 )))) + return FALSE; + } + elseif( $this->getConfig( 'allowEmpty' )) + $value = ''; + else + return FALSE; + $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); + return TRUE; + } +/*********************************************************************************/ +/*********************************************************************************/ +/** + * create element format parts + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.0.6 - 2006-06-20 + * @uses iCalBase::_createFormat() + * @uses calendarComponent::$timezonetype + * @return string + */ + function _createFormat() { + parent::_createFormat(); + $objectname = null; + switch( $this->format ) { + case 'xcal': + return ( isset( $this->timezonetype )) ? strtolower( $this->timezonetype ) : strtolower( $this->objName ); + break; + default: + return ( isset( $this->timezonetype )) ? strtoupper( $this->timezonetype ) : strtoupper( $this->objName ); + break; + } + } +/** + * creates formatted output for calendar component property data value type recur + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.25 - 2013-06-30 + * @param array $recurlabel + * @param array $recurdata + * @uses calendarComponent::getConfig() + * @uses calendarComponent::_createElement() + * @uses calendarComponent::_createParams() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses iCalUtilityFunctions::_recurBydaySort() + * @return string + */ + function _format_recur( $recurlabel, $recurdata ) { + $output = null; + foreach( $recurdata as $therule ) { + if( empty( $therule['value'] )) { + if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel ); + continue; + } + $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null; + $content1 = $content2 = null; + foreach( $therule['value'] as $rulelabel => $rulevalue ) { + switch( strtoupper( $rulelabel )) { + case 'FREQ': { + $content1 .= "FREQ=$rulevalue"; + break; + } + case 'UNTIL': { + $parno = ( isset( $rulevalue['hour'] )) ? 7 : 3; + $content2 .= ';UNTIL='.iCalUtilityFunctions::_date2strdate( $rulevalue, $parno ); + break; + } + case 'COUNT': + case 'INTERVAL': + case 'WKST': { + $content2 .= ";$rulelabel=$rulevalue"; + break; + } + case 'BYSECOND': + case 'BYMINUTE': + case 'BYHOUR': + case 'BYMONTHDAY': + case 'BYYEARDAY': + case 'BYWEEKNO': + case 'BYMONTH': + case 'BYSETPOS': { + $content2 .= ";$rulelabel="; + if( is_array( $rulevalue )) { + foreach( $rulevalue as $vix => $valuePart ) { + $content2 .= ( $vix ) ? ',' : null; + $content2 .= $valuePart; + } + } + else + $content2 .= $rulevalue; + break; + } + case 'BYDAY': { + $byday = array( '' ); + $bx = 0; + foreach( $rulevalue as $bix => $bydayPart ) { + if( ! empty( $byday[$bx] ) && ! ctype_digit( substr( $byday[$bx], -1 ))) // new day + $byday[++$bx] = ''; + if( ! is_array( $bydayPart )) // day without order number + $byday[$bx] .= (string) $bydayPart; + else { // day with order number + foreach( $bydayPart as $bix2 => $bydayPart2 ) + $byday[$bx] .= (string) $bydayPart2; + } + } // end foreach( $rulevalue as $bix => $bydayPart ) + if( 1 < count( $byday )) + usort( $byday, array( 'iCalUtilityFunctions', '_recurBydaySort' )); + $content2 .= ';BYDAY='.implode( ',', $byday ); + break; + } + default: { + $content2 .= ";$rulelabel=$rulevalue"; + break; + } + } + } + $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 ); + } + return $output; + } +/** + * check if property not exists within component + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-15 + * @param string $propName + * @uses calendarComponent::$lastmodified + * @uses calendarComponent::$percentcomplete + * @uses calendarComponent::$recurrenceid + * @uses calendarComponent::$relatedto + * @uses calendarComponent::$requeststatus + * @uses calendarComponent::getConfig() + * @uses calendarComponent::{$propname} + * @return bool + */ + function _notExistProp( $propName ) { + if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed + $propName = strtolower( $propName ); + if( 'last-modified' == $propName ) { if( !isset( $this->lastmodified )) return TRUE; } + elseif( 'percent-complete' == $propName ) { if( !isset( $this->percentcomplete )) return TRUE; } + elseif( 'recurrence-id' == $propName ) { if( !isset( $this->recurrenceid )) return TRUE; } + elseif( 'related-to' == $propName ) { if( !isset( $this->relatedto )) return TRUE; } + elseif( 'request-status' == $propName ) { if( !isset( $this->requeststatus )) return TRUE; } + elseif(( 'x-' != substr($propName,0,2)) && !isset( $this->$propName )) return TRUE; + return FALSE; + } +/*********************************************************************************/ +/*********************************************************************************/ +/** + * get general component config variables or info about subcomponents + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param mixed $config + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$allowEmpty + * @uses calendarComponent::$compix + * @uses calendarComponent::$components + * @uses calendarComponent::$objName + * @uses calendarComponent::getProperty() + * @uses calendarComponent::$format + * @uses calendarComponent::$language + * @uses calendarComponent::$nl + * @uses iCalUtilityFunctions::$miscComps + * @uses calendarComponent::$uid + * @uses calendarComponent::_makeuid() + * @uses calendarComponent::dtstamp + * @uses calendarComponent::_makeDtstamp() + * @uses calendarComponent::$summary + * @uses calendarComponent::$description + * @uses calendarComponent::$dtstart + * @uses calendarComponent::$dtend + * @uses calendarComponent::$due + * @uses calendarComponent::$duration + * @uses calendarComponent::$rrule + * @uses calendarComponent::$rdate + * @uses calendarComponent::$exdate + * @uses calendarComponent::$exrule + * @uses calendarComponent::$action + * @uses calendarComponent::$attach + * @uses calendarComponent::$attendee + * @uses calendarComponent::$categories + * @uses calendarComponent::$class + * @uses calendarComponent::$comment + * @uses calendarComponent::$completed + * @uses calendarComponent::$contact + * @uses calendarComponent::$created + * @uses calendarComponent::$freebusy + * @uses calendarComponent::$geo + * @uses calendarComponent::$lastmodified + * @uses calendarComponent::$location + * @uses calendarComponent::$organizer + * @uses calendarComponent::$percentcomplete + * @uses calendarComponent::$priority + * @uses calendarComponent::$recurrenceid + * @uses calendarComponent::$relatedto + * @uses calendarComponent::$repeat + * @uses calendarComponent::$requeststatus + * @uses calendarComponent::$resources + * @uses calendarComponent::$sequence + * @uses calendarComponent::$sequence + * @uses calendarComponent::$status + * @uses calendarComponent::$transp + * @uses calendarComponent::$trigger + * @uses calendarComponent::$tzid + * @uses calendarComponent::$tzname + * @uses calendarComponent::$tzoffsetfrom + * @uses calendarComponent::$tzoffsetto + * @uses calendarComponent::$tzurl + * @uses calendarComponent::$url + * @uses calendarComponent::$xprop + * @uses calendarComponent::$dtzid + * @uses calendarComponent::$unique_id + * @return value + */ + function getConfig( $config = FALSE) { + if( !$config ) { + $return = array(); + $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' ); + $return['FORMAT'] = $this->getConfig( 'FORMAT' ); + if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' ))) + $return['LANGUAGE'] = $lang; + $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' ); + $return['TZTD'] = $this->getConfig( 'TZID' ); + $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' ); + return $return; + } + switch( strtoupper( $config )) { + case 'ALLOWEMPTY': + return $this->allowEmpty; + break; + case 'COMPSINFO': + unset( $this->compix ); + $info = array(); + if( isset( $this->components )) { + foreach( $this->components as $cix => $component ) { + if( empty( $component )) continue; + $info[$cix]['ordno'] = $cix + 1; + $info[$cix]['type'] = $component->objName; + $info[$cix]['uid'] = $component->getProperty( 'uid' ); + $info[$cix]['props'] = $component->getConfig( 'propinfo' ); + $info[$cix]['sub'] = $component->getConfig( 'compsinfo' ); + } + } + return $info; + break; + case 'FORMAT': + return $this->format; + break; + case 'LANGUAGE': + // get language for calendar component as defined in [RFC 1766] + return $this->language; + break; + case 'NL': + case 'NEWLINECHAR': + return $this->nl; + break; + case 'PROPINFO': + $output = array(); + if( ! in_array( $this->objName, iCalUtilityFunctions::$miscComps )) { + if( empty( $this->uid )) $this->_makeuid(); + $output['UID'] = 1; + if( empty( $this->dtstamp )) $this->_makeDtstamp(); + $output['DTSTAMP'] = 1; + } + if( !empty( $this->summary )) $output['SUMMARY'] = 1; + if( !empty( $this->description )) $output['DESCRIPTION'] = count( $this->description ); + if( !empty( $this->dtstart )) $output['DTSTART'] = 1; + if( !empty( $this->dtend )) $output['DTEND'] = 1; + if( !empty( $this->due )) $output['DUE'] = 1; + if( !empty( $this->duration )) $output['DURATION'] = 1; + if( !empty( $this->rrule )) $output['RRULE'] = count( $this->rrule ); + if( !empty( $this->rdate )) $output['RDATE'] = count( $this->rdate ); + if( !empty( $this->exdate )) $output['EXDATE'] = count( $this->exdate ); + if( !empty( $this->exrule )) $output['EXRULE'] = count( $this->exrule ); + if( !empty( $this->action )) $output['ACTION'] = 1; + if( !empty( $this->attach )) $output['ATTACH'] = count( $this->attach ); + if( !empty( $this->attendee )) $output['ATTENDEE'] = count( $this->attendee ); + if( !empty( $this->categories )) $output['CATEGORIES'] = count( $this->categories ); + if( !empty( $this->class )) $output['CLASS'] = 1; + if( !empty( $this->comment )) $output['COMMENT'] = count( $this->comment ); + if( !empty( $this->completed )) $output['COMPLETED'] = 1; + if( !empty( $this->contact )) $output['CONTACT'] = count( $this->contact ); + if( !empty( $this->created )) $output['CREATED'] = 1; + if( !empty( $this->freebusy )) $output['FREEBUSY'] = count( $this->freebusy ); + if( !empty( $this->geo )) $output['GEO'] = 1; + if( !empty( $this->lastmodified )) $output['LAST-MODIFIED'] = 1; + if( !empty( $this->location )) $output['LOCATION'] = 1; + if( !empty( $this->organizer )) $output['ORGANIZER'] = 1; + if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1; + if( !empty( $this->priority )) $output['PRIORITY'] = 1; + if( !empty( $this->recurrenceid )) $output['RECURRENCE-ID'] = 1; + if( !empty( $this->relatedto )) $output['RELATED-TO'] = count( $this->relatedto ); + if( !empty( $this->repeat )) $output['REPEAT'] = 1; + if( !empty( $this->requeststatus )) $output['REQUEST-STATUS'] = count( $this->requeststatus ); + if( !empty( $this->resources )) $output['RESOURCES'] = count( $this->resources ); + if( !empty( $this->sequence )) $output['SEQUENCE'] = 1; + if( !empty( $this->sequence )) $output['SEQUENCE'] = 1; + if( !empty( $this->status )) $output['STATUS'] = 1; + if( !empty( $this->transp )) $output['TRANSP'] = 1; + if( !empty( $this->trigger )) $output['TRIGGER'] = 1; + if( !empty( $this->tzid )) $output['TZID'] = 1; + if( !empty( $this->tzname )) $output['TZNAME'] = count( $this->tzname ); + if( !empty( $this->tzoffsetfrom )) $output['TZOFFSETFROM'] = 1; + if( !empty( $this->tzoffsetto )) $output['TZOFFSETTO'] = 1; + if( !empty( $this->tzurl )) $output['TZURL'] = 1; + if( !empty( $this->url )) $output['URL'] = 1; + if( !empty( $this->xprop )) $output['X-PROP'] = count( $this->xprop ); + return $output; + break; + case 'SETPROPERTYNAMES': + return array_keys( $this->getConfig( 'propinfo' )); + break; + case 'TZID': + return $this->dtzid; + break; + case 'UNIQUE_ID': + if( empty( $this->unique_id )) + $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost'; + return $this->unique_id; + break; + } + } +/** + * general component config setting + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.18 - 2013-09-06 + * @param mixed $config + * @param string $value + * @param bool $softUpdate + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$allowEmpty + * @uses calendarComponent::$format + * @uses calendarComponent::$language + * @uses calendarComponent::$nl + * @uses calendarComponent::$dtzid + * @uses calendarComponent::$unique_id + * @uses calendarComponent::$components + * @uses calendarComponent::copy() + * @return void + */ + function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) { + if( is_array( $config )) { + $config = array_change_key_case( $config, CASE_UPPER ); + if( isset( $config['NEWLINECHAR'] ) || isset( $config['NL'] )) { + $k = ( isset( $config['NEWLINECHAR'] )) ? 'NEWLINECHAR' : 'NL'; + if( FALSE === $this->setConfig( 'NL', $config[$k] )) + return FALSE; + unset( $config[$k] ); + } + foreach( $config as $cKey => $cValue ) { + if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate )) + return FALSE; + } + return TRUE; + } + else + $config = strtoupper( $config ); + $res = FALSE; + switch( $config ) { + case 'ALLOWEMPTY': + $this->allowEmpty = $value; + $subcfg = array( 'ALLOWEMPTY' => $value ); + $res = TRUE; + break; + case 'FORMAT': + $value = trim( strtolower( $value )); + $this->format = $value; + $this->_createFormat(); + $subcfg = array( 'FORMAT' => $value ); + $res = TRUE; + break; + case 'LANGUAGE': + // set language for calendar component as defined in [RFC 1766] + $value = trim( $value ); + if( empty( $this->language ) || !$softUpdate ) + $this->language = $value; + $subcfg = array( 'LANGUAGE' => $value ); + $res = TRUE; + break; + case 'NL': + case 'NEWLINECHAR': + $this->nl = $value; + $this->_createFormat(); + $subcfg = array( 'NL' => $value ); + $res = TRUE; + break; + case 'TZID': + $this->dtzid = $value; + $subcfg = array( 'TZID' => $value ); + $res = TRUE; + break; + case 'UNIQUE_ID': + $value = trim( $value ); + $this->unique_id = $value; + $subcfg = array( 'UNIQUE_ID' => $value ); + $res = TRUE; + break; + default: // any unvalid config key.. . + return TRUE; + } + if( !$res ) return FALSE; + if( isset( $subcfg ) && !empty( $this->components )) { + foreach( $subcfg as $cfgkey => $cfgvalue ) { + foreach( $this->components as $cix => $component ) { + $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate ); + if( !$res ) + break 2; + $this->components[$cix] = $component->copy(); // PHP4 compliant + } + } + } + return $res; + } +/*********************************************************************************/ +/** + * delete component property value + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $propName bool FALSE => X-property + * @param int $propix specific property in case of multiply occurences + * @uses calendarComponent::_notExistProp() + * @uses iCalUtilityFunctions::$mProps2 + * @uses calendarComponent::$propdelix + * @uses calendarComponent::$action + * @uses calendarComponent::deletePropertyM() + * @uses calendarComponent::$attendee + * @uses calendarComponent::$categories + * @uses calendarComponent::$class + * @uses calendarComponent::$comment + * @uses calendarComponent::$completed + * @uses calendarComponent::$contact + * @uses calendarComponent::$created + * @uses calendarComponent::$description + * @uses calendarComponent::$dtend + * @uses iCalUtilityFunctions::$miscComps + * @uses calendarComponent::$objName + * @uses calendarComponent::$dtstamp + * @uses calendarComponent::$dtstart + * @uses calendarComponent::$due + * @uses calendarComponent::$duration + * @uses calendarComponent::$exdate + * @uses calendarComponent::$exrule + * @uses calendarComponent::$freebusy + * @uses calendarComponent::$geo + * @uses calendarComponent::$lastmodified + * @uses calendarComponent::$location + * @uses calendarComponent::$organizer + * @uses calendarComponent::$percentcomplete + * @uses calendarComponent::$priority + * @uses calendarComponent::$rdate + * @uses calendarComponent::$recurrenceid + * @uses calendarComponent::$relatedto + * @uses calendarComponent::$repeat + * @uses calendarComponent::$requeststatus + * @uses calendarComponent::$resources + * @uses calendarComponent::$rrule + * @uses calendarComponent::$sequence + * @uses calendarComponent::$status + * @uses calendarComponent::$summary + * @uses calendarComponent::$transp + * @uses calendarComponent::$trigger + * @uses calendarComponent::$tzid + * @uses calendarComponent::$tzname + * @uses calendarComponent::$tzoffsetfrom + * @uses calendarComponent::$tzoffsetto + * @uses calendarComponent::$tzurl + * @uses calendarComponent::$uid + * @uses calendarComponent::$url + * @uses calendarComponent::$xprop + * @return bool, if successfull delete TRUE + */ + function deleteProperty( $propName=FALSE, $propix=FALSE ) { + if( $this->_notExistProp( $propName )) return FALSE; + $propName = strtoupper( $propName ); + if( in_array( $propName, iCalUtilityFunctions::$mProps2 )) { + if( !$propix ) + $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1; + $this->propdelix[$propName] = --$propix; + } + $return = FALSE; + switch( $propName ) { + case 'ACTION': + if( !empty( $this->action )) { + $this->action = ''; + $return = TRUE; + } + break; + case 'ATTACH': + return $this->deletePropertyM( $this->attach, $this->propdelix[$propName] ); + break; + case 'ATTENDEE': + return $this->deletePropertyM( $this->attendee, $this->propdelix[$propName] ); + break; + case 'CATEGORIES': + return $this->deletePropertyM( $this->categories, $this->propdelix[$propName] ); + break; + case 'CLASS': + if( !empty( $this->class )) { + $this->class = ''; + $return = TRUE; + } + break; + case 'COMMENT': + return $this->deletePropertyM( $this->comment, $this->propdelix[$propName] ); + break; + case 'COMPLETED': + if( !empty( $this->completed )) { + $this->completed = ''; + $return = TRUE; + } + break; + case 'CONTACT': + return $this->deletePropertyM( $this->contact, $this->propdelix[$propName] ); + break; + case 'CREATED': + if( !empty( $this->created )) { + $this->created = ''; + $return = TRUE; + } + break; + case 'DESCRIPTION': + return $this->deletePropertyM( $this->description, $this->propdelix[$propName] ); + break; + case 'DTEND': + if( !empty( $this->dtend )) { + $this->dtend = ''; + $return = TRUE; + } + break; + case 'DTSTAMP': + if( in_array( $this->objName, iCalUtilityFunctions::$miscComps )) + return FALSE; + if( !empty( $this->dtstamp )) { + $this->dtstamp = ''; + $return = TRUE; + } + break; + case 'DTSTART': + if( !empty( $this->dtstart )) { + $this->dtstart = ''; + $return = TRUE; + } + break; + case 'DUE': + if( !empty( $this->due )) { + $this->due = ''; + $return = TRUE; + } + break; + case 'DURATION': + if( !empty( $this->duration )) { + $this->duration = ''; + $return = TRUE; + } + break; + case 'EXDATE': + return $this->deletePropertyM( $this->exdate, $this->propdelix[$propName] ); + break; + case 'EXRULE': + return $this->deletePropertyM( $this->exrule, $this->propdelix[$propName] ); + break; + case 'FREEBUSY': + return $this->deletePropertyM( $this->freebusy, $this->propdelix[$propName] ); + break; + case 'GEO': + if( !empty( $this->geo )) { + $this->geo = ''; + $return = TRUE; + } + break; + case 'LAST-MODIFIED': + if( !empty( $this->lastmodified )) { + $this->lastmodified = ''; + $return = TRUE; + } + break; + case 'LOCATION': + if( !empty( $this->location )) { + $this->location = ''; + $return = TRUE; + } + break; + case 'ORGANIZER': + if( !empty( $this->organizer )) { + $this->organizer = ''; + $return = TRUE; + } + break; + case 'PERCENT-COMPLETE': + if( !empty( $this->percentcomplete )) { + $this->percentcomplete = ''; + $return = TRUE; + } + break; + case 'PRIORITY': + if( !empty( $this->priority )) { + $this->priority = ''; + $return = TRUE; + } + break; + case 'RDATE': + return $this->deletePropertyM( $this->rdate, $this->propdelix[$propName] ); + break; + case 'RECURRENCE-ID': + if( !empty( $this->recurrenceid )) { + $this->recurrenceid = ''; + $return = TRUE; + } + break; + case 'RELATED-TO': + return $this->deletePropertyM( $this->relatedto, $this->propdelix[$propName] ); + break; + case 'REPEAT': + if( !empty( $this->repeat )) { + $this->repeat = ''; + $return = TRUE; + } + break; + case 'REQUEST-STATUS': + return $this->deletePropertyM( $this->requeststatus, $this->propdelix[$propName] ); + break; + case 'RESOURCES': + return $this->deletePropertyM( $this->resources, $this->propdelix[$propName] ); + break; + case 'RRULE': + return $this->deletePropertyM( $this->rrule, $this->propdelix[$propName] ); + break; + case 'SEQUENCE': + if( !empty( $this->sequence )) { + $this->sequence = ''; + $return = TRUE; + } + break; + case 'STATUS': + if( !empty( $this->status )) { + $this->status = ''; + $return = TRUE; + } + break; + case 'SUMMARY': + if( !empty( $this->summary )) { + $this->summary = ''; + $return = TRUE; + } + break; + case 'TRANSP': + if( !empty( $this->transp )) { + $this->transp = ''; + $return = TRUE; + } + break; + case 'TRIGGER': + if( !empty( $this->trigger )) { + $this->trigger = ''; + $return = TRUE; + } + break; + case 'TZID': + if( !empty( $this->tzid )) { + $this->tzid = ''; + $return = TRUE; + } + break; + case 'TZNAME': + return $this->deletePropertyM( $this->tzname, $this->propdelix[$propName] ); + break; + case 'TZOFFSETFROM': + if( !empty( $this->tzoffsetfrom )) { + $this->tzoffsetfrom = ''; + $return = TRUE; + } + break; + case 'TZOFFSETTO': + if( !empty( $this->tzoffsetto )) { + $this->tzoffsetto = ''; + $return = TRUE; + } + break; + case 'TZURL': + if( !empty( $this->tzurl )) { + $this->tzurl = ''; + $return = TRUE; + } + break; + case 'UID': + if( in_array( $this->objName, iCalUtilityFunctions::$miscComps )) + return FALSE; + if( ! empty( $this->uid )) { + $this->uid = ''; + $return = TRUE; + } + break; + case 'URL': + if( !empty( $this->url )) { + $this->url = ''; + $return = TRUE; + } + break; + default: + $reduced = ''; + if( $propName != 'X-PROP' ) { + if( !isset( $this->xprop[$propName] )) return FALSE; + foreach( $this->xprop as $k => $a ) { + if(( $k != $propName ) && !empty( $a )) + $reduced[$k] = $a; + } + } + else { + if( count( $this->xprop ) <= $propix ) { unset( $this->propdelix[$propName] ); return FALSE; } + $xpropno = 0; + foreach( $this->xprop as $xpropkey => $xpropvalue ) { + if( $propix != $xpropno ) + $reduced[$xpropkey] = $xpropvalue; + $xpropno++; + } + } + $this->xprop = $reduced; + if( empty( $this->xprop )) { + unset( $this->propdelix[$propName] ); + return FALSE; + } + return TRUE; + } + return $return; + } +/*********************************************************************************/ +/** + * delete component property value, fixing components with multiple occurencies + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param array $multiprop component (multi-)property + * @param int $propix removal counter + * @return bool TRUE + */ + function deletePropertyM( & $multiprop, & $propix ) { + if( isset( $multiprop[$propix] )) + unset( $multiprop[$propix] ); + if( empty( $multiprop )) { + $multiprop = ''; + unset( $propix ); + return FALSE; + } + else + return TRUE; + } +/** + * get component property value/params + * + * if property has multiply values, consequtive function calls are needed + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.13 - 2015-03-29 + * @param string $propName + * @param int $propix specific property in case of multiply occurences + * @param bool $inclParam + * @param bool $specform + * @uses calendarComponent::getProperty() + * @uses iCalUtilityFunctions::_geo2str2() + * @uses iCalUtilityFunctions::$geoLatFmt + * @uses iCalUtilityFunctions::$geoLongFmt + * @uses calendarComponent::_notExistProp() + * @uses iCalUtilityFunctions::$mProps2 + * @uses calendarComponent::$propix + * @uses calendarComponent::$action + * @uses calendarComponent::$attendee + * @uses calendarComponent::$categories + * @uses calendarComponent::$class + * @uses calendarComponent::$comment + * @uses calendarComponent::$completed + * @uses calendarComponent::$contact + * @uses calendarComponent::$created + * @uses calendarComponent::$description + * @uses calendarComponent::$dtend + * @uses iCalUtilityFunctions::$miscComps + * @uses calendarComponent::$dtstamp + * @uses calendarComponent::_makeDtstamp() + * @uses calendarComponent::$dtstart + * @uses calendarComponent::$due + * @uses calendarComponent::$duration + * @uses iCalUtilityFunctions::_duration2date() + * @uses calendarComponent::$exdate + * @uses calendarComponent::$exrule + * @uses calendarComponent::$freebusy + * @uses calendarComponent::$geo + * @uses calendarComponent::$lastmodified + * @uses calendarComponent::$location + * @uses calendarComponent::$organizer + * @uses calendarComponent::$percentcomplete + * @uses calendarComponent::$priority + * @uses calendarComponent::$rdate + * @uses calendarComponent::$recurrenceid + * @uses calendarComponent::$relatedto + * @uses calendarComponent::$repeat + * @uses calendarComponent::$requeststatus + * @uses calendarComponent::$resources + * @uses calendarComponent::$rrule + * @uses calendarComponent::$sequence + * @uses calendarComponent::$status + * @uses calendarComponent::$summary + * @uses calendarComponent::$transp + * @uses calendarComponent::$trigger + * @uses calendarComponent::$tzid + * @uses calendarComponent::$tzname + * @uses calendarComponent::$tzoffsetfrom + * @uses calendarComponent::$tzoffsetto + * @uses calendarComponent::$tzurl + * @uses calendarComponent::$uid + * @uses calendarComponent::$objName + * @uses calendarComponent::_makeuid() + * @uses calendarComponent::$url + * @uses calendarComponent::$xprop + * @return mixed + */ + function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) { + if( 'GEOLOCATION' == strtoupper( $propName )) { + $content = ( FALSE === ( $loc = $this->getProperty( 'LOCATION' ))) ? '' : $loc.' '; + if( FALSE === ( $geo = $this->getProperty( 'GEO' ))) + return FALSE; + return $content. + iCalUtilityFunctions::_geo2str2( $geo['latitude'], iCalUtilityFunctions::$geoLatFmt ). + iCalUtilityFunctions::_geo2str2( $geo['longitude'], iCalUtilityFunctions::$geoLongFmt ).'/'; + } + if( $this->_notExistProp( $propName )) return FALSE; + $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; + if( in_array( $propName, iCalUtilityFunctions::$mProps2 )) { + if( empty( $propix )) + $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1; + $this->propix[$propName] = --$propix; + } + switch( $propName ) { + case 'ACTION': + if( isset( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value']; + break; + case 'ATTACH': + $ak = ( is_array( $this->attach )) ? array_keys( $this->attach ) : array(); + while( is_array( $this->attach ) && !isset( $this->attach[$propix] ) && ( 0 < count( $this->attach )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->attach[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value']; + break; + case 'ATTENDEE': + $ak = ( is_array( $this->attendee )) ? array_keys( $this->attendee ) : array(); + while( is_array( $this->attendee ) && !isset( $this->attendee[$propix] ) && ( 0 < count( $this->attendee )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->attendee[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value']; + break; + case 'CATEGORIES': + $ak = ( is_array( $this->categories )) ? array_keys( $this->categories ) : array(); + while( is_array( $this->categories ) && !isset( $this->categories[$propix] ) && ( 0 < count( $this->categories )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->categories[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value']; + break; + case 'CLASS': + if( isset( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value']; + break; + case 'COMMENT': + $ak = ( is_array( $this->comment )) ? array_keys( $this->comment ) : array(); + while( is_array( $this->comment ) && !isset( $this->comment[$propix] ) && ( 0 < count( $this->comment )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->comment[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value']; + break; + case 'COMPLETED': + if( isset( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value']; + break; + case 'CONTACT': + $ak = ( is_array( $this->contact )) ? array_keys( $this->contact ) : array(); + while( is_array( $this->contact ) && !isset( $this->contact[$propix] ) && ( 0 < count( $this->contact )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->contact[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value']; + break; + case 'CREATED': + if( isset( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value']; + break; + case 'DESCRIPTION': + $ak = ( is_array( $this->description )) ? array_keys( $this->description ) : array(); + while( is_array( $this->description ) && !isset( $this->description[$propix] ) && ( 0 < count( $this->description )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->description[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value']; + break; + case 'DTEND': + if( isset( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value']; + break; + case 'DTSTAMP': + if( in_array( $this->objName, iCalUtilityFunctions::$miscComps )) + return; + if( !isset( $this->dtstamp['value'] )) + $this->_makeDtstamp(); + return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value']; + break; + case 'DTSTART': + if( isset( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value']; + break; + case 'DUE': + if( isset( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value']; + break; + case 'DURATION': + if( ! isset( $this->duration['value'] )) return FALSE; + $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value']; + $params = ( $specform && $inclParam && isset( $this->dtstart['params']['TZID'] )) ? array_merge((array) $this->duration['params'], $this->dtstart['params'] ) : $this->duration['params']; + return ( $inclParam ) ? array( 'value' => $value, 'params' => $params ) : $value; + break; + case 'EXDATE': + $ak = ( is_array( $this->exdate )) ? array_keys( $this->exdate ) : array(); + while( is_array( $this->exdate ) && !isset( $this->exdate[$propix] ) && ( 0 < count( $this->exdate )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->exdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value']; + break; + case 'EXRULE': + $ak = ( is_array( $this->exrule )) ? array_keys( $this->exrule ) : array(); + while( is_array( $this->exrule ) && !isset( $this->exrule[$propix] ) && ( 0 < count( $this->exrule )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->exrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value']; + break; + case 'FREEBUSY': + $ak = ( is_array( $this->freebusy )) ? array_keys( $this->freebusy ) : array(); + while( is_array( $this->freebusy ) && !isset( $this->freebusy[$propix] ) && ( 0 < count( $this->freebusy )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->freebusy[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value']; + break; + case 'GEO': + if( isset( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value']; + break; + case 'LAST-MODIFIED': + if( isset( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value']; + break; + case 'LOCATION': + if( isset( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value']; + break; + case 'ORGANIZER': + if( isset( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value']; + break; + case 'PERCENT-COMPLETE': + if( isset( $this->percentcomplete['value'] )) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value']; + break; + case 'PRIORITY': + if( isset( $this->priority['value'] )) return ( $inclParam ) ? $this->priority : $this->priority['value']; + break; + case 'RDATE': + $ak = ( is_array( $this->rdate )) ? array_keys( $this->rdate ) : array(); + while( is_array( $this->rdate ) && !isset( $this->rdate[$propix] ) && ( 0 < count( $this->rdate )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->rdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value']; + break; + case 'RECURRENCE-ID': + if( isset( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value']; + break; + case 'RELATED-TO': + $ak = ( is_array( $this->relatedto )) ? array_keys( $this->relatedto ) : array(); + while( is_array( $this->relatedto ) && !isset( $this->relatedto[$propix] ) && ( 0 < count( $this->relatedto )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->relatedto[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value']; + break; + case 'REPEAT': + if( isset( $this->repeat['value'] )) return ( $inclParam ) ? $this->repeat : $this->repeat['value']; + break; + case 'REQUEST-STATUS': + $ak = ( is_array( $this->requeststatus )) ? array_keys( $this->requeststatus ) : array(); + while( is_array( $this->requeststatus ) && !isset( $this->requeststatus[$propix] ) && ( 0 < count( $this->requeststatus )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->requeststatus[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value']; + break; + case 'RESOURCES': + $ak = ( is_array( $this->resources )) ? array_keys( $this->resources ) : array(); + while( is_array( $this->resources ) && !isset( $this->resources[$propix] ) && ( 0 < count( $this->resources )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->resources[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value']; + break; + case 'RRULE': + $ak = ( is_array( $this->rrule )) ? array_keys( $this->rrule ) : array(); + while( is_array( $this->rrule ) && !isset( $this->rrule[$propix] ) && ( 0 < count( $this->rrule )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->rrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value']; + break; + case 'SEQUENCE': + if( isset( $this->sequence['value'] )) return ( $inclParam ) ? $this->sequence : $this->sequence['value']; + break; + case 'STATUS': + if( isset( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value']; + break; + case 'SUMMARY': + if( isset( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value']; + break; + case 'TRANSP': + if( isset( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value']; + break; + case 'TRIGGER': + if( isset( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value']; + break; + case 'TZID': + if( isset( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value']; + break; + case 'TZNAME': + $ak = ( is_array( $this->tzname )) ? array_keys( $this->tzname ) : array(); + while( is_array( $this->tzname ) && !isset( $this->tzname[$propix] ) && ( 0 < count( $this->tzname )) && ( $propix < end( $ak ))) + $propix++; + $this->propix[$propName] = $propix; + if( !isset( $this->tzname[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } + return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value']; + break; + case 'TZOFFSETFROM': + if( isset( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value']; + break; + case 'TZOFFSETTO': + if( isset( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value']; + break; + case 'TZURL': + if( isset( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value']; + break; + case 'UID': + if( in_array( $this->objName, iCalUtilityFunctions::$miscComps )) + return FALSE; + if( empty( $this->uid )) + $this->_makeuid(); + return ( $inclParam ) ? $this->uid : $this->uid['value']; + break; + case 'URL': + if( isset( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value']; + break; + default: + if( $propName != 'X-PROP' ) { + if( !isset( $this->xprop[$propName] )) return FALSE; + return ( $inclParam ) ? array( $propName, $this->xprop[$propName] ) + : array( $propName, $this->xprop[$propName]['value'] ); + } + else { + if( empty( $this->xprop )) return FALSE; + $xpropno = 0; + foreach( $this->xprop as $xpropkey => $xpropvalue ) { + if( $propix == $xpropno ) + return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] ) + : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); + else + $xpropno++; + } + return FALSE; // not found ?? + } + } + return FALSE; + } +/** + * returns calendar property unique values for 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO' or 'RESOURCES' and for each, number of occurrence + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param string $propName + * @param array $output incremented result array + * @uses iCalUtilityFunctions::$mProps1 + * @uses calendarComponent::getProperty() + * return void + */ + function _getProperties( $propName, & $output ) { + if( empty( $output )) + $output = array(); + if( !in_array( strtoupper( $propName ), iCalUtilityFunctions::$mProps1 )) + return $output; + while( FALSE !== ( $content = $this->getProperty( $propName ))) { + if( empty( $content )) + continue; + if( is_array( $content )) { + foreach( $content as $part ) { + if( FALSE !== strpos( $part, ',' )) { + $part = explode( ',', $part ); + foreach( $part as $thePart ) { + $thePart = trim( $thePart ); + if( !empty( $thePart )) { + if( !isset( $output[$thePart] )) + $output[$thePart] = 1; + else + $output[$thePart] += 1; + } + } + } + else { + $part = trim( $part ); + if( !isset( $output[$part] )) + $output[$part] = 1; + else + $output[$part] += 1; + } + } + } // end if( is_array( $content )) + elseif( FALSE !== strpos( $content, ',' )) { + $content = explode( ',', $content ); + foreach( $content as $thePart ) { + $thePart = trim( $thePart ); + if( !empty( $thePart )) { + if( !isset( $output[$thePart] )) + $output[$thePart] = 1; + else + $output[$thePart] += 1; + } + } + } // end elseif( FALSE !== strpos( $content, ',' )) + else { + $content = trim( $content ); + if( !empty( $content )) { + if( !isset( $output[$content] )) + $output[$content] = 1; + else + $output[$content] += 1; + } + } + } + ksort( $output ); + } +/** + * general component property setting + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-05 + * @param mixed $args variable number of function arguments, + * first argument is ALWAYS component name, + * second ALWAYS component value! + * @uses calendarComponent::getProperty() + * @uses calendarComponent::_notExistProp() + * @uses calendarComponent::getConfig() + * @uses calendarComponent::setAction() + * @uses calendarComponent::setAttendee() + * @uses calendarComponent::setCategories() + * @uses calendarComponent::setClass() + * @uses calendarComponent::setComment() + * @uses calendarComponent::setCompleted() + * @uses calendarComponent::setContact() + * @uses calendarComponent::setCreated() + * @uses calendarComponent::setDescription() + * @uses calendarComponent::setDtend() + * @uses calendarComponent::setDtstamp() + * @uses calendarComponent::setDtstart() + * @uses calendarComponent::setDue() + * @uses calendarComponent::setDuration() + * @uses calendarComponent::setExdate() + * @uses calendarComponent::setExrule() + * @uses calendarComponent::setFreebusy() + * @uses calendarComponent::setGeo() + * @uses calendarComponent::setLastmodified() + * @uses calendarComponent::setLocation() + * @uses calendarComponent::setOrganizer() + * @uses calendarComponent::setPercentcomplete() + * @uses calendarComponent::setPriority() + * @uses calendarComponent::setRdate() + * @uses calendarComponent::setRecurrenceid() + * @uses calendarComponent::setRelatedto() + * @uses calendarComponent::setRepeat() + * @uses calendarComponent::setRequeststatus() + * @uses calendarComponent::setResources() + * @uses calendarComponent::setRrule() + * @uses calendarComponent::setSequence() + * @uses calendarComponent::setStatus() + * @uses calendarComponent::setSummary() + * @uses calendarComponent::setTransp() + * @uses calendarComponent::setTrigger() + * @uses calendarComponent::setTzid() + * @uses calendarComponent::setTzname() + * @uses calendarComponent::setTzoffsetfrom() + * @uses calendarComponent::setTzoffsetto() + * @uses calendarComponent::setTzurl() + * @uses calendarComponent::setUid() + * @uses calendarComponent::$objName + * @uses calendarComponent::setUrl() + * @uses calendarComponent::setXprop() + * @return void + */ + function setProperty() { + $numargs = func_num_args(); + if( 1 > $numargs ) return FALSE; + $arglist = func_get_args(); + if( $this->_notExistProp( $arglist[0] )) return FALSE; + if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] ))) + return FALSE; + $arglist[0] = strtoupper( $arglist[0] ); + for( $argix=$numargs; $argix < 12; $argix++ ) { + if( !isset( $arglist[$argix] )) + $arglist[$argix] = null; + } + switch( $arglist[0] ) { + case 'ACTION': + return $this->setAction( $arglist[1], $arglist[2] ); + case 'ATTACH': + return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] ); + case 'ATTENDEE': + return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] ); + case 'CATEGORIES': + return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] ); + case 'CLASS': + return $this->setClass( $arglist[1], $arglist[2] ); + case 'COMMENT': + return $this->setComment( $arglist[1], $arglist[2], $arglist[3] ); + case 'COMPLETED': + return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); + case 'CONTACT': + return $this->setContact( $arglist[1], $arglist[2], $arglist[3] ); + case 'CREATED': + return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); + case 'DESCRIPTION': + return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] ); + case 'DTEND': + return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); + case 'DTSTAMP': + return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); + case 'DTSTART': + return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); + case 'DUE': + return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); + case 'DURATION': + return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] ); + case 'EXDATE': + return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] ); + case 'EXRULE': + return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] ); + case 'FREEBUSY': + return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] ); + case 'GEO': + return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] ); + case 'LAST-MODIFIED': + return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); + case 'LOCATION': + return $this->setLocation( $arglist[1], $arglist[2] ); + case 'ORGANIZER': + return $this->setOrganizer( $arglist[1], $arglist[2] ); + case 'PERCENT-COMPLETE': + return $this->setPercentComplete( $arglist[1], $arglist[2] ); + case 'PRIORITY': + return $this->setPriority( $arglist[1], $arglist[2] ); + case 'RDATE': + return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] ); + case 'RECURRENCE-ID': + return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); + case 'RELATED-TO': + return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] ); + case 'REPEAT': + return $this->setRepeat( $arglist[1], $arglist[2] ); + case 'REQUEST-STATUS': + return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] ); + case 'RESOURCES': + return $this->setResources( $arglist[1], $arglist[2], $arglist[3] ); + case 'RRULE': + return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] ); + case 'SEQUENCE': + return $this->setSequence( $arglist[1], $arglist[2] ); + case 'STATUS': + return $this->setStatus( $arglist[1], $arglist[2] ); + case 'SUMMARY': + return $this->setSummary( $arglist[1], $arglist[2] ); + case 'TRANSP': + return $this->setTransp( $arglist[1], $arglist[2] ); + case 'TRIGGER': + return $this->setTrigger( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8], $arglist[9], $arglist[10], $arglist[11] ); + case 'TZID': + return $this->setTzid( $arglist[1], $arglist[2] ); + case 'TZNAME': + return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] ); + case 'TZOFFSETFROM': + return $this->setTzoffsetfrom( $arglist[1], $arglist[2] ); + case 'TZOFFSETTO': + return $this->setTzoffsetto( $arglist[1], $arglist[2] ); + case 'TZURL': + return $this->setTzurl( $arglist[1], $arglist[2] ); + case 'UID': + return $this->setUid( $arglist[1], $arglist[2] ); + case 'URL': + return $this->setUrl( $arglist[1], $arglist[2] ); + default: + return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] ); + } + return FALSE; + } +/*********************************************************************************/ +/** + * parse component unparsed data into properties + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.20.3 - 2015-03-05 + * @param mixed $unparsedtext strict rfc2445 formatted, single property string or array of strings + * @uses calendarComponent::getConfig() + * @uses iCalUtilityFunctions::convEolChar() + * @uses calendarComponent::$unparsed + * @uses calendarComponent::$components + * @uses calendarComponent::copy() + * @uses iCalUtilityFunctions::_splitContent() + * @uses calendarComponent::setProperty() + * @uses iCalUtilityFunctions::_strunrep() + * @uses calendarComponent::parse() + * @return bool FALSE if error occurs during parsing + */ + function parse( $unparsedtext=null ) { + $nl = $this->getConfig( 'nl' ); + if( !empty( $unparsedtext )) { + if( is_array( $unparsedtext )) + $unparsedtext = implode( '\n'.$nl, $unparsedtext ); + $unparsedtext = iCalUtilityFunctions::convEolChar( $unparsedtext, $nl ); + } + elseif( !isset( $this->unparsed )) + $unparsedtext = array(); + else + $unparsedtext = $this->unparsed; + /* skip leading (empty/invalid) lines */ + foreach( $unparsedtext as $lix => $line ) { + if( FALSE !== ( $pos = stripos( $line, 'BEGIN:' ))) { + $unparsedtext[$lix] = substr( $unparsedtext[$lix], $pos ); + break; + } + $tst = trim( $line ); + if(( '\n' == $tst ) || empty( $tst )) + unset( $unparsedtext[$lix] ); + } + $this->unparsed = array(); + $comp = & $this; + $config = $this->getConfig(); + $compsync = $subsync = 0; + foreach ( $unparsedtext as $lix => $line ) { + if( 'END:VALARM' == strtoupper( substr( $line, 0, 10 ))) { + if( 1 != $subsync ) return FALSE; + $this->components[] = $comp->copy(); + $subsync--; + } + elseif( 'END:DAYLIGHT' == strtoupper( substr( $line, 0, 12 ))) { + if( 1 != $subsync ) return FALSE; + $this->components[] = $comp->copy(); + $subsync--; + } + elseif( 'END:STANDARD' == strtoupper( substr( $line, 0, 12 ))) { + if( 1 != $subsync ) return FALSE; + array_unshift( $this->components, $comp->copy()); + $subsync--; + } + elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) { // end: + if( 1 != $compsync ) return FALSE; + if( 0 < $subsync ) + $this->components[] = $comp->copy(); + $compsync--; + break; /* skip trailing empty lines */ + } + elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) { + $comp = new valarm( $config); + $subsync++; + } + elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) { + $comp = new vtimezone( 'standard', $config ); + $subsync++; + } + elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) { + $comp = new vtimezone( 'daylight', $config ); + $subsync++; + } + elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) // begin: + $compsync++; + else + $comp->unparsed[] = $line; + } + if( 0 < $subsync ) + $this->components[] = $comp->copy(); + unset( $config ); + /* concatenate property values spread over several lines */ + $lastix = -1; + $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed' + , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart' + , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo' + , 'last-modified', 'location', 'organizer', 'percent-complete' + , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat' + , 'request-status', 'resources', 'rrule', 'sequence', 'status' + , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom' + , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' ); + $proprows = array(); + for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines + $line = rtrim( $this->unparsed[$i], $nl ); + while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} )) + $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl ); + $proprows[] = $line; + } + /* parse each property 'line' */ + foreach( $proprows as $line ) { + if( '\n' == substr( $line, -2 )) + $line = substr( $line, 0, -2 ); + /* get propname */ + $propname = null; + $cix = 0; + while( isset( $line[$cix] )) { + if( in_array( $line[$cix], array( ':', ';' ))) + break; + else + $propname .= $line[$cix]; + $cix++; + } + if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) { + $propname2 = $propname; + $propname = 'X-'; + } + if( !in_array( strtolower( $propname ), $propnames )) // skip non standard property names + continue; + /* rest of the line is opt.params and value */ + $line = substr( $line, $cix ); + /* separate attributes from value */ + iCalUtilityFunctions::_splitContent( $line, $propAttr ); + /* call setProperty( $propname.. . */ + switch( strtoupper( $propname )) { + case 'ATTENDEE': + foreach( $propAttr as $pix => $attr ) { + if( !in_array( strtoupper( $pix ), array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ))) + continue; + $attr2 = explode( ',', $attr ); + if( 1 < count( $attr2 )) + $propAttr[$pix] = $attr2; + } + $this->setProperty( $propname, $line, $propAttr ); + break; + case 'CATEGORIES': + case 'RESOURCES': + if( FALSE !== strpos( $line, ',' )) { + $content = array( 0 => '' ); + $cix = $lix = 0; + while( FALSE !== substr( $line, $lix, 1 )) { + if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { + $cix++; + $content[$cix] = ''; + } + else + $content[$cix] .= $line[$lix]; + $lix++; + } + if( 1 < count( $content )) { + $content = array_values( $content ); + foreach( $content as $cix => $contentPart ) + $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart ); + $this->setProperty( $propname, $content, $propAttr ); + break; + } + else + $line = reset( $content ); + } + case 'COMMENT': + case 'CONTACT': + case 'DESCRIPTION': + case 'LOCATION': + case 'SUMMARY': + if( empty( $line )) + $propAttr = null; + $this->setProperty( $propname, iCalUtilityFunctions::_strunrep( $line ), $propAttr ); + break; + case 'REQUEST-STATUS': + $values = explode( ';', $line, 3 ); + $values[1] = ( !isset( $values[1] )) ? null : iCalUtilityFunctions::_strunrep( $values[1] ); + $values[2] = ( !isset( $values[2] )) ? null : iCalUtilityFunctions::_strunrep( $values[2] ); + $this->setProperty( $propname + , $values[0] // statcode + , $values[1] // statdesc + , $values[2] // extdata + , $propAttr ); + break; + case 'FREEBUSY': + $fbtype = ( isset( $propAttr['FBTYPE'] )) ? $propAttr['FBTYPE'] : ''; // force setting default, if missing + unset( $propAttr['FBTYPE'] ); + $values = explode( ',', $line ); + foreach( $values as $vix => $value ) { + $value2 = explode( '/', $value ); + if( 1 < count( $value2 )) + $values[$vix] = $value2; + } + $this->setProperty( $propname, $fbtype, $values, $propAttr ); + break; + case 'GEO': + $value = explode( ';', $line, 2 ); + if( 2 > count( $value )) + $value[1] = null; + $this->setProperty( $propname, $value[0], $value[1], $propAttr ); + break; + case 'EXDATE': + $values = ( !empty( $line )) ? explode( ',', $line ) : null; + $this->setProperty( $propname, $values, $propAttr ); + break; + case 'RDATE': + if( empty( $line )) { + $this->setProperty( $propname, $line, $propAttr ); + break; + } + $values = explode( ',', $line ); + foreach( $values as $vix => $value ) { + $value2 = explode( '/', $value ); + if( 1 < count( $value2 )) + $values[$vix] = $value2; + } + $this->setProperty( $propname, $values, $propAttr ); + break; + case 'EXRULE': + case 'RRULE': + $values = explode( ';', $line ); + $recur = array(); + foreach( $values as $value2 ) { + if( empty( $value2 )) + continue; // ;-char in ending position ??? + $value3 = explode( '=', $value2, 2 ); + $rulelabel = strtoupper( $value3[0] ); + switch( $rulelabel ) { + case 'BYDAY': { + $value4 = explode( ',', $value3[1] ); + if( 1 < count( $value4 )) { + foreach( $value4 as $v5ix => $value5 ) { + $value6 = array(); + $dayno = $dayname = null; + $value5 = trim( (string) $value5 ); + if(( ctype_alpha( substr( $value5, -1 ))) && + ( ctype_alpha( substr( $value5, -2, 1 )))) { + $dayname = substr( $value5, -2, 2 ); + if( 2 < strlen( $value5 )) + $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 )); + } + if( $dayno ) + $value6[] = $dayno; + if( $dayname ) + $value6['DAY'] = $dayname; + $value4[$v5ix] = $value6; + } + } + else { + $value4 = array(); + $dayno = $dayname = null; + $value5 = trim( (string) $value3[1] ); + if(( ctype_alpha( substr( $value5, -1 ))) && + ( ctype_alpha( substr( $value5, -2, 1 )))) { + $dayname = substr( $value5, -2, 2 ); + if( 2 < strlen( $value5 )) + $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 )); + } + if( $dayno ) + $value4[] = $dayno; + if( $dayname ) + $value4['DAY'] = $dayname; + } + $recur[$rulelabel] = $value4; + break; + } + default: { + $value4 = explode( ',', $value3[1] ); + if( 1 < count( $value4 )) + $value3[1] = $value4; + $recur[$rulelabel] = $value3[1]; + break; + } + } // end - switch $rulelabel + } // end - foreach( $values.. . + $this->setProperty( $propname, $recur, $propAttr ); + break; + case 'X-': + $propname = ( isset( $propname2 )) ? $propname2 : $propname; + unset( $propname2 ); + case 'ACTION': + case 'CLASSIFICATION': + case 'STATUS': + case 'TRANSP': + case 'UID': + case 'TZID': + case 'RELATED-TO': + case 'TZNAME': + $line = iCalUtilityFunctions::_strunrep( $line ); + default: + $this->setProperty( $propname, $line, $propAttr ); + break; + } // end switch( $propname.. . + } // end - foreach( $proprows.. . + unset( $unparsedtext, $this->unparsed, $proprows ); + if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) { + $ckeys = array_keys( $this->components ); + foreach( $ckeys as $ckey ) { + if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) { + $this->components[$ckey]->parse(); + } + } + } + return TRUE; + } +/*********************************************************************************/ +/*********************************************************************************/ +/** + * return a copy of this component + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.15.4 - 2012-10-18 + * @return object + */ + function copy() { + return unserialize( serialize( $this )); + } +/*********************************************************************************/ +/*********************************************************************************/ +/** + * delete calendar subcomponent from component container + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $arg1 ordno / component type / component uid + * @param mixed $arg2 ordno if arg1 = component type + * @uses calendarComponent::$components + * @uses calendarComponent::$objName + * @uses calendarComponent::getProperty() + * @return void + */ + function deleteComponent( $arg1, $arg2=FALSE ) { + if( !isset( $this->components )) return FALSE; + $argType = $index = null; + if ( ctype_digit( (string) $arg1 )) { + $argType = 'INDEX'; + $index = (int) $arg1 - 1; + } + elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { + $argType = strtolower( $arg1 ); + $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0; + } + $cix2dC = 0; + foreach ( $this->components as $cix => $component) { + if( empty( $component )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) { + unset( $this->components[$cix] ); + return TRUE; + } + elseif( $argType == $component->objName ) { + if( $index == $cix2dC ) { + unset( $this->components[$cix] ); + return TRUE; + } + $cix2dC++; + } + elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { + unset( $this->components[$cix] ); + return TRUE; + } + } + return FALSE; + } +/** + * get calendar component subcomponent from component container + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $arg1 ordno/component type/ component uid + * @param mixed $arg2 ordno if arg1 = component type + * @uses calendarComponent::$components + * @uses calendarComponent::$compix + * @uses calendarComponent::$objName + * @uses calendarComponent::copy() + * @uses calendarComponent::getProperty() + * @return object + */ + function getComponent ( $arg1=FALSE, $arg2=FALSE ) { + if( !isset( $this->components )) return FALSE; + $index = $argType = null; + if ( !$arg1 ) { + $argType = 'INDEX'; + $index = $this->compix['INDEX'] = + ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1; + } + elseif ( ctype_digit( (string) $arg1 )) { + $argType = 'INDEX'; + $index = (int) $arg1; + unset( $this->compix ); + } + elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { + unset( $this->compix['INDEX'] ); + $argType = strtolower( $arg1 ); + if( !$arg2 ) + $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1; + else + $index = (int) $arg2; + } + $index -= 1; + $ckeys = array_keys( $this->components ); + if( !empty( $index) && ( $index > end( $ckeys ))) + return FALSE; + $cix2gC = 0; + foreach( $this->components as $cix => $component ) { + if( empty( $component )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) + return $component->copy(); + elseif( $argType == $component->objName ) { + if( $index == $cix2gC ) + return $component->copy(); + $cix2gC++; + } + elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' ))) + return $component->copy(); + } + /* not found.. . */ + unset( $this->compix ); + return false; + } +/** + * add calendar component as subcomponent to container for subcomponents + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 1.x.x - 2007-04-24 + * @param object $component calendar component + * @uses calendarComponent::setComponent( $component ) + * @return void + */ + function addSubComponent ( $component ) { + $this->setComponent( $component ); + } +/** + * create new calendar component subcomponent, already included within component + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.33 - 2011-01-03 + * @param string $compType subcomponent type + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$components + * @uses calendarComponent::calendarComponent() + * @return object (reference) + */ + function & newComponent( $compType ) { + $config = $this->getConfig(); + $keys = array_keys( $this->components ); + $ix = end( $keys) + 1; + switch( strtoupper( $compType )) { + case 'ALARM': + case 'VALARM': + $this->components[$ix] = new valarm( $config ); + break; + case 'STANDARD': + array_unshift( $this->components, new vtimezone( 'STANDARD', $config )); + $ix = 0; + break; + case 'DAYLIGHT': + $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config ); + break; + default: + return FALSE; + } + return $this->components[$ix]; + } +/** + * add calendar component as subcomponent to container for subcomponents + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param object $component calendar component + * @param mixed $arg1 ordno/component type/ component uid + * @param mixed $arg2 ordno if arg1 = component type + * @uses calendarComponent::$components + * @uses calendarComponent::setConfig() + * @uses calendarComponent::getConfig() + * @uses calendarComponent::$objName + * @uses iCalUtilityFunctions::$miscComps + * @uses calendarComponent::getProperty() + * @uses calendarComponent::copy() + * @uses iCalUtilityFunctions::$mComps + * @return bool + */ + function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) { + if( !isset( $this->components )) return FALSE; + $component->setConfig( $this->getConfig(), FALSE, TRUE ); + if( ! in_array( $component->objName, iCalUtilityFunctions::$miscComps )) { + /* make sure dtstamp and uid is set */ + $dummy = $component->getProperty( 'dtstamp' ); + $dummy = $component->getProperty( 'uid' ); + } + if( !$arg1 ) { // plain insert, last in chain + $this->components[] = $component->copy(); + return TRUE; + } + $argType = $index = null; + if ( ctype_digit( (string) $arg1 )) { // index insert/replace + $argType = 'INDEX'; + $index = (int) $arg1 - 1; + } + elseif( in_array( strtolower( $arg1 ), iCalUtilityFunctions::$mComps )) { + $argType = strtolower( $arg1 ); + $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0; + } + // else if arg1 is set, arg1 must be an UID + $cix2sC = 0; + foreach ( $this->components as $cix => $component2 ) { + if( empty( $component2 )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace + $this->components[$cix] = $component->copy(); + return TRUE; + } + elseif( $argType == $component2->objName ) { // component Type index insert/replace + if( $index == $cix2sC ) { + $this->components[$cix] = $component->copy(); + return TRUE; + } + $cix2sC++; + } + elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace + $this->components[$cix] = $component->copy(); + return TRUE; + } + } + /* arg1=index and not found.. . insert at index .. .*/ + if( 'INDEX' == $argType ) { + $this->components[$index] = $component->copy(); + ksort( $this->components, SORT_NUMERIC ); + } + else /* not found.. . insert last in chain anyway .. .*/ + $this->components[] = $component->copy(); + return TRUE; + } +/** + * creates formatted output for subcomponents + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-10 + * @param array $xcaldecl + * @uses calendarComponent::$objName + * @uses calendarComponent::$components + * @uses calendarComponent::getProperty() + * @uses iCalUtilityFunctions::$fmt + * @uses calendarComponent::copy() + * @uses calendarComponent::setConfig() + * @uses calendarComponent::getConfig() + * @uses calendarComponent::createComponent() + * @uses calendarComponent::$xcaldecl() + * @return string + */ + function createSubComponent() { + $output = null; + if( 'vtimezone' == $this->objName ) { // sort subComponents, first standard, then daylight, in dtstart order + $stdarr = $dlarr = array(); + foreach( $this->components as $component ) { + if( empty( $component )) + continue; + $dt = $component->getProperty( 'dtstart' ); + $key = (int) sprintf( iCalUtilityFunctions::$fmt['dateKey'], (int) $dt['year'], (int) $dt['month'], (int) $dt['day'], (int) $dt['hour'], (int) $dt['min'], (int) $dt['sec'] ); + if( 'standard' == $component->objName ) { + while( isset( $stdarr[$key] )) + $key += 1; + $stdarr[$key] = $component->copy(); + } + elseif( 'daylight' == $component->objName ) { + while( isset( $dlarr[$key] )) + $key += 1; + $dlarr[$key] = $component->copy(); + } + } // end foreach( $this->components as $component ) + $this->components = array(); + ksort( $stdarr, SORT_NUMERIC ); + foreach( $stdarr as $std ) + $this->components[] = $std->copy(); + unset( $stdarr ); + ksort( $dlarr, SORT_NUMERIC ); + foreach( $dlarr as $dl ) + $this->components[] = $dl->copy(); + unset( $dlarr ); + } // end if( 'vtimezone' == $this->objName ) + foreach( $this->components as $component ) { + $component->setConfig( $this->getConfig(), FALSE, TRUE ); + $output .= $component->createComponent( $this->xcaldecl ); + } + return $output; + } +} diff --git a/php/ical/lib/iCal.XML.inc.php b/php/ical/lib/iCal.XML.inc.php new file mode 100644 index 0000000..2d638bb --- /dev/null +++ b/php/ical/lib/iCal.XML.inc.php @@ -0,0 +1,897 @@ + + * @since 2.18.1 - 2013-08-18 + * @param object $calendar iCalcreator vcalendar instance reference + * @uses ICALCREATOR_VERSION + * @uses vcalendar::getProperty() + * @uses _addXMLchild() + * @uses vcalendar::getConfig() + * @uses vcalendar::getComponent() + * @uses calendarComponent::$objName + * @uses calendarComponent::getProperty() + * @return string + */ +function iCal2XML( $calendar ) { + /** fix an SimpleXMLElement instance and create root element */ + $xmlstr = ''; + $xmlstr .= ''; + $xmlstr .= ''; + $xml = new SimpleXMLElement( $xmlstr ); + $vcalendar = $xml->addChild( 'vcalendar' ); + /** fix calendar properties */ + $properties = $vcalendar->addChild( 'properties' ); + $calProps = array( 'version', 'prodid', 'calscale', 'method' ); + foreach( $calProps as $calProp ) { + if( FALSE !== ( $content = $calendar->getProperty( $calProp ))) + _addXMLchild( $properties, $calProp, 'text', $content ); + } + while( FALSE !== ( $content = $calendar->getProperty( FALSE, FALSE, TRUE ))) + _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); + $langCal = $calendar->getConfig( 'language' ); + /** prepare to fix components with properties */ + $components = $vcalendar->addChild( 'components' ); + /** fix component properties */ + while( FALSE !== ( $component = $calendar->getComponent())) { + $compName = $component->objName; + $child = $components->addChild( $compName ); + $properties = $child->addChild( 'properties' ); + $langComp = $component->getConfig( 'language' ); + $props = $component->getConfig( 'setPropertyNames' ); + foreach( $props as $prop ) { + switch( strtolower( $prop )) { + case 'attach': // may occur multiple times, below + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; + unset( $content['params']['VALUE'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'attendee': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); + } + break; + case 'exdate': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time'; + unset( $content['params']['VALUE'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'freebusy': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if( is_array( $content ) && isset( $content['value']['fbtype'] )) { + $content['params']['FBTYPE'] = $content['value']['fbtype']; + unset( $content['value']['fbtype'] ); + } + _addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] ); + } + break; + case 'request-status': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if( !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] ); + } + break; + case 'rdate': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + $type = 'date-time'; + if( isset( $content['params']['VALUE'] )) { + if( 'DATE' == $content['params']['VALUE'] ) + $type = 'date'; + elseif( 'PERIOD' == $content['params']['VALUE'] ) + $type = 'period'; + } + unset( $content['params']['VALUE'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'categories': + case 'comment': + case 'contact': + case 'description': + case 'related-to': + case 'resources': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); + } + break; + case 'x-prop': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); + break; + case 'created': // single occurence below, if set + case 'completed': + case 'dtstamp': + case 'last-modified': + $utcDate = TRUE; + case 'dtstart': + case 'dtend': + case 'due': + case 'recurrence-id': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time'; + unset( $content['params']['VALUE'] ); + if(( isset( $content['params']['TZID'] ) && empty( $content['params']['TZID'] )) || @is_null( $content['params']['TZID'] )) + unset( $content['params']['TZID'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + unset( $utcDate ); + break; + case 'duration': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); + break; + case 'exrule': + case 'rrule': + while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); + break; + case 'class': + case 'location': + case 'status': + case 'summary': + case 'transp': + case 'tzid': + case 'uid': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if((( 'location' == $prop ) || ( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); + } + break; + case 'geo': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] ); + break; + case 'organizer': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { + if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); + } + break; + case 'percent-complete': + case 'priority': + case 'sequence': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); + break; + case 'tzurl': + case 'url': + if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] ); + break; + } // end switch( $prop ) + } // end foreach( $props as $prop ) + /** fix subComponent properties, if any */ + while( FALSE !== ( $subcomp = $component->getComponent())) { + $subCompName = $subcomp->objName; + $child2 = $child->addChild( $subCompName ); + $properties = $child2->addChild( 'properties' ); + $langComp = $subcomp->getConfig( 'language' ); + $subCompProps = $subcomp->getConfig( 'setPropertyNames' ); + foreach( $subCompProps as $prop ) { + switch( strtolower( $prop )) { + case 'attach': // may occur multiple times, below + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; + unset( $content['params']['VALUE'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'attendee': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); + } + break; + case 'comment': + case 'tzname': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + if( !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); + } + break; + case 'rdate': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + $type = 'date-time'; + if( isset( $content['params']['VALUE'] )) { + if( 'DATE' == $content['params']['VALUE'] ) + $type = 'date'; + elseif( 'PERIOD' == $content['params']['VALUE'] ) + $type = 'period'; + } + unset( $content['params']['VALUE'] ); + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'x-prop': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); + break; + case 'action': // single occurence below, if set + case 'description': + case 'summary': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { + if( $langComp ) + $content['params']['LANGUAGE'] = $langComp; + elseif( $langCal ) + $content['params']['LANGUAGE'] = $langCal; + } + _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); + } + break; + case 'dtstart': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + unset( $content['value']['tz'], $content['params']['VALUE'] ); // always local time + _addXMLchild( $properties, $prop, 'date-time', $content['value'], $content['params'] ); + } + break; + case 'duration': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); + break; + case 'repeat': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); + break; + case 'trigger': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { + if( isset( $content['value']['year'] ) && + isset( $content['value']['month'] ) && + isset( $content['value']['day'] )) + $type = 'date-time'; + else { + $type = 'duration'; + if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] )) + $content['params']['RELATED'] = 'END'; + } + _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); + } + break; + case 'tzoffsetto': + case 'tzoffsetfrom': + if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] ); + break; + case 'rrule': + while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) + _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); + break; + } // switch( $prop ) + } // end foreach( $subCompProps as $prop ) + } // end while( FALSE !== ( $subcomp = $component->getComponent())) + } // end while( FALSE !== ( $component = $calendar->getComponent())) + return $xml->asXML(); +} +/** + * Add children to a SimpleXMLelement + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.18.10 - 2013-09-04 + * @param object $parent reference to a SimpleXMLelement node + * @param string $name new element node name + * @param string $type content type, subelement(-s) name + * @param string $content new subelement content + * @param array $params new element 'attributes' + * @uses iCalUtilityFunctions::_duration2str() + * @uses iCalUtilityFunctions::_geo2str2() + * @uses iCalUtilityFunctions::$geoLatFmt + * @uses iCalUtilityFunctions::$geoLongFmt + * @return void + */ +function _addXMLchild( & $parent, $name, $type, $content, $params=array()) { + static $fmtYmd = '%04d-%02d-%02d'; + static $fmtYmdHis = '%04d-%02d-%02dT%02d:%02d:%02d'; + /** create new child node */ + $name = strtolower( $name ); + $child = $parent->addChild( $name ); + if( !empty( $params )) { + $parameters = $child->addChild( 'parameters' ); + foreach( $params as $param => $parVal ) { + if( 'VALUE' == $param ) + continue; + $param = strtolower( $param ); + if( 'x-' == substr( $param, 0, 2 )) { + $p1 = $parameters->addChild( $param ); + $p2 = $p1->addChild( 'unknown', htmlspecialchars( $parVal )); + } + else { + $p1 = $parameters->addChild( $param ); + switch( $param ) { + case 'altrep': + case 'dir': $ptype = 'uri'; break; + case 'delegated-from': + case 'delegated-to': + case 'member': + case 'sent-by': $ptype = 'cal-address'; break; + case 'rsvp': $ptype = 'boolean'; break ; + default: $ptype = 'text'; break; + } + if( is_array( $parVal )) { + foreach( $parVal as $pV ) + $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV )); + } + else + $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal )); + } + } + } // end if( !empty( $params )) + if(( empty( $content ) && ( '0' != $content )) || ( !is_array( $content) && ( '-' != substr( $content, 0, 1 ) && ( 0 > $content )))) + return; + /** store content */ + switch( $type ) { + case 'binary': + $v = $child->addChild( $type, $content ); + break; + case 'boolean': + break; + case 'cal-address': + $v = $child->addChild( $type, $content ); + break; + case 'date': + if( array_key_exists( 'year', $content )) + $content = array( $content ); + foreach( $content as $date ) { + $str = sprintf( $fmtYmd, (int) $date['year'], (int) $date['month'], (int) $date['day'] ); + $v = $child->addChild( $type, $str ); + } + break; + case 'date-time': + if( array_key_exists( 'year', $content )) + $content = array( $content ); + foreach( $content as $dt ) { + if( !isset( $dt['hour'] )) $dt['hour'] = 0; + if( !isset( $dt['min'] )) $dt['min'] = 0; + if( !isset( $dt['sec'] )) $dt['sec'] = 0; + $str = sprintf( $fmtYmdHis, (int) $dt['year'], (int) $dt['month'], (int) $dt['day'], (int) $dt['hour'], (int) $dt['min'], (int) $dt['sec'] ); + if( isset( $dt['tz'] ) && ( 'Z' == $dt['tz'] )) + $str .= 'Z'; + $v = $child->addChild( $type, $str ); + } + break; + case 'duration': + $output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ? '-' : ''; + $v = $child->addChild( $type, $output.iCalUtilityFunctions::_duration2str( $content ) ); + break; + case 'geo': + if( !empty( $content )) { + $v1 = $child->addChild( 'latitude', iCalUtilityFunctions::_geo2str2( $content['latitude'], iCalUtilityFunctions::$geoLatFmt )); + $v1 = $child->addChild( 'longitude', iCalUtilityFunctions::_geo2str2( $content['longitude'], iCalUtilityFunctions::$geoLongFmt )); + } + break; + case 'integer': + $v = $child->addChild( $type, (string) $content ); + break; + case 'period': + if( !is_array( $content )) + break; + foreach( $content as $period ) { + $v1 = $child->addChild( $type ); + $str = sprintf( $fmtYmdHis, (int) $period[0]['year'], (int) $period[0]['month'], (int) $period[0]['day'], (int) $period[0]['hour'], (int) $period[0]['min'], (int) $period[0]['sec'] ); + if( isset( $period[0]['tz'] ) && ( 'Z' == $period[0]['tz'] )) + $str .= 'Z'; + $v2 = $v1->addChild( 'start', $str ); + if( array_key_exists( 'year', $period[1] )) { + $str = sprintf( $fmtYmdHis, (int) $period[1]['year'], (int) $period[1]['month'], (int) $period[1]['day'], (int) $period[1]['hour'], (int) $period[1]['min'], (int) $period[1]['sec'] ); + if( isset($period[1]['tz'] ) && ( 'Z' == $period[1]['tz'] )) + $str .= 'Z'; + $v2 = $v1->addChild( 'end', $str ); + } + else + $v2 = $v1->addChild( 'duration', iCalUtilityFunctions::_duration2str( $period[1] )); + } + break; + case 'recur': + $content = array_change_key_case( $content ); + foreach( $content as $rulelabel => $rulevalue ) { + switch( $rulelabel ) { + case 'until': + if( isset( $rulevalue['hour'] )) + $str = sprintf( $fmtYmdHis, (int) $rulevalue['year'], (int) $rulevalue['month'], (int) $rulevalue['day'], (int) $rulevalue['hour'], (int) $rulevalue['min'], (int) $rulevalue['sec'] ).'Z'; + else + $str = sprintf( $fmtYmd, (int) $rulevalue['year'], (int) $rulevalue['month'], (int) $rulevalue['day'] ); + $v = $child->addChild( $rulelabel, $str ); + break; + case 'bysecond': + case 'byminute': + case 'byhour': + case 'bymonthday': + case 'byyearday': + case 'byweekno': + case 'bymonth': + case 'bysetpos': { + if( is_array( $rulevalue )) { + foreach( $rulevalue as $vix => $valuePart ) + $v = $child->addChild( $rulelabel, $valuePart ); + } + else + $v = $child->addChild( $rulelabel, $rulevalue ); + break; + } + case 'byday': { + if( isset( $rulevalue['DAY'] )) { + $str = ( isset( $rulevalue[0] )) ? $rulevalue[0] : ''; + $str .= $rulevalue['DAY']; + $p = $child->addChild( $rulelabel, $str ); + } + else { + foreach( $rulevalue as $valuePart ) { + if( isset( $valuePart['DAY'] )) { + $str = ( isset( $valuePart[0] )) ? $valuePart[0] : ''; + $str .= $valuePart['DAY']; + $p = $child->addChild( $rulelabel, $str ); + } + else + $p = $child->addChild( $rulelabel, $valuePart ); + } + } + break; + } + case 'freq': + case 'count': + case 'interval': + case 'wkst': + default: + $p = $child->addChild( $rulelabel, $rulevalue ); + break; + } // end switch( $rulelabel ) + } // end foreach( $content as $rulelabel => $rulevalue ) + break; + case 'rstatus': + $v = $child->addChild( 'code', number_format( (float) $content['statcode'], 2, '.', '')); + $v = $child->addChild( 'description', htmlspecialchars( $content['text'] )); + if( isset( $content['extdata'] )) + $v = $child->addChild( 'data', htmlspecialchars( $content['extdata'] )); + break; + case 'text': + if( !is_array( $content )) + $content = array( $content ); + foreach( $content as $part ) + $v = $child->addChild( $type, htmlspecialchars( $part )); + break; + case 'time': + break; + case 'uri': + $v = $child->addChild( $type, $content ); + break; + case 'utc-offset': + if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) { + $str = substr( $content, 0, 1 ); + $content = substr( $content, 1 ); + } + else + $str = '+'; + $str .= substr( $content, 0, 2 ).':'.substr( $content, 2, 2 ); + if( 4 < strlen( $content )) + $str .= ':'.substr( $content, 4 ); + $v = $child->addChild( $type, $str ); + break; + case 'unknown': + default: + if( is_array( $content )) + $content = implode( '', $content ); + $v = $child->addChild( 'unknown', htmlspecialchars( $content )); + break; + } +} +/** + * parse xml file into iCalcreator instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.22 - 2013-06-18 + * @param string $xmlfile + * @param array $iCalcfg iCalcreator config array (opt) + * @return mixediCalcreator instance or FALSE on error + */ +function XMLfile2iCal( $xmlfile, $iCalcfg=array()) { + if( FALSE === ( $xmlstr = file_get_contents( $xmlfile ))) + return FALSE; + return xml2iCal( $xmlstr, $iCalcfg ); +} +/** + * parse xml string into iCalcreator instance, alias of XML2iCal + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.22 - 2013-06-18 + * @param string $xmlstr + * @param array $iCalcfg iCalcreator config array (opt) + * @return mixed iCalcreator instance or FALSE on error + */ +function XMLstr2iCal( $xmlstr, $iCalcfg=array()) { + return XML2iCal( $xmlstr, $iCalcfg); +} +/** + * parse xml string into iCalcreator instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.22 - 2013-06-20 + * @param string $xmlstr + * @param array $iCalcfg iCalcreator config array (opt) + * @uses vcalendar::vcalendar() + * @uses XMLgetComps() + * @return mixed iCalcreator instance or FALSE on error + */ +function XML2iCal( $xmlstr, $iCalcfg=array()) { + $xmlstr = str_replace( array( "\r\n", "\n\r", "\n", "\r" ), '', $xmlstr ); + $xml = XMLgetTagContent1( $xmlstr, 'vcalendar', $endIx ); + $iCal = new vcalendar( $iCalcfg ); + XMLgetComps( $iCal, $xmlstr ); + unset( $xmlstr ); + return $iCal; +} +/** + * parse XML string into iCalcreator components + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param object $iCal iCalcreator vcalendar or component object instance + * @param string $xml + * @uses iCalUtilityFunctions::$allComps + * @uses XMLgetTagContent1() + * @uses XMLgetProps() + * @uses XMLgetTagContent2() + * @uses vcalendar::newComponent() + * @uses iCalUtilityFunctions::$allComps + * @uses XMLgetComps() + * @return object + */ +function XMLgetComps( $iCal, $xml ) { + $sx = 0; + while(( FALSE !== substr( $xml, ( $sx + 11 ), 1 )) && + ( '' != substr( $xml, $sx, 12 )) && ( '' != substr( $xml, $sx, 12 ))) + $sx += 1; + if( FALSE === substr( $xml, ( $sx + 11 ), 1 )) + return FALSE; + if( '' == substr( $xml, $sx, 12 )) { + $xml2 = XMLgetTagContent1( $xml, 'properties', $endIx ); + XMLgetProps( $iCal, $xml2 ); + $xml = substr( $xml, $endIx ); + } + if( '' == substr( $xml, 0, 12 )) + $xml = XMLgetTagContent1( $xml, 'components', $endIx ); + while( ! empty( $xml )) { + $xml2 = XMLgetTagContent2( $xml, $tagName, $endIx ); + if( in_array( strtolower( $tagName ), iCalUtilityFunctions::$allComps ) && + ( FALSE !== ( $subComp = $iCal->newComponent( $tagName )))) + XMLgetComps( $subComp, $xml2 ); + $xml = substr( $xml, $endIx); + } + unset( $xml ); + return $iCal; +} +/** + * parse XML into iCalcreator properties + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param array $iCal iCalcreator calendar/component instance + * @param string $xml + * @uses XMLgetTagContent2() + * @uses vcalendar::setProperty() + * @uses calendarComponent::setproperty() + * @uses XMLgetTagContent1() + * @uses vcalendar::setProperty() + * @return void + */ +function XMLgetProps( $iCal, $xml) { + while( ! empty( $xml )) { + $xml2 = XMLgetTagContent2( $xml, $propName, $endIx ); + $propName = strtoupper( $propName ); + if( empty( $xml2 ) && ( '0' != $xml2 )) { + $iCal->setProperty( $propName ); + $xml = substr( $xml, $endIx); + continue; + } + $params = array(); + if( '' == substr( $xml2, 0, 13 )) + $xml2 = substr( $xml2, 13 ); + elseif( '' == substr( $xml2, 0, 12 )) { + $xml3 = XMLgetTagContent1( $xml2, 'parameters', $endIx2 ); + while( ! empty( $xml3 )) { + $xml4 = XMLgetTagContent2( $xml3, $paramKey, $endIx3 ); + $pType = FALSE; // skip parameter valueType + $paramKey = strtoupper( $paramKey ); + static $mParams = array( 'DELEGATED-FROM', 'DELEGATED-TO', 'MEMBER' ); + if( in_array( $paramKey, $mParams )) { + while( ! empty( $xml4 )) { + if( ! isset( $params[$paramKey] )) + $params[$paramKey] = array( XMLgetTagContent1( $xml4, 'cal-address', $endIx4 )); + else + $params[$paramKey][] = XMLgetTagContent1( $xml4, 'cal-address', $endIx4 ); + $xml4 = substr( $xml4, $endIx4 ); + } + } + else { + if( ! isset( $params[$paramKey] )) + $params[$paramKey] = html_entity_decode( XMLgetTagContent2( $xml4, $pType, $endIx4 )); + else + $params[$paramKey] .= ','.html_entity_decode( XMLgetTagContent2( $xml4, $pType, $endIx4 )); + } + $xml3 = substr( $xml3, $endIx3 ); + } + $xml2 = substr( $xml2, $endIx2 ); + } // if( '' == substr( $xml2, 0, 12 )) + $valueType = FALSE; + $value = ( ! empty( $xml2 ) || ( '0' == $xml2 )) ? XMLgetTagContent2( $xml2, $valueType, $endIx3 ) : ''; + switch( $propName ) { + case 'CATEGORIES': + case 'RESOURCES': + $tValue = array(); + while( ! empty( $xml2 )) { + $tValue[] = html_entity_decode( XMLgetTagContent2( $xml2, $valueType, $endIx4 )); + $xml2 = substr( $xml2, $endIx4 ); + } + $value = $tValue; + break; + case 'EXDATE': // multiple single-date(-times) may exist + case 'RDATE': + if( 'period' != $valueType ) { + if( 'date' == $valueType ) + $params['VALUE'] = 'DATE'; + $t = array(); + while( ! empty( $xml2 ) && ( '' == substr( $xml2, 0, 8 ))) { + $xml3 = XMLgetTagContent1( $xml2, 'period', $endIx4 ); // period + $t = array(); + while( ! empty( $xml3 )) { + $t[] = XMLgetTagContent2( $xml3, $pType, $endIx5 ); // start - end/duration + $xml3 = substr( $xml3, $endIx5 ); + } + $value[] = $t; + $xml2 = substr( $xml2, $endIx4 ); + } + break; + case 'TZOFFSETTO': + case 'TZOFFSETFROM': + $value = str_replace( ':', '', $value ); + break; + case 'GEO': + $tValue = array( 'latitude' => $value ); + $tValue['longitude'] = XMLgetTagContent1( substr( $xml2, $endIx3 ), 'longitude', $endIx3 ); + $value = $tValue; + break; + case 'EXRULE': + case 'RRULE': + $tValue = array( $valueType => $value ); + $xml2 = substr( $xml2, $endIx3 ); + $valueType = FALSE; + while( ! empty( $xml2 )) { + $t = XMLgetTagContent2( $xml2, $valueType, $endIx4 ); + switch( $valueType ) { + case 'freq': + case 'count': + case 'until': + case 'interval': + case 'wkst': + $tValue[$valueType] = $t; + break; + case 'byday': + if( 2 == strlen( $t )) + $tValue[$valueType][] = array( 'DAY' => $t ); + else { + $day = substr( $t, -2 ); + $key = substr( $t, 0, ( strlen( $t ) - 2 )); + $tValue[$valueType][] = array( $key, 'DAY' => $day ); + } + break; + default: + $tValue[$valueType][] = $t; + } + $xml2 = substr( $xml2, $endIx4 ); + } + $value = $tValue; + break; + case 'REQUEST-STATUS': + $tValue = array(); + while( ! empty( $xml2 )) { + $t = html_entity_decode( XMLgetTagContent2( $xml2, $valueType, $endIx4 )); + $tValue[$valueType] = $t; + $xml2 = substr( $xml2, $endIx4 ); + } + if( ! empty( $tValue )) + $value = $tValue; + else + $value = array( 'code' => null, 'description' => null ); + break; + default: + switch( $valueType ) { + case 'binary': $params['VALUE'] = 'BINARY'; break; + case 'date': $params['VALUE'] = 'DATE'; break; + case 'date-time': $params['VALUE'] = 'DATE-TIME'; break; + case 'text': + case 'unknown': $value = html_entity_decode( $value ); break; + } + break; + } // end switch( $propName ) + if( 'FREEBUSY' == $propName ) { + $fbtype = $params['FBTYPE']; + unset( $params['FBTYPE'] ); + $iCal->setProperty( $propName, $fbtype, $value, $params ); + } + elseif( 'GEO' == $propName ) + $iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params ); + elseif( 'REQUEST-STATUS' == $propName ) { + if( !isset( $value['data'] )) + $value['data'] = FALSE; + $iCal->setProperty( $propName, $value['code'], $value['description'], $value['data'], $params ); + } + else { + if( empty( $value ) && ( is_array( $value ) || ( '0' > $value ))) + $value = ''; + $iCal->setProperty( $propName, $value, $params ); + } + $xml = substr( $xml, $endIx); + } // end while( ! empty( $xml )) +} +/** + * fetch a specific XML tag content + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.22 - 2013-06-20 + * @param string $xml + * @param string $tagName + * @param int $endIx + * @return mixed + */ +function XMLgetTagContent1( $xml, $tagName, & $endIx=0 ) { + $strlen = strlen( $tagName ); + $sx1 = 0; + while( FALSE !== substr( $xml, $sx1, 1 )) { + if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 1 ), 1 )) && + ( strtolower( "<$tagName>" ) == strtolower( substr( $xml, $sx1, ( $strlen + 2 ))))) + break; + if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 3 ), 1 )) && + ( strtolower( "<$tagName />" ) == strtolower( substr( $xml, $sx1, ( $strlen + 4 ))))) { // empty tag + $endIx = $strlen + 5; + return ''; + } + if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 2 ), 1 )) && + ( strtolower( "<$tagName/>" ) == strtolower( substr( $xml, $sx1, ( $strlen + 3 ))))) { // empty tag + $endIx = $strlen + 4; + return ''; + } + $sx1 += 1; + } + if( FALSE === substr( $xml, $sx1, 1 )) { + $endIx = ( empty( $sx )) ? 0 : $sx - 1; + return ''; + } + if( FALSE === ( $pos = stripos( $xml, "" ))) { // missing end tag?? + $endIx = strlen( $xml ) + 1; + return ''; + } + $endIx = $pos + $strlen + 3; + return substr( $xml, ( $sx1 + $strlen + 2 ), ( $pos - $sx1 - 2 - $strlen )); +} +/** + * fetch next (unknown) XML tagname AND content + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.22 - 2013-06-20 + * @param string $xml + * @param string $tagName + * @param int $endIx + * @return mixed + */ +function XMLgetTagContent2( $xml, & $tagName, & $endIx ) { + $endIx = strlen( $xml ) + 1; // just in case.. . + $sx1 = 0; + while( FALSE !== substr( $xml, $sx1, 1 )) { + if( '<' == substr( $xml, $sx1, 1 )) { + if(( FALSE !== substr( $xml, ( $sx1 + 3 ), 1 )) && ( ''; + $this->attributeDelimiter = $this->nl; + $this->valueInit = null; + break; + default: + $this->componentStart1 = 'BEGIN:'; + $this->componentStart2 = null; + $this->componentEnd1 = 'END:'; + $this->componentEnd2 = $this->nl; + $this->elementStart1 = null; + $this->elementStart2 = null; + $this->elementEnd1 = null; + $this->elementEnd2 = $this->nl; + $this->intAttrDelimiter = ''; + $this->attributeDelimiter = ';'; + $this->valueInit = ':'; + break; + } + return TRUE; + } +/** + * creates formatted output for calendar component property + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-31 + * @param string $label property name + * @param string $attributes property attributes + * @param string $content property content (optional) + * @uses iCalBase::$format + * @uses iCalBase::$elementStart1 + * @uses iCalBase::$xcaldecl + * @uses iCalBase::$intAttrDelimiter + * @uses iCalBase::$attributeDelimiter + * @uses iCalBase::_createElement() + * @uses iCalBase::$elementStart2 + * @uses iCalBase::$nl + * @uses iCalBase::$valueInit + * @uses iCalUtilityFunctions::_size75() + * @uses iCalBase::$elementEnd1 + * @uses iCalBase::$elementEnd2 + * @access protected + * @return string + */ + protected function _createElement( $label, $attributes=null, $content=FALSE ) { + switch( $this->format ) { + case 'xcal': + $label = strtolower( $label ); + break; + default: + $label = strtoupper( $label ); + break; + } + $output = $this->elementStart1.$label; + $categoriesAttrLang = null; + $attachInlineBinary = FALSE; + $attachfmttype = null; + if (( 'xcal' == $this->format) && ( 'x-' == substr( $label, 0, 2 ))) { + $this->xcaldecl[] = array( 'xmldecl' => 'ELEMENT' + , 'ref' => $label + , 'type2' => '(#PCDATA)' ); + } + if( !empty( $attributes )) { + $attributes = trim( $attributes ); + if ( 'xcal' == $this->format ) { + $attributes2 = explode( $this->intAttrDelimiter, $attributes ); + $attributes = null; + foreach( $attributes2 as $aix => $attribute ) { + $attrKVarr = explode( '=', $attribute ); + if( empty( $attrKVarr[0] )) + continue; + if( !isset( $attrKVarr[1] )) { + $attrValue = $attrKVarr[0]; + $attrKey = $aix; + } + elseif( 2 == count( $attrKVarr)) { + $attrKey = strtolower( $attrKVarr[0] ); + $attrValue = $attrKVarr[1]; + } + else { + $attrKey = strtolower( $attrKVarr[0] ); + unset( $attrKVarr[0] ); + $attrValue = implode( '=', $attrKVarr ); + } + if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) { + $attachInlineBinary = TRUE; + if( 'fmttype' == $attrKey ) + $attachfmttype = $attrKey.'='.$attrValue; + continue; + } + elseif(( 'categories' == $label ) && ( 'language' == $attrKey )) + $categoriesAttrLang = $attrKey.'='.$attrValue; + else { + $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' '; + $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null; + if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) { + $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 )); + $attrValue = str_replace( '"', '', $attrValue ); + } + $attributes .= '"'.htmlspecialchars( $attrValue ).'"'; + } + } + } + else { + $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes ); + } + } + if(( 'xcal' == $this->format) && + ((( 'attach' == $label ) && !$attachInlineBinary ) || ( in_array( $label, array( 'tzurl', 'url' ))))) { + $pos = strrpos( $content, "/" ); + $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content; + $this->xcaldecl[] = array( 'xmldecl' => 'ENTITY' + , 'uri' => $docname + , 'ref' => 'SYSTEM' + , 'external' => $content + , 'type' => 'NDATA' + , 'type2' => 'BINERY' ); + $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' '; + $attributes .= 'uri="'.$docname.'"'; + $content = null; + if( 'attach' == $label ) { + $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes ); + $content = $this->nl.$this->_createElement( 'extref', $attributes, null ); + $attributes = null; + } + } + elseif(( 'xcal' == $this->format) && ( 'attach' == $label ) && $attachInlineBinary ) { + $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute + } + $output .= $attributes; + if( !$content && ( '0' != $content )) { + switch( $this->format ) { + case 'xcal': + $output .= ' /'; + $output .= $this->elementStart2.$this->nl; + return $output; + break; + default: + $output .= $this->elementStart2.$this->valueInit; + return iCalUtilityFunctions::_size75( $output, $this->nl ); + break; + } + } + $output .= $this->elementStart2; + $output .= $this->valueInit.$content; + switch( $this->format ) { + case 'xcal': + return $output.$this->elementEnd1.$label.$this->elementEnd2; + break; + default: + return iCalUtilityFunctions::_size75( $output, $this->nl ); + break; + } + } +/** + * creates formatted output for calendar component property parameters + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-31 + * @param array $params optional + * @param array $ctrKeys optional + * @uses iCalBase::$intAttrDelimiter + * @uses vcalendar::getConfig() + * @uses calendarComponent::getConfig() + * @access protected + * @return string + */ + protected function _createParams( $params=array(), $ctrKeys=array() ) { + if( !is_array( $params ) || empty( $params )) + $params = array(); + $attrLANG = $attr1 = $attr2 = $lang = null; + $CNattrKey = ( in_array( 'CN', $ctrKeys )) ? TRUE : FALSE ; + $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ; + $CNattrExist = $LANGattrExist = FALSE; + $xparams = array(); + $params = array_change_key_case( $params, CASE_UPPER ); + foreach( $params as $paramKey => $paramValue ) { + if(( FALSE !== strpos( $paramValue, ':' )) || + ( FALSE !== strpos( $paramValue, ';' )) || + ( FALSE !== strpos( $paramValue, ',' ))) + $paramValue = '"'.$paramValue.'"'; + if( ctype_digit( (string) $paramKey )) { + $xparams[] = $paramValue; + continue; + } + if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' ))) + $xparams[$paramKey] = $paramValue; + else + $params[$paramKey] = $paramValue; + } + ksort( $xparams, SORT_STRING ); + foreach( $xparams as $paramKey => $paramValue ) { + if( ctype_digit( (string) $paramKey )) + $attr2 .= $this->intAttrDelimiter.$paramValue; + else + $attr2 .= $this->intAttrDelimiter."$paramKey=$paramValue"; + } + if( isset( $params['FMTTYPE'] ) && !in_array( 'FMTTYPE', $ctrKeys )) { + $attr1 .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2; + $attr2 = null; + } + if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING', $ctrKeys )) { + if( !empty( $attr2 )) { + $attr1 .= $attr2; + $attr2 = null; + } + $attr1 .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING']; + } + if( isset( $params['VALUE'] ) && !in_array( 'VALUE', $ctrKeys )) + $attr1 .= $this->intAttrDelimiter.'VALUE='.$params['VALUE']; + if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys )) { + $attr1 .= $this->intAttrDelimiter.'TZID='.$params['TZID']; + } + if( isset( $params['RANGE'] ) && !in_array( 'RANGE', $ctrKeys )) + $attr1 .= $this->intAttrDelimiter.'RANGE='.$params['RANGE']; + if( isset( $params['RELTYPE'] ) && !in_array( 'RELTYPE', $ctrKeys )) + $attr1 .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE']; + if( isset( $params['CN'] ) && $CNattrKey ) { + $attr1 = $this->intAttrDelimiter.'CN='.$params['CN']; + $CNattrExist = TRUE; + } + if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) { + $delim = ( FALSE !== strpos( $params['DIR'], '"' )) ? '' : '"'; + $attr1 .= $this->intAttrDelimiter.'DIR='.$delim.$params['DIR'].$delim; + } + if( isset( $params['SENT-BY'] ) && in_array( 'SENT-BY', $ctrKeys )) + $attr1 .= $this->intAttrDelimiter.'SENT-BY='.$params['SENT-BY']; + if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys )) { + $delim = ( FALSE !== strpos( $params['ALTREP'], '"' )) ? '' : '"'; + $attr1 .= $this->intAttrDelimiter.'ALTREP='.$delim.$params['ALTREP'].$delim; + } + if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) { + $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE']; + $LANGattrExist = TRUE; + } + if( !$LANGattrExist ) { + $lang = $this->getConfig( 'language' ); + if(( $CNattrExist || $LANGattrKey ) && $lang ) + $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang; + } + return $attr1.$attrLANG.$attr2; + } +} diff --git a/php/ical/lib/iCalUtilityFunctions.class.php b/php/ical/lib/iCalUtilityFunctions.class.php new file mode 100644 index 0000000..3825fb6 --- /dev/null +++ b/php/ical/lib/iCalUtilityFunctions.class.php @@ -0,0 +1,2507 @@ + + * @since 2.21.11 - 2015-04-03 + */ +class iCalUtilityFunctions { +/** @var string tmp line delimiter, used in convEolChar (parse) */ + private static $baseDelim = null; +/** @var array protocol prefix, used in _splitContent() */ + private static $parValPrefix = array ( 'MStz' => array( 'utc-', 'utc+', 'gmt-', 'gmt+' ) + , 'Proto3' => array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ) + , 'Proto4' => array( 'crid:', 'news:', 'pres:' ) + , 'Proto6' => array( 'mailto:' )); +/** @var string output format for geo latitude and longitude (before rtrim) */ + public static $geoLatFmt = '%09.6f'; + public static $geoLongFmt = '%8.6f'; +/** @var array date/datetime formats */ + public static $fmt = array( 'Ymd' => '%04d%02d%02d', + 'His' => '%02d%02d%02d', + 'dayOfDays' => 'day %d of %d', + 'durDHis' => '%a days, %h hours, %i min, %s sec', + 'Ymd2' => 'Y-m-d', + 'YmdHis2' => 'Y-m-d H:i:s', + 'YmdHis2e' => 'Y-m-d H:i:s e', + 'YmdHis3' => 'Y-m-d-H-i-s', + 'YmdHise' => '%04d-%02d-%02d %02d:%02d:%02d %s', + 'YmdTHisO' => 'Y-m-d\TH:i:s O', + 'dateKey' => '%04d%02d%02d%02d%02d%02d000', + ); +/** @var array component property UID value */ + public static $vComps = array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy' ); + public static $mComps = array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ); + public static $miscComps = array( 'valarm', 'vtimezone', 'standard', 'daylight' ); + public static $tzComps = array( 'vtimezone', 'standard', 'daylight' ); + public static $allComps = array( 'vtimezone', 'standard', 'daylight', 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm' ); +/** @var array component property collections */ + public static $mProps1 = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' ); + public static $mProps2 = array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE', + 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ); + public static $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' ); + public static $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' ); +/** @var object Store the single instance of iCalUtilityFunctions */ + private static $m_pInstance; +/** + * Private constructor to limit object instantiation to within the class + * + * @access private + */ + private function __construct() { + $m_pInstance = FALSE; + } +/** @var array component property UID value */ +/** + * Getter method for creating/returning the single instance of this class + * + * @uses iCalUtilityFunctions::$m_pInstance + */ + public static function getInstance() { + if (!self::$m_pInstance) + self::$m_pInstance = new iCalUtilityFunctions(); + return self::$m_pInstance; + } +/** + * ensures internal date-time/date format (keyed array) for an input date-time/date array (keyed or unkeyed) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.24 - 2013-06-26 + * @param array $datetime + * @param int $parno optional, default FALSE + * @return array + */ + public static function _chkDateArr( $datetime, $parno=FALSE ) { + $output = array(); + if(( !$parno || ( 6 <= $parno )) && isset( $datetime[3] ) && !isset( $datetime[4] )) { // Y-m-d with tz + $temp = $datetime[3]; + $datetime[3] = $datetime[4] = $datetime[5] = 0; + $datetime[6] = $temp; + } + foreach( $datetime as $dateKey => $datePart ) { + switch ( $dateKey ) { + case '0': case 'year': $output['year'] = $datePart; break; + case '1': case 'month': $output['month'] = $datePart; break; + case '2': case 'day': $output['day'] = $datePart; break; + } + if( 3 != $parno ) { + switch ( $dateKey ) { + case '0': + case '1': + case '2': break; + case '3': case 'hour': $output['hour'] = $datePart; break; + case '4': case 'min' : $output['min'] = $datePart; break; + case '5': case 'sec' : $output['sec'] = $datePart; break; + case '6': case 'tz' : $output['tz'] = $datePart; break; + } + } + } + if( 3 != $parno ) { + if( !isset( $output['hour'] )) $output['hour'] = 0; + if( !isset( $output['min'] )) $output['min'] = 0; + if( !isset( $output['sec'] )) $output['sec'] = 0; + if( isset( $output['tz'] ) && + (( '+0000' == $output['tz'] ) || ( '-0000' == $output['tz'] ) || ( '+000000' == $output['tz'] ) || ( '-000000' == $output['tz'] ))) + $output['tz'] = 'Z'; + } + return $output; + } +/** + * check date(-time) and params arrays for an opt. timezone and if it is a DATE-TIME or DATE (updates $parno and params) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.30 - 2012-01-16 + * @param array $theDate date to check + * @param int $parno no of date parts (i.e. year, month.. .) + * @param array $params property parameters + * @uses iCalUtilityFunctions::_strdate2date() + * @uses iCalUtilityFunctions::_isOffset() + * @return void + */ + public static function _chkdatecfg( $theDate, & $parno, & $params ) { + if( isset( $params['TZID'] )) + $parno = 6; + elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )) + $parno = 3; + else { + if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] )) + $parno = 7; + if( is_array( $theDate )) { + if( isset( $theDate['timestamp'] )) + $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null; + else + $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null; + if( !empty( $tzid )) { + $parno = 7; + if( !iCalUtilityFunctions::_isOffset( $tzid )) + $params['TZID'] = $tzid; // save only timezone + } + elseif( !$parno && ( 3 == count( $theDate )) && + ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))) + $parno = 3; + else + $parno = 6; + } + else { // string + $date = trim( $theDate ); + if( 'Z' == substr( $date, -1 )) + $parno = 7; // UTC DATE-TIME + elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) && + ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' )))) + $parno = 3; // DATE + $date = iCalUtilityFunctions::_strdate2date( $date, $parno ); + unset( $date['unparsedtext'] ); + if( !empty( $date['tz'] )) { + $parno = 7; + if( !iCalUtilityFunctions::_isOffset( $date['tz'] )) + $params['TZID'] = $date['tz']; // save only timezone + } + elseif( empty( $parno )) + $parno = 6; + } + if( isset( $params['TZID'] )) + $parno = 6; + } + } +/** + * vcalendar sort callback function + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-17 + * @param array $a + * @param array $b + * @uses calendarComponent::$objName + * @return int + */ + public static function _cmpfcn( $a, $b ) { + if( empty( $a )) return -1; + if( empty( $b )) return 1; + if( 'vtimezone' == $a->objName ) { + if( 'vtimezone' != $b->objName ) return -1; + elseif( $a->srtk[0] <= $b->srtk[0] ) return -1; + else return 1; + } + elseif( 'vtimezone' == $b->objName ) return 1; + $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' ); + for( $k = 0; $k < 4 ; $k++ ) { + if( empty( $a->srtk[$k] )) return -1; + elseif( empty( $b->srtk[$k] )) return 1; + if( is_array( $a->srtk[$k] )) { + if( is_array( $b->srtk[$k] )) { + foreach( $sortkeys as $key ) { + if ( !isset( $a->srtk[$k][$key] )) return -1; + elseif( !isset( $b->srtk[$k][$key] )) return 1; + if ( empty( $a->srtk[$k][$key] )) return -1; + elseif( empty( $b->srtk[$k][$key] )) return 1; + if ( $a->srtk[$k][$key] == $b->srtk[$k][$key]) + continue; + if (( (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] )) + return -1; + elseif(( (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] )) + return 1; + } + } + else return -1; + } + elseif( is_array( $b->srtk[$k] )) return 1; + elseif( $a->srtk[$k] < $b->srtk[$k] ) return -1; + elseif( $a->srtk[$k] > $b->srtk[$k] ) return 1; + } + return 0; + } +/** + * byte oriented line folding fix + * + * remove any line-endings that may include spaces or tabs + * and convert all line endings (iCal default '\r\n'), + * takes care of '\r\n', '\r' and '\n' and mixed '\r\n'+'\r', '\r\n'+'\n' + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.18.16 - 2014-04-04 + * @param string $text + * @param string $nl + * @uses iCalUtilityFunctions::$baseDelim + * @return string + */ + public static function convEolChar( & $text, $nl ) { + /* fix dummy line separator */ + if( empty( iCalUtilityFunctions::$baseDelim )) { + iCalUtilityFunctions::$baseDelim = substr( microtime(), 2, 4 ); + $base = 'aAbB!cCdD"eEfF#gGhHiIjJ%kKlL&mMnN/oOpP(rRsS)tTuU=vVxX?uUvV*wWzZ-1234_5678|90'; + $len = strlen( $base ) - 1; + for( $p = 0; $p < 6; $p++ ) + iCalUtilityFunctions::$baseDelim .= $base{mt_rand( 0, $len )}; + } + /* fix eol chars */ + $text = str_replace( array( "\r\n", "\n\r", "\n", "\r" ), iCalUtilityFunctions::$baseDelim, $text ); + /* fix empty lines */ + $text = str_replace( iCalUtilityFunctions::$baseDelim.iCalUtilityFunctions::$baseDelim, iCalUtilityFunctions::$baseDelim.str_pad( '', 75 ).iCalUtilityFunctions::$baseDelim, $text ); + /* fix line folding */ + $text = str_replace( iCalUtilityFunctions::$baseDelim, $nl, $text ); + $text = str_replace( array( $nl.' ', $nl."\t" ), '', $text ); + /* split in component/property lines */ + $text = explode( $nl, $text ); + return $text; + } +/** + * create a calendar timezone and standard/daylight components + * + * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone: + * + * BEGIN:VTIMEZONE + * TZID:Europe/Stockholm + * BEGIN:STANDARD + * DTSTART:20101031T020000 + * TZOFFSETFROM:+0200 + * TZOFFSETTO:+0100 + * TZNAME:CET + * END:STANDARD + * BEGIN:DAYLIGHT + * DTSTART:20100328T030000 + * TZOFFSETFROM:+0100 + * TZOFFSETTO:+0200 + * TZNAME:CEST + * END:DAYLIGHT + * END:VTIMEZONE + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-04-03 + * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi + * Additional changes jpirkey + * @param object $calendar iCalcreator calendar instance + * @param string $timezone PHP5 (DateTimeZone) valid timezone + * @param array $xProp *[x-propName => x-propValue], optional + * @param int $from unix timestamp + * @param int $to unix timestamp + * @uses vcalendar::getProperty() + * @uses iCalUtilityFunctions::$fmt + * @uses vcalendar::newComponent() + * @uses calendarComponent::setproperty() + * @uses iCalUtilityFunctions::offsetSec2His() + * @return bool + */ + public static function createTimezone( & $calendar, $timezone, $xProp=array(), $from=null, $to=null ) { + if( empty( $timezone )) + return FALSE; + if( !empty( $from ) && !is_int( $from )) + return FALSE; + if( !empty( $to ) && !is_int( $to )) + return FALSE; + try { + $dtz = new DateTimeZone( $timezone ); + $transitions = $dtz->getTransitions(); + $utcTz = new DateTimeZone( 'UTC' ); + } + catch( Exception $e ) { return FALSE; } + if( empty( $from ) || empty( $to )) { + $dates = array_keys( $calendar->getProperty( 'dtstart' )); + if( empty( $dates )) + $dates = array( date( 'Ymd' )); + } + if( ! empty( $from )) { + $dateFrom = new DateTime( "@$from" ); // set lowest date (UTC) + $dateFrom->modify( '-7 month' ); // set $dateFrom to seven month before the lowest date + } + else { + $from = reset( $dates ); // set lowest date to the lowest dtstart date + $dateFrom = new DateTime( $from.'T000000', $dtz ); + $dateFrom->modify( '-7 month' ); // set $dateFrom to seven month before the lowest date + $dateFrom->setTimezone( $utcTz ); // convert local date to UTC + } + $dateFromYmd = $dateFrom->format( iCalUtilityFunctions::$fmt['Ymd2'] ); + if( ! empty( $to )) + $dateTo = new DateTime( "@$to" ); // set end date (UTC) + else { + $to = end( $dates ); // set highest date to the highest dtstart date + $dateTo = new DateTime( $to.'T235959', $dtz ); + $dateTo->modify( '+18 month' ); // set $dateTo to 18 month after the highest date + $dateTo->setTimezone( $utcTz ); // convert local date to UTC + } + $dateToYmd = $dateTo->format( iCalUtilityFunctions::$fmt['Ymd2'] ); + unset( $dtz ); + $transTemp = array(); + $prevOffsetfrom = 0; + $stdIx = $dlghtIx = null; + $prevTrans = FALSE; + foreach( $transitions as $tix => $trans ) { // all transitions in date-time order!! + $date = new DateTime( "@{$trans['ts']}" ); // set transition date (UTC) + $transDateYmd = $date->format( iCalUtilityFunctions::$fmt['Ymd2'] ); + if ( $transDateYmd < $dateFromYmd ) { + $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom + $prevTrans = $trans; // save it in case we don't find any that match + $prevTrans['offsetfrom'] = ( 0 < $tix ) ? $transitions[$tix-1]['offset'] : 0; + continue; + } + if( $transDateYmd > $dateToYmd ) + break; // loop always (?) breaks here + if( !empty( $prevOffsetfrom ) || ( 0 == $prevOffsetfrom )) { + $trans['offsetfrom'] = $prevOffsetfrom; // i.e. set previous offsetto as offsetFrom + $date->modify( $trans['offsetfrom'].'seconds' ); // convert utc date to local date + $d = $date->format( iCalUtilityFunctions::$fmt['YmdHis3'] ); + $d = explode( '-', $d ); // set date to array to ease up dtstart and (opt) rdate setting + $trans['time'] = array( 'year' => (int) $d[0], 'month' => (int) $d[1], 'day' => (int) $d[2], 'hour' => (int) $d[3], 'min' => (int) $d[4], 'sec' => (int) $d[5] ); + } + $prevOffsetfrom = $trans['offset']; + if( TRUE !== $trans['isdst'] ) { // standard timezone + if( !empty( $stdIx ) && isset( $transTemp[$stdIx]['offsetfrom'] ) && // check for any repeating rdate's (in order) + ( $transTemp[$stdIx]['abbr'] == $trans['abbr'] ) && + ( $transTemp[$stdIx]['offsetfrom'] == $trans['offsetfrom'] ) && + ( $transTemp[$stdIx]['offset'] == $trans['offset'] )) { + $transTemp[$stdIx]['rdate'][] = $trans['time']; + continue; + } + $stdIx = $tix; + } // end standard timezone + else { // daylight timezone + if( !empty( $dlghtIx ) && isset( $transTemp[$dlghtIx]['offsetfrom'] ) && // check for any repeating rdate's (in order) + ( $transTemp[$dlghtIx]['abbr'] == $trans['abbr'] ) && + ( $transTemp[$dlghtIx]['offsetfrom'] == $trans['offsetfrom'] ) && + ( $transTemp[$dlghtIx]['offset'] == $trans['offset'] )) { + $transTemp[$dlghtIx]['rdate'][] = $trans['time']; + continue; + } + $dlghtIx = $tix; + } // end daylight timezone + $transTemp[$tix] = $trans; + } // end foreach( $transitions as $tix => $trans ) + $tz = $calendar->newComponent( 'vtimezone' ); + $tz->setproperty( 'tzid', $timezone ); + if( !empty( $xProp )) { + foreach( $xProp as $xPropName => $xPropValue ) + if( 'x-' == strtolower( substr( $xPropName, 0, 2 ))) + $tz->setproperty( $xPropName, $xPropValue ); + } + if( empty( $transTemp )) { // if no match found + if( $prevTrans ) { // then we use the last transition (before startdate) for the tz info + $date = new DateTime( "@{$prevTrans['ts']}" );// set transition date (UTC) + $date->modify( $prevTrans['offsetfrom'].'seconds' ); // convert utc date to local date + $d = $date->format( iCalUtilityFunctions::$fmt['YmdHis3'] ); + $d = explode( '-', $d ); // set date to array to ease up dtstart setting + $prevTrans['time'] = array( 'year' => (int) $d[0], 'month' => (int) $d[1], 'day' => (int) $d[2], 'hour' => (int) $d[3], 'min' => (int) $d[4], 'sec' => (int) $d[5] ); + $transTemp[0] = $prevTrans; + } + else { // or we use the timezone identifier to BUILD the standard tz info (?) + $date = new DateTime( 'now', new DateTimeZone( $timezone )); + $transTemp[0] = array( 'time' => $date->format( iCalUtilityFunctions::$fmt['YmdTHisO'] ), + 'offset' => $date->format( 'Z' ), + 'offsetfrom' => $date->format( 'Z' ), + 'isdst' => FALSE ); + } + } + unset( $transitions, $date, $prevTrans ); + foreach( $transTemp as $tix => $trans ) { // create standard/daylight subcomponents + $type = ( TRUE !== $trans['isdst'] ) ? 'standard' : 'daylight'; + $scomp = $tz->newComponent( $type ); + $scomp->setProperty( 'dtstart', $trans['time'] ); +// $scomp->setProperty( 'x-utc-timestamp', $tix.' : '.$trans['ts'] ); // test ### + if( !empty( $trans['abbr'] )) + $scomp->setProperty( 'tzname', $trans['abbr'] ); + if( isset( $trans['offsetfrom'] )) + $scomp->setProperty( 'tzoffsetfrom', iCalUtilityFunctions::offsetSec2His( $trans['offsetfrom'] )); + $scomp->setProperty( 'tzoffsetto', iCalUtilityFunctions::offsetSec2His( $trans['offset'] )); + if( isset( $trans['rdate'] )) + $scomp->setProperty( 'RDATE', $trans['rdate'] ); + } + return TRUE; + } +/** + * creates formatted output for calendar component property data value type date/date-time + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-10 + * @param array $datetime + * @param int $parno optional, default 6 + * @uses iCalUtilityFunctions::$fmt + * @uses iCalUtilityFunctions::_isOffset() + * @uses iCalUtilityFunctions::_tz2offset() + * @return string + */ + public static function _date2strdate( $datetime, $parno=6 ) { + if( !isset( $datetime['year'] ) && + !isset( $datetime['month'] ) && + !isset( $datetime['day'] ) && + !isset( $datetime['hour'] ) && + !isset( $datetime['min'] ) && + !isset( $datetime['sec'] )) + return; + $output = null; + foreach( $datetime as $dkey => & $dvalue ) + if( 'tz' != $dkey ) $dvalue = (int) $dvalue; + $output = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $datetime['year'], $datetime['month'], $datetime['day'] ); + if( 3 == $parno ) + return $output; + if( !isset( $datetime['hour'] )) $datetime['hour'] = 0; + if( !isset( $datetime['min'] )) $datetime['min'] = 0; + if( !isset( $datetime['sec'] )) $datetime['sec'] = 0; + $output .= 'T'.sprintf( iCalUtilityFunctions::$fmt['His'], $datetime['hour'], $datetime['min'], $datetime['sec'] ); + if( isset( $datetime['tz'] ) && ( '' < trim( $datetime['tz'] ))) { + $datetime['tz'] = trim( $datetime['tz'] ); + if( 'Z' == $datetime['tz'] ) + $parno = 7; + elseif( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) { + $parno = 7; + $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] ); + try { + $d = new DateTime( $output, new DateTimeZone( 'UTC' )); + if( 0 != $offset ) // adjust för offset + $d->modify( "$offset seconds" ); + $output = $d->format( 'Ymd\THis' ); + } + catch( Exception $e ) { + $output = date( 'Ymd\THis', mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] - $offset), $datetime['month'], $datetime['day'], $datetime['year'] )); + } + } + if( 7 == $parno ) + $output .= 'Z'; + } + return $output; + } +/** + * ensures internal duration format for input in array format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.19.4 - 2014-03-14 + * @param array $duration + * @return array + */ + public static function _duration2arr( $duration ) { + $seconds = 0; + foreach( $duration as $durKey => $durValue ) { + if( empty( $durValue )) continue; + switch ( $durKey ) { + case '0': case 'week': + $seconds += (((int) $durValue ) * 60 * 60 * 24 * 7 ); + break; + case '1': case 'day': + $seconds += (((int) $durValue ) * 60 * 60 * 24 ); + break; + case '2': case 'hour': + $seconds += (((int) $durValue ) * 60 * 60 ); + break; + case '3': case 'min': + $seconds += (((int) $durValue ) * 60 ); + break; + case '4': case 'sec': + $seconds += (int) $durValue; + break; + } + } + $output = array(); + $output['week'] = (int) floor( $seconds / ( 60 * 60 * 24 * 7 )); + if(( 0 < $output['week'] ) && ( 0 == ( $seconds % ( 60 * 60 * 24 * 7 )))) + return $output; + unset( $output['week'] ); + $output['day'] = (int) floor( $seconds / ( 60 * 60 * 24 )); + $seconds = ( $seconds % ( 60 * 60 * 24 )); + $output['hour'] = (int) floor( $seconds / ( 60 * 60 )); + $seconds = ( $seconds % ( 60 * 60 )); + $output['min'] = (int) floor( $seconds / 60 ); + $output['sec'] = ( $seconds % 60 ); + if( empty( $output['day'] )) + unset( $output['day'] ); + if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] )) + unset( $output['hour'], $output['min'], $output['sec'] ); + return $output; + } +/** + * convert startdate+duration to a array format datetime + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param array $startdate + * @param array $duration + * @uses iCalUtilityFunctions::$fmt + * @return array, date format + */ + public static function _duration2date( $startdate, $duration ) { + $dateOnly = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE; + $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0; + $startdate['min'] = ( isset( $startdate['min'] )) ? $startdate['min'] : 0; + $startdate['sec'] = ( isset( $startdate['sec'] )) ? $startdate['sec'] : 0; + $dtend = 0; + if( isset( $duration['week'] )) $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 ); + if( isset( $duration['day'] )) $dtend += ( $duration['day'] * 24 * 60 * 60 ); + if( isset( $duration['hour'] )) $dtend += ( $duration['hour'] * 60 *60 ); + if( isset( $duration['min'] )) $dtend += ( $duration['min'] * 60 ); + if( isset( $duration['sec'] )) $dtend += $duration['sec']; + $date = date( iCalUtilityFunctions::$fmt['YmdHis3'], + mktime((int) $startdate['hour'], (int) $startdate['min'], (int) ( $startdate['sec'] + $dtend ), (int) $startdate['month'], (int) $startdate['day'], (int) $startdate['year'] )); + $d = explode( '-', $date ); + $dtend2 = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); + if( isset( $startdate['tz'] )) + $dtend2['tz'] = $startdate['tz']; + if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] ))) + unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] ); + return $dtend2; + } +/** + * ensures internal duration format for an input string (iCal) formatted duration + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.14.1 - 2012-09-25 + * @param string $duration + * @uses iCalUtilityFunctions::_duration2arr() + * @return array + */ + public static function _durationStr2arr( $duration ) { + $duration = (string) trim( $duration ); + while( 'P' != strtoupper( substr( $duration, 0, 1 ))) { + if( 0 < strlen( $duration )) + $duration = substr( $duration, 1 ); + else + return false; // no leading P !?!? + } + $duration = substr( $duration, 1 ); // skip P + $duration = str_replace ( 't', 'T', $duration ); + $duration = str_replace ( 'T', '', $duration ); + $output = array(); + $val = null; + for( $ix=0; $ix < strlen( $duration ); $ix++ ) { + switch( strtoupper( substr( $duration, $ix, 1 ))) { + case 'W': + $output['week'] = $val; + $val = null; + break; + case 'D': + $output['day'] = $val; + $val = null; + break; + case 'H': + $output['hour'] = $val; + $val = null; + break; + case 'M': + $output['min'] = $val; + $val = null; + break; + case 'S': + $output['sec'] = $val; + $val = null; + break; + default: + if( !ctype_digit( substr( $duration, $ix, 1 ))) + return false; // unknown duration control character !?!? + else + $val .= substr( $duration, $ix, 1 ); + } + } + return iCalUtilityFunctions::_duration2arr( $output ); + } +/** + * creates formatted output for calendar component property data value type duration + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.15.8 - 2012-10-30 + * @param array $duration, array( week, day, hour, min, sec ) + * @return string + */ + public static function _duration2str( array $duration ) { + if( isset( $duration['week'] ) || + isset( $duration['day'] ) || + isset( $duration['hour'] ) || + isset( $duration['min'] ) || + isset( $duration['sec'] )) + $ok = TRUE; + else + return; + if( isset( $duration['week'] ) && ( 0 < $duration['week'] )) + return 'P'.$duration['week'].'W'; + $output = 'P'; + if( isset($duration['day'] ) && ( 0 < $duration['day'] )) + $output .= $duration['day'].'D'; + if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) || + ( isset( $duration['min']) && ( 0 < $duration['min'] )) || + ( isset( $duration['sec']) && ( 0 < $duration['sec'] ))) { + $output .= 'T'; + $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '0H'; + $output .= ( isset( $duration['min']) && ( 0 < $duration['min'] )) ? $duration['min']. 'M' : '0M'; + $output .= ( isset( $duration['sec']) && ( 0 < $duration['sec'] )) ? $duration['sec']. 'S' : '0S'; + } + if( 'P' == $output ) + $output = 'PT0H0M0S'; + return $output; + } +/** + * removes expkey+expvalue from array and returns hitval (if found) else returns elseval + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-11-08 + * @param array $array iCal property parameters + * @param string $expkey expected key + * @param string $expval expected value + * @param int $hitVal return value if found + * @param int $elseVal return value if not found + * @param int $preSet return value if already preset + * @return int + */ + public static function _existRem( & $array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) { + if( $preSet ) + return $preSet; + if( !is_array( $array ) || ( 0 == count( $array ))) + return $elseVal; + foreach( $array as $key => $value ) { + if( strtoupper( $expkey ) == strtoupper( $key )) { + if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) { + unset( $array[$key] ); + return $hitVal; + } + } + } + return $elseVal; + } +/** + * check if dates are in scope + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.7 - 2015-03-25 + * @param object $start datetime + * @param object $scopeStart datetime + * @param object $end datetime + * @param object $scopeEnd datetime + * @param string $format + * @return bool + */ + public static function _inScope( $start, $scopeStart, $end, $scopeEnd, $format ) { + return (( $start->format( $format ) >= $scopeStart->format( $format )) && + ( $end->format( $format ) <= $scopeEnd->format( $format ))); +} +/** + * mgnt geo part output + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.10 - 2013-09-02 + * @param float $ll + * @param string $format + * @return string + */ + public static function _geo2str2( $ll, $format ) { + if( 0.0 < $ll ) + $sign = '+'; + else + $sign = ( 0.0 > $ll ) ? '-' : ''; + return rtrim( rtrim( $sign.sprintf( $format, abs( $ll )), '0' ), '.' ); + } +/** + * checks if input contains a (array formatted) date/time + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.24 - 2013-07-02 + * @param array $input + * @uses iCalUtilityFunctions::_strdate2date() + * @return bool + */ + public static function _isArrayDate( $input ) { + if( !is_array( $input ) || isset( $input['week'] ) || isset( $input['timestamp'] ) || ( 3 > count( $input ))) + return FALSE; + if( 7 == count( $input )) + return TRUE; + if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) + return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); + if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] )) + return FALSE; + if(( 0 == $input[0] ) || ( 0 == $input[1] ) || ( 0 == $input[2] )) + return FALSE; + if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] )) + return FALSE; + if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) && + checkdate((int) $input[1], (int) $input[2], (int) $input[0] )) + return TRUE; + $input = iCalUtilityFunctions::_strdate2date( $input[1].'/'.$input[2].'/'.$input[0], 3 ); // m - d - Y + if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) + return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); + return FALSE; + } +/** + * checks if input array contains a timestamp date + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.16 - 2008-10-18 + * @param array $input + * @return bool + */ + public static function _isArrayTimestampDate( $input ) { + return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ; + } +/** + * controls if input string contains (trailing) UTC/iCal offset + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.14.1 - 2012-09-21 + * @param string $input + * @return bool + */ + public static function _isOffset( $input ) { + $input = trim( (string) $input ); + if( 'Z' == substr( $input, -1 )) + return TRUE; + elseif(( 5 <= strlen( $input )) && + ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) && + ( '0000' <= substr( $input, -4 )) && ( '9999' >= substr( $input, -4 ))) + return TRUE; + elseif(( 7 <= strlen( $input )) && + ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) && + ( '000000' <= substr( $input, -6 )) && ( '999999' >= substr( $input, -6 ))) + return TRUE; + return FALSE; + } +/** + * (very simple) conversion of a MS timezone to a PHP5 valid (Date-)timezone + * matching (MS) UCT offset and time zone descriptors + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.14.1 - 2012-09-16 + * @param string $timezone to convert + * @uses iCalUtilityFunctions::_tz2offset() + * @return bool + */ + public static function ms2phpTZ( & $timezone ) { + if( empty( $timezone )) + return FALSE; + $search = str_replace( '"', '', $timezone ); + $search = str_replace( array('GMT', 'gmt', 'utc' ), 'UTC', $search ); + if( '(UTC' != substr( $search, 0, 4 )) + return FALSE; + if( FALSE === ( $pos = strpos( $search, ')' ))) + return FALSE; + $pos = strpos( $search, ')' ); + $searchOffset = substr( $search, 4, ( $pos - 4 )); + $searchOffset = iCalUtilityFunctions::_tz2offset( str_replace( ':', '', $searchOffset )); + while( ' ' ==substr( $search, ( $pos + 1 ))) + $pos += 1; + $searchText = trim( str_replace( array( '(', ')', '&', ',', ' ' ), ' ', substr( $search, ( $pos + 1 )) )); + $searchWords = explode( ' ', $searchText ); + $timezone_abbreviations = DateTimeZone::listAbbreviations(); + $hits = array(); + foreach( $timezone_abbreviations as $name => $transitions ) { + foreach( $transitions as $cnt => $transition ) { + if( empty( $transition['offset'] ) || + empty( $transition['timezone_id'] ) || + ( $transition['offset'] != $searchOffset )) + continue; + $cWords = explode( '/', $transition['timezone_id'] ); + $cPrio = $hitCnt = $rank = 0; + foreach( $cWords as $cWord ) { + if( empty( $cWord )) + continue; + $cPrio += 1; + $sPrio = 0; + foreach( $searchWords as $sWord ) { + if( empty( $sWord ) || ( 'time' == strtolower( $sWord ))) + continue; + $sPrio += 1; + if( strtolower( $cWord ) == strtolower( $sWord )) { + $hitCnt += 1; + $rank += ( $cPrio + $sPrio ); + } + else + $rank += 10; + } + } + if( 0 < $hitCnt ) { + $hits[$rank][] = $transition['timezone_id']; + } + } + } + unset( $timezone_abbreviations ); + if( empty( $hits )) + return FALSE; + ksort( $hits ); + foreach( $hits as $rank => $tzs ) { + if( !empty( $tzs )) { + $timezone = reset( $tzs ); + return TRUE; + } + } + return FALSE; + } +/** + * transforms offset in seconds to [-/+]hhmm[ss] + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2011-05-02 + * @param string $seconds + * @return string + */ + public static function offsetSec2His( $seconds ) { + if( '-' == substr( $seconds, 0, 1 )) { + $prefix = '-'; + $seconds = substr( $seconds, 1 ); + } + elseif( '+' == substr( $seconds, 0, 1 )) { + $prefix = '+'; + $seconds = substr( $seconds, 1 ); + } + else + $prefix = '+'; + $output = ''; + $hour = (int) floor( $seconds / 3600 ); + if( 10 > $hour ) + $hour = '0'.$hour; + $seconds = $seconds % 3600; + $min = (int) floor( $seconds / 60 ); + if( 10 > $min ) + $min = '0'.$min; + $output = $hour.$min; + $seconds = $seconds % 60; + if( 0 < $seconds) { + if( 9 < $seconds) + $output .= $seconds; + else + $output .= '0'.$seconds; + } + return $prefix.$output; + } +/** + * updates an array with dates based on a recur pattern + * + * if missing, UNTIL is set 1 year from startdate (emergency break) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-10 + * @param array $result array to update, array([Y-m-d] => bool) + * @param array $recur pattern for recurrency (only value part, params ignored) + * @param mixed $wdate component start date, string / array / (datetime) obj + * @param mixed $fcnStart start date, string / array / (datetime) obj + * @param mixed $fcnEnd end date, string / array / (datetime) obj + * @uses iCalUtilityFunctions::_strDate2arr() + * @uses iCalUtilityFunctions::$fmt + * @uses iCalUtilityFunctions::_stepdate() + * @uses iCalUtilityFunctions::_recurIntervalIx() + * @uses iCalUtilityFunctions::_recurBYcntcheck() + * @return void + * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start OR not at all + */ + public static function _recur2date( & $result, $recur, $wdate, $fcnStart, $fcnEnd=FALSE ) { + if( is_string( $wdate )) + iCalUtilityFunctions::_strDate2arr( $wdate ); + elseif( is_a( $wdate, 'DateTime' )) { + $wdate = $wdate->format( iCalUtilityFunctions::$fmt['YmdHis2'] ); + iCalUtilityFunctions::_strDate2arr( $wdate ); + } + foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v; + $wdateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day'] ); + $wdateHis = sprintf( iCalUtilityFunctions::$fmt['His'], $wdate['hour'], $wdate['min'], $wdate['sec'] ); + $untilHis = $wdateHis; + if( is_string( $fcnStart )) + iCalUtilityFunctions::_strDate2arr( $fcnStart ); + elseif( is_a( $fcnStart, 'DateTime' )) { + $fcnStart = $fcnStart->format( iCalUtilityFunctions::$fmt['YmdHis2'] ); + iCalUtilityFunctions::_strDate2arr( $fcnStart ); + } + foreach( $fcnStart as $k => $v ) if( ctype_digit( $v )) $fcnStart[$k] = (int) $v; + $fcnStartYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $fcnStart['year'], $fcnStart['month'], $fcnStart['day'] ); + if( is_string( $fcnEnd )) + iCalUtilityFunctions::_strDate2arr( $fcnEnd ); + elseif( is_a( $fcnEnd, 'DateTime' )) { + $fcnEnd = $fcnEnd->format( iCalUtilityFunctions::$fmt['YmdHis2'] ); + iCalUtilityFunctions::_strDate2arr( $fcnEnd ); + } + if( !$fcnEnd ) { + $fcnEnd = $fcnStart; + $fcnEnd['year'] += 1; + } + foreach( $fcnEnd as $k => $v ) if( ctype_digit( $v )) $fcnEnd[$k] = (int) $v; + $fcnEndYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $fcnEnd['year'], $fcnEnd['month'], $fcnEnd['day'] ); +// echo "recur _in_ comp start ".implode('-',$wdate)." period start ".implode('-',$fcnStart)." period end ".implode('-',$fcnEnd)."
\n"; +// echo 'recur='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $recur, TRUE ))."
\n"; // test ### + if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] )) + $recur['UNTIL'] = $fcnEnd; // create break + if( isset( $recur['UNTIL'] )) { + foreach( $recur['UNTIL'] as $k => $v ) if( ctype_digit( $v )) $recur['UNTIL'][$k] = (int) $v; + unset( $recur['UNTIL']['tz'] ); + if( $fcnEnd > $recur['UNTIL'] ) { + $fcnEnd = $recur['UNTIL']; // emergency break + $fcnEndYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $fcnEnd['year'], $fcnEnd['month'], $fcnEnd['day'] ); + } + if( isset( $recur['UNTIL']['hour'] )) + $untilHis = sprintf( iCalUtilityFunctions::$fmt['His'], $recur['UNTIL']['hour'], $recur['UNTIL']['min'], $recur['UNTIL']['sec'] ); + else + $untilHis = sprintf( iCalUtilityFunctions::$fmt['His'], 23, 59, 59 ); +// echo 'recurUNTIL='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $recur['UNTIL'], TRUE )).", untilHis={$untilHis}
\n"; // test ### + } +// echo 'fcnEnd:'.$fcnEndYMD.$untilHis."
\n";//test + if( $wdateYMD > $fcnEndYMD ) { +// echo 'recur out of date, '.implode('-',$wdate).', end='.implode('-',$fcnEnd)."
\n";//test + return array(); // nothing to do.. . + } + if( !isset( $recur['FREQ'] )) // "MUST be specified.. ." + $recur['FREQ'] = 'DAILY'; // ?? + $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ?? + if( !isset( $recur['INTERVAL'] )) + $recur['INTERVAL'] = 1; + $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence + /* find out how to step up dates and set index for interval count */ + $step = array(); + if( 'YEARLY' == $recur['FREQ'] ) + $step['year'] = 1; + elseif( 'MONTHLY' == $recur['FREQ'] ) + $step['month'] = 1; + elseif( 'WEEKLY' == $recur['FREQ'] ) + $step['day'] = 7; + else + $step['day'] = 1; + if( isset( $step['year'] ) && isset( $recur['BYMONTH'] )) + $step = array( 'month' => 1 ); + if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ?? + $step = array( 'day' => 7 ); + if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] )) + $step = array( 'day' => 1 ); + $intervalarr = array(); + if( 1 < $recur['INTERVAL'] ) { + $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); + $intervalarr = array( $intervalix => 0 ); + } + if( isset( $recur['BYSETPOS'] )) { // save start date + weekno + $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array(); +// echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold
\n"; // test ### + if( is_array( $recur['BYSETPOS'] )) { + foreach( $recur['BYSETPOS'] as $bix => $bval ) + $recur['BYSETPOS'][$bix] = (int) $bval; + } + else + $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] ); + if( 'YEARLY' == $recur['FREQ'] ) { + $wdate['month'] = $wdate['day'] = 1; // start from beginning of year + $wdateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day'] ); + iCalUtilityFunctions::_stepdate( $fcnEnd, $fcnEndYMD, array( 'year' => 1 )); // make sure to count whole last year + } + elseif( 'MONTHLY' == $recur['FREQ'] ) { + $wdate['day'] = 1; // start from beginning of month + $wdateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day'] ); + iCalUtilityFunctions::_stepdate( $fcnEnd, $fcnEndYMD, array( 'month' => 1 )); // make sure to count whole last month + } + else + iCalUtilityFunctions::_stepdate( $fcnEnd, $fcnEndYMD, $step); // make sure to count whole last period +// echo "BYSETPOS endDat =".implode('-',$fcnEnd).' step='.var_export($step,TRUE)."
\n";//test### + $bysetposWold = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year'] )); + $bysetposYold = $wdate['year']; + $bysetposMold = $wdate['month']; + $bysetposDold = $wdate['day']; + } + else + iCalUtilityFunctions::_stepdate( $wdate, $wdateYMD, $step); + $year_old = null; + static $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' ); + /* MAIN LOOP */ +// echo "recur start:$wdateYMD, end:$fcnEndYMD
\n";//test + while( TRUE ) { +// echo "recur while:$wdateYMD, end:$fcnEndYMD
\n";//test + if( $wdateYMD.$wdateHis > $fcnEndYMD.$untilHis ) + break; + if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) + break; + if( $year_old != $wdate['year'] ) { + $year_old = $wdate['year']; + $daycnts = array(); + $yeardays = $weekno = 0; + $yeardaycnt = array(); + foreach( $daynames as $dn ) + $yeardaycnt[$dn] = 0; + for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters + $daycnts[$m] = array(); + $weekdaycnt = array(); + foreach( $daynames as $dn ) + $weekdaycnt[$dn] = 0; + $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); + for( $d = 1; $d <= $mcnt; $d++ ) { + $daycnts[$m][$d] = array(); + if( isset( $recur['BYYEARDAY'] )) { + $yeardays++; + $daycnts[$m][$d]['yearcnt_up'] = $yeardays; + } + if( isset( $recur['BYDAY'] )) { + $day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] )); + $day = $daynames[$day]; + $daycnts[$m][$d]['DAY'] = $day; + $weekdaycnt[$day]++; + $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day]; + $yeardaycnt[$day]++; + $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day]; + } + if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) + $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year'])); + } // end for( $d = 1; $d <= $mcnt; $d++ ) + } // end for( $m = 1; $m <= 12; $m++ ) + $daycnt = 0; + $yeardaycnt = array(); + if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) { + $weekno = null; + for( $d=31; $d > 25; $d-- ) { // get last weekno for year + if( !$weekno ) + $weekno = $daycnts[12][$d]['weekno_up']; + elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) { + $weekno = $daycnts[12][$d]['weekno_up']; + break; + } + } + } + for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters + $weekdaycnt = array(); + foreach( $daynames as $dn ) + $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0; + $monthcnt = 0; + $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); + for( $d = $mcnt; $d > 0; $d-- ) { + if( isset( $recur['BYYEARDAY'] )) { + $daycnt -= 1; + $daycnts[$m][$d]['yearcnt_down'] = $daycnt; + } + if( isset( $recur['BYMONTHDAY'] )) { + $monthcnt -= 1; + $daycnts[$m][$d]['monthcnt_down'] = $monthcnt; + } + if( isset( $recur['BYDAY'] )) { + $day = $daycnts[$m][$d]['DAY']; + $weekdaycnt[$day] -= 1; + $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day]; + $yeardaycnt[$day] -= 1; + $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day]; + } + if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) + $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1); + } + } // end for( $m = 12; $m > 0; $m-- ) + } // end if( $year_old != $wdate['year'] ) + /* check interval */ + if( 1 < $recur['INTERVAL'] ) { + /* create interval index */ + $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); + /* check interval */ + $currentKey = array_keys( $intervalarr ); + $currentKey = end( $currentKey ); // get last index + if( $currentKey != $intervalix ) + $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 )); + if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) && + ( 0 != $intervalarr[$intervalix] )) { + /* step up date */ +// echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."
\n";//test + iCalUtilityFunctions::_stepdate( $wdate, $wdateYMD, $step); + continue; + } + else // continue within the selected interval + $intervalarr[$intervalix] = 0; +// echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."
\n";//test + } // endif( 1 < $recur['INTERVAL'] ) + $updateOK = TRUE; + if( $updateOK && isset( $recur['BYMONTH'] )) + $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH'] + , $wdate['month'] + ,($wdate['month'] - 13)); + if( $updateOK && isset( $recur['BYWEEKNO'] )) + $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO'] + , $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] + , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] ); + if( $updateOK && isset( $recur['BYYEARDAY'] )) + $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY'] + , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up'] + , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] ); + if( $updateOK && isset( $recur['BYMONTHDAY'] )) + $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY'] + , $wdate['day'] + , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] ); +// echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "
\n";//test### + if( $updateOK && isset( $recur['BYDAY'] )) { + $updateOK = FALSE; + $m = $wdate['month']; + $d = $wdate['day']; + if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no + $daynoexists = $daynosw = $daynamesw = FALSE; + if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] ) + $daynamesw = TRUE; + if( isset( $recur['BYDAY'][0] )) { + $daynoexists = TRUE; + if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] )) + $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] + , $daycnts[$m][$d]['monthdayno_up'] + , $daycnts[$m][$d]['monthdayno_down'] ); + elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) + $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] + , $daycnts[$m][$d]['yeardayno_up'] + , $daycnts[$m][$d]['yeardayno_down'] ); + } + if(( $daynoexists && $daynosw && $daynamesw ) || + ( !$daynoexists && !$daynosw && $daynamesw )) { + $updateOK = TRUE; +// echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK
\n"; // test ### + } +// echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK
\n"; // test ### + } + else { + foreach( $recur['BYDAY'] as $bydayvalue ) { + $daynoexists = $daynosw = $daynamesw = FALSE; + if( isset( $bydayvalue['DAY'] ) && + ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] )) + $daynamesw = TRUE; + if( isset( $bydayvalue[0] )) { + $daynoexists = TRUE; + if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || + isset( $recur['BYMONTH'] )) + $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] + , $daycnts[$m][$d]['monthdayno_up'] + , $daycnts[$m][$d]['monthdayno_down'] ); + elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) + $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] + , $daycnts[$m][$d]['yeardayno_up'] + , $daycnts[$m][$d]['yeardayno_down'] ); + } +// echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw
\n"; // test ### + if(( $daynoexists && $daynosw && $daynamesw ) || + ( !$daynoexists && !$daynosw && $daynamesw )) { + $updateOK = TRUE; + break; + } + } + } + } +// echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "
\n"; // test ### + /* check BYSETPOS */ + if( $updateOK ) { + if( isset( $recur['BYSETPOS'] ) && + ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) { + if( isset( $recur['WEEKLY'] )) { + if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] ) + $bysetposw1[] = $wdateYMD; + else + $bysetposw2[] = $wdateYMD; + } + else { + if(( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && + ( $bysetposYold == $wdate['year'] )) || + ( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) && + (( $bysetposYold == $wdate['year'] ) && + ( $bysetposMold == $wdate['month'] ))) || + ( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && + (( $bysetposYold == $wdate['year'] ) && + ( $bysetposMold == $wdate['month']) && + ( $bysetposDold == $wdate['day'] )))) { +// echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."
\n";//test + $bysetposymd1[] = $wdateYMD; + } + else { +// echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."
\n";//test + $bysetposymd2[] = $wdateYMD; + } + } + } + else { + if( checkdate($wdate['month'], $wdate['day'], $wdate['year'] )) { + /* update result array if BYSETPOS is not set */ + $countcnt++; + if( $fcnStartYMD <= $wdateYMD ) { // only output within period + $result[$wdateYMD] = TRUE; +// echo "recur $wdateYMD
\n";//test + } + } +// else echo "recur, no date $wdateYMD
\n";//test + $updateOK = FALSE; + } + } + /* step up date */ + iCalUtilityFunctions::_stepdate( $wdate, $wdateYMD, $step); + /* check if BYSETPOS is set for updating result array */ + if( $updateOK && isset( $recur['BYSETPOS'] )) { + $bysetpos = FALSE; + if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && + ( $bysetposYold != $wdate['year'] )) { + $bysetpos = TRUE; + $bysetposYold = $wdate['year']; + } + elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] && + (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) { + $bysetpos = TRUE; + $bysetposYold = $wdate['year']; + $bysetposMold = $wdate['month']; + } + elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) { + $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year'])); + if( $bysetposWold != $weekno ) { + $bysetposWold = $weekno; + $bysetpos = TRUE; + } + } + elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && + (( $bysetposYold != $wdate['year'] ) || + ( $bysetposMold != $wdate['month'] ) || + ( $bysetposDold != $wdate['day'] ))) { + $bysetpos = TRUE; + $bysetposYold = $wdate['year']; + $bysetposMold = $wdate['month']; + $bysetposDold = $wdate['day']; + } + if( $bysetpos ) { + if( isset( $recur['BYWEEKNO'] )) { + $bysetposarr1 = & $bysetposw1; + $bysetposarr2 = & $bysetposw2; + } + else { + $bysetposarr1 = & $bysetposymd1; + $bysetposarr2 = & $bysetposymd2; + } + + foreach( $recur['BYSETPOS'] as $ix ) { + if( 0 > $ix ) // both positive and negative BYSETPOS allowed + $ix = ( count( $bysetposarr1 ) + $ix + 1); + $ix--; + if( isset( $bysetposarr1[$ix] )) { + if( $fcnStartYMD <= $bysetposarr1[$ix] ) { // only output within period +// $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, (int) substr( $bysetposarr1[$ix], 4, 2 ), (int) substr( $bysetposarr1[$ix], 6, 2 ), (int) substr( $bysetposarr1[$ix], 0, 3 ))); // test ### +// echo " testYMD (weekno)=$bysetposarr1[$ix] ($testweekno)"; // test ### + $result[$bysetposarr1[$ix]] = TRUE; + } + $countcnt++; + } + if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) + break; + } +// echo "
\n"; // test ### + $bysetposarr1 = $bysetposarr2; + $bysetposarr2 = array(); + } // end if( $bysetpos ) + } // end if( $updateOK && isset( $recur['BYSETPOS'] )) + } // end while( TRUE ) +// echo 'output='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $result, TRUE ))."
\n"; // test ### + } +/** + * _recur2date help function, checking BYDAY (etc) hits + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.12 - 2011-01-03 + * @param array $BYvalue + * @param int $upValue + * @param int $downValue + * @return bool + */ + public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) { + if( is_array( $BYvalue ) && + ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue ))) + return TRUE; + elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue )) + return TRUE; + else + return FALSE; + } +/** + * _recur2date help function, (re-)calculate internal index + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.12 - 2011-01-03 + * @param string $freq + * @param array $date + * @param int $wkst + * @return bool + */ + public static function _recurIntervalIx( $freq, $date, $wkst ) { + /* create interval index */ + switch( $freq ) { + case 'YEARLY': + $intervalix = $date['year']; + break; + case 'MONTHLY': + $intervalix = $date['year'].'-'.$date['month']; + break; + case 'WEEKLY': + $intervalix = (int) date( 'W', mktime( 0, 0, $wkst, (int) $date['month'], (int) $date['day'], (int) $date['year'] )); + break; + case 'DAILY': + default: + $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day']; + break; + } + return $intervalix; + } +/** + * sort recur dates + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.12 - 2011-01-03 + * @param array $bydaya + * @param array $bydayb + * @return int + */ + public static function _recurBydaySort( $bydaya, $bydayb ) { + static $days = array( 'SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6 ); + return ( $days[substr( $bydaya, -2 )] < $days[substr( $bydayb, -2 )] ) ? -1 : 1; + } +/** + * convert input format for exrule and rrule to internal format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-10 + * @param array $rexrule + * @uses iCalUtilityFunctions::_strDate2arr() + * @uses iCalUtilityFunctions::_isArrayTimestampDate() + * @uses iCalUtilityFunctions::_timestamp2date() + * @uses iCalUtilityFunctions::_chkDateArr() + * @uses iCalUtilityFunctions::_isOffset() + * @uses iCalUtilityFunctions::$fmt + * @uses iCalUtilityFunctions::_strdate2date() + * @return array + */ + public static function _setRexrule( $rexrule ) { + $input = array(); + if( empty( $rexrule )) + return $input; + $rexrule = array_change_key_case( $rexrule, CASE_UPPER ); + foreach( $rexrule as $rexrulelabel => $rexrulevalue ) { + if( 'UNTIL' != $rexrulelabel ) + $input[$rexrulelabel] = $rexrulevalue; + else { + iCalUtilityFunctions::_strDate2arr( $rexrulevalue ); + if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp, always date-time UTC + $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 7, 'UTC' ); + elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) { // date or UTC date-time + $parno = ( isset( $rexrulevalue['hour'] ) || isset( $rexrulevalue[4] )) ? 7 : 3; + $d = iCalUtilityFunctions::_chkDateArr( $rexrulevalue, $parno ); + if(( 3 < $parno ) && isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); + $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $input[$rexrulelabel]['unparsedtext'] ); + } + else + $input[$rexrulelabel] = $d; + } + elseif( 8 <= strlen( trim( $rexrulevalue ))) { // ex. textual date-time 2006-08-03 10:12:18 => UTC + $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $rexrulevalue ); + unset( $input['$rexrulelabel']['unparsedtext'] ); + } + if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] )) + $input[$rexrulelabel]['tz'] = 'Z'; + } + } + /* set recurrence rule specification in rfc2445 order */ + $input2 = array(); + if( isset( $input['FREQ'] )) + $input2['FREQ'] = $input['FREQ']; + if( isset( $input['UNTIL'] )) + $input2['UNTIL'] = $input['UNTIL']; + elseif( isset( $input['COUNT'] )) + $input2['COUNT'] = $input['COUNT']; + if( isset( $input['INTERVAL'] )) + $input2['INTERVAL'] = $input['INTERVAL']; + if( isset( $input['BYSECOND'] )) + $input2['BYSECOND'] = $input['BYSECOND']; + if( isset( $input['BYMINUTE'] )) + $input2['BYMINUTE'] = $input['BYMINUTE']; + if( isset( $input['BYHOUR'] )) + $input2['BYHOUR'] = $input['BYHOUR']; + if( isset( $input['BYDAY'] )) { + if( !is_array( $input['BYDAY'] )) // ensure upper case.. . + $input2['BYDAY'] = strtoupper( $input['BYDAY'] ); + else { + foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) { + if( 'DAY' == strtoupper( $BYDAYx )) + $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv ); + elseif( !is_array( $BYDAYv )) { + $input2['BYDAY'][$BYDAYx] = $BYDAYv; + } + else { + foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) { + if( 'DAY' == strtoupper( $BYDAYx2 )) + $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 ); + else + $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2; + } + } + } + } + } + if( isset( $input['BYMONTHDAY'] )) + $input2['BYMONTHDAY'] = $input['BYMONTHDAY']; + if( isset( $input['BYYEARDAY'] )) + $input2['BYYEARDAY'] = $input['BYYEARDAY']; + if( isset( $input['BYWEEKNO'] )) + $input2['BYWEEKNO'] = $input['BYWEEKNO']; + if( isset( $input['BYMONTH'] )) + $input2['BYMONTH'] = $input['BYMONTH']; + if( isset( $input['BYSETPOS'] )) + $input2['BYSETPOS'] = $input['BYSETPOS']; + if( isset( $input['WKST'] )) + $input2['WKST'] = $input['WKST']; + return $input2; + } +/** + * convert format for input date to internal date with parameters + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param string $tz optional + * @param array $params optional + * @param string $caller optional + * @param string $objName optional + * @param string $tzid optional + * @uses iCalUtilityFunctions::$tzComps + * @uses iCalUtilityFunctions::_strDate2arr() + * @uses iCalUtilityFunctions::_isArrayDate() + * @uses iCalUtilityFunctions::_chkDateArr() + * @uses iCalUtilityFunctions::_isOffset() + * @uses iCalUtilityFunctions::_setParams() + * @uses iCalUtilityFunctions::_existRem() + * @uses iCalUtilityFunctions::$fmt + * @uses iCalUtilityFunctions::_isArrayTimestampDate() + * @uses iCalUtilityFunctions::_timestamp2date() + * @uses iCalUtilityFunctions::_strdate2date() + * @return array + */ + public static function _setDate( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE, $caller=null, $objName=null, $tzid=FALSE ) { + $input = $parno = null; + $localtime = (( 'dtstart' == $caller ) && in_array( $objName, iCalUtilityFunctions::$tzComps )) ? TRUE : FALSE; + iCalUtilityFunctions::_strDate2arr( $year ); + if( iCalUtilityFunctions::_isArrayDate( $year )) { + $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, FALSE ); //$parno ); + if( 100 > $input['value']['year'] ) + $input['value']['year'] += 2000; + if( $localtime ) + unset( $month['VALUE'], $month['TZID'] ); + elseif( !isset( $month['TZID'] ) && isset( $tzid )) + $month['TZID'] = $tzid; + if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) + unset( $month['TZID'] ); + elseif( !isset( $input['value']['tz'] ) && isset( $month['TZID'] ) && iCalUtilityFunctions::_isOffset( $month['TZID'] )) { + $input['value']['tz'] = $month['TZID']; + unset( $month['TZID'] ); + } + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); + $hitval = ( isset( $input['value']['tz'] )) ? 7 : 6; + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval ); + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $input['value'] ), $parno ); + if( 6 > $parno ) + unset( $input['value']['tz'], $input['params']['TZID'], $tzid ); + if(( 6 <= $parno ) && isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { + $d = $input['value']; + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); + $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno ); + unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); + } + if( isset( $input['value']['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { + $input['params']['TZID'] = $input['value']['tz']; + unset( $input['value']['tz'] ); + } + } // end if( iCalUtilityFunctions::_isArrayDate( $year )) + elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { + if( $localtime ) unset ( $month['VALUE'], $month['TZID'] ); + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); + $hitval = 7; + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno ); + if( isset( $year['tz'] ) && !empty( $year['tz'] )) { + if( !iCalUtilityFunctions::_isOffset( $year['tz'] )) { + $input['params']['TZID'] = $year['tz']; + unset( $year['tz'], $tzid ); + } + else { + if( isset( $input['params']['TZID'] ) && !empty( $input['params']['TZID'] )) { + if( !iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) + unset( $tzid ); + else + unset( $input['params']['TZID']); + } + elseif( isset( $tzid ) && !iCalUtilityFunctions::_isOffset( $tzid )) + $input['params']['TZID'] = $tzid; + } + } + elseif( isset( $input['params']['TZID'] ) && !empty( $input['params']['TZID'] )) { + if( iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { + $year['tz'] = $input['params']['TZID']; + unset( $input['params']['TZID']); + if( isset( $tzid ) && !empty( $tzid ) && !iCalUtilityFunctions::_isOffset( $tzid )) + $input['params']['TZID'] = $tzid; + } + } + elseif( isset( $tzid ) && !empty( $tzid )) { + if( iCalUtilityFunctions::_isOffset( $tzid )) { + $year['tz'] = $tzid; + unset( $input['params']['TZID']); + } + else + $input['params']['TZID'] = $tzid; + } + $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, $parno ); + } // end elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) + elseif( 8 <= strlen( trim((string) $year ))) { // ex. 2006-08-03 10:12:18 [[[+/-]1234[56]] / timezone] + if( $localtime ) + unset( $month['VALUE'], $month['TZID'] ); + elseif( !isset( $month['TZID'] ) && !empty( $tzid )) + $month['TZID'] = $tzid; + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno ); + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno ); + $input['value'] = iCalUtilityFunctions::_strdate2date( $year, $parno ); + if( 3 == $parno ) + unset( $input['value']['tz'], $input['params']['TZID'] ); + unset( $input['value']['unparsedtext'] ); + if( isset( $input['value']['tz'] )) { + if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { + $d = $input['value']; + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); + $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); + } + else { + $input['params']['TZID'] = $input['value']['tz']; + unset( $input['value']['tz'] ); + } + } + elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { + $d = $input['value']; + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $input['params']['TZID'] ); + $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); + } + } // end elseif( 8 <= strlen( trim((string) $year ))) + else { + if( is_array( $params )) + $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); + elseif( is_array( $tz )) { + $input['params'] = iCalUtilityFunctions::_setParams( $tz, array( 'VALUE' => 'DATE-TIME' )); + $tz = FALSE; + } + elseif( is_array( $hour )) { + $input['params'] = iCalUtilityFunctions::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' )); + $hour = $min = $sec = $tz = FALSE; + } + if( $localtime ) + unset ( $input['params']['VALUE'], $input['params']['TZID'] ); + elseif( !isset( $tz ) && !isset( $input['params']['TZID'] ) && !empty( $tzid )) + $input['params']['TZID'] = $tzid; + elseif( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) + unset( $input['params']['TZID'] ); + elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { + $tz = $input['params']['TZID']; + unset( $input['params']['TZID'] ); + } + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); + $hitval = ( iCalUtilityFunctions::_isOffset( $tz )) ? 7 : 6; + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno ); + $input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day ); + if( 3 != $parno ) { + $input['value']['hour'] = ( $hour ) ? $hour : '0'; + $input['value']['min'] = ( $min ) ? $min : '0'; + $input['value']['sec'] = ( $sec ) ? $sec : '0'; + if( !empty( $tz )) + $input['value']['tz'] = $tz; + $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], $parno ); + if( !empty( $tz ) && !iCalUtilityFunctions::_isOffset( $tz )) + $strdate .= ( 'Z' == $tz ) ? $tz : ' '.$tz; + $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno ); + unset( $input['value']['unparsedtext'] ); + if( isset( $input['value']['tz'] )) { + if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { + $d = $input['value']; + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); + $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); + } + else { + $input['params']['TZID'] = $input['value']['tz']; + unset( $input['value']['tz'] ); + } + } + elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { + $d = $input['value']; + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $input['params']['TZID'] ); + $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); + } + } + } // end else (i.e. using all arguments) + if(( 3 == $parno ) || ( isset( $input['params']['VALUE'] ) && ( 'DATE' == $input['params']['VALUE'] ))) { + $input['params']['VALUE'] = 'DATE'; + unset( $input['value']['hour'], $input['value']['min'], $input['value']['sec'], $input['value']['tz'], $input['params']['TZID'] ); + } + elseif( isset( $input['params']['TZID'] )) { + if(( 'UTC' == strtoupper( $input['params']['TZID'] )) || ( 'GMT' == strtoupper( $input['params']['TZID'] ))) { + $input['value']['tz'] = 'Z'; + unset( $input['params']['TZID'] ); + } + else + unset( $input['value']['tz'] ); + } + elseif( isset( $input['value']['tz'] )) { + if(( 'UTC' == strtoupper( $input['value']['tz'] )) || ( 'GMT' == strtoupper( $input['value']['tz'] ))) + $input['value']['tz'] = 'Z'; + if( 'Z' != $input['value']['tz'] ) { + $input['params']['TZID'] = $input['value']['tz']; + unset( $input['value']['tz'] ); + } + else + unset( $input['params']['TZID'] ); + } + if( $localtime ) + unset( $input['value']['tz'], $input['params']['TZID'] ); + return $input; + } +/** + * convert format for input date (UTC) to internal date with parameters + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-10 + * @param mixed $year + * @param mixed $month optional + * @param int $day optional + * @param int $hour optional + * @param int $min optional + * @param int $sec optional + * @param array $params optional + * @uses iCalUtilityFunctions::_strDate2arr() + * @uses iCalUtilityFunctions::_isArrayDate() + * @uses iCalUtilityFunctions::_chkDateArr() + * @uses iCalUtilityFunctions::_setParams() + * @uses iCalUtilityFunctions::_isOffset() + * @uses iCalUtilityFunctions::$fmt + * @uses iCalUtilityFunctions::_strdate2date() + * @uses iCalUtilityFunctions::_isArrayTimestampDate() + * @uses iCalUtilityFunctions::_timestamp2date() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses iCalUtilityFunctions::_existRem() + * @return array + */ + public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { + $input = null; + iCalUtilityFunctions::_strDate2arr( $year ); + if( iCalUtilityFunctions::_isArrayDate( $year )) { + $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, 7 ); + if( isset( $input['value']['year'] ) && ( 100 > $input['value']['year'] )) + $input['value']['year'] += 2000; + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); + unset( $input['params']['VALUE'] ); + if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) + $tzid = $input['value']['tz']; + elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) + $tzid = $input['params']['TZID']; + else + $tzid = ''; + unset( $input['params']['VALUE'], $input['params']['TZID'] ); + if( !empty( $tzid ) && ( 'Z' != $tzid ) && iCalUtilityFunctions::_isOffset( $tzid )) { + $d = $input['value']; + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $tzid ); + $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $input['value']['unparsedtext'] ); + } + } + elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { + if( isset( $year['tz'] ) && ! iCalUtilityFunctions::_isOffset( $year['tz'] )) + $year['tz'] = 'UTC'; + elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) + $year['tz'] = $input['params']['TZID']; + else + $year['tz'] = 'UTC'; + $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, 7 ); + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); + unset( $input['params']['VALUE'], $input['params']['TZID'] ); + } + elseif( 8 <= strlen( trim((string) $year ))) { // ex. 2006-08-03 10:12:18 + $input['value'] = iCalUtilityFunctions::_strdate2date( $year, 7 ); + unset( $input['value']['unparsedtext'] ); + $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); + if(( !isset( $input['value']['tz'] ) || empty( $input['value']['tz'] )) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { + $d = $input['value']; + $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $input['params']['TZID'] ); + $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $input['value']['unparsedtext'] ); + } + unset( $input['params']['VALUE'], $input['params']['TZID'] ); + } + else { + $input['value'] = array( 'year' => $year + , 'month' => $month + , 'day' => $day + , 'hour' => $hour + , 'min' => $min + , 'sec' => $sec ); + if( isset( $tz )) $input['value']['tz'] = $tz; + if(( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) || + ( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))) { + if( !isset( $tz ) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) + $input['value']['tz'] = $input['params']['TZID']; + unset( $input['params']['TZID'] ); + $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], 7 ); + $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); + unset( $input['value']['unparsedtext'] ); + } + $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); + unset( $input['params']['VALUE'] ); + } + $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default + if( !isset( $input['value']['hour'] )) $input['value']['hour'] = 0; + if( !isset( $input['value']['min'] )) $input['value']['min'] = 0; + if( !isset( $input['value']['sec'] )) $input['value']['sec'] = 0; + $input['value']['tz'] = 'Z'; + return $input; + } +/** + * check index and set (an indexed) content in multiple value array + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.12 - 2011-01-03 + * @param array $valArr + * @param mixed $value + * @param array $params + * @param array $defaults + * @param int $index + * @uses iCalUtilityFunctions::_setParams() + * @return void + */ + public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) { + if( !is_array( $valArr )) $valArr = array(); + if( $index ) + $index = $index - 1; + elseif( 0 < count( $valArr )) { + $keys = array_keys( $valArr ); + $index = end( $keys ) + 1; + } + else + $index = 0; + $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults )); + ksort( $valArr ); + } +/** + * set input (formatted) parameters- component property attributes + * + * default parameters can be set, if missing + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.18.10 - 2013-09-04 + * @param array $params + * @param array $defaults + * @return array + */ + public static function _setParams( $params, $defaults=FALSE ) { + if( !is_array( $params)) + $params = array(); + $input = array(); + $params = array_change_key_case( $params, CASE_UPPER ); + foreach( $params as $paramKey => $paramValue ) { + if( is_array( $paramValue )) { + foreach( $paramValue as $pkey => $pValue ) { + if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 ))) + $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 )); + } + } + elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 ))) + $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 )); + if( 'VALUE' == $paramKey ) + $input['VALUE'] = strtoupper( $paramValue ); + else + $input[$paramKey] = $paramValue; + } + if( is_array( $defaults )) { + foreach( $defaults as $paramKey => $paramValue ) { + if( !isset( $input[$paramKey] )) + $input[$paramKey] = $paramValue; + } + } + return (0 < count( $input )) ? $input : null; + } +/** + * set sort arguments/parameters in component + * + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param object $c valendar component + * @param string $sortArg + * @uses calendarComponent::$srtk + * @uses calendarComponent::$objName + * @uses calendarComponent::$getProperty() + * @uses iCalUtilityFunctions::$mProps1 + * @uses calendarComponent::_getProperties() + * @uses iCalUtilityFunctions::_date2strdate() + * @return void + */ + public static function _setSortArgs( $c, $sortArg=FALSE ) { + $c->srtk = array( '0', '0', '0', '0' ); + if( 'vtimezone' == $c->objName ) { + if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' ))) + $c->srtk[0] = 0; + return; + } + elseif( $sortArg ) { + if( in_array( $sortArg, iCalUtilityFunctions::$mProps1 )) { + $propValues = array(); + $c->_getProperties( $sortArg, $propValues ); + if( !empty( $propValues )) { + $sk = array_keys( $propValues ); + $c->srtk[0] = $sk[0]; + if( 'RELATED-TO' == $sortArg ) + $c->srtk[0] .= $c->getProperty( 'uid' ); + } + elseif( 'RELATED-TO' == $sortArg ) + $c->srtk[0] = $c->getProperty( 'uid' ); + } + elseif( FALSE !== ( $d = $c->getProperty( $sortArg ))) { + $c->srtk[0] = $d; + if( 'UID' == $sortArg ) { + if( FALSE !== ( $d = $c->getProperty( 'recurrence-id' ))) { + $c->srtk[1] = iCalUtilityFunctions::_date2strdate( $d ); + if( FALSE === ( $c->srtk[2] = $c->getProperty( 'sequence' ))) + $c->srtk[2] = PHP_INT_MAX; + } + else + $c->srtk[1] = $c->srtk[2] = PHP_INT_MAX; + } + } + return; + } // end elseif( $sortArg ) + if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) { + $c->srtk[0] = iCalUtilityFunctions::_strdate2date( $d[1] ); + unset( $c->srtk[0]['unparsedtext'] ); + } + elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' ))) + $c->srtk[0] = 0; // sortkey 0 : dtstart + if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' ))) { + $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); // sortkey 1 : dtend/due(/duration) + unset( $c->srtk[1]['unparsedtext'] ); + } + elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) { + if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' ))) { + $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); + unset( $c->srtk[1]['unparsedtext'] ); + } + elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' ))) + if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE ))) + $c->srtk[1] = 0; + } + if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' ))) // sortkey 2 : created/dtstamp + if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' ))) + $c->srtk[2] = 0; + if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' ))) // sortkey 3 : uid + $c->srtk[3] = 0; + } +/** + * break lines at pos 75 + * + * Lines of text SHOULD NOT be longer than 75 octets, excluding the line + * break. Long content lines SHOULD be split into a multiple line + * representations using a line "folding" technique. That is, a long + * line can be split between any two characters by inserting a CRLF + * immediately followed by a single linear white space character (i.e., + * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence + * of CRLF followed immediately by a single linear white space character + * is ignored (i.e., removed) when processing the content type. + * + * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where + * the reserved expression "\n" in the arg $string could be broken up by the + * folding of lines, causing ambiguity in the return string. + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @param string $string + * @param string $nl + * @return string + */ + public static function _size75( $string, $nl ) { + $tmp = $string; + $string = ''; + $cCnt = $x = 0; + while( TRUE ) { + if( !isset( $tmp[$x] )) { + $string .= $nl; // loop breakes here + break; + } + elseif(( 74 <= $cCnt ) && ( '\\' == $tmp[$x] ) && ( 'n' == $tmp[$x+1] )) { + $string .= $nl.' \n'; // don't break lines inside '\n' + $x += 2; + if( !isset( $tmp[$x] )) { + $string .= $nl; + break; + } + $cCnt = 3; + } + elseif( 75 <= $cCnt ) { + $string .= $nl.' '; + $cCnt = 1; + } + $byte = ord( $tmp[$x] ); + $string .= $tmp[$x]; + switch( TRUE ) { // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + case(( $byte >= 0x20 ) && ( $byte <= 0x7F )): // characters U-00000000 - U-0000007F (same as ASCII) + $cCnt += 1; + break; // add a one byte character + case(( $byte & 0xE0) == 0xC0 ): // characters U-00000080 - U-000007FF, mask 110XXXXX + if( isset( $tmp[$x+1] )) { + $cCnt += 1; + $string .= $tmp[$x+1]; + $x += 1; // add a two bytes character + } + break; + case(( $byte & 0xF0 ) == 0xE0 ): // characters U-00000800 - U-0000FFFF, mask 1110XXXX + if( isset( $tmp[$x+2] )) { + $cCnt += 1; + $string .= $tmp[$x+1].$tmp[$x+2]; + $x += 2; // add a three bytes character + } + break; + case(( $byte & 0xF8 ) == 0xF0 ): // characters U-00010000 - U-001FFFFF, mask 11110XXX + if( isset( $tmp[$x+3] )) { + $cCnt += 1; + $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3]; + $x += 3; // add a four bytes character + } + break; + case(( $byte & 0xFC ) == 0xF8 ): // characters U-00200000 - U-03FFFFFF, mask 111110XX + if( isset( $tmp[$x+4] )) { + $cCnt += 1; + $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4]; + $x += 4; // add a five bytes character + } + break; + case(( $byte & 0xFE ) == 0xFC ): // characters U-04000000 - U-7FFFFFFF, mask 1111110X + if( isset( $tmp[$x+5] )) { + $cCnt += 1; + $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4].$tmp[$x+5]; + $x += 5; // add a six bytes character + } + default: // add any other byte without counting up $cCnt + break; + } // end switch( TRUE ) + $x += 1; // next 'byte' to test + } // end while( TRUE ) { + return $string; + } +/** + * sort callback function for exdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-07 + * @param array $a + * @param array $b + * @uses iCalUtilityFunctions::$fmt + * @return int + */ + public static function _sortExdate1( $a, $b ) { + $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $a['year'], (int) $a['month'], (int) $a['day'] ); + $as .= ( isset( $a['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $a['hour'], (int) $a['min'], (int) $a['sec'] ) : ''; + $bs = sprintf( iCalUtilityFunctions::$fmt['His'], (int) $b['year'], (int) $b['month'], (int) $b['day'] ); + $bs .= ( isset( $b['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $b['hour'], (int) $b['min'], (int) $b['sec'] ) : ''; + return strcmp( $as, $bs ); + } +/** + * sort callback function for exdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-07 + * @param array $a + * @param array $b + * @uses iCalUtilityFunctions::$fmt + * @return int + */ + public static function _sortExdate2( $a, $b ) { + $val = reset( $a['value'] ); + $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); + $as .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; + $val = reset( $b['value'] ); + $bs = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); + $bs .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; + return strcmp( $as, $bs ); + } +/** + * sort callback function for rdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-07 + * @param array $a + * @param array $b + * @uses iCalUtilityFunctions::$fmt + * @return int + */ + public static function _sortRdate1( $a, $b ) { + $val = isset( $a['year'] ) ? $a : $a[0]; + $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); + $as .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; + $val = isset( $b['year'] ) ? $b : $b[0]; + $bs = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); + $bs .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; + return strcmp( $as, $bs ); + } +/** + * sort callback function for rdate + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-07 + * @param array $a + * @param array $b + * @uses iCalUtilityFunctions::$fmt + * @return int + */ + public static function _sortRdate2( $a, $b ) { + $val = isset( $a['value'][0]['year'] ) ? $a['value'][0] : $a['value'][0][0]; + if( empty( $val )) + $as = ''; + else { + $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); + $as .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; + } + $val = isset( $b['value'][0]['year'] ) ? $b['value'][0] : $b['value'][0][0]; + if( empty( $val )) + $bs = ''; + else { + $bs = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); + $bs .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; + } + return strcmp( $as, $bs ); + } +/** + * separate property attributes from property value + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.18.6 - 2013-08-29 + * @param string $line property content + * @param array $propAttr property parameters + * @uses iCalUtilityFunctions::$parValPrefix + * @return void + */ + public static function _splitContent( & $line, & $propAttr=null ) { + $attr = array(); + $attrix = -1; + $clen = strlen( $line ); + $WithinQuotes = FALSE; + $cix = 0; + while( FALSE !== substr( $line, $cix, 1 )) { + if( ! $WithinQuotes && ( ':' == $line[$cix] ) && + ( substr( $line,$cix, 3 ) != '://' ) && + ( ! in_array( strtolower( substr( $line,$cix - 6, 4 )), iCalUtilityFunctions::$parValPrefix['MStz'] )) && + ( ! in_array( strtolower( substr( $line,$cix - 3, 4 )), iCalUtilityFunctions::$parValPrefix['Proto3'] )) && + ( ! in_array( strtolower( substr( $line,$cix - 4, 5 )), iCalUtilityFunctions::$parValPrefix['Proto4'] )) && + ( ! in_array( strtolower( substr( $line,$cix - 6, 7 )), iCalUtilityFunctions::$parValPrefix['Proto6'] ))) { + $attrEnd = TRUE; + if(( $cix < ( $clen - 4 )) && + ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr?? + for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) { + if( '://' == substr( $line, $c2ix - 2, 3 )) { + $attrEnd = FALSE; + break; // an URI with a portnr!! + } + } + } + if( $attrEnd) { + $line = substr( $line, ( $cix + 1 )); + break; + } + $cix++; + } + if( '"' == $line[$cix] ) + $WithinQuotes = ! $WithinQuotes; + if( ';' == $line[$cix] ) + $attr[++$attrix] = null; + else + $attr[$attrix] .= $line[$cix]; + $cix++; + } + /* make attributes in array format */ + $propAttr = array(); + foreach( $attr as $attribute ) { + $attrsplit = explode( '=', $attribute, 2 ); + if( 1 < count( $attrsplit )) + $propAttr[$attrsplit[0]] = $attrsplit[1]; + } + } +/** + * step date, return updated date, array and timpstamp + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-10 + * @param array $date date to step + * @param string $dateYMD date YMD + * @param array $step default array( 'day' => 1 ) + * @uses iCalUtilityFunctions::$fmt + * @return void + */ + public static function _stepdate( & $date, & $dateYMD, $step=array( 'day' => 1 )) { + if( !isset( $date['hour'] )) $date['hour'] = 0; + if( !isset( $date['min'] )) $date['min'] = 0; + if( !isset( $date['sec'] )) $date['sec'] = 0; + if( isset( $step['day'] )) + $mcnt = date( 't', mktime( (int) $date['hour'], (int) $date['min'], (int) $date['sec'], (int) $date['month'], (int) $date['day'], (int) $date['year'] )); + foreach( $step as $stepix => $stepvalue ) + $date[$stepix] += $stepvalue; + if( isset( $step['month'] )) { + if( 12 < $date['month'] ) { + $date['year'] += 1; + $date['month'] -= 12; + } + } + elseif( isset( $step['day'] )) { + if( $mcnt < $date['day'] ) { + $date['day'] -= $mcnt; + $date['month'] += 1; + if( 12 < $date['month'] ) { + $date['year'] += 1; + $date['month'] -= 12; + } + } + } + $dateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $date['year'], (int) $date['month'], (int) $date['day'] ); + unset( $mcnt ); + } +/** + * convert a date from specific string to array format + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.8 - 2012-01-27 + * @param mixed $input + * @return bool, TRUE on success + */ + public static function _strDate2arr( & $input ) { + if( is_array( $input )) + return FALSE; + if( 5 > strlen( (string) $input )) + return FALSE; + $work = $input; + if( 2 == substr_count( $work, '-' )) + $work = str_replace( '-', '', $work ); + if( 2 == substr_count( $work, '/' )) + $work = str_replace( '/', '', $work ); + if( !ctype_digit( substr( $work, 0, 8 ))) + return FALSE; + $temp = array( 'year' => (int) substr( $work, 0, 4 ) + , 'month' => (int) substr( $work, 4, 2 ) + , 'day' => (int) substr( $work, 6, 2 )); + if( !checkdate( $temp['month'], $temp['day'], $temp['year'] )) + return FALSE; + if( 8 == strlen( $work )) { + $input = $temp; + return TRUE; + } + if(( ' ' == substr( $work, 8, 1 )) || ( 'T' == substr( $work, 8, 1 )) || ( 't' == substr( $work, 8, 1 ))) + $work = substr( $work, 9 ); + elseif( ctype_digit( substr( $work, 8, 1 ))) + $work = substr( $work, 8 ); + else + return FALSE; + if( 2 == substr_count( $work, ':' )) + $work = str_replace( ':', '', $work ); + if( !ctype_digit( substr( $work, 0, 4 ))) + return FALSE; + $temp['hour'] = substr( $work, 0, 2 ); + $temp['min'] = substr( $work, 2, 2 ); + if((( 0 > $temp['hour'] ) || ( $temp['hour'] > 23 )) || + (( 0 > $temp['min'] ) || ( $temp['min'] > 59 ))) + return FALSE; + if( ctype_digit( substr( $work, 4, 2 ))) { + $temp['sec'] = substr( $work, 4, 2 ); + if(( 0 > $temp['sec'] ) || ( $temp['sec'] > 59 )) + return FALSE; + $len = 6; + } + else { + $temp['sec'] = 0; + $len = 4; + } + if( $len < strlen( $work)) + $temp['tz'] = trim( substr( $work, 6 )); + $input = $temp; + return TRUE; + } +/** + * ensures internal date-time/date format for input date-time/date in string fromat + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-15 + * Modified to also return original string value by Yitzchok Lavi + * @param array $datetime + * @param int $parno optional, default FALSE + * @param moxed $wtz optional, default null + * @uses iCalUtilityFunctions::_isOffset() + * @uses iCalUtilityFunctions::_strDate2arr() + * @uses iCalUtilityFunctions::_isOffset() + * @uses iCalUtilityFunctions::_tz2offset() + * @uses iCalUtilityFunctions::$fmt + * @return array + */ + public static function _strdate2date( $datetime, $parno=FALSE, $wtz=null ) { + $unparseddatetime = $datetime; + $datetime = (string) trim( $datetime ); + $tz = null; + $offset = 0; + $tzSts = FALSE; + $len = strlen( $datetime ); + if( 'Z' == substr( $datetime, -1 )) { + $tz = 'Z'; + $datetime = trim( substr( $datetime, 0, ( $len - 1 ))); + $tzSts = TRUE; + } + if( iCalUtilityFunctions::_isOffset( substr( $datetime, -5, 5 ))) { // [+/-]NNNN offset + $tz = substr( $datetime, -5, 5 ); + $datetime = trim( substr( $datetime, 0, ($len - 5))); + } + elseif( iCalUtilityFunctions::_isOffset( substr( $datetime, -7, 7 ))) { // [+/-]NNNNNN offset + $tz = substr( $datetime, -7, 7 ); + $datetime = trim( substr( $datetime, 0, ($len - 7))); + } + elseif( empty( $wtz ) && ctype_digit( substr( $datetime, 0, 4 )) && ctype_digit( substr( $datetime, -2, 2 )) && iCalUtilityFunctions::_strDate2arr( $datetime )) { + $output = $datetime; + if( !empty( $tz )) + $output['tz'] = 'Z'; + $output['unparsedtext'] = $unparseddatetime; + return $output; + } + else { + $cx = $tx = 0; // find any trailing timezone or offset + $len = strlen( $datetime ); + for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) { + $char = substr( $datetime, $cx, 1 ); + if(( ' ' == $char ) || ctype_digit( $char )) + break; // if exists, tz ends here.. . ? + else + $tx--; // tz length counter + } + if( 0 > $tx ) { // if any + $tz = substr( $datetime, $tx ); + $datetime = trim( substr( $datetime, 0, $len + $tx )); + } + if(( ctype_digit( substr( $datetime, 0, 8 )) && ( 'T' == substr( $datetime, 8, 1 )) && ctype_digit( substr( $datetime, -6, 6 ))) || + ( ctype_digit( substr( $datetime, 0, 14 )))) + $tzSts = TRUE; + } + if( empty( $tz ) && !empty( $wtz )) + $tz = $wtz; + if( 3 == $parno ) + $tz = null; + if( !empty( $tz )) { // tz set + if(( 'Z' != $tz ) && ( iCalUtilityFunctions::_isOffset( $tz ))) { + $offset = (string) iCalUtilityFunctions::_tz2offset( $tz ) * -1; + $tz = 'UTC'; + $tzSts = TRUE; + } + elseif( !empty( $wtz )) + $tzSts = TRUE; + $tz = trim( $tz ); + if(( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz ))) + $tz = 'UTC'; + if( 0 < substr_count( $datetime, '-' )) + $datetime = str_replace( '-', '/', $datetime ); + try { + $d = new DateTime( $datetime, new DateTimeZone( $tz )); + if( 0 != $offset ) // adjust for offset + $d->modify( $offset.' seconds' ); + $datestring = $d->format( iCalUtilityFunctions::$fmt['YmdHis3'] ); + unset( $d ); + } + catch( Exception $e ) { + $datestring = date( iCalUtilityFunctions::$fmt['YmdHis3'], strtotime( $datetime )); + } + } // end if( !empty( $tz )) + else + $datestring = date( iCalUtilityFunctions::$fmt['YmdHis3'], strtotime( $datetime )); + if( 'UTC' == $tz ) + $tz = 'Z'; + $d = explode( '-', $datestring ); + $output = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2] ); + if( !$parno || ( 3 != $parno )) { // parno is set to 6 or 7 + $output['hour'] = $d[3]; + $output['min'] = $d[4]; + $output['sec'] = $d[5]; + if(( $tzSts || ( 7 == $parno )) && !empty( $tz )) + $output['tz'] = $tz; + } + // return original string in the array in case strtotime failed to make sense of it + $output['unparsedtext'] = $unparseddatetime; + return $output; + } +/********************************************************************************/ +/** + * special characters management output + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @param string $string + * @param string $format + * @param string $nl + * @return string + */ + public static function _strrep( $string, $format, $nl ) { + switch( $format ) { + case 'xcal': + $string = str_replace( '\n', $nl, $string); + $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string )))); + break; + default: + $pos = 0; + $specChars = array( 'n', 'N', 'r', ',', ';' ); + while( isset( $string[$pos] )) { + if( FALSE === ( $pos = strpos( $string, "\\", $pos ))) + break; + if( !in_array( substr( $string, $pos, 1 ), $specChars )) { + $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 )); + $pos += 1; + } + $pos += 1; + } + if( FALSE !== strpos( $string, '"' )) + $string = str_replace('"', "'", $string); + if( FALSE !== strpos( $string, ',' )) + $string = str_replace(',', '\,', $string); + if( FALSE !== strpos( $string, ';' )) + $string = str_replace(';', '\;', $string); + if( FALSE !== strpos( $string, "\r\n" )) + $string = str_replace( "\r\n", '\n', $string); + elseif( FALSE !== strpos( $string, "\r" )) + $string = str_replace( "\r", '\n', $string); + elseif( FALSE !== strpos( $string, "\n" )) + $string = str_replace( "\n", '\n', $string); + if( FALSE !== strpos( $string, '\N' )) + $string = str_replace( '\N', '\n', $string); +// if( FALSE !== strpos( $string, $nl )) + $string = str_replace( $nl, '\n', $string); + break; + } + return $string; + } +/** + * special characters management input (from iCal file) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.16.2 - 2012-12-18 + * @param string $string + * @return string + */ + public static function _strunrep( $string ) { + $string = str_replace( '\\\\', '\\', $string); + $string = str_replace( '\,', ',', $string); + $string = str_replace( '\;', ';', $string); +// $string = str_replace( '\n', $nl, $string); // ?? + return $string; + } +/** + * convert timestamp to date array, default UTC or adjusted for offset/timezone + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-07 + * @param mixed $timestamp + * @param int $parno + * @param string $wtz + * @uses iCalUtilityFunctions::_isOffset() + * @uses iCalUtilityFunctions::_tz2offset() + * @uses iCalUtilityFunctions::$fmt + * @return array + */ + public static function _timestamp2date( $timestamp, $parno=6, $wtz=null ) { + if( is_array( $timestamp )) { + $tz = ( isset( $timestamp['tz'] )) ? $timestamp['tz'] : $wtz; + $timestamp = $timestamp['timestamp']; + } + $tz = ( isset( $tz )) ? $tz : $wtz; + $offset = 0; + if( empty( $tz ) || ( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz ))) + $tz = 'UTC'; + elseif( iCalUtilityFunctions::_isOffset( $tz )) { + $offset = iCalUtilityFunctions::_tz2offset( $tz ); + } + try { + $d = new DateTime( "@$timestamp" ); // set UTC date + if( 0 != $offset ) // adjust for offset + $d->modify( $offset.' seconds' ); + elseif( 'UTC' != $tz ) + $d->setTimezone( new DateTimeZone( $tz )); // convert to local date + $date = $d->format( iCalUtilityFunctions::$fmt['YmdHis3'] ); + unset( $d ); + } + catch( Exception $e ) { + $date = date( iCalUtilityFunctions::$fmt['YmdHis3'], $timestamp ); + } + $date = explode( '-', $date ); + $output = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2] ); + if( 3 != $parno ) { + $output['hour'] = $date[3]; + $output['min'] = $date[4]; + $output['sec'] = $date[5]; + if(( 'UTC' == $tz ) || ( 0 == $offset )) + $output['tz'] = 'Z'; + } + return $output; + } +/** + * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.15.1 - 2012-10-17 + * @param mixed $date date to alter + * @param string $tzFrom PHP valid 'from' timezone + * @param string $tzTo PHP valid 'to' timezone, default 'UTC' + * @param string $format date output format, default 'Ymd\THis' + * @uses iCalUtilityFunctions::_isArrayDate() + * @uses iCalUtilityFunctions::_date2strdate() + * @uses iCalUtilityFunctions::_chkDateArr() + * @return bool + */ + public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) { + if( is_array( $date ) && isset( $date['timestamp'] )) { + try { + $d = new DateTime( "@{$date['timestamp']}" ); // set UTC date + $d->setTimezone(new DateTimeZone( $tzFrom )); // convert to 'from' date + } + catch( Exception $e ) { return FALSE; } + } + else { + if( iCalUtilityFunctions::_isArrayDate( $date )) { + if( isset( $date['tz'] )) + unset( $date['tz'] ); + $date = iCalUtilityFunctions::_date2strdate( iCalUtilityFunctions::_chkDateArr( $date )); + } + if( 'Z' == substr( $date, -1 )) + $date = substr( $date, 0, ( strlen( $date ) - 2 )); + try { $d = new DateTime( $date, new DateTimeZone( $tzFrom )); } + catch( Exception $e ) { return FALSE; } + } + try { $d->setTimezone( new DateTimeZone( $tzTo )); } + catch( Exception $e ) { return FALSE; } + $date = $d->format( $format ); + return TRUE; + } +/** + * convert offset, [+/-]HHmm[ss], to seconds, used when correcting UTC to localtime or v.v. + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.4 - 2012-01-11 + * @param string $tz + * @return integer + */ + public static function _tz2offset( $tz ) { + $tz = trim( (string) $tz ); + $offset = 0; + if((( 5 != strlen( $tz )) && ( 7 != strlen( $tz ))) || + (( '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) || + (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) || + (( 7 == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 )))) + return $offset; + $hours2sec = (int) substr( $tz, 1, 2 ) * 3600; + $min2sec = (int) substr( $tz, 3, 2 ) * 60; + $sec = ( 7 == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00'; + $offset = $hours2sec + $min2sec + $sec; + $offset = ('-' == substr( $tz, 0, 1 )) ? $offset * -1 : $offset; + return $offset; + } +} diff --git a/php/ical/lib/iCaldateTime.class.php b/php/ical/lib/iCaldateTime.class.php new file mode 100644 index 0000000..55f7c3a --- /dev/null +++ b/php/ical/lib/iCaldateTime.class.php @@ -0,0 +1,129 @@ + + * @since 2.21.7 - 2015-03-10 + */ +class iCaldateTime extends dateTime { +/** @var string default date[-time] format */ + public $dateFormat = 'Y-m-d H:i:s e'; +/** @var string default object instance date[-time] 'key' */ + public $key = null; +/** @var array date[-time] origin */ + public $SCbools = array(); +/** + * return time (His) array + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.7 - 2015-03-07 + * @return array + */ + public function getTime() { + return explode( ':', $this->format( 'H:i:s' )); + } +/** + * return the timezone name + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.7 - 2015-03-07 + * @return string + */ + public function getTimezoneName() { + $tz = $this->getTimezone(); + return $tz->getName(); + } +/** + * return formatted date + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.7 - 2015-03-07 + * @param string $format + * @uses iCaldateTime::$dateFormat + * @return string + */ + public function format( $format=null ) { + if( empty( $format ) && isset( $this->dateFormat )) + $format = $this->dateFormat; + return parent::format( $format ); + } +/** + * return iCaldateTime object instance based on date array and timezone(s) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-28 + * @param array $date + * @param array $params + * @param array $tz + * @param string $dtstartTz + * @uses iCalUtilityFunctions::$fmt + * @uses iCaldateTime::getTimezoneName() + * @uses iCaldateTime::$dateFormat + * @uses iCaldateTime::$key + * @return object instance + */ + public static function factory( array $date, $params=null, $tz=null, $dtstartTz=null ) { + if( isset( $params['TZID'] ) && ! empty( $params['TZID'] )) + $tz = ( 'Z' == $params['TZID'] ) ? 'UTC' : $params['TZID']; + elseif( isset( $tz['tz'] ) && ! empty( $tz['tz'] )) + $tz = ( 'Z' == $tz['tz'] ) ? 'UTC' : $tz['tz']; + else + $tz = ini_get( 'date_default_timezone_set' ); + $strdate = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $date['year'], (int) $date['month'], (int) $date['day'] ); + if( isset( $date['hour'] )) + $strdate .= 'T'.sprintf( iCalUtilityFunctions::$fmt['His'], (int) $date['hour'], (int) $date['min'], (int) $date['sec'] ); + try { + $timezone = new DateTimeZone( $tz ); + $d = new iCaldateTime( $strdate, $timezone ); + } + catch( Exception $e ) { + $d = new iCaldateTime( $strdate ); + } + if( ! empty( $dtstartTz )) { + if( 'Z' == $dtstartTz ) + $dtstartTz = 'UTC'; + if( $dtstartTz != $d->getTimezoneName()) { // set the same timezone as dtstart + try { + $timezone = new DateTimeZone( $dtstartTz ); + $d->setTimezone( $timezone ); + } + catch( Exception $e ) {} + } + } + unset( $timezone, $strdate ); + if( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )) { + $d->dateFormat = 'Y-m-d'; + $d->key = $d->format( 'Ymd' ); + } + else + $d->key = $d->format( 'YmdHis' ); + return $d; + } +} diff --git a/php/ical/lib/valarm.class.php b/php/ical/lib/valarm.class.php new file mode 100644 index 0000000..9264285 --- /dev/null +++ b/php/ical/lib/valarm.class.php @@ -0,0 +1,135 @@ + + * @since 2.5.1 - 2008-10-12 + */ +class valarm extends calendarComponent { +/** + * @var array $action component property value + * @var array $attach component property value + * @var array $attendee component property value + * @var array $description component property value + * @var array $duration component property value + * @var array $repeat component property value + * @var array $summary component property value + * @var array $trigger component property value + * @access protected + */ + protected $action; + protected $attach; + protected $attendee; + protected $description; + protected $duration; + protected $repeat; + protected $summary; + protected $trigger; +/** + * constructor for calendar component VALARM object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @uses valarm::calendarComponent() + * @uses valarm::$action + * @uses valarm::$attach + * @uses valarm::$attendee + * @uses valarm::$description + * @uses valarm::$duration + * @uses valarm::$repeat + * @uses valarm::$summary + * @uses valarm::$trigger + * @uses valarm::$xprop + * @uses calendarComponent::setConfig() + */ + function valarm( $config = array()) { + $this->calendarComponent(); + $this->action = ''; + $this->attach = ''; + $this->attendee = ''; + $this->description = ''; + $this->duration = ''; + $this->repeat = ''; + $this->summary = ''; + $this->trigger = ''; + $this->xprop = ''; + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + } +/** + * create formatted output for calendar component VALARM object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-22 + * @param array $xcaldecl + * @uses calendarComponent::_createFormat() + * @uses calendarComponent::$componentStart1 + * @uses calendarComponent::$componentStart2 + * @uses calendarComponent::$nl + * @uses calendarComponent::createAction() + * @uses calendarComponent::createAttach() + * @uses calendarComponent::createAttendee() + * @uses calendarComponent::createDescription() + * @uses calendarComponent::createDuration() + * @uses calendarComponent::createRepeat() + * @uses calendarComponent::createSummary() + * @uses calendarComponent::createTrigger() + * @uses calendarComponent::createXprop() + * @uses calendarComponent::$componentEnd1 + * @uses calendarComponent::$componentEnd2 + * @uses calendarComponent::$xcaldecl + * @return string + */ + function createComponent( & $xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createAction(); + $component .= $this->createAttach(); + $component .= $this->createAttendee(); + $component .= $this->createDescription(); + $component .= $this->createDuration(); + $component .= $this->createRepeat(); + $component .= $this->createSummary(); + $component .= $this->createTrigger(); + $component .= $this->createXprop(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} diff --git a/php/ical/lib/vcalendar.class.php b/php/ical/lib/vcalendar.class.php new file mode 100644 index 0000000..33e9dad --- /dev/null +++ b/php/ical/lib/vcalendar.class.php @@ -0,0 +1,2132 @@ + + * @since 2.21.11 - 2015-03-31 + */ +class vcalendar extends iCalBase { +/** + * @var array $calscale calendar property variable + * @var array $method calendar property variable + * @var array $prodid calendar property variable + * @var array $version calendar property variable + * @access private + */ + private $calscale; + private $method; + private $prodid; + private $version; +/** + * @var array $directory calendar config variable + * @var array $filename calendar config variable + * @var array $url calendar config variable + * @access private + */ + private $directory; + private $filename; + private $url; +/** + * redirect headers + * + * @var array $headers + * @access private + * @static + */ + private static $headers = array( 'Content-Encoding: gzip', + 'Vary: *', + 'Content-Length: %s', + 'Content-Type: application/calendar+xml; charset=utf-8', + 'Content-Type: text/calendar; charset=utf-8', + 'Content-Disposition: attachment; filename="%s"', + 'Content-Disposition: inline; filename="%s"', + 'Cache-Control: max-age=10', + ); +/** + * constructor for calendar object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.15 - 2015-04-04 + * @param array $config + * @uses vcalendar::_makeVersion() + * @uses vcalendar::$calscale + * @uses vcalendar::$method + * @uses vcalendar::_makeUnique_id() + * @uses vcalendar::$prodid + * @uses vcalendar::$xprop + * @uses vcalendar::$language + * @uses vcalendar::$directory + * @uses vcalendar::$filename + * @uses vcalendar::$url + * @uses vcalendar::$dtzid + * @uses vcalendar::setConfig() + * @uses vcalendar::$xcaldecl + * @uses vcalendar::$components + */ + function vcalendar ( $config = array()) { + $this->_makeVersion(); + $this->calscale = null; + $this->method = null; + $this->_makeUnique_id(); + $this->prodid = null; + $this->xprop = array(); + $this->language = null; + $this->directory = '.'; + $this->filename = null; + $this->url = null; + $this->dtzid = null; +/** + * language = + */ + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + $this->xcaldecl = array(); + $this->components = array(); + } +/** + * return iCalcreator version number + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.18.5 - 2013-08-29 + * @uses ICALCREATOR_VERSION + * @return string + */ + public static function iCalcreatorVersion() { + return trim( substr( ICALCREATOR_VERSION, strpos( ICALCREATOR_VERSION, ' ' ))); + } +/*********************************************************************************/ +/** + * Property Name: CALSCALE + */ +/** + * creates formatted output for calendar property calscale + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @uses vcalendar::$calscale + * @uses vcalendar::$format + * @uses vcalendar::$nl + * @return string + */ + function createCalscale() { + if( empty( $this->calscale )) return FALSE; + switch( $this->format ) { + case 'xcal': + return $this->nl.' calscale="'.$this->calscale.'"'; + break; + default: + return 'CALSCALE:'.$this->calscale.$this->nl; + break; + } + } +/** + * set calendar property calscale + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-21 + * @param string $value + * @uses vcalendar::$calscale + * @return void + */ + function setCalscale( $value ) { + if( empty( $value )) return FALSE; + $this->calscale = $value; + } +/*********************************************************************************/ +/** + * Property Name: METHOD + */ +/** + * creates formatted output for calendar property method + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @uses vcalendar::$method + * @uses vcalendar::$format + * @uses vcalendar::$nl + * @return string + */ + function createMethod() { + if( empty( $this->method )) return FALSE; + switch( $this->format ) { + case 'xcal': + return $this->nl.' method="'.$this->method.'"'; + break; + default: + return 'METHOD:'.$this->method.$this->nl; + break; + } + } +/** + * set calendar property method + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-20-23 + * @param string $value + * @uses vcalendar::$method + * @return bool + */ + function setMethod( $value ) { + if( empty( $value )) return FALSE; + $this->method = $value; + return TRUE; + } +/*********************************************************************************/ +/** + * Property Name: PRODID + * + */ +/** + * creates formatted output for calendar property prodid + * + * @copyright copyright (c) 2007-2013 Kjell-Inge Gustafsson, kigkonsult, All rights reserved + * @license http://kigkonsult.se/downloads/dl.php?f=LGPL + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-31 + * @uses vcalendar::$prodid + * @uses vcalendar::_makeProdid() + * @uses vcalendar::$format + * @uses vcalendar::$nl + * @uses vcalendar::_createElement() + * @return string + */ + function createProdid() { + if( !isset( $this->prodid )) + $this->_makeProdid(); + switch( $this->format ) { + case 'xcal': + return $this->nl.' prodid="'.$this->prodid.'"'; + break; + default: + return $this->_createElement( 'PRODID', '', $this->prodid ); + break; + } + } +/** + * make default value for calendar prodid, do NOT alter or remove this method or invoke of this method + * + * @copyright copyright (c) 2007-2013 Kjell-Inge Gustafsson, kigkonsult, All rights reserved + * @license http://kigkonsult.se/downloads/dl.php?f=LGPL + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.6.8 - 2009-12-30 + * @uses vcalendar::$prodid + * @uses vcalendar::$unique_id + * @uses ICALCREATOR_VERSION + * @uses vcalendar::$language + * @return void + */ + function _makeProdid() { + $this->prodid = '-//'.$this->unique_id.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION.'//'.strtoupper( $this->language ); + } +/** + * Conformance: The property MUST be specified once in an iCalendar object. + * Description: The vendor of the implementation SHOULD assure that this + * is a globally unique identifier; using some technique such as an FPI + * value, as defined in [ISO 9070]. + */ +/** + * make default unique_id for calendar prodid + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 0.3.0 - 2006-08-10 + * @uses vcalendar::$unique_id + * @return void + */ + function _makeUnique_id() { + $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost'; + } +/*********************************************************************************/ +/** + * Property Name: VERSION + * + * Description: A value of "2.0" corresponds to this memo. + */ +/** + * creates formatted output for calendar property version + + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @uses vcalendar::$version + * @uses vcalendar::$format + * @uses vcalendar::$nl + * @return string + */ + function createVersion() { + if( empty( $this->version )) + $this->_makeVersion(); + switch( $this->format ) { + case 'xcal': + return $this->nl.' version="'.$this->version.'"'; + break; + default: + return 'VERSION:'.$this->version.$this->nl; + break; + } + } +/** + * set default calendar version + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 0.3.0 - 2006-08-10 + * @uses vcalendar::$version + * @return void + */ + function _makeVersion() { + $this->version = '2.0'; + } +/** + * set calendar version + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.4.8 - 2008-10-23 + * @param string $value + * @uses vcalendar::$version + * @return void + */ + function setVersion( $value ) { + if( empty( $value )) return FALSE; + $this->version = $value; + return TRUE; + } +/*********************************************************************************/ +/*********************************************************************************/ +/** + * delete calendar property value + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $propName bool FALSE => X-property + * @param int $propix specific property in case of multiply occurences + * @uses vcalendar::$propdelix + * @uses vcalendar::$calscale + * @uses vcalendar::$method + * @uses vcalendar::$xprop + * @return bool, if successfull delete + */ + function deleteProperty( $propName=FALSE, $propix=FALSE ) { + $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; + if( !$propix ) + $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1; + $this->propdelix[$propName] = --$propix; + $return = FALSE; + switch( $propName ) { + case 'CALSCALE': + if( isset( $this->calscale )) { + $this->calscale = null; + $return = TRUE; + } + break; + case 'METHOD': + if( isset( $this->method )) { + $this->method = null; + $return = TRUE; + } + break; + default: + $reduced = array(); + if( $propName != 'X-PROP' ) { + if( !isset( $this->xprop[$propName] )) { unset( $this->propdelix[$propName] ); return FALSE; } + foreach( $this->xprop as $k => $a ) { + if(( $k != $propName ) && !empty( $a )) + $reduced[$k] = $a; + } + } + else { + if( count( $this->xprop ) <= $propix ) return FALSE; + $xpropno = 0; + foreach( $this->xprop as $xpropkey => $xpropvalue ) { + if( $propix != $xpropno ) + $reduced[$xpropkey] = $xpropvalue; + $xpropno++; + } + } + $this->xprop = $reduced; + if( empty( $this->xprop )) { + unset( $this->propdelix[$propName] ); + return FALSE; + } + return TRUE; + } + return $return; + } +/** + * get calendar property value/params + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.09 - 2015-03-29 + * @param string $propName + * @param int $propix specific property in case of multiply occurences + * @param bool $inclParam + * @uses vcalendar::$propix + * @uses vcalendar::$components + * @uses calendarComponent::$objName + * @uses iCalUtilityFunctions::$vComps + * @uses iCalUtilityFunctions::$mProps1 + * @uses calendarComponent::_getProperties() + * @uses calendarComponent::getProperty() + * @uses iCalUtilityFunctions::_geo2str2() + * @uses iCalUtilityFunctions::$geoLatFmt + * @uses iCalUtilityFunctions::$geoLongFmt + * @uses iCalUtilityFunctions::$fmt + * @uses vcalendar::$calscale + * @uses vcalendar::$method + * @uses vcalendar::$prodid + * @uses vcalendar::_makeProdid() + * @uses vcalendar::$version + * @uses vcalendar::$xprop + * @return mixed + */ + function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) { + $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; + if( 'X-PROP' == $propName ) { + if( empty( $propix )) + $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1; + $this->propix[$propName] = --$propix; + } + switch( $propName ) { + case 'ATTENDEE': + case 'CATEGORIES': + case 'CONTACT': + case 'DTSTART': + case 'GEOLOCATION': + case 'LOCATION': + case 'ORGANIZER': + case 'PRIORITY': + case 'RESOURCES': + case 'STATUS': + case 'SUMMARY': + case 'RECURRENCE-ID-UID': + case 'RELATED-TO': + case 'R-UID': + case 'UID': + case 'URL': + $output = array(); + foreach ( $this->components as $cix => $component) { + if( !in_array( $component->objName, iCalUtilityFunctions::$vComps )) + continue; + if( in_array( $propName, iCalUtilityFunctions::$mProps1 )) { + $component->_getProperties( $propName, $output ); + continue; + } + elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) { + if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' ))) + $content = $component->getProperty( 'UID' ); + } + elseif( 'GEOLOCATION' == $propName ) { + $content = ( FALSE === ( $loc = $component->getProperty( 'LOCATION' ))) ? '' : $loc.' '; + if( FALSE === ( $geo = $component->getProperty( 'GEO' ))) + continue; + $content .= iCalUtilityFunctions::_geo2str2( $geo['latitude'], iCalUtilityFunctions::$geoLatFmt ). + iCalUtilityFunctions::_geo2str2( $geo['longitude'], iCalUtilityFunctions::$geoLongFmt ).'/'; + } + elseif( FALSE === ( $content = $component->getProperty( $propName ))) + continue; + if(( FALSE === $content ) || empty( $content )) + continue; + elseif( is_array( $content )) { + if( isset( $content['year'] )) { + $key = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $content['year'], (int) $content['month'], (int) $content['day'] ); + if( !isset( $output[$key] )) + $output[$key] = 1; + else + $output[$key] += 1; + } + else { + foreach( $content as $partValue => $partCount ) { + if( !isset( $output[$partValue] )) + $output[$partValue] = $partCount; + else + $output[$partValue] += $partCount; + } + } + } // end elseif( is_array( $content )) { + elseif( !isset( $output[$content] )) + $output[$content] = 1; + else + $output[$content] += 1; + } // end foreach ( $this->components as $cix => $component) + if( !empty( $output )) + ksort( $output ); + return $output; + break; + case 'CALSCALE': + return ( !empty( $this->calscale )) ? $this->calscale : FALSE; + break; + case 'METHOD': + return ( !empty( $this->method )) ? $this->method : FALSE; + break; + case 'PRODID': + if( empty( $this->prodid )) + $this->_makeProdid(); + return $this->prodid; + break; + case 'VERSION': + return ( !empty( $this->version )) ? $this->version : FALSE; + break; + default: + if( $propName != 'X-PROP' ) { + if( !isset( $this->xprop[$propName] )) return FALSE; + return ( $inclParam ) ? array( $propName, $this->xprop[$propName] ) + : array( $propName, $this->xprop[$propName]['value'] ); + } + else { + if( empty( $this->xprop )) return FALSE; + $xpropno = 0; + foreach( $this->xprop as $xpropkey => $xpropvalue ) { + if( $propix == $xpropno ) + return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] ) + : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); + else + $xpropno++; + } + unset( $this->propix[$propName] ); + return FALSE; // not found ?? + } + } + return FALSE; + } +/** + * general vcalendar property setting + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.2.13 - 2007-11-04 + * @param mixed $args variable number of function arguments, + * first argument is ALWAYS component name, + * second ALWAYS component value! + * @uses vcalendar::setCalscale() + * @uses vcalendar::setMethod() + * @uses vcalendar::setVersion() + * @uses vcalendar::setXprop() + * @return bool + */ + function setProperty () { + $numargs = func_num_args(); + if( 1 > $numargs ) + return FALSE; + $arglist = func_get_args(); + $arglist[0] = strtoupper( $arglist[0] ); + switch( $arglist[0] ) { + case 'CALSCALE': + return $this->setCalscale( $arglist[1] ); + case 'METHOD': + return $this->setMethod( $arglist[1] ); + case 'VERSION': + return $this->setVersion( $arglist[1] ); + default: + if( !isset( $arglist[1] )) $arglist[1] = null; + if( !isset( $arglist[2] )) $arglist[2] = null; + return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] ); + } + return FALSE; + } +/*********************************************************************************/ +/** + * get vcalendar config values or * calendar components + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.11.7 - 2012-01-12 + * @param mixed $config + * @uses vcalendar::getConfig() + * @uses vcalendar::$allowEmpty + * @uses vcalendar::$components + * @uses calendarComponent::_getProperties() + * @uses calendarComponent::$objName + * @uses calendarComponent::getProperty() + * @uses calendarComponent::_getConfig() + * @uses vcalendar::$url + * @uses vcalendar::$delimiter + * @uses vcalendar::$directory + * @uses vcalendar::$filename + * @uses vcalendar::$format + * @uses vcalendar::$language + * @uses vcalendar::$nl + * @uses vcalendar::$dtzid + * @uses vcalendar::$unique_id + * @return value + */ + function getConfig( $config = FALSE ) { + if( !$config ) { + $return = array(); + $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' ); + $return['DELIMITER'] = $this->getConfig( 'DELIMITER' ); + $return['DIRECTORY'] = $this->getConfig( 'DIRECTORY' ); + $return['FILENAME'] = $this->getConfig( 'FILENAME' ); + $return['DIRFILE'] = $this->getConfig( 'DIRFILE' ); + $return['FILESIZE'] = $this->getConfig( 'FILESIZE' ); + $return['FORMAT'] = $this->getConfig( 'FORMAT' ); + if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' ))) + $return['LANGUAGE'] = $lang; + $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' ); + $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' ); + if( FALSE !== ( $url = $this->getConfig( 'URL' ))) + $return['URL'] = $url; + $return['TZID'] = $this->getConfig( 'TZID' ); + return $return; + } + switch( strtoupper( $config )) { + case 'ALLOWEMPTY': + return $this->allowEmpty; + break; + case 'COMPSINFO': + unset( $this->compix ); + $info = array(); + foreach( $this->components as $cix => $component ) { + if( empty( $component )) continue; + $info[$cix]['ordno'] = $cix + 1; + $info[$cix]['type'] = $component->objName; + $info[$cix]['uid'] = $component->getProperty( 'uid' ); + $info[$cix]['props'] = $component->getConfig( 'propinfo' ); + $info[$cix]['sub'] = $component->getConfig( 'compsinfo' ); + } + return $info; + break; + case 'DELIMITER': + return $this->delimiter; + break; + case 'DIRECTORY': + if( empty( $this->directory ) && ( '0' != $this->directory )) + $this->directory = '.'; + return $this->directory; + break; + case 'DIRFILE': + return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' ); + break; + case 'FILEINFO': + return array( $this->getConfig( 'directory' ) + , $this->getConfig( 'filename' ) + , $this->getConfig( 'filesize' )); + break; + case 'FILENAME': + if( empty( $this->filename ) && ( '0' != $this->filename )) { + if( 'xcal' == $this->format ) + $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. . + else + $this->filename = date( 'YmdHis' ).'.ics'; + } + return $this->filename; + break; + case 'FILESIZE': + $size = 0; + if( empty( $this->url )) { + $dirfile = $this->getConfig( 'dirfile' ); + if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile )))) + $size = 0; + clearstatcache(); + } + return $size; + break; + case 'FORMAT': + return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal'; + break; + case 'LANGUAGE': + /* get language for calendar component as defined in [RFC 1766] */ + return $this->language; + break; + case 'NL': + case 'NEWLINECHAR': + return $this->nl; + break; + case 'TZID': + return $this->dtzid; + break; + case 'UNIQUE_ID': + return $this->unique_id; + break; + case 'URL': + if( !empty( $this->url )) + return $this->url; + else + return FALSE; + break; + } + } +/** + * general vcalendar config setting + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.20.2 - 2014-05-09 + * @param mixed $config + * @param string $value + * @uses vcalendar::setConfig() + * @uses vcalendar::$allowEmpty + * @uses vcalendar::$components + * @uses vcalendar::$url + * @uses vcalendar::$delimiter + * @uses vcalendar::$directory + * @uses vcalendar::$filename + * @uses vcalendar::$format + * @uses vcalendar::$language + * @uses vcalendar::$nl + * @uses vcalendar::$dtzid + * @uses vcalendar::$unique_id + * @uses vcalendar::_makeProdid() + * @uses vcalendar::$components + * @uses calendarComponent::setConfig() + * @uses calendarComponent::copy() + * @return void + */ + function setConfig( $config, $value = FALSE) { + if( is_array( $config )) { + $config = array_change_key_case( $config, CASE_UPPER ); + if( isset( $config['DELIMITER'] )) { + if( FALSE === $this->setConfig( 'DELIMITER', $config['DELIMITER'] )) + return FALSE; + unset( $config['DELIMITER'] ); + } + if( isset( $config['DIRECTORY'] )) { + if( FALSE === $this->setConfig( 'DIRECTORY', $config['DIRECTORY'] )) + return FALSE; + unset( $config['DIRECTORY'] ); + } + foreach( $config as $cKey => $cValue ) { + if( FALSE === $this->setConfig( $cKey, $cValue )) + return FALSE; + } + return TRUE; + } + else + $res = FALSE; + $config = strtoupper( $config ); + switch( $config ) { + case 'ALLOWEMPTY': + $this->allowEmpty = $value; + $subcfg = array( 'ALLOWEMPTY' => $value ); + $res = TRUE; + break; + case 'DELIMITER': + $this->delimiter = $value; + return TRUE; + break; + case 'DIRECTORY': + if( FALSE === ( $value = realpath( rtrim( trim( $value ), $this->delimiter )))) + return FALSE; + else { + /* local directory */ + $this->directory = $value; + $this->url = null; + return TRUE; + } + break; + case 'FILENAME': + $value = trim( $value ); + $dirfile = $this->directory.$this->delimiter.$value; + if( file_exists( $dirfile )) { + /* local file exists */ + if( is_readable( $dirfile ) || is_writable( $dirfile )) { + clearstatcache(); + $this->filename = $value; + return TRUE; + } + else + return FALSE; + } + elseif( is_readable( $this->directory ) || is_writable( $this->directory )) { + /* read- or writable directory */ + clearstatcache(); + $this->filename = $value; + return TRUE; + } + else + return FALSE; + break; + case 'FORMAT': + $value = trim( strtolower( $value )); + if( 'xcal' == $value ) { + $this->format = 'xcal'; + $this->attributeDelimiter = $this->nl; + $this->valueInit = null; + } + else { + $this->format = null; + $this->attributeDelimiter = ';'; + $this->valueInit = ':'; + } + $subcfg = array( 'FORMAT' => $value ); + $res = TRUE; + break; + case 'LANGUAGE': // set language for calendar component as defined in [RFC 1766] + $value = trim( $value ); + $this->language = $value; + $this->_makeProdid(); + $subcfg = array( 'LANGUAGE' => $value ); + $res = TRUE; + break; + case 'NL': + case 'NEWLINECHAR': + $this->nl = $value; + if( 'xcal' == $value ) { + $this->attributeDelimiter = $this->nl; + $this->valueInit = null; + } + else { + $this->attributeDelimiter = ';'; + $this->valueInit = ':'; + } + $subcfg = array( 'NL' => $value ); + $res = TRUE; + break; + case 'TZID': + $this->dtzid = $value; + $subcfg = array( 'TZID' => $value ); + $res = TRUE; + break; + case 'UNIQUE_ID': + $value = trim( $value ); + $this->unique_id = $value; + $this->_makeProdid(); + $subcfg = array( 'UNIQUE_ID' => $value ); + $res = TRUE; + break; + case 'URL': + /* remote file - URL */ + $value = str_replace( array( 'HTTP://', 'WEBCAL://', 'webcal://' ), 'http://', trim( $value )); + $value = str_replace( 'HTTPS://', 'https://', trim( $value )); + if(( 'http://' != substr( $value, 0, 7 )) && ( 'https://' != substr( $value, 0, 8 ))) + return FALSE; + $this->directory = '.'; + $this->url = $value; + if( '.ics' != strtolower( substr( $value, -4 ))) + unset( $this->filename ); + else + $this->filename = basename( $value ); + return TRUE; + break; + default: // any unvalid config key.. . + return TRUE; + } + if( !$res ) return FALSE; + if( isset( $subcfg ) && !empty( $this->components )) { + foreach( $subcfg as $cfgkey => $cfgvalue ) { + foreach( $this->components as $cix => $component ) { + $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE ); + if( !$res ) + break 2; + $this->components[$cix] = $component->copy(); // PHP4 compliant + } + } + } + return $res; + } +/*********************************************************************************/ +/** + * add calendar component to container + * + * alias to setComponent + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 1.x.x - 2007-04-24 + * @param object $component calendar component + * @uses vcalendar::setComponent() + * @return void + */ + function addComponent( $component ) { + $this->setComponent( $component ); + } +/** + * delete calendar component from container + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.8 - 2011-03-15 + * @param mixed $arg1 ordno / component type / component uid + * @param mixed $arg2 optional, ordno if arg1 = component type + * @uses vcalendar::$components + * @uses calendarComponent::$objName + * @uses calendarComponent::getProperty() + * @return void + */ + function deleteComponent( $arg1, $arg2=FALSE ) { + $argType = $index = null; + if ( ctype_digit( (string) $arg1 )) { + $argType = 'INDEX'; + $index = (int) $arg1 - 1; + } + elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { + $argType = strtolower( $arg1 ); + $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0; + } + $cix1dC = 0; + foreach ( $this->components as $cix => $component) { + if( empty( $component )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) { + unset( $this->components[$cix] ); + return TRUE; + } + elseif( $argType == $component->objName ) { + if( $index == $cix1dC ) { + unset( $this->components[$cix] ); + return TRUE; + } + $cix1dC++; + } + elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { + unset( $this->components[$cix] ); + return TRUE; + } + } + return FALSE; + } +/** + * get calendar component from container + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param mixed $arg1 optional, ordno/component type/ component uid + * @param mixed $arg2 optional, ordno if arg1 = component type + * @uses vcalendar::$compix + * @uses vcalendar::$components + * @uses iCalUtilityFunctions::$dateProps + * @uses iCalUtilityFunctions::$otherProps + * @uses iCalUtilityFunctions::$mProps1 + * @uses calendarComponent::copy() + * @uses calendarComponent::$objName + * @uses calendarComponent::_getProperties() + * @uses calendarComponent::getProperty() + * @uses iCalUtilityFunctions::$fmt + * @return object + */ + function getComponent( $arg1=FALSE, $arg2=FALSE ) { + $index = $argType = null; + if ( !$arg1 ) { // first or next in component chain + $argType = 'INDEX'; + $index = $this->compix['INDEX'] = ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1; + } + elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) + $arg2 = implode( '-', array_keys( $arg1 )); + $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1; + } + elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain + $argType = 'INDEX'; + $index = (int) $arg1; + unset( $this->compix ); + } + elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name + unset( $this->compix['INDEX'] ); + $argType = strtolower( $arg1 ); + if( !$arg2 ) + $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1; + elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 )) + $index = (int) $arg2; + } + elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument + if( !$arg2 ) + $index = $this->compix[$arg1] = ( isset( $this->compix[$arg1] )) ? $this->compix[$arg1] + 1 : 1; + elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 )) + $index = (int) $arg2; + } + if( isset( $index )) + $index -= 1; + $ckeys = array_keys( $this->components ); + if( !empty( $index) && ( $index > end( $ckeys ))) + return FALSE; + $cix1gC = 0; + foreach ( $this->components as $cix => $component) { + if( empty( $component )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) + return $component->copy(); + elseif( $argType == $component->objName ) { + if( $index == $cix1gC ) + return $component->copy(); + $cix1gC++; + } + elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) + $hit = array(); + $arg1 = array_change_key_case( $arg1, CASE_UPPER ); + foreach( $arg1 as $pName => $pValue ) { + if( !in_array( $pName, iCalUtilityFunctions::$dateProps ) && !in_array( $pName, iCalUtilityFunctions::$otherProps )) + continue; + if( in_array( $pName, iCalUtilityFunctions::$mProps1 )) { // multiple occurrence + $propValues = array(); + $component->_getProperties( $pName, $propValues ); + $propValues = array_keys( $propValues ); + $hit[] = ( in_array( $pValue, $propValues )) ? TRUE : FALSE; + continue; + } // end if(.. .// multiple occurrence + if( FALSE === ( $value = $component->getProperty( $pName ))) { // single occurrence + $hit[] = FALSE; // missing property + continue; + } + if( 'SUMMARY' == $pName ) { // exists within (any case) + $hit[] = ( FALSE !== stripos( $value, $pValue )) ? TRUE : FALSE; + continue; + } + if( in_array( $pName, iCalUtilityFunctions::$dateProps )) { + $valuedate = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $value['year'], (int) $value['month'], (int) $value['day'] ); + if( 8 < strlen( $pValue )) { + if( isset( $value['hour'] )) { + if( 'T' == substr( $pValue, 8, 1 )) + $pValue = str_replace( 'T', '', $pValue ); + $valuedate .= sprintf( iCalUtilityFunctions::$fmt['His'], (int) $value['hour'], (int) $value['min'], (int) $value['sec'] ); + } + else + $pValue = substr( $pValue, 0, 8 ); + } + $hit[] = ( $pValue == $valuedate ) ? TRUE : FALSE; + continue; + } + elseif( !is_array( $value )) + $value = array( $value ); + foreach( $value as $part ) { + $part = ( FALSE !== strpos( $part, ',' )) ? explode( ',', $part ) : array( $part ); + foreach( $part as $subPart ) { + if( $pValue == $subPart ) { + $hit[] = TRUE; + continue 3; + } + } + } // end foreach( $value as $part ) + $hit[] = FALSE; // no hit in property + } // end foreach( $arg1 as $pName => $pValue ) + if( in_array( TRUE, $hit )) { + if( $index == $cix1gC ) + return $component->copy(); + $cix1gC++; + } + } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) + elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { // UID + if( $index == $cix1gC ) + return $component->copy(); + $cix1gC++; + } + } // end foreach ( $this->components.. . + /* not found.. . */ + unset( $this->compix ); + return FALSE; + } +/** + * create new calendar component, already included within calendar + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.20.4 - 2014-08-24 + * @param string $compType component type + * @uses vcalendar::$components + * @return object + */ + function & newComponent( $compType ) { + $config = $this->getConfig(); + $keys = array_keys( $this->components ); + $ix = ( empty( $keys )) ? 0 : end( $keys) + 1; + switch( strtoupper( $compType )) { + case 'EVENT': + case 'VEVENT': + $this->components[$ix] = new vevent( $config ); + break; + case 'TODO': + case 'VTODO': + $this->components[$ix] = new vtodo( $config ); + break; + case 'JOURNAL': + case 'VJOURNAL': + $this->components[$ix] = new vjournal( $config ); + break; + case 'FREEBUSY': + case 'VFREEBUSY': + $this->components[$ix] = new vfreebusy( $config ); + break; + case 'TIMEZONE': + case 'VTIMEZONE': + array_unshift( $this->components, new vtimezone( $config )); + $ix = 0; + break; + default: + return FALSE; + } + return $this->components[$ix]; + } +/** + * select components from calendar on date or selectOption basis + * + * Ensure DTSTART is set for every component. + * No date controls occurs. + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-31 + * @param mixed $startY optional, (int) start Year, default current Year + * ALT. (obj) start date (datetime) + * ALT. array selecOptions ( *[ => ] ) + * @param mixed $startM optional, (int) start Month, default current Month + * ALT. (obj) end date (datetime) + * @param int $startD optional, start Day, default current Day + * @param int $endY optional, end Year, default $startY + * @param int $endM optional, end Month, default $startM + * @param int $endD optional, end Day, default $startD + * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s) + * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][] + * TRUE => output : array[] (ignores split) + * @param bool $any optional, TRUE (default) - select component(-s) that occurs within period + * FALSE - only component(-s) that starts within period + * @param bool $split optional, TRUE (default) - one component copy every DAY it occurs during the + * period (implies flat=FALSE) + * FALSE - one occurance of component only in output array + * @uses vcalendar::$components + * @uses vcalendar::selectComponents2() + * @uses iCalUtilityFunctions::$vComps + * @uses calendarComponent::$objName + * @uses calendarComponent::getProperty() + * @uses iCaldateTime::factory() + * @uses iCaldateTime::getTimezoneName() + * @uses iCaldateTime::getTime() + * @uses iCalUtilityFunctions::$fmt + * @uses iCaldateTime::$SCbools + * @uses iCaldateTime::format() + * @uses iCalUtilityFunctions::_strDate2arr() + * @uses iCalUtilityFunctions::_recur2date() + * @uses iCalUtilityFunctions::_inScope() + * @uses calendarComponent::copy() + * @uses calendarComponent::setProperty() + * @uses iCalUtilityFunctions::$fmt + * @uses calendarComponent::deleteProperty() + * @uses iCalUtilityFunctions::_setSortArgs() + * @uses iCalUtilityFunctions::_cmpfcn() + * @return array or FALSE + */ + function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) { + /* check if empty calendar */ + if( 0 >= count( $this->components )) return FALSE; + if( is_array( $startY )) + return $this->selectComponents2( $startY ); + /* check default dates */ + if( is_a( $startY, 'DateTime' ) && is_a( $startM, 'DateTime' )) { + $endY = $startM->format( 'Y' ); + $endM = $startM->format( 'm' ); + $endD = $startM->format( 'd' ); + $startD = $startY->format( 'd' ); + $startM = $startY->format( 'm' ); + $startY = $startY->format( 'Y' ); + } + else { + if( ! $startY ) $startY = date( 'Y' ); + if( ! $startM ) $startM = date( 'm' ); + if( ! $startD ) $startD = date( 'd' ); + if( ! $endY ) $endY = $startY; + if( ! $endM ) $endM = $startM; + if( ! $endD ) $endD = $startD; + } +// echo "selectComp args={$startY}-{$startM}-{$startD} - {$endY}-{$endM}-{$endD}
\n"; $tcnt = 0;// test ### + /* check component types */ + if( empty( $cType )) + $cType = iCalUtilityFunctions::$vComps; + else { + if( ! is_array( $cType )) + $cType = array( $cType ); + $cType = array_map( 'strtolower', $cType ); + foreach( $cType as $cix => $theType ) { + if( !in_array( $theType, iCalUtilityFunctions::$vComps )) + $cType[$cix] = 'vevent'; + } + $cType = array_unique( $cType ); + } + if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination + $split = FALSE; + if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination + $split = FALSE; + /* iterate components */ + $result = array(); + $this->sort( 'UID' ); + $compUIDcmp = null; + $exdatelist = $recurridList = array(); + $intervalP1D = new DateInterval( 'P1D' ); + foreach ( $this->components as $cix => $component ) { + if( empty( $component )) continue; + /* deselect unvalid type components */ + if( !in_array( $component->objName, $cType )) + continue; + unset( $compStart, $compEnd ); + /* select start from dtstart or due if dtstart is missing */ + $prop = $component->getProperty( 'dtstart', FALSE, TRUE ); + if( empty( $prop ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $prop = $component->getProperty( 'due', FALSE, TRUE )))) + continue; + if( empty( $prop )) + continue; + /* get UID */ + $compUID = $component->getProperty( 'UID' ); + if( $compUIDcmp != $compUID ) { + $compUIDcmp = $compUID; + $exdatelist = $recurridList = array(); + } + $recurrid = FALSE; +// file_put_contents( '/opt/work/iCal/iCalcreator/iCalcreator-2.20.x/log/log.txt', "#$cix".PHP_EOL.var_export( $component, TRUE ).PHP_EOL.PHP_EOL, FILE_APPEND ); // test ### + $compStart = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'] ); + $dtstartTz = $compStart->getTimezoneName(); + if( isset( $prop['params']['VALUE'] ) && ( 'DATE' == $prop['params']['VALUE'] )) + $compStartHis = ''; + else { + $his = $compStart->getTime(); + $compStartHis = sprintf( iCalUtilityFunctions::$fmt['His'], (int) $his[0], (int) $his[1], (int) $his[2] ); + } + /* get end date from dtend/due/duration properties */ + if( FALSE !== ( $prop = $component->getProperty( 'dtend', FALSE, TRUE ))) { + $compEnd = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz ); + $compEnd->SCbools[ 'dtendExist'] = TRUE; + } + if( empty( $prop ) && ( $component->objName == 'vtodo' ) && ( FALSE !== ( $prop = $component->getProperty( 'due', FALSE, TRUE )))) { + $compEnd = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz ); + $compEnd->SCbools[ 'dueExist'] = TRUE; + } + if( empty( $prop ) && ( FALSE !== ( $prop = $component->getProperty( 'duration', FALSE, TRUE, TRUE )))) { // in dtend (array) format + $compEnd = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz ); + $compEnd->SCbools[ 'durationExist'] = TRUE; + } + if( ! empty( $prop ) && ! isset( $prop['value']['hour'] )) { + /* a DTEND without time part denotes an end of an event that actually ends the day before, + for an all-day event DTSTART=20071201 DTEND=20071202, taking place 20071201!!! */ + $compEnd->SCbools[ 'endAllDayEvent'] = TRUE; + $compEnd->modify( '-1 day' ); + $compEnd->setTime( 23, 59, 59 ); + } + unset( $prop ); + if( empty( $compEnd )) { + $compDuration = FALSE; + $compEnd = clone $compStart; + $compEnd->setTime( 23, 59, 59 ); // 23:59:59 the same day as start + } + else { + if( $compEnd->format( 'Ymd' ) < $compStart->format( 'Ymd' )) { // MUST be after start date!! + $compEnd = clone $compStart; + $compEnd->setTime( 23, 59, 59 ); // 23:59:59 the same day as start or ??? + } + $compDuration = $compStart->diff( $compEnd ); // DateInterval + } + /* check recurrence-id (note, a missing sequence is the same as sequence=0 so don't test for sequence), to alter when hit dtstart/recurlist */ + if( FALSE !== ( $prop = $component->getProperty( 'recurrence-id', FALSE, TRUE ))) { + $recurrid = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz ); + $rangeSet = ( isset( $prop['params']['RANGE'] ) && ( 'THISANDFUTURE' == $prop['params']['RANGE'] )) ? TRUE : FALSE; + $recurridList[$recurrid->key] = array( clone $compStart, clone $compEnd, $compDuration, $rangeSet ); // change recur this day to new YmdHis/duration/range +// echo "adding comp no:$cix with date=".$compStart->format(iCalUtilityFunctions::$fmt['YmdHis2e'])." to recurridList id={$recurrid->key}, newDate={$compStart->key}
\n"; // test ### + unset( $prop ); + continue; // ignore any other props in the component + } // end recurrence-id/sequence test +// else echo "comp no:$cix with date=".$compStart->format().", NO recurrence-id
\n"; // test ### + ksort( $recurridList, SORT_STRING ); +// echo 'recurridList='.implode(', ', array_keys( $recurridList ))."
\n"; // test ### + $fcnStart = clone $compStart; + $fcnStart->setDate((int) $startY, (int) $startM, (int) $startD ); + $fcnStart->setTime( 0, 0, 0 ); + $fcnEnd = clone $compEnd; + $fcnEnd->setDate((int) $endY, (int) $endM, (int) $endD ); + $fcnEnd->setTime( 23, 59, 59 ); +// echo 'compStart='.$compStart->format().', compEnd'.$compEnd->format(); if($compDuration)echo ', interval='.$compDuration->format( iCalUtilityFunctions::$fmt['durDHis'] ); echo "
\n"; $tcnt = 0;// test ### + /* ************************************************************* + make a list of optional exclude dates for component occurence from exrule and exdate + *********************************************************** */ + $workStart = clone $compStart; + $workStart->sub( $compDuration ? $compDuration : $intervalP1D ); + $workEnd = clone $fcnEnd; + $workEnd->add( $compDuration ? $compDuration : $intervalP1D ); + while( FALSE !== ( $prop = $component->getProperty( 'EXRULE' ))) { + $exdatelist2 = array(); + if( isset( $prop['UNTIL']['hour'] )) { // convert until date to dtstart timezone + $until = iCaldateTime::factory( $prop['UNTIL'], array( 'TZID' => 'UTC' ), null, $dtstartTz ); + $until = $until->format(); + iCalUtilityFunctions::_strDate2arr( $until ); + $prop['UNTIL'] = $until; + } + iCalUtilityFunctions::_recur2date( $exdatelist2, $prop, $compStart, $workStart, $workEnd ); + foreach( $exdatelist2 as $k => $v ) + $exdatelist[$k.$compStartHis] = $v; // point out exact every excluded ocurrence (incl. opt. His) + unset( $until, $exdatelist2 ); + } + while( FALSE !== ( $prop = $component->getProperty( 'EXDATE', FALSE, TRUE ))) { // - start check exdate + foreach( $prop['value'] as $exdate ) { + $exdate = iCaldateTime::factory( $exdate, $prop['params'], $exdate, $dtstartTz ); + $exdatelist[$exdate->key] = TRUE; + } // end - foreach( $exdate as $exdate ) + } // end - check exdate + unset( $prop, $exdate ); +// echo 'exdatelist=' .implode(', ', array_keys( $exdatelist )) ."
\n"; // test ### + /* ************************************************************* + select only components within.. . + *********************************************************** */ + $xRecurrence = 1; + if(( ! $any && iCalUtilityFunctions::_inScope( $compStart, $fcnStart, $compStart, $fcnEnd, $compStart->dateFormat )) || + ( $any && iCalUtilityFunctions::_inScope( $fcnEnd, $compStart, $fcnStart, $compEnd, $compStart->dateFormat ))) { + /* add the selected component (WITHIN valid dates) to output array */ + if( $flat ) { // any=true/false, ignores split + if( !$recurrid ) + $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id) + } + elseif( $split ) { // split the original component +// echo 'split org.:'.$compStart->format().' < '.$fcnStart->format( 'Ymd His e' )."
\n"; // test ### + if( $compStart->format( iCalUtilityFunctions::$fmt['YmdHis2'] ) < $fcnStart->format( iCalUtilityFunctions::$fmt['YmdHis2'] )) + $rstart = clone $fcnStart; + else + $rstart = clone $compStart; + if( $compEnd->format( iCalUtilityFunctions::$fmt['YmdHis2'] ) > $fcnEnd->format( iCalUtilityFunctions::$fmt['YmdHis2'] )) + $rend = clone $fcnEnd; + else + $rend = clone $compEnd; +// echo "going to test comp no:$cix, rstart=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )." (key={$rstart->key}), end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."
\n"; // test ### + if( ! isset( $exdatelist[$rstart->key] )) { // not excluded in exrule/exdate + if( isset( $recurridList[$rstart->key] )) { // change start day to new YmdHis/duration + $k = $rstart->key; +// echo "recurridList HIT, key={$k}, recur Date=".$recurridList[$k][0]->key."
\n"; // test ### + $rstart = clone $recurridList[$k][0]; + $startHis = $rstart->getTime(); + $rend = clone $rstart; + if( FALSE !== $recurridList[$k][2] ) + $rend->add( $recurridList[$k][2] ); + elseif( FALSE !== $compDuration ) + $rend->add( $compDuration ); + $endHis = $rend->getTime(); + unset( $recurridList[$k] ); + } + else { + $startHis = $compStart->getTime(); + $endHis = $compEnd->getTime(); + } +// echo "_____testing comp no:$cix, rstart=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )." (key={$rstart->key}), end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."
\n"; // test ### + $cnt = 0; // exclude any recurrence START date, found in exdatelist or recurridList but accept the reccurence-id comp itself + $occurenceDays = 1 + (int) $rstart->diff( $rend )->format( '%a' ); // count the days (incl start day) + while( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) <= $rend->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { + $cnt += 1; + if( 1 < $occurenceDays ) + $component->setProperty( 'X-OCCURENCE', sprintf( iCalUtilityFunctions::$fmt['dayOfDays'], $cnt, $occurenceDays )); + if( 1 < $cnt ) + $rstart->setTime( 0, 0, 0 ); + else { + $rstart->setTime( $startHis[0], $startHis[1], $startHis[2] ); + $exdatelist[$rstart->key] = $compDuration; // make sure to exclude start day from the recurrence pattern + } + $component->setProperty( 'X-CURRENT-DTSTART', $rstart->format( $compStart->dateFormat )); + if( FALSE !== $compDuration ) { + $propName = ( isset( $compEnd->SCbools[ 'dueExist'] )) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND'; + if( $cnt < $occurenceDays ) + $rstart->setTime( 23, 59, 59 ); + else + $rstart->setTime( $endHis[0], $endHis[1], $endHis[2] ); + $component->setProperty( $propName, $rstart->format( $compEnd->dateFormat )); + } + $result[(int)$rstart->format( 'Y' )][(int)$rstart->format( 'm' )][(int)$rstart->format( 'd' )][$compUID] = $component->copy(); // copy to output + $rstart->add( $intervalP1D ); + } // end while(( $rstart->format( 'Ymd' ) < $rend->format( 'Ymd' )) + unset( $cnt, $occurenceDays ); + } // end if( ! isset( $exdatelist[$rstart->key] )) +// else echo "skip no:$cix with date=".$compStart->format()."
\n"; // test ### + unset( $rstart, $rend ); + } // end elseif( $split ) - else use component date + else { // !$flat && !$split, i.e. no flat array and DTSTART within period + $tstart = ( isset( $recurridList[$compStart->key] )) ? clone $recurridList[$k][0] : clone $compStart; +// echo "going to test comp no:$cix with checkDate={$compStart->key} with recurridList=".implode(',',array_keys($recurridList)); // test ### + if( ! $any || ! isset( $exdatelist[$tstart->key] )) { // exclude any recurrence date, found in exdatelist +// echo " and copied to output
\n"; // test ### + $result[(int)$tstart->format( 'Y' )][(int)$tstart->format( 'm' )][(int)$tstart->format( 'd' )][$compUID] = $component->copy(); // copy to output + } + unset( $tstart ); + } + } // end (dt)start within the period OR occurs within the period + /* ************************************************************* + if 'any' components, check components with reccurrence rules, removing all excluding dates + *********************************************************** */ + if( TRUE === $any ) { + $recurlist = array(); + /* make a list of optional repeating dates for component occurence, rrule, rdate */ + while( FALSE !== ( $prop = $component->getProperty( 'RRULE' ))) { // get all rrule dates (multiple values allowed) + $recurlist2 = array(); + if( isset( $prop['UNTIL']['hour'] )) { // convert $rrule['UNTIL'] to the same timezone as DTSTART !! + $until = iCaldateTime::factory( $prop['UNTIL'], array( 'TZID' => 'UTC' ), null, $dtstartTz ); + $until = $until->format(); + iCalUtilityFunctions::_strDate2arr( $until ); + $prop['UNTIL'] = $until; + } + iCalUtilityFunctions::_recur2date( $recurlist2, $prop, $compStart, $workStart, $workEnd ); + foreach( $recurlist2 as $recurkey => $recurvalue ) { // recurkey=Ymd + $recurkey .= $compStartHis; // add opt His + if( ! isset( $exdatelist[$recurkey] )) + $recurlist[$recurkey] = $compDuration; // DateInterval or FALSE + } + unset( $prop, $until, $recurlist2 ); + } + $workStart = clone $fcnStart; + $workStart->sub( $compDuration ? $compDuration : $intervalP1D ); + $format = $compStart->dateFormat; + while( FALSE !== ( $prop = $component->getProperty( 'RDATE', FALSE, TRUE ))) { + $rdateFmt = ( isset( $prop['params']['VALUE'] )) ? $prop['params']['VALUE'] : 'DATETIME'; + $params = $prop['params']; + $prop = $prop['value']; + foreach( $prop as $theRdate ) { + if( 'PERIOD' == $rdateFmt ) { // all days within PERIOD + $rdate = iCaldateTime::factory( $theRdate[0], $params, $theRdate[0], $dtstartTz ); + if( ! iCalUtilityFunctions::_inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) || isset( $exdatelist[$rdate->key] )) + continue; + if( isset( $theRdate[1]['year'] )) // date-date period end + $recurlist[$rdate->key] = $rdate->diff( iCaldateTime::factory( $theRdate[1], $params, $theRdate[1], $dtstartTz )); + else // period duration + $recurlist[$rdate->key] = new DateInterval( iCalUtilityFunctions::_duration2str( $theRdate[1] )); + } // end if( 'PERIOD' == $rdateFmt ) + elseif( 'DATE' == $rdateFmt ) { // single recurrence, date + $rdate = iCaldateTime::factory( $theRdate, array_merge( $params, array( 'TZID' => $dtstartTz )), null, $dtstartTz ); + if( iCalUtilityFunctions::_inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) && ! isset( $exdatelist[$rdate->key] )) + $recurlist[$rdate->key.$compStartHis] = $compDuration; // set start date for recurrence + DateInterval/FALSE (+opt His) + } // end DATE + else { // start DATETIME + $rdate = iCaldateTime::factory( $theRdate, $params, $theRdate, $dtstartTz ); + if( iCalUtilityFunctions::_inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) && ! isset( $exdatelist[$rdate->key] )) + $recurlist[$rdate->key] = $compDuration; // set start datetime for recurrence DateInterval/FALSE + } // end DATETIME + } // end foreach( $prop as $theRdate ) + } // end while( FALSE !== ( $prop = $component->getProperty( 'rdate', FALSE, TRUE ))) + unset( $prop, $workStart, $format, $theRdate, $rdate, $rend ); + foreach( $recurridList as $rKey => $rVal ) { // check for recurrence-id, i.e. alter recur Ymd[His] and duration + if( isset( $recurlist[$rKey] )) { + unset( $recurlist[$rKey] ); + $recurlist[$rVal[0]->key] = ( FALSE !== $rVal[2] ) ? $rVal[2] : $compDuration; +// echo "alter recurfrom {$rKey} to {$rVal[0]->key} ";if(FALSE!==$dur)echo " ({$dur->format( '%a days, %h-%i-%s' )})";echo "
\n"; // test ### + } + } + ksort( $recurlist, SORT_STRING ); +// echo 'recurlist=' .implode(', ', array_keys( $recurlist )) ."
\n"; // test ### +// echo 'recurridList=' .implode(', ', array_keys( $recurridList )) ."
\n"; // test ### + /* ************************************************************* + output all remaining components in recurlist + *********************************************************** */ + if( 0 < count( $recurlist )) { + $component2 = $component->copy(); + $compUID = $component2->getProperty( 'UID' ); + $workStart = clone $fcnStart; + $workStart->sub( $compDuration ? $compDuration : $intervalP1D ); + $YmdOld = null; + foreach( $recurlist as $recurkey => $durvalue ) { + if( $YmdOld == substr( $recurkey, 0, 8 )) // skip overlapping recur the same day, i.e. RDATE before RRULE + continue; + $YmdOld = substr( $recurkey, 0, 8 ); + $rstart = clone $compStart; + $rstart->setDate((int) substr( $recurkey, 0, 4 ), (int) substr( $recurkey, 4, 2 ), (int) substr( $recurkey, 6, 2 )); + $rstart->setTime((int) substr( $recurkey, 8, 2 ), (int) substr( $recurkey, 10, 2 ), (int) substr( $recurkey, 12, 2 )); +// echo "recur start=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."
\n"; // test ###; + /* add recurring components within valid dates to output array, only start date set */ + if( $flat ) { + if( !isset( $result[$compUID] )) // only one comp + $result[$compUID] = $component2->copy(); // copy to output + } + /* add recurring components within valid dates to output array, split for each day */ + elseif( $split ) { + $rend = clone $rstart; + if( FALSE !== $durvalue ) + $rend->add( $durvalue ); + if( $rend->format( iCalUtilityFunctions::$fmt['Ymd2'] ) > $fcnEnd->format( iCalUtilityFunctions::$fmt['Ymd2'] )) + $rend = clone $fcnEnd; +// echo "recur 1={$recurkey}, start=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] ).", end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] );if($durvalue) echo ", duration=".$durvalue->format( iCalUtilityFunctions::$fmt['durDHis'] );echo "
\n"; // test ### + $xRecurrence += 1; + $cnt = 0; + $occurenceDays = 1 + (int) $rstart->diff( $rend )->format( '%a' ); // count the days (incl start day) + while( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) <= $rend->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { // iterate.. . + $cnt += 1; + if( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) < $fcnStart->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { // date before dtstart +// echo "recur 3, start=".$rstart->format( 'Y-m-d H:i:s' )." >= fcnStart=".$fcnStart->format( 'Y-m-d H:i:s' )."
\n"; // test ### + $rstart->add( $intervalP1D ); + $rstart->setTime( 0, 0, 0 ); + continue; + } + elseif( 2 == $cnt ) + $rstart->setTime( 0, 0, 0 ); + $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); + if( 1 < $occurenceDays ) + $component2->setProperty( 'X-OCCURENCE', sprintf( iCalUtilityFunctions::$fmt['dayOfDays'], $cnt, $occurenceDays )); + else + $component2->deleteProperty( 'X-OCCURENCE' ); + $component2->setProperty( 'X-CURRENT-DTSTART', $rstart->format( $compStart->dateFormat )); + $propName = ( isset( $compEnd->SCbools[ 'dueExist'] )) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND'; + if( FALSE !== $durvalue ) { + if( $cnt < $occurenceDays ) + $rstart->setTime( 23, 59, 59 ); + else { + $His = $rend->getTime(); // set end time + $rstart->setTime( $His[0], $His[1], $His[2] ); + } + $component2->setProperty( $propName, $rstart->format( $compEnd->dateFormat )); +// echo "checking date, (day {$cnt} of {$occurenceDays}), _end_=".$rstart->format( 'Y-m-d H:i:s e' )."
"; // test ###; + } + else + $component2->deleteProperty( $propName ); + $result[(int)$rstart->format( 'Y' )][(int)$rstart->format( 'm' )][(int)$rstart->format( 'd' )][$compUID] = $component2->copy(); // copy to output + $rstart->add( $intervalP1D ); + } // end while( $rstart->format( 'Ymd' ) <= $rend->format( 'Ymd' )) + unset( $rstart, $rend ); + } // end elseif( $split ) + elseif( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) >= $fcnStart->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { + $xRecurrence += 1; // date within period //* flat=FALSE && split=FALSE => one comp every recur startdate *// + $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); + $component2->setProperty( 'X-CURRENT-DTSTART', $rstart->format( $compStart->dateFormat )); + $propName = ( isset( $compEnd->SCbools[ 'dueExist'] )) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND'; + if( FALSE !== $durvalue ) { + $rstart->add( $durvalue ); + $component2->setProperty( $propName, $rstart->format( $compEnd->dateFormat )); + } + else + $component2->deleteProperty( $propName ); + $result[(int)$rstart->format( 'Y' )][(int)$rstart->format( 'm' )][(int)$rstart->format( 'd' )][$compUID] = $component2->copy(); // copy to output + } // end elseif( $rstart >= $fcnStart ) + unset( $rstart ); + } // end foreach( $recurlist as $recurkey => $durvalue ) + unset( $component2, $xRecurrence, $compUID, $workStart, $rstart ); + } // end if( 0 < count( $recurlist )) + } // end if( TRUE === $any ) + unset( $component ); + } // end foreach ( $this->components as $cix => $component ) + unset( $recurrid, $recurridList, $fcnStart, $fcnEnd, $compStart, $compEnd, $exdatelist, $recurlist ); // clean up + if( 0 >= count( $result )) + return FALSE; + elseif( !$flat ) { + foreach( $result as $y => $yeararr ) { + foreach( $yeararr as $m => $montharr ) { + foreach( $montharr as $d => $dayarr ) { + if( empty( $result[$y][$m][$d] )) + unset( $result[$y][$m][$d] ); + else { + $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index + if( 1 < count( $result[$y][$m][$d] )) { + foreach( $result[$y][$m][$d] as & $c ) // sort + iCalUtilityFunctions::_setSortArgs( $c ); + usort( $result[$y][$m][$d], array( 'iCalUtilityFunctions', '_cmpfcn' )); + } + } + } // end foreach( $montharr as $d => $dayarr ) + if( empty( $result[$y][$m] )) + unset( $result[$y][$m] ); + else + ksort( $result[$y][$m] ); + } // end foreach( $yeararr as $m => $montharr ) + if( empty( $result[$y] )) + unset( $result[$y] ); + else + ksort( $result[$y] ); + }// end foreach( $result as $y => $yeararr ) + if( empty( $result )) + unset( $result ); + else + ksort( $result ); + } // end elseif( !$flat ) + if( 0 >= count( $result )) + return FALSE; + return $result; + } +/** + * select components from calendar on based on specific property value(-s) + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param array $selectOptions (string) key => (mixed) value, (key=propertyName) + * @uses vcalendar::$components + * @uses calendarComponent::$objName + * @uses iCalUtilityFunctions::$vComps + * @uses calendarComponent::getProperty() + * @uses iCalUtilityFunctions::$otherProps + * @uses calendarComponent::copy() + * @uses iCalUtilityFunctions::$mProps1 + * @uses calendarComponent::_getProperties() + * @return array + */ + function selectComponents2( $selectOptions ) { +// $output = array(); + $selectOptions = array_change_key_case( $selectOptions, CASE_UPPER ); + foreach( $this->components as $cix => $component3 ) { + if( !in_array( $component3->objName, iCalUtilityFunctions::$vComps )) + continue; + $uid = $component3->getProperty( 'UID' ); + foreach( $selectOptions as $propName => $pvalue ) { + if( !in_array( $propName, iCalUtilityFunctions::$otherProps )) + continue; + if( !is_array( $pvalue )) + $pvalue = array( $pvalue ); + if(( 'UID' == $propName ) && in_array( $uid, $pvalue )) { + $output[$uid][] = $component3->copy(); + continue; + } + elseif( in_array( $propName, iCalUtilityFunctions::$mProps1 )) { + $propValues = array(); + $component3->_getProperties( $propName, $propValues ); + $propValues = array_keys( $propValues ); + foreach( $pvalue as $theValue ) { + if( in_array( $theValue, $propValues )) { // && !isset( $output[$uid] )) { + $output[$uid][] = $component3->copy(); + break; + } + } + continue; + } // end elseif( // multiple occurrence? + elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single occurrence + continue; + if( is_array( $d )) { + foreach( $d as $part ) { + if( in_array( $part, $pvalue ) && !isset( $output[$uid] )) + $output[$uid][] = $component3->copy(); + } + } + elseif(( 'SUMMARY' == $propName ) && !isset( $output[$uid] )) { + foreach( $pvalue as $pval ) { + if( FALSE !== stripos( $d, $pval )) { + $output[$uid][] = $component3->copy(); + break; + } + } + } + elseif( in_array( $d, $pvalue ) && !isset( $output[$uid] )) + $output[$uid][] = $component3->copy(); + } // end foreach( $selectOptions as $propName => $pvalue ) { + } // end foreach( $this->components as $cix => $component3 ) { + if( !empty( $output )) { + ksort( $output ); // uid order + $output2 = array(); + foreach( $output as $uid => $components ) { + foreach( $components as $component ) + $output2[] = $component; + } + $output = $output2; + } + return $output; + } +/** + * replace calendar component in vcalendar + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param object $component calendar component + * @uses calendarComponent::$objName + * @uses iCalUtilityFunctions::$vComps + * @uses vcalendar::setComponent() + * @uses calendarComponent::getProperty() + * @return bool + */ + function replaceComponent( $component ) { + if( in_array( $component->objName, iCalUtilityFunctions::$vComps )) + return $this->setComponent( $component, $component->getProperty( 'UID' )); + if(( 'vtimezone' != $component->objName ) || ( FALSE === ( $tzid = $component->getProperty( 'TZID' )))) + return FALSE; + foreach( $this->components as $cix => $comp ) { + if( 'vtimezone' != $component->objName ) + continue; + if( $tzid == $comp->getComponent( 'TZID' )) { + unset( $component->propix, $component->compix ); + $this->components[$cix] = $component; + return TRUE; + } + } + return FALSE; + } +/** + * add calendar component to calendar + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param object $component calendar component + * @param mixed $arg1 optional, ordno/component type/ component uid + * @param mixed $arg2 optional, ordno if arg1 = component type + * @uses calendarComponent::setConfig() + * @uses vcalendar::getConfig() + * @uses calendarComponent::$objName + * @uses calendarComponent::getProperty() + * @uses vcalendar::$components + * @uses iCalUtilityFunctions::$mComps + * @uses calendarComponent::copy() + * @return bool + */ + function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) { + $component->setConfig( $this->getConfig(), FALSE, TRUE ); + if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) { + /* make sure dtstamp and uid is set */ + $dummy1 = $component->getProperty( 'dtstamp' ); + $dummy2 = $component->getProperty( 'uid' ); + } + unset( $component->propix, $component->compix ); + if( !$arg1 ) { // plain insert, last in chain + $this->components[] = $component->copy(); + return TRUE; + } + $argType = $index = null; + if ( ctype_digit( (string) $arg1 )) { // index insert/replace + $argType = 'INDEX'; + $index = (int) $arg1 - 1; + } + elseif( in_array( strtolower( $arg1 ), iCalUtilityFunctions::$mComps )) { + $argType = strtolower( $arg1 ); + $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0; + } + // else if arg1 is set, arg1 must be an UID + $cix1sC = 0; + foreach ( $this->components as $cix => $component2) { + if( empty( $component2 )) continue; + if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace + $this->components[$cix] = $component->copy(); + return TRUE; + } + elseif( $argType == $component2->objName ) { // component Type index insert/replace + if( $index == $cix1sC ) { + $this->components[$cix] = $component->copy(); + return TRUE; + } + $cix1sC++; + } + elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace + $this->components[$cix] = $component->copy(); + return TRUE; + } + } + /* arg1=index and not found.. . insert at index .. .*/ + if( 'INDEX' == $argType ) { + $this->components[$index] = $component->copy(); + ksort( $this->components, SORT_NUMERIC ); + } + else /* not found.. . insert last in chain anyway .. .*/ + $this->components[] = $component->copy(); + return TRUE; + } +/** + * sort iCal compoments + * + * ascending sort on properties (if exist) x-current-dtstart, dtstart, + * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid if called without arguments, + * otherwise sorting on specific (argument) property values + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.11 - 2015-03-21 + * @param string $sortArg + * @uses vcalendar::$components + * @uses iCalUtilityFunctions::$otherProps + * @uses iCalUtilityFunctions::_setSortArgs() + * @uses iCalUtilityFunctions::_cmpfcn() + * @return void + */ + function sort( $sortArg=FALSE ) { + if( ! is_array( $this->components ) || ( 2 > count( $this->components ))) + return; + if( $sortArg ) { + $sortArg = strtoupper( $sortArg ); + if( ! in_array( $sortArg, iCalUtilityFunctions::$otherProps ) && ( 'DTSTAMP' != $sortArg )) + $sortArg = FALSE; + } + foreach( $this->components as & $c ) + iCalUtilityFunctions::_setSortArgs( $c, $sortArg ); + usort( $this->components, array( 'iCalUtilityFunctions', '_cmpfcn' )); + } +/** + * parse iCal text/file into vcalendar, components, properties and parameters + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.12 - 2014-03-10 + * @param mixed $unparsedtext strict rfc2445 formatted, single property string or array of property strings + * @param resource $context PHP resource context + * @uses iCalUtilityFunctions::convEolChar() + * @uses vcalendar::getConfig() + * @uses vcalendar::$components + * @uses calendarComponent::copy() + * @uses vevent::construct() + * @uses vfreebusy::construct() + * @uses vjournal::construct() + * @uses vtodo::construct() + * @uses vtimezone::construct() + * @uses vcalendar::$unparsed + * @uses iCalUtilityFunctions::_splitContent() + * @uses iCalUtilityFunctions::_strunrep() + * @uses vcalendar::setProperty() + * @uses calendarComponent::$unparsed + * @uses calendarComponent::parse() + * @return bool FALSE if error occurs during parsing + */ + function parse( $unparsedtext=FALSE, $context=null ) { + $nl = $this->getConfig( 'nl' ); + if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) { + /* directory+filename is set previously via setConfig url or directory+filename */ + if( FALSE === ( $file = $this->getConfig( 'url' ))) { + if( FALSE === ( $file = $this->getConfig( 'dirfile' ))) + return FALSE; /* err 1 */ + if( ! is_file( $file )) + return FALSE; /* err 2 */ + if( ! is_readable( $file )) + return FALSE; /* err 3 */ + } + /* READ FILE */ + if( ! empty( $context ) && filter_var( $file, FILTER_VALIDATE_URL ) && + ( FALSE === ( $rows = file_get_contents( $file, FALSE, $context )))) + return FALSE; /* err 6 */ + elseif( FALSE === ( $rows = file_get_contents( $file ))) + return FALSE; /* err 5 */ + } // end if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) + elseif( is_array( $unparsedtext )) + $rows = implode( '\n'.$nl, $unparsedtext ); + else + $rows = & $unparsedtext; + /* fix line folding */ + $rows = iCalUtilityFunctions::convEolChar( $rows, $nl ); + /* skip leading (empty/invalid) lines (and remove leading BOM chars etc) */ + foreach( $rows as $lix => $line ) { + if( FALSE !== stripos( $line, 'BEGIN:VCALENDAR' )) { + $rows[$lix] = 'BEGIN:VCALENDAR'; + break; + } + unset( $rows[$lix] ); + } + $rcnt = count( $rows ); + if( 3 > $rcnt ) /* err 10 */ + return FALSE; + /* skip trailing empty lines and ensure an end row */ + $lix = array_keys( $rows ); + $lix = end( $lix ); + while( 3 < $lix ) { + $tst = trim( $rows[$lix] ); + if(( '\n' == $tst ) || empty( $tst )) { + unset( $rows[$lix] ); + $lix--; + continue; + } + if( FALSE === stripos( $rows[$lix], 'END:VCALENDAR' )) + $rows[] = 'END:VCALENDAR'; + else + $rows[$lix] = 'END:VCALENDAR'; + break; + } + $comp = & $this; + $calsync = $compsync = 0; + /* identify components and update unparsed data within component */ + $config = $this->getConfig(); + $endtxt = array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' ); + foreach( $rows as $lix => $line ) { + if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) { + $calsync++; + continue; + } + elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) { + if( 0 < $compsync ) + $this->components[] = $comp->copy(); + $compsync--; + $calsync--; + break; + } + elseif( 1 != $calsync ) + return FALSE; /* err 20 */ + elseif( in_array( strtoupper( substr( $line, 0, 6 )), $endtxt )) { + $this->components[] = $comp->copy(); + $compsync--; + continue; + } + if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) { + $comp = new vevent( $config ); + $compsync++; + } + elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) { + $comp = new vfreebusy( $config ); + $compsync++; + } + elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) { + $comp = new vjournal( $config ); + $compsync++; + } + elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) { + $comp = new vtodo( $config ); + $compsync++; + } + elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) { + $comp = new vtimezone( $config ); + $compsync++; + } + else { /* update component with unparsed data */ + $comp->unparsed[] = $line; + } + } // end foreach( $rows as $line ) + unset( $config, $endtxt ); + /* parse data for calendar (this) object */ + if( isset( $this->unparsed ) && is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) { + /* concatenate property values spread over several lines */ + $propnames = array( 'calscale','method','prodid','version','x-' ); + $proprows = array(); + for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines + $line = rtrim( $this->unparsed[$i], $nl ); + while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} )) + $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl ); + $proprows[] = $line; + } + foreach( $proprows as $line ) { + if( '\n' == substr( $line, -2 )) + $line = substr( $line, 0, -2 ); + /* get property name */ + $propname = ''; + $cix = 0; + while( FALSE !== ( $char = substr( $line, $cix, 1 ))) { + if( in_array( $char, array( ':', ';' ))) + break; + else + $propname .= $char; + $cix++; + } + /* skip non standard property names */ + if(( 'x-' != strtolower( substr( $propname, 0, 2 ))) && !in_array( strtolower( $propname ), $propnames )) + continue; + /* ignore version/prodid properties */ + if( in_array( strtolower( $propname ), array( 'version', 'prodid' ))) + continue; + /* rest of the line is opt.params and value */ + $line = substr( $line, $cix); + /* separate attributes from value */ + iCalUtilityFunctions::_splitContent( $line, $propAttr ); + /* update Property */ + if( FALSE !== strpos( $line, ',' )) { + $content = array( 0 => '' ); + $cix = $lix = 0; + while( FALSE !== substr( $line, $lix, 1 )) { + if(( 0 < $lix ) && ( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { + $cix++; + $content[$cix] = ''; + } + else + $content[$cix] .= $line[$lix]; + $lix++; + } + if( 1 < count( $content )) { + foreach( $content as $cix => $contentPart ) + $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart ); + $this->setProperty( $propname, $content, $propAttr ); + continue; + } + else + $line = reset( $content ); + $line = iCalUtilityFunctions::_strunrep( $line ); + } + $this->setProperty( $propname, rtrim( $line, "\x00..\x1F" ), $propAttr ); + } // end - foreach( $this->unparsed.. . + } // end - if( is_array( $this->unparsed.. . + unset( $unparsedtext, $rows, $this->unparsed, $proprows ); + /* parse Components */ + if( is_array( $this->components ) && ( 0 < count( $this->components ))) { + $ckeys = array_keys( $this->components ); + foreach( $ckeys as $ckey ) { + if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) { + $this->components[$ckey]->parse(); + } + } + } + else + return FALSE; /* err 91 or something.. . */ + return TRUE; + } +/*********************************************************************************/ +/** + * creates formatted output for calendar object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.07 - 2015-03-31 + * @uses vcalendar::$format + * @uses vcalendar::$nl + * @uses vcalendar::createVersion() + * @uses vcalendar::createProdid() + * @uses vcalendar::createCalscale() + * @uses vcalendar::createMethod() + * @uses vcalendar::createXprop() + * @uses vcalendar::$components + * @uses calendarComponent::setConfig() + * @uses vcalendar::getConfig() + * @uses calendarComponent::createComponent() + * @uses vcalendar::$xcaldecl + * @return string + */ + function createCalendar() { + parent::_createFormat(); + $calendarInit = $calendarxCaldecl = $calendarStart = $calendar = ''; + switch( $this->format ) { + case 'xcal': + $calendarInit = ''.$this->nl. + 'nl. + '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"'; + $calendarStart = '>'.$this->nl.'nl; + break; + } + $calendarStart .= $this->createVersion(); + $calendarStart .= $this->createProdid(); + $calendarStart .= $this->createCalscale(); + $calendarStart .= $this->createMethod(); + if( 'xcal' == $this->format ) + $calendarStart .= '>'.$this->nl; + $calendar .= $this->createXprop(); + foreach( $this->components as $component ) { + if( empty( $component )) continue; + $component->setConfig( $this->getConfig(), FALSE, TRUE ); + $calendar .= $component->createComponent( $this->xcaldecl ); + } + if(( 'xcal' == $this->format ) && ( 0 < count( $this->xcaldecl ))) { // xCal only + $calendarInit .= ' ['; + $old_xcaldecl = array(); + foreach( $this->xcaldecl as $declix => $declPart ) { + if(( 0 < count( $old_xcaldecl)) && + isset( $declPart['uri'] ) && isset( $declPart['external'] ) && + isset( $old_xcaldecl['uri'] ) && isset( $old_xcaldecl['external'] ) && + ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) && + ( in_array( $declPart['external'], $old_xcaldecl['external'] ))) + continue; // no duplicate uri and ext. references + if(( 0 < count( $old_xcaldecl)) && + !isset( $declPart['uri'] ) && !isset( $declPart['uri'] ) && + isset( $declPart['ref'] ) && isset( $old_xcaldecl['ref'] ) && + ( in_array( $declPart['ref'], $old_xcaldecl['ref'] ))) + continue; // no duplicate element declarations + $calendarxCaldecl .= $this->nl.' $declValue ) { + switch( $declKey ) { // index + case 'xmldecl': // no 1 + $calendarxCaldecl .= $declValue.' '; + break; + case 'uri': // no 2 + $calendarxCaldecl .= $declValue.' '; + $old_xcaldecl['uri'][] = $declValue; + break; + case 'ref': // no 3 + $calendarxCaldecl .= $declValue.' '; + $old_xcaldecl['ref'][] = $declValue; + break; + case 'external': // no 4 + $calendarxCaldecl .= '"'.$declValue.'" '; + $old_xcaldecl['external'][] = $declValue; + break; + case 'type': // no 5 + $calendarxCaldecl .= $declValue.' '; + break; + case 'type2': // no 6 + $calendarxCaldecl .= $declValue; + break; + } + } + $calendarxCaldecl .= '>'; + } + $calendarxCaldecl .= $this->nl.']'; + } // end if(( 'xcal'... + switch( $this->format ) { + case 'xcal': + $calendar .= ''.$this->nl; + break; + default: + $calendar .= 'END:VCALENDAR'.$this->nl; + break; + } + return $calendarInit.$calendarxCaldecl.$calendarStart.$calendar; + } +/** + * a HTTP redirect header is sent with created, updated and/or parsed calendar + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.5 - 2015-03-29 + * @param bool $utf8Encode + * @param bool $gzip + * @param bool $cdType TRUE : Content-Disposition: attachment... (default), FALSE : ...inline... + * @uses vcalendar::getConfig() + * @uses vcalendar::createCalendar() + * @uses vcalendar::$headers + * @uses vcalendar::$format + * @return bool TRUE on success, FALSE on error + */ + function returnCalendar( $utf8Encode=FALSE, $gzip=FALSE, $cdType=TRUE ) { + $filename = $this->getConfig( 'filename' ); + $output = $this->createCalendar(); + if( $utf8Encode ) + $output = utf8_encode( $output ); + $fsize = null; + if( $gzip ) { + $output = gzencode( $output, 9 ); + $fsize = strlen( $output ); + header( self::$headers[0] ); + header( self::$headers[1] ); + } + else { + if( FALSE !== ( $temp = tempnam( sys_get_temp_dir(), 'iCr' ))) { + if( FALSE !== file_put_contents( $temp, $output )) + $fsize = @filesize( $temp ); + unlink( $temp ); + } + } + if( ! empty( $fsize )) + header( sprintf( self::$headers[2], $fsize )); + if( 'xcal' == $this->format ) + header( self::$headers[3] ); + else + header( self::$headers[4] ); + $cdType = ( $cdType ) ? 5 : 6; + header( sprintf( self::$headers[$cdType], $filename )); + header( self::$headers[7] ); + echo $output; + return TRUE; + } +/** + * save content in a file + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.5 - 2015-03-29 + * @uses vcalendar::createComponent() + * @uses vcalendar::getConfig() + * @return bool TRUE on success, FALSE on error + */ + function saveCalendar() { + $output = $this->createCalendar(); + if( FALSE === ( $dirfile = $this->getConfig( 'url' ))) + $dirfile = $this->getConfig( 'dirfile' ); + return ( FALSE === file_put_contents( $dirfile, $output, LOCK_EX )) ? FALSE : TRUE; + } +/** + * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent + * else FALSE is returned + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.21.5 - 2015-03-29 + * @param int $timeout default 3600 sec + * @param bool $cdType TRUE : Content-Disposition: attachment... (default), FALSE : ...inline... + * @uses vcalendar::getConfig() + * @uses vcalendar::$headers + * @uses vcalendar::$format + * @return bool TRUE on success, FALSE on error + */ + function useCachedCalendar( $timeout=3600, $cdType=TRUE ) { + if( FALSE === ( $dirfile = $this->getConfig( 'url' ))) + $dirfile = $this->getConfig( 'dirfile' ); + if( ! is_file( $dirfile ) || ! is_readable( $dirfile )) + return FALSE; + if( time() - filemtime( $dirfile ) > $timeout ) + return FALSE; + clearstatcache(); + $dirfile = $this->getConfig( 'dirfile' ); + $fsize = @filesize( $dirfile ); + $filename = $this->getConfig( 'filename' ); + if( 'xcal' == $this->format ) + header( self::$headers[3] ); + else + header( self::$headers[4] ); + if( ! empty( $fsize )) + header( sprintf( self::$headers[2], $fsize )); + $cdType = ( $cdType ) ? 5 : 6; + header( sprintf( self::$headers[$cdType], $filename )); + header( self::$headers[7] ); + if( FALSE === ( $fp = @fopen( $dirfile, 'r' ))) + return FALSE; + fpassthru( $fp ); + fclose( $fp ); + return TRUE; + } +} diff --git a/php/ical/lib/vevent.class.php b/php/ical/lib/vevent.class.php new file mode 100644 index 0000000..fc17dde --- /dev/null +++ b/php/ical/lib/vevent.class.php @@ -0,0 +1,269 @@ + + * @since 2.5.1 - 2008-10-12 + */ +class vevent extends calendarComponent { +/** + * @var array $attach component property value + * @var array $attendee component property value + * @var array $categories component property value + * @var array $comment component property value + * @var array $contact component property value + * @var array $class component property value + * @var array $created component property value + * @var array $description component property value + * @var array $dtend component property value + * @var array $dtstart component property value + * @var array $duration component property value + * @var array $exdate component property value + * @var array $exrule component property value + * @var array $geo component property value + * @var array $lastmodified component property value + * @var array $location component property value + * @var array $organizer component property value + * @var array $priority component property value + * @var array $rdate component property value + * @var array $recurrenceid component property value + * @var array $relatedto component property value + * @var array $requeststatus component property value + * @var array $resources component property value + * @var array $rrule component property value + * @var array $sequence component property value + * @var array $status component property value + * @var array $summary component property value + * @var array $transp component property value + * @var array $url component property value + * @access protected + */ + protected $attach; + protected $attendee; + protected $categories; + protected $comment; + protected $contact; + protected $class; + protected $created; + protected $description; + protected $dtend; + protected $dtstart; + protected $duration; + protected $exdate; + protected $exrule; + protected $geo; + protected $lastmodified; + protected $location; + protected $organizer; + protected $priority; + protected $rdate; + protected $recurrenceid; + protected $relatedto; + protected $requeststatus; + protected $resources; + protected $rrule; + protected $sequence; + protected $status; + protected $summary; + protected $transp; + protected $url; +/** + * constructor for calendar component VEVENT object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @uses vevent::calendarComponent() + * @uses vevent::$attach + * @uses vevent::$attendee + * @uses vevent::$categories + * @uses vevent::$class + * @uses vevent::$comment + * @uses vevent::$contact + * @uses vevent::$created + * @uses vevent::$description + * @uses vevent::$dtstart + * @uses vevent::$dtend + * @uses vevent::$duration + * @uses vevent::$exdate + * @uses vevent::$exrule + * @uses vevent::$geo + * @uses vevent::$lastmodified + * @uses vevent::$location + * @uses vevent::$organizer + * @uses vevent::$priority + * @uses vevent::$rdate + * @uses vevent::$recurrenceid + * @uses vevent::$relatedto + * @uses vevent::$requeststatus + * @uses vevent::$resources + * @uses vevent::$rrule + * @uses vevent::$sequence + * @uses vevent::$status + * @uses vevent::$summary + * @uses vevent::$transp + * @uses vevent::$url + * @uses vevent::$xprop + * @uses vevent::$components + * @uses calendarComponent::setConfig() + */ + function vevent( $config = array()) { + $this->calendarComponent(); + $this->attach = ''; + $this->attendee = ''; + $this->categories = ''; + $this->class = ''; + $this->comment = ''; + $this->contact = ''; + $this->created = ''; + $this->description = ''; + $this->dtstart = ''; + $this->dtend = ''; + $this->duration = ''; + $this->exdate = ''; + $this->exrule = ''; + $this->geo = ''; + $this->lastmodified = ''; + $this->location = ''; + $this->organizer = ''; + $this->priority = ''; + $this->rdate = ''; + $this->recurrenceid = ''; + $this->relatedto = ''; + $this->requeststatus = ''; + $this->resources = ''; + $this->rrule = ''; + $this->sequence = ''; + $this->status = ''; + $this->summary = ''; + $this->transp = ''; + $this->url = ''; + $this->xprop = ''; + $this->components = array(); + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + } +/** + * create formatted output for calendar component VEVENT object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.10.16 - 2011-10-28 + * @param array $xcaldecl + * @uses calendarComponent::_createFormat() + * @uses calendarComponent::$componentStart1 + * @uses calendarComponent::$componentStart2 + * @uses calendarComponent::$nl; + * @uses calendarComponent::createUid() + * @uses calendarComponent::createDtstamp() + * @uses calendarComponent::createAttach() + * @uses calendarComponent::createAttendee() + * @uses calendarComponent::createCategories() + * @uses calendarComponent::createComment() + * @uses calendarComponent::createContact() + * @uses calendarComponent::createClass() + * @uses calendarComponent::createCreated() + * @uses calendarComponent::createDescription() + * @uses calendarComponent::createDtstart() + * @uses calendarComponent::createDtend() + * @uses calendarComponent::createDuration() + * @uses calendarComponent::createExdate() + * @uses calendarComponent::createExrule() + * @uses calendarComponent::createGeo() + * @uses calendarComponent::createLastModified() + * @uses calendarComponent::createLocation() + * @uses calendarComponent::createOrganizer() + * @uses calendarComponent::createPriority() + * @uses calendarComponent::createRdate() + * @uses calendarComponent::createRrule() + * @uses calendarComponent::createRelatedTo() + * @uses calendarComponent::createRequestStatus() + * @uses calendarComponent::createRecurrenceid() + * @uses calendarComponent::createResources() + * @uses calendarComponent::createSequence() + * @uses calendarComponent::createStatus() + * @uses calendarComponent::createSummary() + * @uses calendarComponent::createTransp() + * @uses calendarComponent::createUrl() + * @uses calendarComponent::createXprop() + * @uses calendarComponent::createSubComponent() + * @uses calendarComponent::$componentEnd1 + * @uses calendarComponent::$componentEnd2 + * @uses calendarComponent::$xcaldecl + * @return string + */ + function createComponent( & $xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createUid(); + $component .= $this->createDtstamp(); + $component .= $this->createAttach(); + $component .= $this->createAttendee(); + $component .= $this->createCategories(); + $component .= $this->createComment(); + $component .= $this->createContact(); + $component .= $this->createClass(); + $component .= $this->createCreated(); + $component .= $this->createDescription(); + $component .= $this->createDtstart(); + $component .= $this->createDtend(); + $component .= $this->createDuration(); + $component .= $this->createExdate(); + $component .= $this->createExrule(); + $component .= $this->createGeo(); + $component .= $this->createLastModified(); + $component .= $this->createLocation(); + $component .= $this->createOrganizer(); + $component .= $this->createPriority(); + $component .= $this->createRdate(); + $component .= $this->createRrule(); + $component .= $this->createRelatedTo(); + $component .= $this->createRequestStatus(); + $component .= $this->createRecurrenceid(); + $component .= $this->createResources(); + $component .= $this->createSequence(); + $component .= $this->createStatus(); + $component .= $this->createSummary(); + $component .= $this->createTransp(); + $component .= $this->createUrl(); + $component .= $this->createXprop(); + $component .= $this->createSubComponent(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} diff --git a/php/ical/lib/vfreebusy.class.php b/php/ical/lib/vfreebusy.class.php new file mode 100644 index 0000000..0ba0bdd --- /dev/null +++ b/php/ical/lib/vfreebusy.class.php @@ -0,0 +1,149 @@ + + * @since 2.5.1 - 2008-10-12 + */ +class vfreebusy extends calendarComponent { +/** + * @var array $attendee component property value + * @var array $comment component property value + * @var array $contact component property value + * @var array $dtend component property value + * @var array $dtstart component property value + * @var array $duration component property value + * @var array $freebusy component property value + * @var array $organizer component property value + * @var array $requeststatus component property value + * @var array $url component property value + * @access protected + */ + protected $attendee; + protected $comment; + protected $contact; + protected $dtend; + protected $dtstart; + protected $duration; + protected $freebusy; + protected $organizer; + protected $requeststatus; + protected $url; +/** + * constructor for calendar component VFREEBUSY object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @uses vjournal::calendarComponent() + * @uses vjournal::$attendee + * @uses vjournal::$comment + * @uses vjournal::$contact + * @uses vjournal::$dtend + * @uses vjournal::$dtstart + * @uses vjournal::$dtduration + * @uses vjournal::$organizer + * @uses vjournal::$requeststatus + * @uses vjournal::$url + * @uses vjournal::$xprop + * @uses calendarComponent::setConfig() + */ + function vfreebusy( $config = array()) { + $this->calendarComponent(); + $this->attendee = ''; + $this->comment = ''; + $this->contact = ''; + $this->dtend = ''; + $this->dtstart = ''; + $this->duration = ''; + $this->freebusy = ''; + $this->organizer = ''; + $this->requeststatus = ''; + $this->url = ''; + $this->xprop = ''; + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + } +/** + * create formatted output for calendar component VFREEBUSY object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.3.1 - 2007-11-19 + * @param array $xcaldecl + * @uses calendarComponent::_createFormat() + * @uses calendarComponent::createUid() + * @uses calendarComponent::createDtstamp() + * @uses calendarComponent::createAttendee() + * @uses calendarComponent::createComment() + * @uses calendarComponent::createContact() + * @uses calendarComponent::createDtstart() + * @uses calendarComponent::createDtend() + * @uses calendarComponent::createDuration() + * @uses calendarComponent::createFreebusy() + * @uses calendarComponent::createOrganizer() + * @uses calendarComponent::createRequestStatus() + * @uses calendarComponent::createUrl() + * @uses calendarComponent::createXprop() + * @uses calendarComponent::createUrl() + * @uses calendarComponent::createXprop() + * @uses calendarComponent::$componentEnd1 + * @uses calendarComponent::$componentEnd2 + * @uses calendarComponent::$xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createUid(); + $component .= $this->createDtstamp(); + $component .= $this->createAttendee(); + $component .= $this->createComment(); + $component .= $this->createContact(); + $component .= $this->createDtstart(); + $component .= $this->createDtend(); + $component .= $this->createDuration(); + $component .= $this->createFreebusy(); + $component .= $this->createOrganizer(); + $component .= $this->createRequestStatus(); + $component .= $this->createUrl(); + $component .= $this->createXprop(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} diff --git a/php/ical/lib/vjournal.class.php b/php/ical/lib/vjournal.class.php new file mode 100644 index 0000000..f74adee --- /dev/null +++ b/php/ical/lib/vjournal.class.php @@ -0,0 +1,222 @@ + + * @since 2.5.1 - 2008-10-12 + */ +class vjournal extends calendarComponent { +/** + * @var array $attach component property value + * @var array $attendee component property value + * @var array $categories component property value + * @var array $comment component property value + * @var array $contact component property value + * @var array $class component property value + * @var array $created component property value + * @var array $description component property value + * @var array $dtstart component property value + * @var array $exdate component property value + * @var array $exrule component property value + * @var array $lastmodified component property value + * @var array $organizer component property value + * @var array $rdate component property value + * @var array $recurrenceid component property value + * @var array $relatedto component property value + * @var array $requeststatus component property value + * @var array $rrule component property value + * @var array $sequence component property value + * @var array $status component property value + * @var array $summary component property value + * @var array $url component property value + * @access protected + */ + protected $attach; + protected $attendee; + protected $categories; + protected $comment; + protected $contact; + protected $class; + protected $created; + protected $description; + protected $dtstart; + protected $exdate; + protected $exrule; + protected $lastmodified; + protected $organizer; + protected $rdate; + protected $recurrenceid; + protected $relatedto; + protected $requeststatus; + protected $rrule; + protected $sequence; + protected $status; + protected $summary; + protected $url; +/** + * constructor for calendar component VJOURNAL object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @uses vjournal::calendarComponent() + * @uses vjournal::$attach + * @uses vjournal::$attendee + * @uses vjournal::$categories + * @uses vjournal::$class + * @uses vjournal::$comment + * @uses vjournal::$contact + * @uses vjournal::$created + * @uses vjournal::$description + * @uses vjournal::$dtstart + * @uses vjournal::$exdate + * @uses vjournal::$exrule + * @uses vjournal::$lastmodified + * @uses vjournal::$organizer + * @uses vjournal::$rdate + * @uses vjournal::$recurrenceid + * @uses vjournal::$relatedto + * @uses vjournal::$requeststatus + * @uses vjournal::$rrule + * @uses vjournal::$sequence + * @uses vjournal::$status + * @uses vjournal::$summary + * @uses vjournal::$url + * @uses vjournal::$xprop + * @uses calendarComponent::setConfig() + */ + function vjournal( $config = array()) { + $this->calendarComponent(); + $this->attach = ''; + $this->attendee = ''; + $this->categories = ''; + $this->class = ''; + $this->comment = ''; + $this->contact = ''; + $this->created = ''; + $this->description = ''; + $this->dtstart = ''; + $this->exdate = ''; + $this->exrule = ''; + $this->lastmodified = ''; + $this->organizer = ''; + $this->rdate = ''; + $this->recurrenceid = ''; + $this->relatedto = ''; + $this->requeststatus = ''; + $this->rrule = ''; + $this->sequence = ''; + $this->status = ''; + $this->summary = ''; + $this->url = ''; + $this->xprop = ''; + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + } +/** + * create formatted output for calendar component VJOURNAL object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-12 + * @param array $xcaldecl + * @uses calendarComponent::_createFormat() + * @uses calendarComponent::$componentStart1 + * @uses calendarComponent::$componentStart2 + * @uses calendarComponent::$nl + * @uses calendarComponent::createUid() + * @uses calendarComponent::createDtstamp() + * @uses calendarComponent::createAttach() + * @uses calendarComponent::createAttendee() + * @uses calendarComponent::createCategories() + * @uses calendarComponent::createClass() + * @uses calendarComponent::createComment() + * @uses calendarComponent::createContact() + * @uses calendarComponent::createDescription() + * @uses calendarComponent::createDtstart() + * @uses calendarComponent::createExdate() + * @uses calendarComponent::createExrule() + * @uses calendarComponent::createLastModified() + * @uses calendarComponent::createOrganizer() + * @uses calendarComponent::createRdate() + * @uses calendarComponent::createRelatedTo() + * @uses calendarComponent::createRequestStatus() + * @uses calendarComponent::createRecurrenceid() + * @uses calendarComponent::createRrule() + * @uses calendarComponent::createSequence() + * @uses calendarComponent::createStatus() + * @uses calendarComponent::createSummary() + * @uses calendarComponent::createUrl() + * @uses calendarComponent::createXprop() + * @uses calendarComponent::$componentEnd1 + * @uses calendarComponent::$componentEnd2 + * @uses calendarComponent::$xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createUid(); + $component .= $this->createDtstamp(); + $component .= $this->createAttach(); + $component .= $this->createAttendee(); + $component .= $this->createCategories(); + $component .= $this->createClass(); + $component .= $this->createComment(); + $component .= $this->createContact(); + $component .= $this->createCreated(); + $component .= $this->createDescription(); + $component .= $this->createDtstart(); + $component .= $this->createExdate(); + $component .= $this->createExrule(); + $component .= $this->createLastModified(); + $component .= $this->createOrganizer(); + $component .= $this->createRdate(); + $component .= $this->createRequestStatus(); + $component .= $this->createRecurrenceid(); + $component .= $this->createRelatedTo(); + $component .= $this->createRrule(); + $component .= $this->createSequence(); + $component .= $this->createStatus(); + $component .= $this->createSummary(); + $component .= $this->createUrl(); + $component .= $this->createXprop(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} diff --git a/php/ical/lib/vtimezone.class.php b/php/ical/lib/vtimezone.class.php new file mode 100644 index 0000000..d65bf7c --- /dev/null +++ b/php/ical/lib/vtimezone.class.php @@ -0,0 +1,164 @@ + + * @since 2.5.1 - 2008-10-12 + */ +class vtimezone extends calendarComponent { + /** @var array $timezonetype component property value */ + public $timezonetype; +/** + * @var array $comment component property value + * @var array $dtstart component property value + * @var array $lastmodified component property value + * @var array $rdate component property value + * @var array $rrule component property value + * @var array $tzid component property value + * @var array $tzname component property value + * @var array $tzoffsetfrom component property value + * @var array $tzoffsetto component property value + * @var array $tzurl component property value + * @access protected + */ + protected $comment; + protected $dtstart; + protected $lastmodified; + protected $rdate; + protected $rrule; + protected $tzid; + protected $tzname; + protected $tzoffsetfrom; + protected $tzoffsetto; + protected $tzurl; +/** + * constructor for calendar component VTIMEZONE object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param mixed $timezonetype default FALSE ( STANDARD / DAYLIGHT ) + * @param array $config + * @uses vtimezone::$timezonetype + * @uses vtimezone::calendarComponent() + * @uses vtimezone::$comment + * @uses vtimezone::$dtstart + * @uses vtimezone::$lastmodified + * @uses vtimezone::$rdate + * @uses vtimezone::$rrule + * @uses vtimezone::$tzid + * @uses vtimezone::$tzname + * @uses vtimezone::$tzoffsetfrom + * @uses vtimezone::$tzoffsetto + * @uses vtimezone::$tzurl + * @uses vtimezone::$xprop + * @uses vtimezone::$components + * @uses calendarComponent::setConfig() + */ + function vtimezone( $timezonetype=FALSE, $config = array()) { + if( is_array( $timezonetype )) { + $config = $timezonetype; + $timezonetype = FALSE; + } + if( !$timezonetype ) + $this->timezonetype = 'VTIMEZONE'; + else + $this->timezonetype = strtoupper( $timezonetype ); + $this->calendarComponent(); + $this->comment = ''; + $this->dtstart = ''; + $this->lastmodified = ''; + $this->rdate = ''; + $this->rrule = ''; + $this->tzid = ''; + $this->tzname = ''; + $this->tzoffsetfrom = ''; + $this->tzoffsetto = ''; + $this->tzurl = ''; + $this->xprop = ''; + $this->components = array(); + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + } +/** + * create formatted output for calendar component VTIMEZONE object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-10-25 + * @param array $xcaldecl + * @uses calendarComponent::_createFormat() + * @uses calendarComponent::$componentStart1 + * @uses calendarComponent::$componentStart2 + * @uses calendarComponent::$nl + * @uses calendarComponent::createTzid() + * @uses calendarComponent::createLastModified() + * @uses calendarComponent::createTzurl() + * @uses calendarComponent::createDtstart() + * @uses calendarComponent::createTzoffsetfrom() + * @uses calendarComponent::createTzoffsetto() + * @uses calendarComponent::createComment() + * @uses calendarComponent::createRdate() + * @uses calendarComponent::createRrule() + * @uses calendarComponent::createTzname() + * @uses calendarComponent::createXprop() + * @uses calendarComponent::createSubComponent() + * @uses calendarComponent::createXprop() + * @uses calendarComponent::$componentEnd1 + * @uses calendarComponent::$componentEnd2 + * @uses calendarComponent::$xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createTzid(); + $component .= $this->createLastModified(); + $component .= $this->createTzurl(); + $component .= $this->createDtstart(); + $component .= $this->createTzoffsetfrom(); + $component .= $this->createTzoffsetto(); + $component .= $this->createComment(); + $component .= $this->createRdate(); + $component .= $this->createRrule(); + $component .= $this->createTzname(); + $component .= $this->createXprop(); + $component .= $this->createSubComponent(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} diff --git a/php/ical/lib/vtodo.class.php b/php/ical/lib/vtodo.class.php new file mode 100644 index 0000000..8e373e9 --- /dev/null +++ b/php/ical/lib/vtodo.class.php @@ -0,0 +1,273 @@ + + * @since 2.5.1 - 2008-10-12 + */ +class vtodo extends calendarComponent { +/** + * @var array $attach component property value + * @var array $attendee component property value + * @var array $categories component property value + * @var array $comment component property value + * @var array $completed component property value + * @var array $contact component property value + * @var array $class component property value + * @var array $created component property value + * @var array $description component property value + * @var array $dtstart component property value + * @var array $due component property value + * @var array $duration component property value + * @var array $exdate component property value + * @var array $exrule component property value + * @var array $geo component property value + * @var array $lastmodified component property value + * @var array $location component property value + * @var array $organizer component property value + * @var array $percentcomplete component property value + * @var array $priority component property value + * @var array $rdate component property value + * @var array $recurrenceid component property value + * @var array $relatedto component property value + * @var array $requeststatus component property value + * @var array $resources component property value + * @var array $rrule component property value + * @var array $sequence component property value + * @var array $status component property value + * @var array $summary component property value + * @var arrayr $url; + * @access protected + */ + protected $attach; + protected $attendee; + protected $categories; + protected $comment; + protected $completed; + protected $contact; + protected $class; + protected $created; + protected $description; + protected $dtstart; + protected $due; + protected $duration; + protected $exdate; + protected $exrule; + protected $geo; + protected $lastmodified; + protected $location; + protected $organizer; + protected $percentcomplete; + protected $priority; + protected $rdate; + protected $recurrenceid; + protected $relatedto; + protected $requeststatus; + protected $resources; + protected $rrule; + protected $sequence; + protected $status; + protected $summary; + protected $url; +/** + * constructor for calendar component VTODO object + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.8.2 - 2011-05-01 + * @param array $config + * @uses vtodo::calendarComponent() + * @uses vtodo::$attach + * @uses vtodo::$attendee + * @uses vtodo::$categories + * @uses vtodo::$class + * @uses vtodo::$comment + * @uses vtodo::$completed + * @uses vtodo::$contact + * @uses vtodo::$created + * @uses vtodo::$description + * @uses vtodo::$dtstart + * @uses vtodo::$due + * @uses vtodo::$duration + * @uses vtodo::$exdate + * @uses vtodo::$exrule + * @uses vtodo::$geo + * @uses vtodo::$lastmodified + * @uses vtodo::$location + * @uses vtodo::$organizer + * @uses vtodo::$percentcomplete + * @uses vtodo::$priority + * @uses vtodo::$rdate + * @uses vtodo::$recurrenceid + * @uses vtodo::$relatedto + * @uses vtodo::$requeststatus + * @uses vtodo::$resources + * @uses vtodo::$rrule + * @uses vtodo::$sequence + * @uses vtodo::$status + * @uses vtodo::$summary + * @uses vtodo::$url + * @uses vtodo::$xprop + * @uses vtodo::$components + * @uses calendarComponent::setConfig() + */ + function vtodo( $config = array()) { + $this->calendarComponent(); + $this->attach = ''; + $this->attendee = ''; + $this->categories = ''; + $this->class = ''; + $this->comment = ''; + $this->completed = ''; + $this->contact = ''; + $this->created = ''; + $this->description = ''; + $this->dtstart = ''; + $this->due = ''; + $this->duration = ''; + $this->exdate = ''; + $this->exrule = ''; + $this->geo = ''; + $this->lastmodified = ''; + $this->location = ''; + $this->organizer = ''; + $this->percentcomplete = ''; + $this->priority = ''; + $this->rdate = ''; + $this->recurrenceid = ''; + $this->relatedto = ''; + $this->requeststatus = ''; + $this->resources = ''; + $this->rrule = ''; + $this->sequence = ''; + $this->status = ''; + $this->summary = ''; + $this->url = ''; + $this->xprop = ''; + $this->components = array(); + if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) + $config['language'] = ICAL_LANG; + if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; + if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; + if( !isset( $config['format'] )) $config['format'] = 'iCal'; + if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; + $this->setConfig( $config ); + } +/** + * create formatted output for calendar component VTODO object instance + * + * @author Kjell-Inge Gustafsson, kigkonsult + * @since 2.5.1 - 2008-11-07 + * @param array $xcaldecl + * @uses calendarComponent::_createFormat() + * @uses calendarComponent::$componentStart1 + * @uses calendarComponent::$componentStart2 + * @uses calendarComponent::$nl; + * @uses calendarComponent::createUid() + * @uses calendarComponent::createDtstamp() + * @uses calendarComponent::createAttach() + * @uses calendarComponent::createAttendee() + * @uses calendarComponent::createCategories() + * @uses calendarComponent::createClass() + * @uses calendarComponent::createComment() + * @uses calendarComponent::createCompleted() + * @uses calendarComponent::createContact() + * @uses calendarComponent::createDescription() + * @uses calendarComponent::createDtstart() + * @uses calendarComponent::createDtend() + * @uses calendarComponent::createDuration() + * @uses calendarComponent::createExdate() + * @uses calendarComponent::createExrule() + * @uses calendarComponent::createGeo() + * @uses calendarComponent::createLastModified() + * @uses calendarComponent::createLocation() + * @uses calendarComponent::createOrganizer() + * @uses calendarComponent::createPriority() + * @uses calendarComponent::createRdate() + * @uses calendarComponent::createRelatedTo() + * @uses calendarComponent::createRequestStatus() + * @uses calendarComponent::createRecurrenceid() + * @uses calendarComponent::createResources() + * @uses calendarComponent::createRrule() + * @uses calendarComponent::createSequence() + * @uses calendarComponent::createStatus() + * @uses calendarComponent::createSummary() + * @uses calendarComponent::createUrl() + * @uses calendarComponent::createXprop() + * @uses calendarComponent::createSubComponent() + * @uses calendarComponent::$componentEnd1 + * @uses calendarComponent::$componentEnd2 + * @uses calendarComponent::$xcaldecl + * @return string + */ + function createComponent( &$xcaldecl ) { + $objectname = $this->_createFormat(); + $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; + $component .= $this->createUid(); + $component .= $this->createDtstamp(); + $component .= $this->createAttach(); + $component .= $this->createAttendee(); + $component .= $this->createCategories(); + $component .= $this->createClass(); + $component .= $this->createComment(); + $component .= $this->createCompleted(); + $component .= $this->createContact(); + $component .= $this->createCreated(); + $component .= $this->createDescription(); + $component .= $this->createDtstart(); + $component .= $this->createDue(); + $component .= $this->createDuration(); + $component .= $this->createExdate(); + $component .= $this->createExrule(); + $component .= $this->createGeo(); + $component .= $this->createLastModified(); + $component .= $this->createLocation(); + $component .= $this->createOrganizer(); + $component .= $this->createPercentComplete(); + $component .= $this->createPriority(); + $component .= $this->createRdate(); + $component .= $this->createRelatedTo(); + $component .= $this->createRequestStatus(); + $component .= $this->createRecurrenceid(); + $component .= $this->createResources(); + $component .= $this->createRrule(); + $component .= $this->createSequence(); + $component .= $this->createStatus(); + $component .= $this->createSummary(); + $component .= $this->createUrl(); + $component .= $this->createXprop(); + $component .= $this->createSubComponent(); + $component .= $this->componentEnd1.$objectname.$this->componentEnd2; + if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { + foreach( $this->xcaldecl as $localxcaldecl ) + $xcaldecl[] = $localxcaldecl; + } + return $component; + } +} diff --git a/php/module.calendar.php b/php/module.calendar.php index 84b753e..6735aca 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -20,10 +20,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ - -include_once('mapi/class.recurrence.php'); -include_once('plugins/calendarimporter/php/ical/class.icalcreator.php'); -include_once('plugins/calendarimporter/php/ical/class.icalparser.php'); + +include_once('ical/class.icalcreator.php'); +include_once('ical/class.icalparser.php'); class CalendarModule extends Module { From 67b6a332f2f6632cff078a8c39a4e612d855b249 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 20 Jun 2016 15:48:24 +0200 Subject: [PATCH 02/17] javascript rework --- config.php | 6 +- js/data/Actions.js | 65 +++++ js/data/ResponseHandler.js | 24 +- js/dialogs/ImportPanel.js | 447 +++++--------------------------- js/plugin.calendarimporter.js | 279 +++++++++++--------- js/settings/SettingsWidget.js | 9 - js/ui/ContextMenu.js | 33 +-- manifest.xml | 6 +- php/module.calendar.php | 2 +- php/plugin.calendarimporter.php | 1 - 10 files changed, 304 insertions(+), 568 deletions(-) create mode 100644 js/data/Actions.js diff --git a/config.php b/config.php index 34bd778..6796ccf 100644 --- a/config.php +++ b/config.php @@ -1,13 +1,11 @@ + * Copyright (C) 2012-2016 Christoph Haas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/** + * ResponseHandler + * + * This class handles all responses from the php backend + */ +Ext.namespace('Zarafa.plugins.calendarimporter.data'); + +/** + * @class Zarafa.plugins.calendarimporter.data.Actions + * Common actions which can be used within {@link Ext.Button buttons} + * or other {@link Ext.Component components} with action handlers. + * @singleton + */ +Zarafa.plugins.calendarimporter.data.Actions = { + /** + * Callback for the export request. + * @param {Object} response + */ + downloadICS: function (response) { + if (response.status == false) { + Zarafa.common.dialogs.MessageBox.show({ + title : dgettext('plugin_files', 'Warning'), + msg : dgettext('plugin_files', response.message), + icon : Zarafa.common.dialogs.MessageBox.WARNING, + buttons: Zarafa.common.dialogs.MessageBox.OK + }); + } else { + var downloadFrame = Ext.getBody().createChild({ + tag: 'iframe', + cls: 'x-hidden' + }); + + var url = document.URL; + var link = url.substring(0, url.lastIndexOf('/') + 1); + + link += "index.php?sessionid=" + container.getUser().getSessionId() + "&load=custom&name=download_ics"; + link = Ext.urlAppend(link, "token=" + encodeURIComponent(response.download_token)); + link = Ext.urlAppend(link, "filename=" + encodeURIComponent(response.filename)); + + downloadFrame.dom.contentWindow.location = link; + } + } +}; \ No newline at end of file diff --git a/js/data/ResponseHandler.js b/js/data/ResponseHandler.js index 6fc4f9f..1d338c1 100644 --- a/js/data/ResponseHandler.js +++ b/js/data/ResponseHandler.js @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ - + /** * ResponseHandler * @@ -38,46 +38,46 @@ Zarafa.plugins.calendarimporter.data.ResponseHandler = Ext.extend(Zarafa.core.da * @cfg {Function} successCallback The function which * will be called after success request. */ - successCallback : null, - + successCallback: null, + /** * Call the successCallback callback function. * @param {Object} response Object contained the response data. */ - doExport : function(response) { + doExport: function (response) { this.successCallback(response); }, - + /** * Call the successCallback callback function. * @param {Object} response Object contained the response data. */ - doList : function(response) { + doList: function (response) { this.successCallback(response); }, - + /** * Call the successCallback callback function. * @param {Object} response Object contained the response data. */ - doImport : function(response) { + doImport: function (response) { this.successCallback(response); }, - + /** * Call the successCallback callback function. * @param {Object} response Object contained the response data. */ - doAttachmentpath : function(response) { + doImportattachment: function (response) { this.successCallback(response); }, - + /** * In case exception happened on server, server will return * exception response with the code of exception. * @param {Object} response Object contained the response data. */ - doError: function(response) { + doError: function (response) { alert("error response code: " + response.error.info.code); } }); diff --git a/js/dialogs/ImportPanel.js b/js/dialogs/ImportPanel.js index 190d7a8..cf3a3cc 100644 --- a/js/dialogs/ImportPanel.js +++ b/js/dialogs/ImportPanel.js @@ -372,7 +372,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { name: "choosen_timezone", value: Zarafa.plugins.calendarimporter.data.Timezones.unMap(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_timezone")), width: 100, - fieldLabel: "Select a timezone (optional)", + fieldLabel: "Timezone", store: Zarafa.plugins.calendarimporter.data.Timezones.store, labelSeperator: ":", mode: 'local', @@ -393,7 +393,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { ref: 'dstcheck', name: "dst_check", width: 100, - fieldLabel: "Ignore DST (optional)", + fieldLabel: "Ignore DST", boxLabel: 'This will ignore "Daylight saving time" offsets.', labelSeperator: ":", border: false, @@ -529,14 +529,15 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { parseCalendar: function (icsPath, timezone, ignoredst) { this.loadMask.show(); - // call export function here! + var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({ - successCallback: this.handleParsingResult.createDelegate(this) + successCallback: this.handleParsingResult, + scope: this }); container.getRequest().singleRequest( 'calendarmodule', - 'import', + 'load', { ics_filepath: icsPath, timezone: timezone, @@ -550,17 +551,17 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { this.loadMask.hide(); if(response["status"] == true) { - Ext.getCmp('submitButton').enable(); - Ext.getCmp('submitAllButton').enable(); - - if(typeof response.parsed.calendar["X-WR-TIMEZONE"] !== "undefined") {; + this.submitButton.enable(); + this.submitAllButton.enable(); + + if(typeof response.parsed.calendar["X-WR-TIMEZONE"] !== "undefined") { this.timezone = response.parsed.calendar["X-WR-TIMEZONE"]; this.timezoneselector.setValue(Zarafa.plugins.calendarimporter.data.Timezones.unMap(this.timezone)); } this.reloadGridStore(response.parsed); } else { - Ext.getCmp('submitButton').disable(); - Ext.getCmp('submitAllButton').disable(); + this.submitButton.disable(); + this.submitAllButton.disable(); Zarafa.common.dialogs.MessageBox.show({ title : _('Parser Error'), msg : _(response["message"]), @@ -575,54 +576,6 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { this.dialog.close() }, - convertToAppointmentRecord: function (calendarFolder,entry) { - var newRecord = Zarafa.core.data.RecordFactory.createRecordObjectByMessageClass('IPM.Appointment', { - startdate: new Date(entry.start), - duedate: (entry.end != null) ? - new Date(entry.end) : - new Date(entry.start).add(Date.HOUR, 1), - location: entry.location, - subject: entry.title, - body: entry.description, - commonstart: new Date(entry.start), - commonend: (entry.end != null) ? - new Date(entry.end) : - new Date(entry.start).add(Date.HOUR, 1), - timezone: this.timezone, - parent_entryid: calendarFolder.get('entryid'), - store_entryid: calendarFolder.get('store_entryid') - }); - - var busystate = new Array("FREE", "TENTATIVE", "BUSY", "OOF"); - var zlabel = new Array("NONE", "IMPORTANT", "WORK", "PERSONAL", "HOLIDAY", "REQUIRED", "TRAVEL REQUIRED", "PREPARATION REQUIERED", "BIRTHDAY", "SPECIAL DATE", "PHONE INTERVIEW"); - - /* optional fields */ - if(entry.priority !== "") { - newRecord.data.importance = entry.priority; - } - if(entry.label !== "") { - newRecord.data.label = zlabel.indexOf(entry.label); - } - if(entry.busy !== "") { - newRecord.data.busystatus = busystate.indexOf(entry.busy); - } - if(entry.privatestate !== "") { - newRecord.data["private"] = entry.privatestate == "PUBLIC" ? false : true; - } - if(entry.organizer !== "") { - newRecord.data.sent_representing_email_address = entry.organizer; - } - if(entry.trigger != null) { - newRecord.data.reminder = true; - newRecord.data.reminder_minutes = new Date((entry.start - entry.trigger)/60); - newRecord.data.reminder_time = new Date(entry.trigger); - } else { - newRecord.data.reminder = false; - } - - return newRecord; - }, - importCheckedEvents: function () { var newRecords = this.eventgrid.selModel.getSelections(); this.importEvents(newRecords); @@ -635,295 +588,15 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { this.importEvents(newRecords); }, - exportAllEvents: function () { - //receive existing calendar store - var calValue = this.calendarselector.value; - - if(calValue == undefined) { // no calendar choosen - Zarafa.common.dialogs.MessageBox.show({ - title : _('Error'), - msg : _('You have to choose a calendar!'), - icon : Zarafa.common.dialogs.MessageBox.ERROR, - buttons : Zarafa.common.dialogs.MessageBox.OK - }); - } else { - var calexist = true; - var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar'); - var pubStore = container.getHierarchyStore().getPublicStore(); - var pubSubFolders = []; - var pubFolder; - - if(typeof pubStore !== "undefined") { - try { - pubFolder = pubStore.getDefaultFolder("publicfolders"); - pubSubFolders = pubFolder.getChildren(); - } catch (e) { - console.log("Error opening the shared folder..."); - console.log(e); - } - } - - if(calValue != "calendar") { - var subFolders = calendarFolder.getChildren(); - var i = 0; - - /* add public folders if any exist */ - for(i = 0; i < pubSubFolders.length; i++) { - if(pubSubFolders[i].isContainerClass("IPF.Appointment")){ - subFolders.push(pubSubFolders[i]); - } - } - - for(i=0;ijs/calendarimporter-debug.js js/data/timezones.js - js/external/Ext.util.base64.js - js/plugin.calendarimporter.js + js/data/Actions.js js/data/ResponseHandler.js + js/external/Ext.util.base64.js + js/ui/ContextMenu.js js/dialogs/ImportContentPanel.js js/dialogs/ImportPanel.js js/dialogs/settings/SettingsWidget.js @@ -35,6 +36,7 @@ js/dialogs/settings/ui/CalSyncPanel.js js/dialogs/settings/dialogs/CalSyncEditContentPanel.js js/dialogs/settings/dialogs/CalSyncEditPanel.js + js/plugin.calendarimporter.js resources/css/calendarimporter-min.css diff --git a/php/module.calendar.php b/php/module.calendar.php index 6735aca..a5529a1 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -63,7 +63,7 @@ class CalendarModule extends Module { case "import": $result = $this->importCalendar($actionType, $actionData); break; - case "attachmentpath": + case "importattachment": $result = $this->getAttachmentPath($actionType, $actionData); break; default: diff --git a/php/plugin.calendarimporter.php b/php/plugin.calendarimporter.php index ba28d83..fcb688c 100644 --- a/php/plugin.calendarimporter.php +++ b/php/plugin.calendarimporter.php @@ -67,7 +67,6 @@ class Plugincalendarimporter extends Plugin { 'plugins' => Array( 'calendarimporter' => Array( 'enable' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE, - 'enable_export' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_EXPORT, 'enable_sync' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_SYNC, 'default_calendar' => PLUGIN_CALENDARIMPORTER_DEFAULT, 'default_timezone' => PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE From f97daa514be92e4a59ada154eeb91d359a041797 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 20 Jun 2016 16:19:05 +0200 Subject: [PATCH 03/17] new ics lib --- .idea/calendarimporter.iml | 10 + php/.gitignore | 5 + php/composer.json | 5 + php/download.php | 68 +- php/ical/class.icalcreator.php | 64 - php/ical/class.icalparser.php | 751 --- php/ical/lib/calendarComponent.class.php | 4661 ------------------- php/ical/lib/iCal.XML.inc.php | 897 ---- php/ical/lib/iCal.tz.inc.php | 292 -- php/ical/lib/iCal.vCard.inc.php | 166 - php/ical/lib/iCalBase.class.php | 427 -- php/ical/lib/iCalUtilityFunctions.class.php | 2507 ---------- php/ical/lib/iCaldateTime.class.php | 129 - php/ical/lib/valarm.class.php | 135 - php/ical/lib/vcalendar.class.php | 2132 --------- php/ical/lib/vevent.class.php | 269 -- php/ical/lib/vfreebusy.class.php | 149 - php/ical/lib/vjournal.class.php | 222 - php/ical/lib/vtimezone.class.php | 164 - php/ical/lib/vtodo.class.php | 273 -- php/module.calendar.php | 439 +- php/plugin.calendarimporter.php | 35 +- 22 files changed, 348 insertions(+), 13452 deletions(-) create mode 100644 php/.gitignore create mode 100644 php/composer.json delete mode 100644 php/ical/class.icalcreator.php delete mode 100644 php/ical/class.icalparser.php delete mode 100644 php/ical/lib/calendarComponent.class.php delete mode 100644 php/ical/lib/iCal.XML.inc.php delete mode 100644 php/ical/lib/iCal.tz.inc.php delete mode 100644 php/ical/lib/iCal.vCard.inc.php delete mode 100644 php/ical/lib/iCalBase.class.php delete mode 100644 php/ical/lib/iCalUtilityFunctions.class.php delete mode 100644 php/ical/lib/iCaldateTime.class.php delete mode 100644 php/ical/lib/valarm.class.php delete mode 100644 php/ical/lib/vcalendar.class.php delete mode 100644 php/ical/lib/vevent.class.php delete mode 100644 php/ical/lib/vfreebusy.class.php delete mode 100644 php/ical/lib/vjournal.class.php delete mode 100644 php/ical/lib/vtimezone.class.php delete mode 100644 php/ical/lib/vtodo.class.php diff --git a/.idea/calendarimporter.iml b/.idea/calendarimporter.iml index c956989..0c76797 100644 --- a/.idea/calendarimporter.iml +++ b/.idea/calendarimporter.iml @@ -4,5 +4,15 @@ + + + + + + + + + +
\ No newline at end of file diff --git a/php/.gitignore b/php/.gitignore new file mode 100644 index 0000000..3d60449 --- /dev/null +++ b/php/.gitignore @@ -0,0 +1,5 @@ +composer.phar +.composer.lock +composer.lock +vendor +vendor/* diff --git a/php/composer.json b/php/composer.json new file mode 100644 index 0000000..7579244 --- /dev/null +++ b/php/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "kigkonsult/icalcreator": "dev-master" + } +} \ No newline at end of file diff --git a/php/download.php b/php/download.php index f94d39e..215fddb 100644 --- a/php/download.php +++ b/php/download.php @@ -1,9 +1,10 @@ - * Copyright (C) 2012-2014 Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,25 +21,52 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -$basedir = $_GET["basedir"]; -$secid = $_GET["secid"]; -$fileid = $_GET["fileid"]; -$realname = $_GET["realname"]; +class DownloadHandler +{ + /** + * Download the given vcf file. + * @return bool + */ + public static function doDownload() + { + if (isset($_GET["token"])) { + $token = $_GET["token"]; + } else { + return false; + } -$secfile = $basedir . "/secid." . $secid; -$icsfile = $basedir . "/" . $fileid . "." . $secid; + if (isset($_GET["filename"])) { + $filename = $_GET["filename"]; + } else { + return false; + } -// if the secid file exists -> download! -if(file_exists($secfile)) { - @header("Last-Modified: " . @gmdate("D, d M Y H:i:s",time()) . " GMT"); - @header("Content-type: text/calendar"); - header("Content-Length: " . filesize($icsfile)); - header("Content-Disposition: attachment; filename=" . $realname . ".ics"); + // validate token + if (!ctype_alnum($token)) { // token is a md5 hash + return false; + } - //write ics - readfile($icsfile); - unlink($secfile); - unlink($icsfile); -} + $file = PLUGIN_CALENDARIMPORTER_TMP_UPLOAD . "ics_" . $token . ".ics"; -?> \ No newline at end of file + if (!file_exists($file)) { // invalid token + return false; + } + + // set headers here + header('Content-Disposition: attachment; filename="' . $filename . '"'); + + // no caching + header('Expires: 0'); // set expiration time + header('Content-Description: File Transfer'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Content-Length: ' . filesize($file)); + header('Content-Type: application/octet-stream'); + header('Pragma: public'); + flush(); + + // print the downloaded file + readfile($file); + ignore_user_abort(true); + unlink($file); + } +} \ No newline at end of file diff --git a/php/ical/class.icalcreator.php b/php/ical/class.icalcreator.php deleted file mode 100644 index 80cd8f3..0000000 --- a/php/ical/class.icalcreator.php +++ /dev/null @@ -1,64 +0,0 @@ - - * @license http://www.opensource.org/licenses/mit-license.php MIT License - * @link https://github.com/MartinThoma/ics-parser/ - * @link https://github.com/johngrogg/ics-parser - * @version 1.0.3 - */ - -/** - * This is the ICal class - * - * @param {string} filename The name of the file which should be parsed - * @constructor - */ -class ICal -{ - /* How many ToDos are in this iCal? */ - public /** @type {int} */ $todo_count = 0; - - /* How many events are in this iCal? */ - public /** @type {int} */ $event_count = 0; - - /* How many freebusy are in this iCal? */ - public /** @type {int} */ $freebusy_count = 0; - - /* The parsed calendar */ - public /** @type {Array} */ $cal; - - /* Which keyword has been added to cal at last? */ - private /** @type {string} */ $last_keyword; - - /* The value in years to use for indefinite, recurring events */ - public /** @type {int} */ $default_span = 2; - - /** - * Creates the iCal Object - * - * @param {mixed} $filename The path to the iCal-file or an array of lines from an iCal file - * - * @return Object The iCal Object - */ - public function __construct($filename=false) - { - if (!$filename) { - return false; - } - - if (is_array($filename)) { - $lines = $filename; - } else { - $lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - } - - return $this->initLines($lines); - } - - - /** - * Initializes lines from a URL - * - * @url {string} $url The url of the ical file to download and initialize. Unless you know what you're doing, it should begin with "http://" - * - * @return Object The iCal Object - */ - public function initURL($url) - { - $contents = file_get_contents($url); - - $lines = explode("\n", $contents); - - return $this->initLines($lines); - } - - - /** - * Initializes lines from a string - * - * @param {string} $contents The contents of the ical file to initialize - * - * @return Object The iCal Object - */ - public function initString($contents) - { - $lines = explode("\n", $contents); - - return $this->initLines($lines); - } - - - /** - * Initializes lines from file - * - * @param {array} $lines The lines to initialize - * - * @return Object The iCal Object - */ - public function initLines($lines) - { - if (stristr($lines[0], 'BEGIN:VCALENDAR') === false) { - return false; - } else { - foreach ($lines as $line) { - $line = rtrim($line); // Trim trailing whitespace - $add = $this->keyValueFromString($line); - - if ($add === false) { - $this->addCalendarComponentWithKeyAndValue($component, false, $line); - continue; - } - - $keyword = $add[0]; - $values = $add[1]; // Could be an array containing multiple values - - if (!is_array($values)) { - if (!empty($values)) { - $values = array($values); // Make an array as not already - $blank_array = array(); // Empty placeholder array - array_push($values, $blank_array); - } else { - $values = array(); // Use blank array to ignore this line - } - } else if (empty($values[0])) { - $values = array(); // Use blank array to ignore this line - } - - $values = array_reverse($values); // Reverse so that our array of properties is processed first - - foreach ($values as $value) { - switch ($line) { - // http://www.kanzaki.com/docs/ical/vtodo.html - case 'BEGIN:VTODO': - $this->todo_count++; - $component = 'VTODO'; - break; - - // http://www.kanzaki.com/docs/ical/vevent.html - case 'BEGIN:VEVENT': - if (!is_array($value)) { - $this->event_count++; - } - $component = 'VEVENT'; - break; - - // http://www.kanzaki.com/docs/ical/vfreebusy.html - case 'BEGIN:VFREEBUSY': - $this->freebusy_count++; - $component = 'VFREEBUSY'; - break; - - // All other special strings - case 'BEGIN:VCALENDAR': - case 'BEGIN:DAYLIGHT': - // http://www.kanzaki.com/docs/ical/vtimezone.html - case 'BEGIN:VTIMEZONE': - case 'BEGIN:STANDARD': - case 'BEGIN:VALARM': - $component = $value; - break; - case 'END:VALARM': - case 'END:VTODO': // End special text - goto VCALENDAR key - case 'END:VEVENT': - case 'END:VFREEBUSY': - case 'END:VCALENDAR': - case 'END:DAYLIGHT': - case 'END:VTIMEZONE': - case 'END:STANDARD': - $component = 'VCALENDAR'; - break; - default: - $this->addCalendarComponentWithKeyAndValue($component, $keyword, $value); - break; - } - } - } - $this->process_recurrences(); - return $this->cal; - } - } - - /** - * Add to $this->ical array one value and key. - * - * @param {string} $component This could be VTODO, VEVENT, VCALENDAR, ... - * @param {string} $keyword The keyword, for example DTSTART - * @param {string} $value The value, for example 20110105T090000Z - * - * @return {None} - */ - public function addCalendarComponentWithKeyAndValue($component, $keyword, $value) - { - if ($keyword == false) { - $keyword = $this->last_keyword; - } - - switch ($component) { - case 'VTODO': - $this->cal[$component][$this->todo_count - 1][$keyword] = $value; - break; - case 'VEVENT': - if (!isset($this->cal[$component][$this->event_count - 1][$keyword . '_array'])) { - $this->cal[$component][$this->event_count - 1][$keyword . '_array'] = array(); // Create array() - } - - if (is_array($value)) { - array_push($this->cal[$component][$this->event_count - 1][$keyword . '_array'], $value); // Add array of properties to the end - } else { - if (!isset($this->cal[$component][$this->event_count - 1][$keyword])) { - $this->cal[$component][$this->event_count - 1][$keyword] = $value; - } - - $this->cal[$component][$this->event_count - 1][$keyword . '_array'][] = $value; - - // Glue back together for multi-line content - if ($this->cal[$component][$this->event_count - 1][$keyword] != $value) { - $ord = (isset($value[0])) ? ord($value[0]) : NULL; // First char - - if (in_array($ord, array(9, 32))) { // Is space or tab? - $value = substr($value, 1); // Only trim the first character - } - - if (is_array($this->cal[$component][$this->event_count - 1][$keyword . '_array'][1])) { // Account for multiple definitions of current keyword (e.g. ATTENDEE) - $this->cal[$component][$this->event_count - 1][$keyword] .= ';' . $value; // Concat value *with separator* as content spans multiple lines - } else { - if ($keyword === 'EXDATE') { - // This will give out a comma separated EXDATE string as per RFC2445 - // Example: EXDATE:19960402T010000Z,19960403T010000Z,19960404T010000Z - // Usage: $event['EXDATE'] will print out 19960402T010000Z,19960403T010000Z,19960404T010000Z - $this->cal[$component][$this->event_count - 1][$keyword] .= ',' . $value; - } else { - // Concat value as content spans multiple lines - $this->cal[$component][$this->event_count - 1][$keyword] .= $value; - } - } - } - } - break; - case 'VFREEBUSY': - $this->cal[$component][$this->freebusy_count - 1][$keyword] = $value; - break; - default: - $this->cal[$component][$keyword] = $value; - break; - } - $this->last_keyword = $keyword; - } - - /** - * Get a key-value pair of a string. - * - * @param {string} $text which is like "VCALENDAR:Begin" or "LOCATION:" - * - * @return {array} array("VCALENDAR", "Begin") - */ - public function keyValueFromString($text) - { - // Match colon separator outside of quoted substrings - // Fallback to nearest semicolon outside of quoted substrings, if colon cannot be found - // Do not try and match within the value paired with the keyword - preg_match('/(.*?)(?::(?=(?:[^"]*"[^"]*")*[^"]*$)|;(?=[^:]*$))([\w\W]*)/', htmlspecialchars($text, ENT_QUOTES, 'UTF-8'), $matches); - - if (count($matches) == 0) { - return false; - } - - if (preg_match('/^([A-Z-]+)([;][\w\W]*)?$/', $matches[1])) { - $matches = array_splice($matches, 1, 2); // Remove first match and re-align ordering - - // Process properties - if (preg_match('/([A-Z-]+)[;]([\w\W]*)/', $matches[0], $properties)) { - array_shift($properties); // Remove first match - $matches[0] = $properties[0]; // Fix to ignore everything in keyword after a ; (e.g. Language, TZID, etc.) - array_shift($properties); // Repeat removing first match - - $formatted = array(); - foreach ($properties as $property) { - preg_match_all('~[^\r\n";]+(?:"[^"\\\]*(?:\\\.[^"\\\]*)*"[^\r\n";]*)*~', $property, $attributes); // Match semicolon separator outside of quoted substrings - $attributes = (sizeof($attributes) == 0) ? array($property) : reset($attributes); // Remove multi-dimensional array and use the first key - - foreach ($attributes as $attribute) { - preg_match_all('~[^\r\n"=]+(?:"[^"\\\]*(?:\\\.[^"\\\]*)*"[^\r\n"=]*)*~', $attribute, $values); // Match equals sign separator outside of quoted substrings - $value = (sizeof($values) == 0) ? NULL : reset($values); // Remove multi-dimensional array and use the first key - - if (is_array($value) && isset($value[1])) { - $formatted[$value[0]] = trim($value[1], '"'); // Remove double quotes from beginning and end only - } - } - } - - $properties[0] = $formatted; // Assign the keyword property information - - array_unshift($properties, $matches[1]); // Add match to beginning of array - $matches[1] = $properties; - } - - return $matches; - } else { - return false; // Ignore this match - } - } - - /** - * Return Unix timestamp from iCal date time format - * - * @param {string} $icalDate A Date in the format YYYYMMDD[T]HHMMSS[Z] or - * YYYYMMDD[T]HHMMSS - * - * @return {int} - */ - public static function iCalDateToUnixTimestamp($icalDate) - { - $icalDate = str_replace('T', '', $icalDate); - $icalDate = str_replace('Z', '', $icalDate); - - $pattern = '/([0-9]{4})'; // 1: YYYY - $pattern .= '([0-9]{2})'; // 2: MM - $pattern .= '([0-9]{2})'; // 3: DD - $pattern .= '([0-9]{0,2})'; // 4: HH - $pattern .= '([0-9]{0,2})'; // 5: MM - $pattern .= '([0-9]{0,2})/'; // 6: SS - preg_match($pattern, $icalDate, $date); - - // Unix timestamp can't represent dates before 1970 - if ($date[1] <= 1970) { - return false; - } - // Unix timestamps after 03:14:07 UTC 2038-01-19 might cause an overflow - // if 32 bit integers are used. - $timestamp = mktime((int)$date[4], (int)$date[5], (int)$date[6], (int)$date[2], (int)$date[3], (int)$date[1]); - return $timestamp; - } - - /** - * Processes recurrences - * - * @author John Grogg - * @return {array} - */ - public function process_recurrences() - { - $array = $this->cal; - $events = $array['VEVENT']; - if (empty($events)) - return false; - foreach ($array['VEVENT'] as $anEvent) { - if (isset($anEvent['RRULE']) && $anEvent['RRULE'] != '') { - // Recurring event, parse RRULE and add appropriate duplicate events - $rrules = array(); - $rrule_strings = explode(';', $anEvent['RRULE']); - foreach ($rrule_strings as $s) { - list($k, $v) = explode('=', $s); - $rrules[$k] = $v; - } - // Get frequency - $frequency = $rrules['FREQ']; - // Get Start timestamp - $start_timestamp = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']); - $end_timestamp = $this->iCalDateToUnixTimestamp($anEvent['DTEND']); - $event_timestamp_offset = $end_timestamp - $start_timestamp; - // Get Interval - $interval = (isset($rrules['INTERVAL']) && $rrules['INTERVAL'] != '') ? $rrules['INTERVAL'] : 1; - - if (in_array($frequency, array('MONTHLY', 'YEARLY')) && isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { - // Deal with BYDAY - $day_number = intval($rrules['BYDAY']); - if (empty($day_number)) { // Returns 0 when no number defined in BYDAY - if (!isset($rrules['BYSETPOS'])) { - $day_number = 1; // Set first as default - } else if (is_numeric($rrules['BYSETPOS'])) { - $day_number = $rrules['BYSETPOS']; - } - } - $day_number = ($day_number == -1) ? 6 : $day_number; // Override for our custom key (6 => 'last') - $week_day = substr($rrules['BYDAY'], -2); - $day_ordinals = array(1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth', 6 => 'last'); - $weekdays = array('SU' => 'sunday', 'MO' => 'monday', 'TU' => 'tuesday', 'WE' => 'wednesday', 'TH' => 'thursday', 'FR' => 'friday', 'SA' => 'saturday'); - } - - $until_default = date_create('now'); - $until_default->modify($this->default_span . ' year'); - $until_default->setTime(23, 59, 59); // End of the day - $until_default = date_format($until_default, 'Ymd\THis'); - - if (isset($rrules['UNTIL'])) { - // Get Until - $until = $this->iCalDateToUnixTimestamp($rrules['UNTIL']); - } else if (isset($rrules['COUNT'])) { - $frequency_conversion = array('DAILY' => 'day', 'WEEKLY' => 'week', 'MONTHLY' => 'month', 'YEARLY' => 'year'); - $count_orig = (is_numeric($rrules['COUNT']) && $rrules['COUNT'] > 1) ? $rrules['COUNT'] : 0; - $count = ($count_orig - 1); // Remove one to exclude the occurrence that initialises the rule - $count += ($count > 0) ? $count * ($interval - 1) : 0; - $offset = "+$count " . $frequency_conversion[$frequency]; - $until = strtotime($offset, $start_timestamp); - - if (in_array($frequency, array('MONTHLY', 'YEARLY')) && isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { - $dtstart = date_create($anEvent['DTSTART']); - for ($i = 1; $i <= $count; $i++) { - $dtstart_clone = clone $dtstart; - $dtstart_clone->modify('next ' . $frequency_conversion[$frequency]); - $offset = "{$day_ordinals[$day_number]} {$weekdays[$week_day]} of " . $dtstart_clone->format('F Y H:i:01'); - $dtstart->modify($offset); - } - - // Jumping X months forwards doesn't mean the end date will fall on the same day defined in BYDAY - // Use the largest of these to ensure we are going far enough in the future to capture our final end day - $until = max($until, $dtstart->format('U')); - } - - unset($offset); - } else { - $until = $this->iCalDateToUnixTimestamp($until_default); - } - - if (!isset($anEvent['EXDATE_array'])) { - $anEvent['EXDATE_array'] = array(); - } - - // Decide how often to add events and do so - switch ($frequency) { - case 'DAILY': - // Simply add a new event each interval of days until UNTIL is reached - $offset = "+$interval day"; - $recurring_timestamp = strtotime($offset, $start_timestamp); - - while ($recurring_timestamp <= $until) { - // Add event - $anEvent['DTSTART'] = date('Ymd\THis', $recurring_timestamp); - $anEvent['DTEND'] = date('Ymd\THis', $recurring_timestamp + $event_timestamp_offset); - - $search_date = $anEvent['DTSTART']; - $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { - return is_string($val) && strpos($search_date, $val) === 0; - }); - - if (!$is_excluded) { - $events[] = $anEvent; - } - - // Move forwards - $recurring_timestamp = strtotime($offset, $recurring_timestamp); - } - break; - case 'WEEKLY': - // Create offset - $offset = "+$interval week"; - // Build list of days of week to add events - $weekdays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); - - if (isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { - $bydays = explode(',', $rrules['BYDAY']); - } else { - $weekTemp = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); - $findDay = $weekTemp[date('w', $start_timestamp)]; - $bydays = array($findDay); - } - - // Get timestamp of first day of start week - $week_recurring_timestamp = (date('w', $start_timestamp) == 0) ? $start_timestamp : strtotime('last Sunday ' . date('H:i:s', $start_timestamp), $start_timestamp); - - // Step through weeks - while ($week_recurring_timestamp <= $until) { - // Add events for bydays - $day_recurring_timestamp = $week_recurring_timestamp; - - foreach ($weekdays as $day) { - // Check if day should be added - - if (in_array($day, $bydays) && $day_recurring_timestamp > $start_timestamp && $day_recurring_timestamp <= $until) { - // Add event to day - $anEvent['DTSTART'] = date('Ymd\THis', $day_recurring_timestamp); - $anEvent['DTEND'] = date('Ymd\THis', $day_recurring_timestamp + $event_timestamp_offset); - - $search_date = $anEvent['DTSTART']; - $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); - - if (!$is_excluded) { - $events[] = $anEvent; - } - } - - // Move forwards a day - $day_recurring_timestamp = strtotime('+1 day', $day_recurring_timestamp); - } - - // Move forwards $interval weeks - $week_recurring_timestamp = strtotime($offset, $week_recurring_timestamp); - } - break; - case 'MONTHLY': - // Create offset - $offset = "+$interval month"; - $recurring_timestamp = strtotime($offset, $start_timestamp); - - if (isset($rrules['BYMONTHDAY']) && $rrules['BYMONTHDAY'] != '') { - // Deal with BYMONTHDAY - $monthdays = explode(',', $rrules['BYMONTHDAY']); - - while ($recurring_timestamp <= $until) { - foreach ($monthdays as $monthday) { - // Add event - $anEvent['DTSTART'] = date('Ym' . sprintf('%02d', $monthday) . '\THis', $recurring_timestamp); - $anEvent['DTEND'] = date('Ymd\THis', $this->iCalDateToUnixTimestamp($anEvent['DTSTART']) + $event_timestamp_offset); - - $search_date = $anEvent['DTSTART']; - $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); - - if (!$is_excluded) { - $events[] = $anEvent; - } - } - - // Move forwards - $recurring_timestamp = strtotime($offset, $recurring_timestamp); - } - } else if (isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { - $start_time = date('His', $start_timestamp); - - while ($recurring_timestamp <= $until) { - $event_start_desc = "{$day_ordinals[$day_number]} {$weekdays[$week_day]} of " . date('F Y H:i:s', $recurring_timestamp); - $event_start_timestamp = strtotime($event_start_desc); - - if ($event_start_timestamp > $start_timestamp && $event_start_timestamp < $until) { - $anEvent['DTSTART'] = date('Ymd\T', $event_start_timestamp) . $start_time; - $anEvent['DTEND'] = date('Ymd\THis', $this->iCalDateToUnixTimestamp($anEvent['DTSTART']) + $event_timestamp_offset); - - $search_date = $anEvent['DTSTART']; - $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); - - if (!$is_excluded) { - $events[] = $anEvent; - } - } - - // Move forwards - $recurring_timestamp = strtotime($offset, $recurring_timestamp); - } - } - break; - case 'YEARLY': - // Create offset - $offset = "+$interval year"; - $recurring_timestamp = strtotime($offset, $start_timestamp); - $month_names = array(1 => 'January', 2 => 'February', 3 => 'March', 4 => 'April', 5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August', 9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December'); - - // Check if BYDAY rule exists - if (isset($rrules['BYDAY']) && $rrules['BYDAY'] != '') { - $start_time = date('His', $start_timestamp); - - while ($recurring_timestamp <= $until) { - $event_start_desc = "{$day_ordinals[$day_number]} {$weekdays[$week_day]} of {$month_names[$rrules['BYMONTH']]} " . date('Y H:i:s', $recurring_timestamp); - $event_start_timestamp = strtotime($event_start_desc); - - if ($event_start_timestamp > $start_timestamp && $event_start_timestamp < $until) { - $anEvent['DTSTART'] = date('Ymd\T', $event_start_timestamp) . $start_time; - $anEvent['DTEND'] = date('Ymd\THis', $this->iCalDateToUnixTimestamp($anEvent['DTSTART']) + $event_timestamp_offset); - - $search_date = $anEvent['DTSTART']; - $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); - - if (!$is_excluded) { - $events[] = $anEvent; - } - } - - // Move forwards - $recurring_timestamp = strtotime($offset, $recurring_timestamp); - } - } else { - $day = date('d', $start_timestamp); - $start_time = date('His', $start_timestamp); - - // Step through years - while ($recurring_timestamp <= $until) { - // Add specific month dates - if (isset($rrules['BYMONTH']) && $rrules['BYMONTH'] != '') { - $event_start_desc = "$day {$month_names[$rrules['BYMONTH']]} " . date('Y H:i:s', $recurring_timestamp); - } else { - $event_start_desc = $day . date('F Y H:i:s', $recurring_timestamp); - } - - $event_start_timestamp = strtotime($event_start_desc); - - if ($event_start_timestamp > $start_timestamp && $event_start_timestamp < $until) { - $anEvent['DTSTART'] = date('Ymd\T', $event_start_timestamp) . $start_time; - $anEvent['DTEND'] = date('Ymd\THis', $this->iCalDateToUnixTimestamp($anEvent['DTSTART']) + $event_timestamp_offset); - - $search_date = $anEvent['DTSTART']; - $is_excluded = array_filter($anEvent['EXDATE_array'], function($val) use ($search_date) { return is_string($val) && strpos($search_date, $val) === 0; }); - - if (!$is_excluded) { - $events[] = $anEvent; - } - } - - // Move forwards - $recurring_timestamp = strtotime($offset, $recurring_timestamp); - } - } - break; - - $events = (isset($count_orig) && sizeof($events) > $count_orig) ? array_slice($events, 0, $count_orig) : $events; // Ensure we abide by COUNT if defined - } - } - } - $this->cal['VEVENT'] = $events; - } - - /** - * Returns an array of arrays with all events. Every event is an associative - * array and each property is an element it. - * - * @return {array} - */ - public function events() - { - $array = $this->cal; - return $array['VEVENT']; - } - - /** - * Returns the calendar name - * - * @return {calendar name} - */ - public function calendarName() - { - return $this->cal['VCALENDAR']['X-WR-CALNAME']; - } - - /** - * Returns the calendar description - * - * @return {calendar description} - */ - public function calendarDescription() - { - return $this->cal['VCALENDAR']['X-WR-CALDESC']; - } - - /** - * Returns an array of arrays with all free/busy events. Every event is - * an associative array and each property is an element it. - * - * @return {array} - */ - public function freeBusyEvents() - { - $array = $this->cal; - return $array['VFREEBUSY']; - } - - /** - * Returns a boolean value whether the current calendar has events or not - * - * @return {boolean} - */ - public function hasEvents() - { - return (count($this->events()) > 0) ? true : false; - } - - /** - * Returns false when the current calendar has no events in range, else the - * events. - * - * Note that this function makes use of a UNIX timestamp. This might be a - * problem on January the 29th, 2038. - * See http://en.wikipedia.org/wiki/Unix_time#Representing_the_number - * - * @param {boolean} $rangeStart Either true or false - * @param {boolean} $rangeEnd Either true or false - * - * @return {mixed} - */ - public function eventsFromRange($rangeStart = false, $rangeEnd = false) - { - $events = $this->sortEventsWithOrder($this->events(), SORT_ASC); - - if (!$events) { - return false; - } - - $extendedEvents = array(); - - if ($rangeStart === false) { - $rangeStart = new DateTime(); - } else { - $rangeStart = new DateTime($rangeStart); - } - - if ($rangeEnd === false or $rangeEnd <= 0) { - $rangeEnd = new DateTime('2038/01/18'); - } else { - $rangeEnd = new DateTime($rangeEnd); - } - - $rangeStart = $rangeStart->format('U'); - $rangeEnd = $rangeEnd->format('U'); - - // Loop through all events by adding two new elements - foreach ($events as $anEvent) { - $timestamp = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']); - if ($timestamp >= $rangeStart && $timestamp <= $rangeEnd) { - $extendedEvents[] = $anEvent; - } - } - - return $extendedEvents; - } - - /** - * Returns a boolean value whether the current calendar has events or not - * - * @param {array} $events An array with events. - * @param {array} $sortOrder Either SORT_ASC, SORT_DESC, SORT_REGULAR, - * SORT_NUMERIC, SORT_STRING - * - * @return {boolean} - */ - public function sortEventsWithOrder($events, $sortOrder = SORT_ASC) - { - $extendedEvents = array(); - - // Loop through all events by adding two new elements - foreach ($events as $anEvent) { - if (!array_key_exists('UNIX_TIMESTAMP', $anEvent)) { - $anEvent['UNIX_TIMESTAMP'] = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']); - } - - if (!array_key_exists('REAL_DATETIME', $anEvent)) { - $anEvent['REAL_DATETIME'] = date('d.m.Y', $anEvent['UNIX_TIMESTAMP']); - } - - $extendedEvents[] = $anEvent; - } - - foreach ($extendedEvents as $key => $value) { - $timestamp[$key] = $value['UNIX_TIMESTAMP']; - } - array_multisort($timestamp, $sortOrder, $extendedEvents); - - return $extendedEvents; - } -} \ No newline at end of file diff --git a/php/ical/lib/calendarComponent.class.php b/php/ical/lib/calendarComponent.class.php deleted file mode 100644 index 0bae538..0000000 --- a/php/ical/lib/calendarComponent.class.php +++ /dev/null @@ -1,4661 +0,0 @@ - - * @since 2.2.11 - 2015-03-31 - */ -class calendarComponent extends iCalBase { -/** @var array component property UID value */ - protected $uid; -/** @var array component property DTSTAMP value */ - protected $dtstamp; -/** @var string component type */ - public $objName; -/** - * constructor for calendar component object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.6 - 2011-05-17 - * @uses calendarComponent::$objName - * @uses calendarComponent::$timezonetype - * @uses calendarComponent::$uid - * @uses calendarComponent::$dtstamp - * @uses calendarComponent::$language - * @uses calendarComponent::$nl - * @uses calendarComponent::$unique_id - * @uses calendarComponent::$format - * @uses calendarComponent::$dtzid - * @uses calendarComponent::$allowEmpty - * @uses calendarComponent::$xcaldecl - * @uses calendarComponent::_createFormat() - * @uses calendarComponent::_makeDtstamp() - */ - function calendarComponent() { - $this->objName = ( isset( $this->timezonetype )) ? - strtolower( $this->timezonetype ) : get_class ( $this ); - $this->uid = array(); - $this->dtstamp = array(); - - $this->language = null; - $this->nl = null; - $this->unique_id = null; - $this->format = null; - $this->dtzid = null; - $this->allowEmpty = TRUE; - $this->xcaldecl = array(); - - $this->_createFormat(); - $this->_makeDtstamp(); - } -/*********************************************************************************/ -/** - * Property Name: ACTION - */ -/** - * creates formatted output for calendar component property action - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-22 - * @uses calendarComponent::$action - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses calendarComponent::_createElement() - * @return string - */ - function createAction() { - if( empty( $this->action )) return FALSE; - if( empty( $this->action['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE; - $attributes = $this->_createParams( $this->action['params'] ); - return $this->_createElement( 'ACTION', $attributes, $this->action['value'] ); - } -/** - * set calendar component property action - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE" - * @param mixed $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$action - * @uses calendarComponent::$action - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setAction( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: ATTACH - */ -/** - * creates formatted output for calendar component property attach - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.16 - 2012-02-04 - * @uses calendarComponent::$attach - * @uses calendarComponent::_createParams() - * @uses calendarComponent::$format - * @uses calendarComponent::$intAttrDelimiter - * @uses calendarComponent::$attributeDelimiter - * @uses calendarComponent::$valueInit - * @uses calendarComponent::$nl - * @uses calendarComponent::_createElement() - * @return string - */ - function createAttach() { - if( empty( $this->attach )) return FALSE; - $output = null; - foreach( $this->attach as $attachPart ) { - if( !empty( $attachPart['value'] )) { - $attributes = $this->_createParams( $attachPart['params'] ); - if(( 'xcal' != $this->format ) && isset( $attachPart['params']['VALUE'] ) && ( 'BINARY' == $attachPart['params']['VALUE'] )) { - $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes ); - $str = 'ATTACH'.$attributes.$this->valueInit.$attachPart['value']; - $output = substr( $str, 0, 75 ).$this->nl; - $str = substr( $str, 75 ); - $output .= ' '.chunk_split( $str, 74, $this->nl.' ' ); - if( ' ' == substr( $output, -1 )) - $output = rtrim( $output ); - if( $this->nl != substr( $output, ( 0 - strlen( $this->nl )))) - $output .= $this->nl; - return $output; - } - $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] ); - } - elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' ); - } - return $output; - } -/** - * set calendar component property attach - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @param integer $index - * @uses calendarComponent::$attach - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setMval() - * @return bool - */ - function setAttach( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: ATTENDEE - */ -/** - * creates formatted output for calendar component property attendee - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.12 - 2012-01-31 - * @uses calendarComponent::$attendee - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::$intAttrDelimiter - * @return string - */ - function createAttendee() { - if( empty( $this->attendee )) return FALSE; - $output = null; - foreach( $this->attendee as $attendeePart ) { // start foreach 1 - if( empty( $attendeePart['value'] )) { - if( $this->getConfig( 'allowEmpty' )) - $output .= $this->_createElement( 'ATTENDEE' ); - continue; - } - $attendee1 = $attendee2 = null; - foreach( $attendeePart as $paramlabel => $paramvalue ) { // start foreach 2 - if( 'value' == $paramlabel ) - $attendee2 .= $paramvalue; - elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif - $mParams = array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ); - foreach( $paramvalue as $pKey => $pValue ) { // fix (opt) quotes - if( is_array( $pValue ) || in_array( $pKey, $mParams )) - continue; - if(( FALSE !== strpos( $pValue, ':' )) || - ( FALSE !== strpos( $pValue, ';' )) || - ( FALSE !== strpos( $pValue, ',' ))) - $paramvalue[$pKey] = '"'.$pValue.'"'; - } - // set attenddee parameters in rfc2445 order - if( isset( $paramvalue['CUTYPE'] )) - $attendee1 .= $this->intAttrDelimiter.'CUTYPE='.$paramvalue['CUTYPE']; - if( isset( $paramvalue['MEMBER'] )) { - $attendee1 .= $this->intAttrDelimiter.'MEMBER='; - foreach( $paramvalue['MEMBER'] as $cix => $opv ) - $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; - } - if( isset( $paramvalue['ROLE'] )) - $attendee1 .= $this->intAttrDelimiter.'ROLE='.$paramvalue['ROLE']; - if( isset( $paramvalue['PARTSTAT'] )) - $attendee1 .= $this->intAttrDelimiter.'PARTSTAT='.$paramvalue['PARTSTAT']; - if( isset( $paramvalue['RSVP'] )) - $attendee1 .= $this->intAttrDelimiter.'RSVP='.$paramvalue['RSVP']; - if( isset( $paramvalue['DELEGATED-TO'] )) { - $attendee1 .= $this->intAttrDelimiter.'DELEGATED-TO='; - foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv ) - $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; - } - if( isset( $paramvalue['DELEGATED-FROM'] )) { - $attendee1 .= $this->intAttrDelimiter.'DELEGATED-FROM='; - foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv ) - $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ; - } - if( isset( $paramvalue['SENT-BY'] )) - $attendee1 .= $this->intAttrDelimiter.'SENT-BY='.$paramvalue['SENT-BY']; - if( isset( $paramvalue['CN'] )) - $attendee1 .= $this->intAttrDelimiter.'CN='.$paramvalue['CN']; - if( isset( $paramvalue['DIR'] )) { - $delim = ( FALSE === strpos( $paramvalue['DIR'], '"' )) ? '"' : ''; - $attendee1 .= $this->intAttrDelimiter.'DIR='.$delim.$paramvalue['DIR'].$delim; - } - if( isset( $paramvalue['LANGUAGE'] )) - $attendee1 .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE']; - $xparams = array(); - foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3 - if( ctype_digit( (string) $optparamlabel )) { - $xparams[] = $optparamvalue; - continue; - } - if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' ))) - $xparams[$optparamlabel] = $optparamvalue; - } // end foreach 3 - ksort( $xparams, SORT_STRING ); - foreach( $xparams as $paramKey => $paramValue ) { - if( ctype_digit( (string) $paramKey )) - $attendee1 .= $this->intAttrDelimiter.$paramValue; - else - $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue"; - } // end foreach 3 - } // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) - } // end foreach 2 - $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 ); - } // end foreach 1 - return $output; - } -/** - * set calendar component property attach - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.18.13 - 2013-09-22 - * @param string $value - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$objName - * @uses iCalUtilityFunctions::_existRem() - * @uses iCalUtilityFunctions::_setMval() - * @return bool - */ - function setAttendee( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// may exist.. . also in params - if( !empty( $value )) { - if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) - $value = 'MAILTO:'.$value; - elseif( !empty( $value )) - $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); - $value = str_replace( 'mailto:', 'MAILTO:', $value ); - } - $params2 = array(); - if( is_array($params )) { - $optarrays = array(); - $params = array_change_key_case( $params, CASE_UPPER ); - foreach( $params as $optparamlabel => $optparamvalue ) { - if(( 'X-' != substr( $optparamlabel, 0, 2 )) && (( 'vfreebusy' == $this->objName ) || ( 'valarm' == $this->objName ))) - continue; - switch( $optparamlabel ) { - case 'MEMBER': - case 'DELEGATED-TO': - case 'DELEGATED-FROM': - if( !is_array( $optparamvalue )) - $optparamvalue = array( $optparamvalue ); - foreach( $optparamvalue as $part ) { - $part = trim( $part ); - if(( '"' == substr( $part, 0, 1 )) && - ( '"' == substr( $part, -1 ))) - $part = substr( $part, 1, ( strlen( $part ) - 2 )); - if( 'mailto:' != strtolower( substr( $part, 0, 7 ))) - $part = "MAILTO:$part"; - else - $part = 'MAILTO:'.substr( $part, 7 ); - $optarrays[$optparamlabel][] = $part; - } - break; - default: - if(( '"' == substr( $optparamvalue, 0, 1 )) && - ( '"' == substr( $optparamvalue, -1 ))) - $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 )); - if( 'SENT-BY' == $optparamlabel ) { - if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 ))) - $optparamvalue = "MAILTO:$optparamvalue"; - else - $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 ); - } - $params2[$optparamlabel] = $optparamvalue; - break; - } // end switch( $optparamlabel.. . - } // end foreach( $optparam.. . - foreach( $optarrays as $optparamlabel => $optparams ) - $params2[$optparamlabel] = $optparams; - } - // remove defaults - iCalUtilityFunctions::_existRem( $params2, 'CUTYPE', 'INDIVIDUAL' ); - iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' ); - iCalUtilityFunctions::_existRem( $params2, 'ROLE', 'REQ-PARTICIPANT' ); - iCalUtilityFunctions::_existRem( $params2, 'RSVP', 'FALSE' ); - // check language setting - if( isset( $params2['CN' ] )) { - $lang = $this->getConfig( 'language' ); - if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang )) - $params2['LANGUAGE' ] = $lang; - } - iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: CATEGORIES - */ -/** - * creates formatted output for calendar component property categories - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$categories - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @uses calendarComponent::$format - * @uses calendarComponent::$nl - * @return string - */ - function createCategories() { - if( empty( $this->categories )) return FALSE; - $output = null; - foreach( $this->categories as $category ) { - if( empty( $category['value'] )) { - if ( $this->getConfig( 'allowEmpty' )) - $output .= $this->_createElement( 'CATEGORIES' ); - continue; - } - $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' )); - if( is_array( $category['value'] )) { - foreach( $category['value'] as $cix => $categoryPart ) - $category['value'][$cix] = iCalUtilityFunctions::_strrep( $categoryPart, $this->format, $this->nl ); - $content = implode( ',', $category['value'] ); - } - else - $content = iCalUtilityFunctions::_strrep( $category['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'CATEGORIES', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property categories - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param mixed $value - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$categories - * @uses iCalUtilityFunctions::_setMval() - * @return bool - */ - function setCategories( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: CLASS - */ -/** - * creates formatted output for calendar component property class - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 0.9.7 - 2006-11-20 - * @uses calendarComponent::$class - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createClass() { - if( empty( $this->class )) return FALSE; - if( empty( $this->class['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE; - $attributes = $this->_createParams( $this->class['params'] ); - return $this->_createElement( 'CLASS', $attributes, $this->class['value'] ); - } -/** - * set calendar component property class - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name - * @param array $params optional - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$class - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setClass( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: COMMENT - */ -/** - * creates formatted output for calendar component property comment - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$comment - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @uses calendarComponent::$format - * @uses calendarComponent::$nl - * @return string - */ - function createComment() { - if( empty( $this->comment )) return FALSE; - $output = null; - foreach( $this->comment as $commentPart ) { - if( empty( $commentPart['value'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' ); - continue; - } - $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $commentPart['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'COMMENT', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property comment - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setMval() - * @uses calendarComponent::$comment - * @return bool - */ - function setComment( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: COMPLETED - */ -/** - * creates formatted output for calendar component property completed - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-22 - * @return string - * @uses calendarComponent::$completed - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses iCalUtilityFunctions::_date2strdate(); - * @uses calendarComponent::_createParams() - */ - function createCompleted( ) { - if( empty( $this->completed )) return FALSE; - if( !isset( $this->completed['value']['year'] ) && - !isset( $this->completed['value']['month'] ) && - !isset( $this->completed['value']['day'] ) && - !isset( $this->completed['value']['hour'] ) && - !isset( $this->completed['value']['min'] ) && - !isset( $this->completed['value']['sec'] )) - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'COMPLETED' ); - else return FALSE; - $formatted = iCalUtilityFunctions::_date2strdate( $this->completed['value'], 7 ); - $attributes = $this->_createParams( $this->completed['params'] ); - return $this->_createElement( 'COMPLETED', $attributes, $formatted ); - } -/** - * set calendar component property completed - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param mixed $year - * @param mixed $month - * @param int $day - * @param int $hour - * @param int $min - * @param int $sec - * @param array $params - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setParams() - * @uses calendarComponent::$completed - * @uses iCalUtilityFunctions::_setDate2() - * @return bool - */ - function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->completed = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } - else - return FALSE; - } - $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: CONTACT - */ -/** - * creates formatted output for calendar component property contact - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$contact - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @return string - */ - function createContact() { - if( empty( $this->contact )) return FALSE; - $output = null; - foreach( $this->contact as $contact ) { - if( !empty( $contact['value'] )) { - $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $contact['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'CONTACT', $attributes, $content ); - } - elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' ); - } - return $output; - } -/** - * set calendar component property contact - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setMval() - * @uses calendarComponent::$contact - * @return bool - */ - function setContact( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: CREATED - */ -/** - * creates formatted output for calendar component property created - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @uses calendarComponent::$created - * @uses iCalUtilityFunctions::_date2strdate() - * @uses calendarComponent::_createParams() - * @uses calendarComponent::_createElement() - * @return string - */ - function createCreated() { - if( empty( $this->created )) return FALSE; - $formatted = iCalUtilityFunctions::_date2strdate( $this->created['value'], 7 ); - $attributes = $this->_createParams( $this->created['params'] ); - return $this->_createElement( 'CREATED', $attributes, $formatted ); - } -/** - * set calendar component property created - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.19.5 - 2014-03-29 - * @param mixed $year - * @param mixed $month - * @param int $day - * @param int $hour - * @param int $min - * @param int $sec - * @param mixed $params - * @uses calendarComponent::$created - * @uses iCalUtilityFunctions::_setDate2() - * @return bool - */ - function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( !isset( $year )) - $year = gmdate( 'Ymd\THis' ); - $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DESCRIPTION - */ -/** - * creates formatted output for calendar component property description - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$description - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::getConfig() - * @return string - */ - function createDescription() { - if( empty( $this->description )) return FALSE; - $output = null; - foreach( $this->description as $description ) { - if( !empty( $description['value'] )) { - $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $description['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'DESCRIPTION', $attributes, $content ); - } - elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' ); - } - return $output; - } -/** - * set calendar component property description - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$objName - * @uses iCalUtilityFunctions::_setMval() - * @uses calendarComponent::$description - * @return bool - */ - function setDescription( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; } - if( 'vjournal' != $this->objName ) - $index = 1; - iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DTEND - */ -/** - * creates formatted output for calendar component property dtend - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.4 - 2012-09-26 - * @uses calendarComponent::$dtend - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses calendarComponent::_createParams() - * @uses calendarComponent::_createElement() - * @return string - */ - function createDtend() { - if( empty( $this->dtend )) return FALSE; - if( !isset( $this->dtend['value']['year'] ) && - !isset( $this->dtend['value']['month'] ) && - !isset( $this->dtend['value']['day'] ) && - !isset( $this->dtend['value']['hour'] ) && - !isset( $this->dtend['value']['min'] ) && - !isset( $this->dtend['value']['sec'] )) - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'DTEND' ); - else return FALSE; - $parno = ( isset( $this->dtend['params']['VALUE'] ) && ( 'DATE' == $this->dtend['params']['VALUE'] )) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $this->dtend['value'], $parno ); - $attributes = $this->_createParams( $this->dtend['params'] ); - return $this->_createElement( 'DTEND', $attributes, $formatted ); - } -/** - * set calendar component property dtend - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param mixed $year - * @param mixed $month - * @param int $day - * @param int $hour - * @param int $min - * @param int $sec - * @param string $tz - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$dtend - * @uses iCalUtilityFunctions::_setParams() - * @uses iCalUtilityFunctions::_setDate() - * @return bool - */ - function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->dtend = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } - else - return FALSE; - } - $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DTSTAMP - */ -/** - * creates formatted output for calendar component property dtstamp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.4 - 2008-03-07 - * @uses calendarComponent::$dtstamp - * @uses calendarComponent::_makeDtstamp() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses calendarComponent::_createParams() - * @uses calendarComponent::_createElement() - * @return string - */ - function createDtstamp() { - if( !isset( $this->dtstamp['value']['year'] ) && - !isset( $this->dtstamp['value']['month'] ) && - !isset( $this->dtstamp['value']['day'] ) && - !isset( $this->dtstamp['value']['hour'] ) && - !isset( $this->dtstamp['value']['min'] ) && - !isset( $this->dtstamp['value']['sec'] )) - $this->_makeDtstamp(); - $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstamp['value'], 7 ); - $attributes = $this->_createParams( $this->dtstamp['params'] ); - return $this->_createElement( 'DTSTAMP', $attributes, $formatted ); - } -/** - * computes datestamp for calendar component object instance dtstamp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @uses iCalUtilityFunctions::$fmt - * @uses calendarComponent::$dtstamp - * @return void - */ - function _makeDtstamp() { - $d = gmdate( iCalUtilityFunctions::$fmt['YmdHis3'], time()); - $date = explode( '-', $d ); - $this->dtstamp['value'] = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2], 'hour' => $date[3], 'min' => $date[4], 'sec' => $date[5], 'tz' => 'Z' ); - $this->dtstamp['params'] = null; - } -/** - * set calendar component property dtstamp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-23 - * @param mixed $year - * @param mixed $month - * @param int $day - * @param int $hour - * @param int $min - * @param int $sec - * @param array $params - * @uses calendarComponent::_makeDtstamp() - * @uses calendarComponent::$dtstamp - * @uses iCalUtilityFunctions::_setDate2() - * @return TRUE - */ - function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( empty( $year )) - $this->_makeDtstamp(); - else - $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DTSTART - */ -/** - * creates formatted output for calendar component property dtstart - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.4 - 2012-09-26 - * @uses calendarComponent::$dtstart - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses calendarComponent::_createParams() - * @return string - */ - function createDtstart() { - if( empty( $this->dtstart )) return FALSE; - if( !isset( $this->dtstart['value']['year'] ) && - !isset( $this->dtstart['value']['month'] ) && - !isset( $this->dtstart['value']['day'] ) && - !isset( $this->dtstart['value']['hour'] ) && - !isset( $this->dtstart['value']['min'] ) && - !isset( $this->dtstart['value']['sec'] )) { - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'DTSTART' ); - else return FALSE; - } - if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) - unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] ); - $parno = ( isset( $this->dtstart['params']['VALUE'] ) && ( 'DATE' == $this->dtstart['params']['VALUE'] )) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstart['value'], $parno ); - $attributes = $this->_createParams( $this->dtstart['params'] ); - return $this->_createElement( 'DTSTART', $attributes, $formatted ); - } -/** - * set calendar component property dtstart - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param mixed $year - * @param mixed $month - * @param int $day - * @param int $hour - * @param int $min - * @param int $sec - * @param string $tz - * @param array $params - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setParams() - * @uses calendarComponent::$dtstart - * @uses iCalUtilityFunctions::_setDate() - * @return bool - */ - function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->dtstart = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } - else - return FALSE; - } - $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName, $this->getConfig( 'TZID' )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DUE - */ -/** - * creates formatted output for calendar component property due - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.4 - 2012-09-26 - * @uses calendarComponent::$due - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses calendarComponent::_createParams() - * @return string - */ - function createDue() { - if( empty( $this->due )) return FALSE; - if( !isset( $this->due['value']['year'] ) && - !isset( $this->due['value']['month'] ) && - !isset( $this->due['value']['day'] ) && - !isset( $this->due['value']['hour'] ) && - !isset( $this->due['value']['min'] ) && - !isset( $this->due['value']['sec'] )) { - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'DUE' ); - else - return FALSE; - } - $parno = ( isset( $this->due['params']['VALUE'] ) && ( 'DATE' == $this->due['params']['VALUE'] )) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $this->due['value'], $parno ); - $attributes = $this->_createParams( $this->due['params'] ); - return $this->_createElement( 'DUE', $attributes, $formatted ); - } -/** - * set calendar component property due - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param mixed $year - * @param mixed $month - * @param int $day - * @param int $hour - * @param int $min - * @param int $sec - * @param string $tz - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$due - * @uses iCalUtilityFunctions::_setParams() - * @uses iCalUtilityFunctions::_setDate() - * @return bool - */ - function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->due = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } - else - return FALSE; - } - $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: DURATION - */ -/** - * creates formatted output for calendar component property duration - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-05-25 - * @uses calendarComponent::$duration - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_duration2str() - * @return string - */ - function createDuration() { - if( empty( $this->duration )) return FALSE; - if( !isset( $this->duration['value']['week'] ) && - !isset( $this->duration['value']['day'] ) && - !isset( $this->duration['value']['hour'] ) && - !isset( $this->duration['value']['min'] ) && - !isset( $this->duration['value']['sec'] )) - if( $this->getConfig( 'allowEmpty' )) - return $this->_createElement( 'DURATION' ); - else return FALSE; - $attributes = $this->_createParams( $this->duration['params'] ); - return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_duration2str( $this->duration['value'] )); - } -/** - * set calendar component property duration - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-05-25 - * @param mixed $week - * @param mixed $day - * @param int $hour - * @param int $min - * @param int $sec - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$duration - * @uses iCalUtilityFunctions::_duration2arr() - * @uses iCalUtilityFunctions::_durationStr2arr() - * @uses iCalUtilityFunctions::_setParams() - * @uses iCalUtilityFunctions::_duration2arr() - * @return bool - */ - function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec )) { - if( $this->getConfig( 'allowEmpty' )) - $week = $day = null; - else - return FALSE; - } - if( is_array( $week ) && ( 1 <= count( $week ))) - $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); - elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) { - $week = trim( $week ); - if( in_array( substr( $week, 0, 1 ), array( '+', '-' ))) - $week = substr( $week, 1 ); - $this->duration = array( 'value' => iCalUtilityFunctions::_durationStr2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day )); - } - else - $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( array( 'week' => $week, 'day' => $day, 'hour' => $hour, 'min' => $min, 'sec' => $sec )) - , 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: EXDATE - */ -/** - * creates formatted output for calendar component property exdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.5 - 2012-12-28 - * @uses calendarComponent::$exdate - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses iCalUtilityFunctions::_sortExdate1() - * @uses iCalUtilityFunctions::_sortExdate2() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses calendarComponent::_createParams() - * @return string - */ - function createExdate() { - if( empty( $this->exdate )) return FALSE; - $output = null; - $exdates = array(); - foreach( $this->exdate as $theExdate ) { - if( empty( $theExdate['value'] )) { - if( $this->getConfig( 'allowEmpty' )) - $output .= $this->_createElement( 'EXDATE' ); - continue; - } - if( 1 < count( $theExdate['value'] )) - usort( $theExdate['value'], array( 'iCalUtilityFunctions', '_sortExdate1' )); - $exdates[] = $theExdate; - } - if( 1 < count( $exdates )) - usort( $exdates, array( 'iCalUtilityFunctions', '_sortExdate2' )); - foreach( $exdates as $theExdate ) { - $content = $attributes = null; - foreach( $theExdate['value'] as $eix => $exdatePart ) { - $parno = count( $exdatePart ); - $formatted = iCalUtilityFunctions::_date2strdate( $exdatePart, $parno ); - if( isset( $theExdate['params']['TZID'] )) - $formatted = str_replace( 'Z', '', $formatted); - if( 0 < $eix ) { - if( isset( $theExdate['value'][0]['tz'] )) { - if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) || - ( 'Z' == $theExdate['value'][0]['tz'] )) { - if( 'Z' != substr( $formatted, -1 )) - $formatted .= 'Z'; - } - else - $formatted = str_replace( 'Z', '', $formatted ); - } - else - $formatted = str_replace( 'Z', '', $formatted ); - } // end if( 0 < $eix ) - $content .= ( 0 < $eix ) ? ','.$formatted : $formatted; - } // end foreach( $theExdate['value'] as $eix => $exdatePart ) - $attributes .= $this->_createParams( $theExdate['params'] ); - $output .= $this->_createElement( 'EXDATE', $attributes, $content ); - } // end foreach( $exdates as $theExdate ) - return $output; - } -/** - * set calendar component property exdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-10 - * @param array $exdates - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setMval() - * @uses calendarComponent::$exdate - * @uses iCalUtilityFunctions::_setParams() - * @uses iCalUtilityFunctions::_chkdatecfg() - * @uses iCalUtilityFunctions::_existRem() - * @uses iCalUtilityFunctions::_strDate2arr() - * @uses iCalUtilityFunctions::_isArrayTimestampDate() - * @uses iCalUtilityFunctions::_isOffset() - * @uses iCalUtilityFunctions::_timestamp2date() - * @uses iCalUtilityFunctions::_chkDateArr() - * @uses iCalUtilityFunctions::$fmt - * @uses iCalUtilityFunctions::_strdate2date() - * @return bool - */ - function setExdate( $exdates, $params=FALSE, $index=FALSE ) { - if( empty( $exdates )) { - if( $this->getConfig( 'allowEmpty' )) { - iCalUtilityFunctions::_setMval( $this->exdate, '', $params, FALSE, $index ); - return TRUE; - } - else - return FALSE; - } - $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ))); - $toZ = ( isset( $input['params']['TZID'] ) && in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) ? TRUE : FALSE; - /* ev. check 1:st date and save ev. timezone **/ - iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] ); - iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter - foreach( $exdates as $eix => $theExdate ) { - iCalUtilityFunctions::_strDate2arr( $theExdate ); - if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate )) { - if( isset( $theExdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theExdate['tz'] )) { - if( isset( $input['params']['TZID'] )) - $theExdate['tz'] = $input['params']['TZID']; - else - $input['params']['TZID'] = $theExdate['tz']; - } - $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno ); - } - elseif( is_array( $theExdate )) { - $d = iCalUtilityFunctions::_chkDateArr( $theExdate, $parno ); - if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); - $exdatea = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $exdatea['unparsedtext'] ); - } - else - $exdatea = $d; - } - elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18 - $exdatea = iCalUtilityFunctions::_strdate2date( $theExdate, $parno ); - unset( $exdatea['unparsedtext'] ); - } - if( 3 == $parno ) - unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] ); - elseif( isset( $exdatea['tz'] )) - $exdatea['tz'] = (string) $exdatea['tz']; - if( isset( $input['params']['TZID'] ) || - ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) || - ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) || - ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] ))) - unset( $exdatea['tz'] ); - if( $toZ ) // time zone Z - $exdatea['tz'] = 'Z'; - $input['value'][] = $exdatea; - } - if( 0 >= count( $input['value'] )) - return FALSE; - if( 3 == $parno ) { - $input['params']['VALUE'] = 'DATE'; - unset( $input['params']['TZID'] ); - } - if( $toZ ) // time zone Z - unset( $input['params']['TZID'] ); - iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: EXRULE - */ -/** - * creates formatted output for calendar component property exrule - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-22 - * @uses calendarComponent::$exrule - * @uses calendarComponent::_format_recur() - * @return string - */ - function createExrule() { - if( empty( $this->exrule )) return FALSE; - return $this->_format_recur( 'EXRULE', $this->exrule ); - } -/** - * set calendar component property exdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param array $exruleset - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setMval() - * @uses calendarComponent::$exrule - * @uses iCalUtilityFunctions::_setRexrule() - * @return bool - */ - function setExrule( $exruleset, $params=FALSE, $index=FALSE ) { - if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = ''; else return FALSE; - iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: FREEBUSY - */ -/** - * creates formatted output for calendar component property freebusy - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.27 - 2013-07-05 - * @uses calendarComponent::$freebusy - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::$intAttrDelimiter - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_sortRdate1() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses iCalUtilityFunctions::_duration2str() - * @return string - */ - function createFreebusy() { - if( empty( $this->freebusy )) return FALSE; - $output = null; - foreach( $this->freebusy as $freebusyPart ) { - if( empty( $freebusyPart['value'] ) || (( 1 == count( $freebusyPart['value'] )) && isset( $freebusyPart['value']['fbtype'] ))) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' ); - continue; - } - $attributes = $content = null; - if( isset( $freebusyPart['value']['fbtype'] )) { - $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype']; - unset( $freebusyPart['value']['fbtype'] ); - $freebusyPart['value'] = array_values( $freebusyPart['value'] ); - } - else - $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY'; - $attributes .= $this->_createParams( $freebusyPart['params'] ); - $fno = 1; - $cnt = count( $freebusyPart['value']); - if( 1 < $cnt ) - usort( $freebusyPart['value'], array( 'iCalUtilityFunctions', '_sortRdate1' )); - foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) { - $formatted = iCalUtilityFunctions::_date2strdate( $freebusyPeriod[0] ); - $content .= $formatted; - $content .= '/'; - $cnt2 = count( $freebusyPeriod[1]); - if( array_key_exists( 'year', $freebusyPeriod[1] )) // date-time - $cnt2 = 7; - elseif( array_key_exists( 'week', $freebusyPeriod[1] )) // duration - $cnt2 = 5; - if(( 7 == $cnt2 ) && // period= -> date-time - isset( $freebusyPeriod[1]['year'] ) && - isset( $freebusyPeriod[1]['month'] ) && - isset( $freebusyPeriod[1]['day'] )) { - $content .= iCalUtilityFunctions::_date2strdate( $freebusyPeriod[1] ); - } - else { // period= -> dur-time - $content .= iCalUtilityFunctions::_duration2str( $freebusyPeriod[1] ); - } - if( $fno < $cnt ) - $content .= ','; - $fno++; - } - $output .= $this->_createElement( 'FREEBUSY', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property freebusy - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $fbType - * @param array $fbValues - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$freebusy - * @uses iCalUtilityFunctions::_isArrayDate() - * @uses iCalUtilityFunctions::_setMval() - * @uses iCalUtilityFunctions::_chkDateArr() - * @uses iCalUtilityFunctions::_isArrayTimestampDate() - * @uses iCalUtilityFunctions::_timestamp2date() - * @uses iCalUtilityFunctions::_duration2arr() - * @uses iCalUtilityFunctions::_durationStr2arr() - * @uses iCalUtilityFunctions::_strdate2date() - * @return bool - */ - function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) { - if( empty( $fbValues )) { - if( $this->getConfig( 'allowEmpty' )) { - iCalUtilityFunctions::_setMval( $this->freebusy, '', $params, FALSE, $index ); - return TRUE; - } - else - return FALSE; - } - $fbType = strtoupper( $fbType ); - if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) && - ( 'X-' != substr( $fbType, 0, 2 ))) - $fbType = 'BUSY'; - $input = array( 'fbtype' => $fbType ); - foreach( $fbValues as $fbPeriod ) { // periods => period - if( empty( $fbPeriod )) - continue; - $freebusyPeriod = array(); - foreach( $fbPeriod as $fbMember ) { // pairs => singlepart - $freebusyPairMember = array(); - if( is_array( $fbMember )) { - if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value - $freebusyPairMember = iCalUtilityFunctions::_chkDateArr( $fbMember, 7 ); - $freebusyPairMember['tz'] = 'Z'; - } - elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value - $freebusyPairMember = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 ); - $freebusyPairMember['tz'] = 'Z'; - } - else { // array format duration - $freebusyPairMember = iCalUtilityFunctions::_duration2arr( $fbMember ); - } - } - elseif(( 3 <= strlen( trim( $fbMember ))) && // string format duration - ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) { - if( 'P' != $fbMember{0} ) - $fbmember = substr( $fbMember, 1 ); - $freebusyPairMember = iCalUtilityFunctions::_durationStr2arr( $fbMember ); - } - elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18 - $freebusyPairMember = iCalUtilityFunctions::_strdate2date( $fbMember, 7 ); - unset( $freebusyPairMember['unparsedtext'] ); - $freebusyPairMember['tz'] = 'Z'; - } - $freebusyPeriod[] = $freebusyPairMember; - } - $input[] = $freebusyPeriod; - } - iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: GEO - */ -/** - * creates formatted output for calendar component property geo - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.18.10 - 2013-09-03 - * @uses calendarComponent::$geo - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_geo2str2() - * @uses iCalUtilityFunctions::$geoLongFmt - * @uses iCalUtilityFunctions::$geoLatFmt - * @return string - */ - function createGeo() { - if( empty( $this->geo )) return FALSE; - if( empty( $this->geo['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE; - return $this->_createElement( 'GEO', $this->_createParams( $this->geo['params'] ), - iCalUtilityFunctions::_geo2str2( $this->geo['value']['latitude'], iCalUtilityFunctions::$geoLatFmt ). - ';'.iCalUtilityFunctions::_geo2str2( $this->geo['value']['longitude'], iCalUtilityFunctions::$geoLongFmt )); - } -/** - * set calendar component property geo - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.18.10 - 2013-09-03 - * @param mixed $latitude - * @param mixed $longitude - * @param array $params - * @uses calendarComponent::$geo - * @uses iCalUtilityFunctions::_setParams() - * @uses calendarComponent::getConfig() - * @return bool - */ - function setGeo( $latitude, $longitude, $params=FALSE ) { - if( isset( $latitude ) && isset( $longitude )) { - if( !is_array( $this->geo )) $this->geo = array(); - $this->geo['value']['latitude'] = floatval( $latitude ); - $this->geo['value']['longitude'] = floatval( $longitude ); - $this->geo['params'] = iCalUtilityFunctions::_setParams( $params ); - } - elseif( $this->getConfig( 'allowEmpty' )) - $this->geo = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params ) ); - else - return FALSE; - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: LAST-MODIFIED - */ -/** - * creates formatted output for calendar component property last-modified - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @uses calendarComponent::$lastmodified - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses calendarComponent::_createElement() - * @return string - */ - function createLastModified() { - if( empty( $this->lastmodified )) return FALSE; - $attributes = $this->_createParams( $this->lastmodified['params'] ); - $formatted = iCalUtilityFunctions::_date2strdate( $this->lastmodified['value'], 7 ); - return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted ); - } -/** - * set calendar component property completed - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.19.5 - 2014-03-29 - * @param mixed $year optional - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param array $params optional - * @uses calendarComponent::$lastmodified - * @uses iCalUtilityFunctions::_setDate2() - * @return boll - */ - function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - if( empty( $year )) - $year = gmdate( 'Ymd\THis' ); - $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: LOCATION - */ -/** - * creates formatted output for calendar component property location - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$location - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @return string - */ - function createLocation() { - if( empty( $this->location )) return FALSE; - if( empty( $this->location['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE; - $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $this->location['value'], $this->format, $this->nl ); - return $this->_createElement( 'LOCATION', $attributes, $content ); - } -/** - * set calendar component property location - ' - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @uses calendarComponent::$location - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setLocation( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: ORGANIZER - */ -/** - * creates formatted output for calendar component property organizer - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.33 - 2010-12-17 - * @uses calendarComponent::$organizer - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createOrganizer() { - if( empty( $this->organizer )) return FALSE; - if( empty( $this->organizer['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE; - $attributes = $this->_createParams( $this->organizer['params'] - , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' )); - return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] ); - } -/** - * set calendar component property organizer - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$organizer - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setOrganizer( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - if( !empty( $value )) { - if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' ))) - $value = 'MAILTO:'.$value; - elseif( !empty( $value )) - $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos ); - $value = str_replace( 'mailto:', 'MAILTO:', $value ); - } - $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - if( isset( $this->organizer['params']['SENT-BY'] )){ - if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 ))) - $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY']; - else - $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 ); - } - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: PERCENT-COMPLETE - */ -/** - * creates formatted output for calendar component property percent-complete - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @uses calendarComponent::$percentcomplete - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createPercentComplete() { - if( !isset($this->percentcomplete) || ( empty( $this->percentcomplete ) && !is_numeric( $this->percentcomplete ))) return FALSE; - if( !isset( $this->percentcomplete['value'] ) || ( empty( $this->percentcomplete['value'] ) && !is_numeric( $this->percentcomplete['value'] ))) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE; - $attributes = $this->_createParams( $this->percentcomplete['params'] ); - return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] ); - } -/** - * set calendar component property percent-complete - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param int $value - * @param array $params - * @uses calendarComponent::$percentcomplete - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setPercentComplete( $value, $params=FALSE ) { - if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: PRIORITY - */ -/** - * creates formatted output for calendar component property priority - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @uses calendarComponent::$priority - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createPriority() { - if( !isset($this->priority) || ( empty( $this->priority ) && !is_numeric( $this->priority ))) return FALSE; - if( !isset( $this->priority['value'] ) || ( empty( $this->priority['value'] ) && !is_numeric( $this->priority['value'] ))) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE; - $attributes = $this->_createParams( $this->priority['params'] ); - return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] ); - } -/** - * set calendar component property priority - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param int $value - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$priority - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setPriority( $value, $params=FALSE ) { - if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RDATE - */ -/** - * creates formatted output for calendar component property rdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.9 - 2013-01-09 - * @uses calendarComponent::$rdate - * @uses calendarComponent::$objName - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses iCalUtilityFunctions::_sortRdate1() - * @uses iCalUtilityFunctions::_sortRdate2() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_date2strdate() - * @return string - */ - function createRdate() { - if( empty( $this->rdate )) return FALSE; - $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE; - $output = null; - $rdates = array(); - foreach( $this->rdate as $rpix => $theRdate ) { - if( empty( $theRdate['value'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' ); - continue; - } - if( $utctime ) - unset( $theRdate['params']['TZID'] ); - if( 1 < count( $theRdate['value'] )) - usort( $theRdate['value'], array( 'iCalUtilityFunctions', '_sortRdate1' )); - $rdates[] = $theRdate; - } - if( 1 < count( $rdates )) - usort( $rdates, array( 'iCalUtilityFunctions', '_sortRdate2' )); - foreach( $rdates as $rpix => $theRdate ) { - $attributes = $this->_createParams( $theRdate['params'] ); - $cnt = count( $theRdate['value'] ); - $content = null; - $rno = 1; - foreach( $theRdate['value'] as $rix => $rdatePart ) { - $contentPart = null; - if( is_array( $rdatePart ) && - isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD - if( $utctime ) - unset( $rdatePart[0]['tz'] ); - $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[0] ); // PERIOD part 1 - if( $utctime || !empty( $theRdate['params']['TZID'] )) - $formatted = str_replace( 'Z', '', $formatted); - $contentPart .= $formatted; - $contentPart .= '/'; - $cnt2 = count( $rdatePart[1]); - if( array_key_exists( 'year', $rdatePart[1] )) { - if( array_key_exists( 'hour', $rdatePart[1] )) - $cnt2 = 7; // date-time - else - $cnt2 = 3; // date - } - elseif( array_key_exists( 'week', $rdatePart[1] )) // duration - $cnt2 = 5; - if(( 7 == $cnt2 ) && // period= -> date-time - isset( $rdatePart[1]['year'] ) && - isset( $rdatePart[1]['month'] ) && - isset( $rdatePart[1]['day'] )) { - if( $utctime ) - unset( $rdatePart[1]['tz'] ); - $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[1] ); // PERIOD part 2 - if( $utctime || !empty( $theRdate['params']['TZID'] )) - $formatted = str_replace( 'Z', '', $formatted ); - $contentPart .= $formatted; - } - else { // period= -> dur-time - $contentPart .= iCalUtilityFunctions::_duration2str( $rdatePart[1] ); - } - } // PERIOD end - else { // SINGLE date start - if( $utctime ) - unset( $rdatePart['tz'] ); - $parno = ( isset( $theRdate['params']['VALUE'] ) && ( 'DATE' == isset( $theRdate['params']['VALUE'] ))) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart, $parno ); - if( $utctime || !empty( $theRdate['params']['TZID'] )) - $formatted = str_replace( 'Z', '', $formatted); - $contentPart .= $formatted; - } - $content .= $contentPart; - if( $rno < $cnt ) - $content .= ','; - $rno++; - } - $output .= $this->_createElement( 'RDATE', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property rdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-10 - * @param array $rdates - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setMval() - * @uses calendarComponent::$rdate - * @uses iCalUtilityFunctions::_setParams() - * @uses calendarComponent::$objName - * @uses iCalUtilityFunctions::_isArrayDate() - * @uses iCalUtilityFunctions::_chkdatecfg() - * @uses iCalUtilityFunctions::_existRem() - * @uses iCalUtilityFunctions::_strDate2arr() - * @uses iCalUtilityFunctions::_isArrayTimestampDate() - * @uses iCalUtilityFunctions::_isOffset() - * @uses iCalUtilityFunctions::_timestamp2date() - * @uses iCalUtilityFunctions::_chkDateArr() - * @uses iCalUtilityFunctions::$fmt - * @uses iCalUtilityFunctions::_strdate2date() - * @uses iCalUtilityFunctions::_duration2arr() - * @uses iCalUtilityFunctions::_durationStr2arr() - * @return bool - */ - function setRdate( $rdates, $params=FALSE, $index=FALSE ) { - if( empty( $rdates )) { - if( $this->getConfig( 'allowEmpty' )) { - iCalUtilityFunctions::_setMval( $this->rdate, '', $params, FALSE, $index ); - return TRUE; - } - else - return FALSE; - } - $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ))); - if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) { - unset( $input['params']['TZID'] ); - $input['params']['VALUE'] = 'DATE-TIME'; - } - $zArr = array( 'GMT', 'UTC', 'Z' ); - $toZ = ( isset( $params['TZID'] ) && in_array( strtoupper( $params['TZID'] ), $zArr )) ? TRUE : FALSE; - /* check if PERIOD, if not set */ - if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) && - isset( $rdates[0] ) && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) && - isset( $rdates[0][0] ) && isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) && - (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) || - iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) || - ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] ))))) && - ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] )))))) - $input['params']['VALUE'] = 'PERIOD'; - /* check 1:st date, upd. $parno (opt) and save ev. timezone **/ - $date = reset( $rdates ); - if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD - $date = reset( $date ); - iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] ); - iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default - foreach( $rdates as $rpix => $theRdate ) { - $inputa = null; - iCalUtilityFunctions::_strDate2arr( $theRdate ); - if( is_array( $theRdate )) { - if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD - foreach( $theRdate as $rix => $rPeriod ) { - iCalUtilityFunctions::_strDate2arr( $theRdate ); - if( is_array( $rPeriod )) { - if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) { // timestamp - if( isset( $rPeriod['tz'] ) && !iCalUtilityFunctions::_isOffset( $rPeriod['tz'] )) { - if( isset( $input['params']['TZID'] )) - $rPeriod['tz'] = $input['params']['TZID']; - else - $input['params']['TZID'] = $rPeriod['tz']; - } - $inputab = iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno ); - } - elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod )) { - $d = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_chkDateArr( $rPeriod, $parno ) : iCalUtilityFunctions::_chkDateArr( $rPeriod, 6 ); - if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); - $inputab = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $inputab['unparsedtext'] ); - } - else - $inputab = $d; - } - elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) { // text-date - $inputab = iCalUtilityFunctions::_strdate2date( reset( $rPeriod ), $parno ); - unset( $inputab['unparsedtext'] ); - } - else // array format duration - $inputab = iCalUtilityFunctions::_duration2arr( $rPeriod ); - } - elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration - ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) { - if( 'P' != $rPeriod[0] ) - $rPeriod = substr( $rPeriod, 1 ); - $inputab = iCalUtilityFunctions::_durationStr2arr( $rPeriod ); - } - elseif( 8 <= strlen( trim( $rPeriod ))) { // text date ex. 2006-08-03 10:12:18 - $inputab = iCalUtilityFunctions::_strdate2date( $rPeriod, $parno ); - unset( $inputab['unparsedtext'] ); - } - if(( 0 == $rpix ) && ( 0 == $rix )) { - if( isset( $inputab['tz'] ) && in_array( strtoupper( $inputab['tz'] ), $zArr )) { - $inputab['tz'] = 'Z'; - $toZ = TRUE; - } - } - else { - if( isset( $inputa[0]['tz'] ) && ( 'Z' == $inputa[0]['tz'] ) && isset( $inputab['year'] )) - $inputab['tz'] = 'Z'; - else - unset( $inputab['tz'] ); - } - if( $toZ && isset( $inputab['year'] ) ) - $inputab['tz'] = 'Z'; - $inputa[] = $inputab; - } - } // PERIOD end - elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) { // timestamp - if( isset( $theRdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theRdate['tz'] )) { - if( isset( $input['params']['TZID'] )) - $theRdate['tz'] = $input['params']['TZID']; - else - $input['params']['TZID'] = $theRdate['tz']; - } - $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno ); - } - else { // date[-time] - $inputa = iCalUtilityFunctions::_chkDateArr( $theRdate, $parno ); - if( isset( $inputa['tz'] ) && ( 'Z' != $inputa['tz'] ) && iCalUtilityFunctions::_isOffset( $inputa['tz'] )) { - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $inputa['year'], (int) $inputa['month'], (int) $inputa['day'], (int) $inputa['hour'], (int) $inputa['min'], (int) $inputa['sec'], $inputa['tz'] ); - $inputa = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $inputa['unparsedtext'] ); - } - } - } - elseif( 8 <= strlen( trim( $theRdate ))) { // text date ex. 2006-08-03 10:12:18 - $inputa = iCalUtilityFunctions::_strdate2date( $theRdate, $parno ); - unset( $inputa['unparsedtext'] ); - if( $toZ ) - $inputa['tz'] = 'Z'; - } - if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD - if(( 0 == $rpix ) && !$toZ ) - $toZ = ( isset( $inputa['tz'] ) && in_array( strtoupper( $inputa['tz'] ), $zArr )) ? TRUE : FALSE; - if( $toZ ) - $inputa['tz'] = 'Z'; - if( 3 == $parno ) - unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] ); - elseif( isset( $inputa['tz'] )) - $inputa['tz'] = (string) $inputa['tz']; - if( isset( $input['params']['TZID'] ) || ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] )))) - if( !$toZ ) - unset( $inputa['tz'] ); - } - $input['value'][] = $inputa; - } - if( 3 == $parno ) { - $input['params']['VALUE'] = 'DATE'; - unset( $input['params']['TZID'] ); - } - if( $toZ ) - unset( $input['params']['TZID'] ); - iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RECURRENCE-ID - */ -/** - * creates formatted output for calendar component property recurrence-id - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.4 - 2012-09-26 - * @uses calendarComponent::$recurrenceid - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses calendarComponent::_createParams() - * @return string - */ - function createRecurrenceid() { - if( empty( $this->recurrenceid )) return FALSE; - if( empty( $this->recurrenceid['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE; - $parno = ( isset( $this->recurrenceid['params']['VALUE'] ) && ( 'DATE' == $this->recurrenceid['params']['VALUE'] )) ? 3 : null; - $formatted = iCalUtilityFunctions::_date2strdate( $this->recurrenceid['value'], $parno ); - $attributes = $this->_createParams( $this->recurrenceid['params'] ); - return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted ); - } -/** - * set calendar component property recurrence-id - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param mixed $year - * @param mixed $month - * @param int $day - * @param int $hour - * @param int $min - * @param int $sec - * @param string $tz - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$recurrenceid - * @uses iCalUtilityFunctions::_setDate() - * @return bool - */ - function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) { - if( empty( $year )) { - if( $this->getConfig( 'allowEmpty' )) { - $this->recurrenceid = array( 'value' => '', 'params' => null ); - return TRUE; - } - else - return FALSE; - } - $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RELATED-TO - */ -/** - * creates formatted output for calendar component property related-to - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-05-25 - * @uses calendarComponent::$relatedto - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @uses calendarComponent::getConfig() - * @return string - */ - function createRelatedTo() { - if( empty( $this->relatedto )) return FALSE; - $output = null; - foreach( $this->relatedto as $relation ) { - if( !empty( $relation['value'] )) - $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ), iCalUtilityFunctions::_strrep( $relation['value'], $this->format, $this->nl )); - elseif( $this->getConfig( 'allowEmpty' )) - $output .= $this->_createElement( 'RELATED-TO' ); - } - return $output; - } -/** - * set calendar component property related-to - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @param int $index - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_existRem() - * @uses iCalUtilityFunctions::_setMval() - * @return bool - */ - function setRelatedTo( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default - iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: REPEAT - */ -/** - * creates formatted output for calendar component property repeat - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @uses calendarComponent::$repeat - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createRepeat() { - if( !isset( $this->repeat ) || ( empty( $this->repeat ) && !is_numeric( $this->repeat ))) return FALSE; - if( !isset( $this->repeat['value']) || ( empty( $this->repeat['value'] ) && !is_numeric( $this->repeat['value'] ))) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE; - $attributes = $this->_createParams( $this->repeat['params'] ); - return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] ); - } -/** - * set calendar component property repeat - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$repeat - * @uses iCalUtilityFunctions::_setParams() - * @return void - */ - function setRepeat( $value, $params=FALSE ) { - if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: REQUEST-STATUS - */ -/** - * creates formatted output for calendar component property request-status - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$requeststatus - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @return string - */ - function createRequestStatus() { - if( empty( $this->requeststatus )) return FALSE; - $output = null; - foreach( $this->requeststatus as $rstat ) { - if( empty( $rstat['value']['statcode'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' ); - continue; - } - $attributes = $this->_createParams( $rstat['params'], array( 'LANGUAGE' )); - $content = number_format( (float) $rstat['value']['statcode'], 2, '.', ''); - $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['text'], $this->format, $this->nl ); - if( isset( $rstat['value']['extdata'] )) - $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['extdata'], $this->format, $this->nl ); - $output .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property request-status - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param float $statcode - * @param string $text - * @param string $extdata - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$requeststatus - * @uses iCalUtilityFunctions::_setMval() - * @return bool - */ - function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) { - if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = ''; else return FALSE; - $input = array( 'statcode' => $statcode, 'text' => $text ); - if( $extdata ) - $input['extdata'] = $extdata; - iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RESOURCES - */ -/** - * creates formatted output for calendar component property resources - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$resources - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @return string - */ - function createResources() { - if( empty( $this->resources )) return FALSE; - $output = null; - foreach( $this->resources as $resource ) { - if( empty( $resource['value'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' ); - continue; - } - $attributes = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' )); - if( is_array( $resource['value'] )) { - foreach( $resource['value'] as $rix => $resourcePart ) - $resource['value'][$rix] = iCalUtilityFunctions::_strrep( $resourcePart, $this->format, $this->nl ); - $content = implode( ',', $resource['value'] ); - } - else - $content = iCalUtilityFunctions::_strrep( $resource['value'], $this->format, $this->nl ); - $output .= $this->_createElement( 'RESOURCES', $attributes, $content ); - } - return $output; - } -/** - * set calendar component property recources - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param mixed $value - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setMval() - * @uses calendarComponent::$resources - * @return bool - */ - function setResources( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: RRULE - */ -/** - * creates formatted output for calendar component property rrule - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @uses calendarComponent::$rrule - * @uses calendarComponent::_format_recur() - * @return string - */ - function createRrule() { - if( empty( $this->rrule )) return FALSE; - return $this->_format_recur( 'RRULE', $this->rrule ); - } -/** - * set calendar component property rrule - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param array $rruleset - * @param array $params - * @param integer $index - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setMval() - * @uses iCalUtilityFunctions::_setRexrule() - * @return void - */ - function setRrule( $rruleset, $params=FALSE, $index=FALSE ) { - if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = ''; else return FALSE; - iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: SEQUENCE - */ -/** - * creates formatted output for calendar component property sequence - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.9.3 - 2011-05-14 - * @uses calendarComponent::$sequence - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createSequence() { - if( !isset( $this->sequence ) || ( empty( $this->sequence ) && !is_numeric( $this->sequence ))) return FALSE; - if(( !isset($this->sequence['value'] ) || ( empty( $this->sequence['value'] ) && !is_numeric( $this->sequence['value'] ))) && - ( '0' != $this->sequence['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE; - $attributes = $this->_createParams( $this->sequence['params'] ); - return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] ); - } -/** - * set calendar component property sequence - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.8 - 2011-09-19 - * @param int $value - * @param array $params - * @uses calendarComponent::$sequence - * @uses iCalUtilityFunctions::_setParams(); - * @return bool - */ - function setSequence( $value=FALSE, $params=FALSE ) { - if(( empty( $value ) && !is_numeric( $value )) && ( '0' != $value )) - $value = ( isset( $this->sequence['value'] ) && ( -1 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : '0'; - $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: STATUS - */ -/** - * creates formatted output for calendar component property status - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @uses calendarComponent::$status - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createStatus() { - if( empty( $this->status )) return FALSE; - if( empty( $this->status['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE; - $attributes = $this->_createParams( $this->status['params'] ); - return $this->_createElement( 'STATUS', $attributes, $this->status['value'] ); - } -/** - * set calendar component property status - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$status - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setStatus( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: SUMMARY - */ -/** - * creates formatted output for calendar component property summary - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$summary - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @return string - */ - function createSummary() { - if( empty( $this->summary )) return FALSE; - if( empty( $this->summary['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE; - $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' )); - $content = iCalUtilityFunctions::_strrep( $this->summary['value'], $this->format, $this->nl ); - return $this->_createElement( 'SUMMARY', $attributes, $content ); - } -/** - * set calendar component property summary - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param string $params - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setSummary( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TRANSP - */ -/** - * creates formatted output for calendar component property transp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @uses calendarComponent::$transp - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createTransp() { - if( empty( $this->transp )) return FALSE; - if( empty( $this->transp['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE; - $attributes = $this->_createParams( $this->transp['params'] ); - return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] ); - } -/** - * set calendar component property transp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param string $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$transp - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setTransp( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TRIGGER - */ -/** - * creates formatted output for calendar component property trigger - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.16 - 2008-10-21 - * @uses calendarComponent::$trigger - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses calendarComponent::$intAttrDelimiter - * @uses iCalUtilityFunctions::_duration2str() - * @uses calendarComponent::_createParams() - * @return string - */ - function createTrigger() { - if( empty( $this->trigger )) return FALSE; - if( empty( $this->trigger['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE; - $content = $attributes = null; - if( isset( $this->trigger['value']['year'] ) && - isset( $this->trigger['value']['month'] ) && - isset( $this->trigger['value']['day'] )) - $content .= iCalUtilityFunctions::_date2strdate( $this->trigger['value'] ); - else { - if( TRUE !== $this->trigger['value']['relatedStart'] ) - $attributes .= $this->intAttrDelimiter.'RELATED=END'; - if( $this->trigger['value']['before'] ) - $content .= '-'; - $content .= iCalUtilityFunctions::_duration2str( $this->trigger['value'] ); - } - $attributes .= $this->_createParams( $this->trigger['params'] ); - return $this->_createElement( 'TRIGGER', $attributes, $content ); - } -/** - * set calendar component property trigger - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param mixed $year - * @param mixed $month - * @param int $day - * @param int $week - * @param int $hour - * @param int $min - * @param int $sec - * @param bool $relatedStart - * @param bool $before - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$trigger - * @uses iCalUtilityFunctions::_setParams() - * @uses iCalUtilityFunctions::_isArrayTimestampDate() - * @uses iCalUtilityFunctions::_timestamp2date() - * @uses iCalUtilityFunctions::_strdate2date() - * @uses iCalUtilityFunctions::_duration2arr() - * @return bool - */ - function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) { - if( empty( $year ) && ( empty( $month ) || is_array( $month )) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec )) - if( $this->getConfig( 'allowEmpty' )) { - $this->trigger = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $month ) ); - return TRUE; - } - else - return FALSE; - if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp UTC - $params = iCalUtilityFunctions::_setParams( $month ); - $date = iCalUtilityFunctions::_timestamp2date( $year, 7 ); - foreach( $date as $k => $v ) - $$k = $v; - } - elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) { - $params = iCalUtilityFunctions::_setParams( $month ); - if(!(array_key_exists( 'year', $year ) && // exclude date-time - array_key_exists( 'month', $year ) && - array_key_exists( 'day', $year ))) { // when this must be a duration - if( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) - $relatedStart = FALSE; - else - $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE; - $before = ( array_key_exists( 'before', $year ) && ( TRUE !== $year['before'] )) ? FALSE : TRUE; - } - $SSYY = ( array_key_exists( 'year', $year )) ? $year['year'] : null; - $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null; - $day = ( array_key_exists( 'day', $year )) ? $year['day'] : null; - $week = ( array_key_exists( 'week', $year )) ? $year['week'] : null; - $hour = ( array_key_exists( 'hour', $year )) ? $year['hour'] : 0; //null; - $min = ( array_key_exists( 'min', $year )) ? $year['min'] : 0; //null; - $sec = ( array_key_exists( 'sec', $year )) ? $year['sec'] : 0; //null; - $year = $SSYY; - } - elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) { // duration or date in a string - $params = iCalUtilityFunctions::_setParams( $month ); - if( in_array( $year{0}, array( 'P', '+', '-' ))) { // duration - $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) ? FALSE : TRUE; - $before = ( '-' == $year[0] ) ? TRUE : FALSE; - if( 'P' != $year[0] ) - $year = substr( $year, 1 ); - $date = iCalUtilityFunctions::_durationStr2arr( $year); - } - else // date - $date = iCalUtilityFunctions::_strdate2date( $year, 7 ); - unset( $year, $month, $day, $date['unparsedtext'] ); - if( empty( $date )) - $sec = 0; - else - foreach( $date as $k => $v ) - $$k = $v; - } - else // single values in function input parameters - $params = iCalUtilityFunctions::_setParams( $params ); - if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date - $params['VALUE'] = 'DATE-TIME'; - $hour = ( $hour ) ? $hour : 0; - $min = ( $min ) ? $min : 0; - $sec = ( $sec ) ? $sec : 0; - $this->trigger = array( 'params' => $params ); - $this->trigger['value'] = array( 'year' => $year - , 'month' => $month - , 'day' => $day - , 'hour' => $hour - , 'min' => $min - , 'sec' => $sec - , 'tz' => 'Z' ); - return TRUE; - } - elseif(( empty( $year ) && empty( $month )) && // duration - (( !empty( $week ) || ( 0 == $week )) || - ( !empty( $day ) || ( 0 == $day )) || - ( !empty( $hour ) || ( 0 == $hour )) || - ( !empty( $min ) || ( 0 == $min )) || - ( !empty( $sec ) || ( 0 == $sec )))) { - unset( $params['RELATED'] ); // set at output creation (END only) - unset( $params['VALUE'] ); // 'DURATION' default - $this->trigger = array( 'params' => $params ); - $this->trigger['value'] = array(); - if( !empty( $week )) $this->trigger['value']['week'] = $week; - if( !empty( $day )) $this->trigger['value']['day'] = $day; - if( !empty( $hour )) $this->trigger['value']['hour'] = $hour; - if( !empty( $min )) $this->trigger['value']['min'] = $min; - if( !empty( $sec )) $this->trigger['value']['sec'] = $sec; - if( empty( $this->trigger['value'] )) { - $this->trigger['value']['sec'] = 0; - $before = FALSE; - } - else - $this->trigger['value'] = iCalUtilityFunctions::_duration2arr( $this->trigger['value'] ); - $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE; - $before = ( FALSE !== $before ) ? TRUE : FALSE; - $this->trigger['value']['relatedStart'] = $relatedStart; - $this->trigger['value']['before'] = $before; - return TRUE; - } - return FALSE; - } -/*********************************************************************************/ -/** - * Property Name: TZID - */ -/** - * creates formatted output for calendar component property tzid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$tzid - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_strrep() - * @uses calendarComponent::$format - * @uses calendarComponent::$nl - * @return string - */ - function createTzid() { - if( empty( $this->tzid )) return FALSE; - if( empty( $this->tzid['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE; - $attributes = $this->_createParams( $this->tzid['params'] ); - return $this->_createElement( 'TZID', $attributes, iCalUtilityFunctions::_strrep( $this->tzid['value'], $this->format, $this->nl )); - } -/** - * set calendar component property tzid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$tzid - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setTzid( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * .. . - * Property Name: TZNAME - */ -/** - * creates formatted output for calendar component property tzname - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @uses calendarComponent::$tzname - * @uses calendarComponent::_createParams() - * @uses calendarComponent::_createElement() - * @uses iCalUtilityFunctions::_strrep( - * @uses calendarComponent::$format - * @uses calendarComponent::$nl - * @uses calendarComponent::getConfig() - * @return string - */ - function createTzname() { - if( empty( $this->tzname )) return FALSE; - $output = null; - foreach( $this->tzname as $theName ) { - if( !empty( $theName['value'] )) { - $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' )); - $output .= $this->_createElement( 'TZNAME', $attributes, iCalUtilityFunctions::_strrep( $theName['value'], $this->format, $this->nl )); - } - elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' ); - } - return $output; - } -/** - * set calendar component property tzname - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @param integer $index - * @uses calendarComponent::_createParams() - * @uses calendarComponent::$tzname - * @uses iCalUtilityFunctions::_setMval() - * @return bool - */ - function setTzname( $value, $params=FALSE, $index=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index ); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TZOFFSETFROM - */ -/** - * creates formatted output for calendar component property tzoffsetfrom - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @uses calendarComponent::$tzoffsetfrom - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createTzoffsetfrom() { - if( empty( $this->tzoffsetfrom )) return FALSE; - if( empty( $this->tzoffsetfrom['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE; - $attributes = $this->_createParams( $this->tzoffsetfrom['params'] ); - return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] ); - } -/** - * set calendar component property tzoffsetfrom - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$tzoffsetfrom - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setTzoffsetfrom( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TZOFFSETTO - */ -/** - * creates formatted output for calendar component property tzoffsetto - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @uses calendarComponent::$tzoffsetto - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createTzoffsetto() { - if( empty( $this->tzoffsetto )) return FALSE; - if( empty( $this->tzoffsetto['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE; - $attributes = $this->_createParams( $this->tzoffsetto['params'] ); - return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] ); - } -/** - * set calendar component property tzoffsetto - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$tzoffsetto - * @uses iCalUtilityFunctions::_setParams() - * @return bool - */ - function setTzoffsetto( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: TZURL - */ -/** - * creates formatted output for calendar component property tzurl - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @uses calendarComponent::$tzurl - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createTzurl() { - if( empty( $this->tzurl )) return FALSE; - if( empty( $this->tzurl['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE; - $attributes = $this->_createParams( $this->tzurl['params'] ); - return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] ); - } -/** - * set calendar component property tzurl - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @uses calendarComponent::$tzurl - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$tzurl - * @uses iCalUtilityFunctions::_setParams() - * @return boll - */ - function setTzurl( $value, $params=FALSE ) { - if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; - $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: UID - */ -/** - * creates formatted output for calendar component property uid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.19.1 - 2014-02-21 - * @uses calendarComponent::$uid - * @uses calendarComponent::_makeuid(); - * @uses calendarComponent::_createParams() - * @uses calendarComponent::_createElement() - * @return string - */ - function createUid() { - if( empty( $this->uid )) - $this->_makeuid(); - $attributes = $this->_createParams( $this->uid['params'] ); - return $this->_createElement( 'UID', $attributes, $this->uid['value'] ); - } -/** - * create an unique id for this calendar component object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.2.7 - 2007-09-04 - * @uses calendarComponent::$uid - * @uses calendarComponent::getConfig() - * @return void - */ - function _makeUid() { - $date = date('Ymd\THisT'); - $unique = substr(microtime(), 2, 4); - $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890'; - $start = 0; - $end = strlen( $base ) - 1; - $length = 6; - $str = null; - for( $p = 0; $p < $length; $p++ ) - $unique .= $base{mt_rand( $start, $end )}; - $this->uid = array( 'params' => null ); - $this->uid['value'] = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' ); - } -/** - * set calendar component property uid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.19.1 - 2014-02-21 - * @param string $value - * @param array $params - * @uses calendarComponent::$uid - * @uses calendarComponent::_setParams() - * @return bool - */ - function setUid( $value, $params=FALSE ) { - if( empty( $value ) && ( '0' != $value )) return FALSE; // no allowEmpty check here !!!! - $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: URL - */ -/** - * creates formatted output for calendar component property url - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @uses calendarComponent::$url - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @return string - */ - function createUrl() { - if( empty( $this->url )) return FALSE; - if( empty( $this->url['value'] )) - return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE; - $attributes = $this->_createParams( $this->url['params'] ); - return $this->_createElement( 'URL', $attributes, $this->url['value'] ); - } -/** - * set calendar component property url - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.21 - 2013-06-23 - * @param string $value - * @param array $params - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$url - * @uses calendarComponent::_setParams() - * @return bool - */ - function setUrl( $value, $params=FALSE ) { - if( !empty( $value )) { - if( !filter_var( $value, FILTER_VALIDATE_URL ) && ( 'urn' != strtolower( substr( $value, 0, 3 )))) - return FALSE; - } - elseif( $this->getConfig( 'allowEmpty' )) - $value = ''; - else - return FALSE; - $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params )); - return TRUE; - } -/*********************************************************************************/ -/*********************************************************************************/ -/** - * create element format parts - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.0.6 - 2006-06-20 - * @uses iCalBase::_createFormat() - * @uses calendarComponent::$timezonetype - * @return string - */ - function _createFormat() { - parent::_createFormat(); - $objectname = null; - switch( $this->format ) { - case 'xcal': - return ( isset( $this->timezonetype )) ? strtolower( $this->timezonetype ) : strtolower( $this->objName ); - break; - default: - return ( isset( $this->timezonetype )) ? strtoupper( $this->timezonetype ) : strtoupper( $this->objName ); - break; - } - } -/** - * creates formatted output for calendar component property data value type recur - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.25 - 2013-06-30 - * @param array $recurlabel - * @param array $recurdata - * @uses calendarComponent::getConfig() - * @uses calendarComponent::_createElement() - * @uses calendarComponent::_createParams() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses iCalUtilityFunctions::_recurBydaySort() - * @return string - */ - function _format_recur( $recurlabel, $recurdata ) { - $output = null; - foreach( $recurdata as $therule ) { - if( empty( $therule['value'] )) { - if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel ); - continue; - } - $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null; - $content1 = $content2 = null; - foreach( $therule['value'] as $rulelabel => $rulevalue ) { - switch( strtoupper( $rulelabel )) { - case 'FREQ': { - $content1 .= "FREQ=$rulevalue"; - break; - } - case 'UNTIL': { - $parno = ( isset( $rulevalue['hour'] )) ? 7 : 3; - $content2 .= ';UNTIL='.iCalUtilityFunctions::_date2strdate( $rulevalue, $parno ); - break; - } - case 'COUNT': - case 'INTERVAL': - case 'WKST': { - $content2 .= ";$rulelabel=$rulevalue"; - break; - } - case 'BYSECOND': - case 'BYMINUTE': - case 'BYHOUR': - case 'BYMONTHDAY': - case 'BYYEARDAY': - case 'BYWEEKNO': - case 'BYMONTH': - case 'BYSETPOS': { - $content2 .= ";$rulelabel="; - if( is_array( $rulevalue )) { - foreach( $rulevalue as $vix => $valuePart ) { - $content2 .= ( $vix ) ? ',' : null; - $content2 .= $valuePart; - } - } - else - $content2 .= $rulevalue; - break; - } - case 'BYDAY': { - $byday = array( '' ); - $bx = 0; - foreach( $rulevalue as $bix => $bydayPart ) { - if( ! empty( $byday[$bx] ) && ! ctype_digit( substr( $byday[$bx], -1 ))) // new day - $byday[++$bx] = ''; - if( ! is_array( $bydayPart )) // day without order number - $byday[$bx] .= (string) $bydayPart; - else { // day with order number - foreach( $bydayPart as $bix2 => $bydayPart2 ) - $byday[$bx] .= (string) $bydayPart2; - } - } // end foreach( $rulevalue as $bix => $bydayPart ) - if( 1 < count( $byday )) - usort( $byday, array( 'iCalUtilityFunctions', '_recurBydaySort' )); - $content2 .= ';BYDAY='.implode( ',', $byday ); - break; - } - default: { - $content2 .= ";$rulelabel=$rulevalue"; - break; - } - } - } - $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 ); - } - return $output; - } -/** - * check if property not exists within component - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-15 - * @param string $propName - * @uses calendarComponent::$lastmodified - * @uses calendarComponent::$percentcomplete - * @uses calendarComponent::$recurrenceid - * @uses calendarComponent::$relatedto - * @uses calendarComponent::$requeststatus - * @uses calendarComponent::getConfig() - * @uses calendarComponent::{$propname} - * @return bool - */ - function _notExistProp( $propName ) { - if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed - $propName = strtolower( $propName ); - if( 'last-modified' == $propName ) { if( !isset( $this->lastmodified )) return TRUE; } - elseif( 'percent-complete' == $propName ) { if( !isset( $this->percentcomplete )) return TRUE; } - elseif( 'recurrence-id' == $propName ) { if( !isset( $this->recurrenceid )) return TRUE; } - elseif( 'related-to' == $propName ) { if( !isset( $this->relatedto )) return TRUE; } - elseif( 'request-status' == $propName ) { if( !isset( $this->requeststatus )) return TRUE; } - elseif(( 'x-' != substr($propName,0,2)) && !isset( $this->$propName )) return TRUE; - return FALSE; - } -/*********************************************************************************/ -/*********************************************************************************/ -/** - * get general component config variables or info about subcomponents - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param mixed $config - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$allowEmpty - * @uses calendarComponent::$compix - * @uses calendarComponent::$components - * @uses calendarComponent::$objName - * @uses calendarComponent::getProperty() - * @uses calendarComponent::$format - * @uses calendarComponent::$language - * @uses calendarComponent::$nl - * @uses iCalUtilityFunctions::$miscComps - * @uses calendarComponent::$uid - * @uses calendarComponent::_makeuid() - * @uses calendarComponent::dtstamp - * @uses calendarComponent::_makeDtstamp() - * @uses calendarComponent::$summary - * @uses calendarComponent::$description - * @uses calendarComponent::$dtstart - * @uses calendarComponent::$dtend - * @uses calendarComponent::$due - * @uses calendarComponent::$duration - * @uses calendarComponent::$rrule - * @uses calendarComponent::$rdate - * @uses calendarComponent::$exdate - * @uses calendarComponent::$exrule - * @uses calendarComponent::$action - * @uses calendarComponent::$attach - * @uses calendarComponent::$attendee - * @uses calendarComponent::$categories - * @uses calendarComponent::$class - * @uses calendarComponent::$comment - * @uses calendarComponent::$completed - * @uses calendarComponent::$contact - * @uses calendarComponent::$created - * @uses calendarComponent::$freebusy - * @uses calendarComponent::$geo - * @uses calendarComponent::$lastmodified - * @uses calendarComponent::$location - * @uses calendarComponent::$organizer - * @uses calendarComponent::$percentcomplete - * @uses calendarComponent::$priority - * @uses calendarComponent::$recurrenceid - * @uses calendarComponent::$relatedto - * @uses calendarComponent::$repeat - * @uses calendarComponent::$requeststatus - * @uses calendarComponent::$resources - * @uses calendarComponent::$sequence - * @uses calendarComponent::$sequence - * @uses calendarComponent::$status - * @uses calendarComponent::$transp - * @uses calendarComponent::$trigger - * @uses calendarComponent::$tzid - * @uses calendarComponent::$tzname - * @uses calendarComponent::$tzoffsetfrom - * @uses calendarComponent::$tzoffsetto - * @uses calendarComponent::$tzurl - * @uses calendarComponent::$url - * @uses calendarComponent::$xprop - * @uses calendarComponent::$dtzid - * @uses calendarComponent::$unique_id - * @return value - */ - function getConfig( $config = FALSE) { - if( !$config ) { - $return = array(); - $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' ); - $return['FORMAT'] = $this->getConfig( 'FORMAT' ); - if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' ))) - $return['LANGUAGE'] = $lang; - $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' ); - $return['TZTD'] = $this->getConfig( 'TZID' ); - $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' ); - return $return; - } - switch( strtoupper( $config )) { - case 'ALLOWEMPTY': - return $this->allowEmpty; - break; - case 'COMPSINFO': - unset( $this->compix ); - $info = array(); - if( isset( $this->components )) { - foreach( $this->components as $cix => $component ) { - if( empty( $component )) continue; - $info[$cix]['ordno'] = $cix + 1; - $info[$cix]['type'] = $component->objName; - $info[$cix]['uid'] = $component->getProperty( 'uid' ); - $info[$cix]['props'] = $component->getConfig( 'propinfo' ); - $info[$cix]['sub'] = $component->getConfig( 'compsinfo' ); - } - } - return $info; - break; - case 'FORMAT': - return $this->format; - break; - case 'LANGUAGE': - // get language for calendar component as defined in [RFC 1766] - return $this->language; - break; - case 'NL': - case 'NEWLINECHAR': - return $this->nl; - break; - case 'PROPINFO': - $output = array(); - if( ! in_array( $this->objName, iCalUtilityFunctions::$miscComps )) { - if( empty( $this->uid )) $this->_makeuid(); - $output['UID'] = 1; - if( empty( $this->dtstamp )) $this->_makeDtstamp(); - $output['DTSTAMP'] = 1; - } - if( !empty( $this->summary )) $output['SUMMARY'] = 1; - if( !empty( $this->description )) $output['DESCRIPTION'] = count( $this->description ); - if( !empty( $this->dtstart )) $output['DTSTART'] = 1; - if( !empty( $this->dtend )) $output['DTEND'] = 1; - if( !empty( $this->due )) $output['DUE'] = 1; - if( !empty( $this->duration )) $output['DURATION'] = 1; - if( !empty( $this->rrule )) $output['RRULE'] = count( $this->rrule ); - if( !empty( $this->rdate )) $output['RDATE'] = count( $this->rdate ); - if( !empty( $this->exdate )) $output['EXDATE'] = count( $this->exdate ); - if( !empty( $this->exrule )) $output['EXRULE'] = count( $this->exrule ); - if( !empty( $this->action )) $output['ACTION'] = 1; - if( !empty( $this->attach )) $output['ATTACH'] = count( $this->attach ); - if( !empty( $this->attendee )) $output['ATTENDEE'] = count( $this->attendee ); - if( !empty( $this->categories )) $output['CATEGORIES'] = count( $this->categories ); - if( !empty( $this->class )) $output['CLASS'] = 1; - if( !empty( $this->comment )) $output['COMMENT'] = count( $this->comment ); - if( !empty( $this->completed )) $output['COMPLETED'] = 1; - if( !empty( $this->contact )) $output['CONTACT'] = count( $this->contact ); - if( !empty( $this->created )) $output['CREATED'] = 1; - if( !empty( $this->freebusy )) $output['FREEBUSY'] = count( $this->freebusy ); - if( !empty( $this->geo )) $output['GEO'] = 1; - if( !empty( $this->lastmodified )) $output['LAST-MODIFIED'] = 1; - if( !empty( $this->location )) $output['LOCATION'] = 1; - if( !empty( $this->organizer )) $output['ORGANIZER'] = 1; - if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1; - if( !empty( $this->priority )) $output['PRIORITY'] = 1; - if( !empty( $this->recurrenceid )) $output['RECURRENCE-ID'] = 1; - if( !empty( $this->relatedto )) $output['RELATED-TO'] = count( $this->relatedto ); - if( !empty( $this->repeat )) $output['REPEAT'] = 1; - if( !empty( $this->requeststatus )) $output['REQUEST-STATUS'] = count( $this->requeststatus ); - if( !empty( $this->resources )) $output['RESOURCES'] = count( $this->resources ); - if( !empty( $this->sequence )) $output['SEQUENCE'] = 1; - if( !empty( $this->sequence )) $output['SEQUENCE'] = 1; - if( !empty( $this->status )) $output['STATUS'] = 1; - if( !empty( $this->transp )) $output['TRANSP'] = 1; - if( !empty( $this->trigger )) $output['TRIGGER'] = 1; - if( !empty( $this->tzid )) $output['TZID'] = 1; - if( !empty( $this->tzname )) $output['TZNAME'] = count( $this->tzname ); - if( !empty( $this->tzoffsetfrom )) $output['TZOFFSETFROM'] = 1; - if( !empty( $this->tzoffsetto )) $output['TZOFFSETTO'] = 1; - if( !empty( $this->tzurl )) $output['TZURL'] = 1; - if( !empty( $this->url )) $output['URL'] = 1; - if( !empty( $this->xprop )) $output['X-PROP'] = count( $this->xprop ); - return $output; - break; - case 'SETPROPERTYNAMES': - return array_keys( $this->getConfig( 'propinfo' )); - break; - case 'TZID': - return $this->dtzid; - break; - case 'UNIQUE_ID': - if( empty( $this->unique_id )) - $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost'; - return $this->unique_id; - break; - } - } -/** - * general component config setting - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.18 - 2013-09-06 - * @param mixed $config - * @param string $value - * @param bool $softUpdate - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$allowEmpty - * @uses calendarComponent::$format - * @uses calendarComponent::$language - * @uses calendarComponent::$nl - * @uses calendarComponent::$dtzid - * @uses calendarComponent::$unique_id - * @uses calendarComponent::$components - * @uses calendarComponent::copy() - * @return void - */ - function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) { - if( is_array( $config )) { - $config = array_change_key_case( $config, CASE_UPPER ); - if( isset( $config['NEWLINECHAR'] ) || isset( $config['NL'] )) { - $k = ( isset( $config['NEWLINECHAR'] )) ? 'NEWLINECHAR' : 'NL'; - if( FALSE === $this->setConfig( 'NL', $config[$k] )) - return FALSE; - unset( $config[$k] ); - } - foreach( $config as $cKey => $cValue ) { - if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate )) - return FALSE; - } - return TRUE; - } - else - $config = strtoupper( $config ); - $res = FALSE; - switch( $config ) { - case 'ALLOWEMPTY': - $this->allowEmpty = $value; - $subcfg = array( 'ALLOWEMPTY' => $value ); - $res = TRUE; - break; - case 'FORMAT': - $value = trim( strtolower( $value )); - $this->format = $value; - $this->_createFormat(); - $subcfg = array( 'FORMAT' => $value ); - $res = TRUE; - break; - case 'LANGUAGE': - // set language for calendar component as defined in [RFC 1766] - $value = trim( $value ); - if( empty( $this->language ) || !$softUpdate ) - $this->language = $value; - $subcfg = array( 'LANGUAGE' => $value ); - $res = TRUE; - break; - case 'NL': - case 'NEWLINECHAR': - $this->nl = $value; - $this->_createFormat(); - $subcfg = array( 'NL' => $value ); - $res = TRUE; - break; - case 'TZID': - $this->dtzid = $value; - $subcfg = array( 'TZID' => $value ); - $res = TRUE; - break; - case 'UNIQUE_ID': - $value = trim( $value ); - $this->unique_id = $value; - $subcfg = array( 'UNIQUE_ID' => $value ); - $res = TRUE; - break; - default: // any unvalid config key.. . - return TRUE; - } - if( !$res ) return FALSE; - if( isset( $subcfg ) && !empty( $this->components )) { - foreach( $subcfg as $cfgkey => $cfgvalue ) { - foreach( $this->components as $cix => $component ) { - $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate ); - if( !$res ) - break 2; - $this->components[$cix] = $component->copy(); // PHP4 compliant - } - } - } - return $res; - } -/*********************************************************************************/ -/** - * delete component property value - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $propName bool FALSE => X-property - * @param int $propix specific property in case of multiply occurences - * @uses calendarComponent::_notExistProp() - * @uses iCalUtilityFunctions::$mProps2 - * @uses calendarComponent::$propdelix - * @uses calendarComponent::$action - * @uses calendarComponent::deletePropertyM() - * @uses calendarComponent::$attendee - * @uses calendarComponent::$categories - * @uses calendarComponent::$class - * @uses calendarComponent::$comment - * @uses calendarComponent::$completed - * @uses calendarComponent::$contact - * @uses calendarComponent::$created - * @uses calendarComponent::$description - * @uses calendarComponent::$dtend - * @uses iCalUtilityFunctions::$miscComps - * @uses calendarComponent::$objName - * @uses calendarComponent::$dtstamp - * @uses calendarComponent::$dtstart - * @uses calendarComponent::$due - * @uses calendarComponent::$duration - * @uses calendarComponent::$exdate - * @uses calendarComponent::$exrule - * @uses calendarComponent::$freebusy - * @uses calendarComponent::$geo - * @uses calendarComponent::$lastmodified - * @uses calendarComponent::$location - * @uses calendarComponent::$organizer - * @uses calendarComponent::$percentcomplete - * @uses calendarComponent::$priority - * @uses calendarComponent::$rdate - * @uses calendarComponent::$recurrenceid - * @uses calendarComponent::$relatedto - * @uses calendarComponent::$repeat - * @uses calendarComponent::$requeststatus - * @uses calendarComponent::$resources - * @uses calendarComponent::$rrule - * @uses calendarComponent::$sequence - * @uses calendarComponent::$status - * @uses calendarComponent::$summary - * @uses calendarComponent::$transp - * @uses calendarComponent::$trigger - * @uses calendarComponent::$tzid - * @uses calendarComponent::$tzname - * @uses calendarComponent::$tzoffsetfrom - * @uses calendarComponent::$tzoffsetto - * @uses calendarComponent::$tzurl - * @uses calendarComponent::$uid - * @uses calendarComponent::$url - * @uses calendarComponent::$xprop - * @return bool, if successfull delete TRUE - */ - function deleteProperty( $propName=FALSE, $propix=FALSE ) { - if( $this->_notExistProp( $propName )) return FALSE; - $propName = strtoupper( $propName ); - if( in_array( $propName, iCalUtilityFunctions::$mProps2 )) { - if( !$propix ) - $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1; - $this->propdelix[$propName] = --$propix; - } - $return = FALSE; - switch( $propName ) { - case 'ACTION': - if( !empty( $this->action )) { - $this->action = ''; - $return = TRUE; - } - break; - case 'ATTACH': - return $this->deletePropertyM( $this->attach, $this->propdelix[$propName] ); - break; - case 'ATTENDEE': - return $this->deletePropertyM( $this->attendee, $this->propdelix[$propName] ); - break; - case 'CATEGORIES': - return $this->deletePropertyM( $this->categories, $this->propdelix[$propName] ); - break; - case 'CLASS': - if( !empty( $this->class )) { - $this->class = ''; - $return = TRUE; - } - break; - case 'COMMENT': - return $this->deletePropertyM( $this->comment, $this->propdelix[$propName] ); - break; - case 'COMPLETED': - if( !empty( $this->completed )) { - $this->completed = ''; - $return = TRUE; - } - break; - case 'CONTACT': - return $this->deletePropertyM( $this->contact, $this->propdelix[$propName] ); - break; - case 'CREATED': - if( !empty( $this->created )) { - $this->created = ''; - $return = TRUE; - } - break; - case 'DESCRIPTION': - return $this->deletePropertyM( $this->description, $this->propdelix[$propName] ); - break; - case 'DTEND': - if( !empty( $this->dtend )) { - $this->dtend = ''; - $return = TRUE; - } - break; - case 'DTSTAMP': - if( in_array( $this->objName, iCalUtilityFunctions::$miscComps )) - return FALSE; - if( !empty( $this->dtstamp )) { - $this->dtstamp = ''; - $return = TRUE; - } - break; - case 'DTSTART': - if( !empty( $this->dtstart )) { - $this->dtstart = ''; - $return = TRUE; - } - break; - case 'DUE': - if( !empty( $this->due )) { - $this->due = ''; - $return = TRUE; - } - break; - case 'DURATION': - if( !empty( $this->duration )) { - $this->duration = ''; - $return = TRUE; - } - break; - case 'EXDATE': - return $this->deletePropertyM( $this->exdate, $this->propdelix[$propName] ); - break; - case 'EXRULE': - return $this->deletePropertyM( $this->exrule, $this->propdelix[$propName] ); - break; - case 'FREEBUSY': - return $this->deletePropertyM( $this->freebusy, $this->propdelix[$propName] ); - break; - case 'GEO': - if( !empty( $this->geo )) { - $this->geo = ''; - $return = TRUE; - } - break; - case 'LAST-MODIFIED': - if( !empty( $this->lastmodified )) { - $this->lastmodified = ''; - $return = TRUE; - } - break; - case 'LOCATION': - if( !empty( $this->location )) { - $this->location = ''; - $return = TRUE; - } - break; - case 'ORGANIZER': - if( !empty( $this->organizer )) { - $this->organizer = ''; - $return = TRUE; - } - break; - case 'PERCENT-COMPLETE': - if( !empty( $this->percentcomplete )) { - $this->percentcomplete = ''; - $return = TRUE; - } - break; - case 'PRIORITY': - if( !empty( $this->priority )) { - $this->priority = ''; - $return = TRUE; - } - break; - case 'RDATE': - return $this->deletePropertyM( $this->rdate, $this->propdelix[$propName] ); - break; - case 'RECURRENCE-ID': - if( !empty( $this->recurrenceid )) { - $this->recurrenceid = ''; - $return = TRUE; - } - break; - case 'RELATED-TO': - return $this->deletePropertyM( $this->relatedto, $this->propdelix[$propName] ); - break; - case 'REPEAT': - if( !empty( $this->repeat )) { - $this->repeat = ''; - $return = TRUE; - } - break; - case 'REQUEST-STATUS': - return $this->deletePropertyM( $this->requeststatus, $this->propdelix[$propName] ); - break; - case 'RESOURCES': - return $this->deletePropertyM( $this->resources, $this->propdelix[$propName] ); - break; - case 'RRULE': - return $this->deletePropertyM( $this->rrule, $this->propdelix[$propName] ); - break; - case 'SEQUENCE': - if( !empty( $this->sequence )) { - $this->sequence = ''; - $return = TRUE; - } - break; - case 'STATUS': - if( !empty( $this->status )) { - $this->status = ''; - $return = TRUE; - } - break; - case 'SUMMARY': - if( !empty( $this->summary )) { - $this->summary = ''; - $return = TRUE; - } - break; - case 'TRANSP': - if( !empty( $this->transp )) { - $this->transp = ''; - $return = TRUE; - } - break; - case 'TRIGGER': - if( !empty( $this->trigger )) { - $this->trigger = ''; - $return = TRUE; - } - break; - case 'TZID': - if( !empty( $this->tzid )) { - $this->tzid = ''; - $return = TRUE; - } - break; - case 'TZNAME': - return $this->deletePropertyM( $this->tzname, $this->propdelix[$propName] ); - break; - case 'TZOFFSETFROM': - if( !empty( $this->tzoffsetfrom )) { - $this->tzoffsetfrom = ''; - $return = TRUE; - } - break; - case 'TZOFFSETTO': - if( !empty( $this->tzoffsetto )) { - $this->tzoffsetto = ''; - $return = TRUE; - } - break; - case 'TZURL': - if( !empty( $this->tzurl )) { - $this->tzurl = ''; - $return = TRUE; - } - break; - case 'UID': - if( in_array( $this->objName, iCalUtilityFunctions::$miscComps )) - return FALSE; - if( ! empty( $this->uid )) { - $this->uid = ''; - $return = TRUE; - } - break; - case 'URL': - if( !empty( $this->url )) { - $this->url = ''; - $return = TRUE; - } - break; - default: - $reduced = ''; - if( $propName != 'X-PROP' ) { - if( !isset( $this->xprop[$propName] )) return FALSE; - foreach( $this->xprop as $k => $a ) { - if(( $k != $propName ) && !empty( $a )) - $reduced[$k] = $a; - } - } - else { - if( count( $this->xprop ) <= $propix ) { unset( $this->propdelix[$propName] ); return FALSE; } - $xpropno = 0; - foreach( $this->xprop as $xpropkey => $xpropvalue ) { - if( $propix != $xpropno ) - $reduced[$xpropkey] = $xpropvalue; - $xpropno++; - } - } - $this->xprop = $reduced; - if( empty( $this->xprop )) { - unset( $this->propdelix[$propName] ); - return FALSE; - } - return TRUE; - } - return $return; - } -/*********************************************************************************/ -/** - * delete component property value, fixing components with multiple occurencies - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param array $multiprop component (multi-)property - * @param int $propix removal counter - * @return bool TRUE - */ - function deletePropertyM( & $multiprop, & $propix ) { - if( isset( $multiprop[$propix] )) - unset( $multiprop[$propix] ); - if( empty( $multiprop )) { - $multiprop = ''; - unset( $propix ); - return FALSE; - } - else - return TRUE; - } -/** - * get component property value/params - * - * if property has multiply values, consequtive function calls are needed - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.13 - 2015-03-29 - * @param string $propName - * @param int $propix specific property in case of multiply occurences - * @param bool $inclParam - * @param bool $specform - * @uses calendarComponent::getProperty() - * @uses iCalUtilityFunctions::_geo2str2() - * @uses iCalUtilityFunctions::$geoLatFmt - * @uses iCalUtilityFunctions::$geoLongFmt - * @uses calendarComponent::_notExistProp() - * @uses iCalUtilityFunctions::$mProps2 - * @uses calendarComponent::$propix - * @uses calendarComponent::$action - * @uses calendarComponent::$attendee - * @uses calendarComponent::$categories - * @uses calendarComponent::$class - * @uses calendarComponent::$comment - * @uses calendarComponent::$completed - * @uses calendarComponent::$contact - * @uses calendarComponent::$created - * @uses calendarComponent::$description - * @uses calendarComponent::$dtend - * @uses iCalUtilityFunctions::$miscComps - * @uses calendarComponent::$dtstamp - * @uses calendarComponent::_makeDtstamp() - * @uses calendarComponent::$dtstart - * @uses calendarComponent::$due - * @uses calendarComponent::$duration - * @uses iCalUtilityFunctions::_duration2date() - * @uses calendarComponent::$exdate - * @uses calendarComponent::$exrule - * @uses calendarComponent::$freebusy - * @uses calendarComponent::$geo - * @uses calendarComponent::$lastmodified - * @uses calendarComponent::$location - * @uses calendarComponent::$organizer - * @uses calendarComponent::$percentcomplete - * @uses calendarComponent::$priority - * @uses calendarComponent::$rdate - * @uses calendarComponent::$recurrenceid - * @uses calendarComponent::$relatedto - * @uses calendarComponent::$repeat - * @uses calendarComponent::$requeststatus - * @uses calendarComponent::$resources - * @uses calendarComponent::$rrule - * @uses calendarComponent::$sequence - * @uses calendarComponent::$status - * @uses calendarComponent::$summary - * @uses calendarComponent::$transp - * @uses calendarComponent::$trigger - * @uses calendarComponent::$tzid - * @uses calendarComponent::$tzname - * @uses calendarComponent::$tzoffsetfrom - * @uses calendarComponent::$tzoffsetto - * @uses calendarComponent::$tzurl - * @uses calendarComponent::$uid - * @uses calendarComponent::$objName - * @uses calendarComponent::_makeuid() - * @uses calendarComponent::$url - * @uses calendarComponent::$xprop - * @return mixed - */ - function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) { - if( 'GEOLOCATION' == strtoupper( $propName )) { - $content = ( FALSE === ( $loc = $this->getProperty( 'LOCATION' ))) ? '' : $loc.' '; - if( FALSE === ( $geo = $this->getProperty( 'GEO' ))) - return FALSE; - return $content. - iCalUtilityFunctions::_geo2str2( $geo['latitude'], iCalUtilityFunctions::$geoLatFmt ). - iCalUtilityFunctions::_geo2str2( $geo['longitude'], iCalUtilityFunctions::$geoLongFmt ).'/'; - } - if( $this->_notExistProp( $propName )) return FALSE; - $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; - if( in_array( $propName, iCalUtilityFunctions::$mProps2 )) { - if( empty( $propix )) - $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1; - $this->propix[$propName] = --$propix; - } - switch( $propName ) { - case 'ACTION': - if( isset( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value']; - break; - case 'ATTACH': - $ak = ( is_array( $this->attach )) ? array_keys( $this->attach ) : array(); - while( is_array( $this->attach ) && !isset( $this->attach[$propix] ) && ( 0 < count( $this->attach )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->attach[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value']; - break; - case 'ATTENDEE': - $ak = ( is_array( $this->attendee )) ? array_keys( $this->attendee ) : array(); - while( is_array( $this->attendee ) && !isset( $this->attendee[$propix] ) && ( 0 < count( $this->attendee )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->attendee[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value']; - break; - case 'CATEGORIES': - $ak = ( is_array( $this->categories )) ? array_keys( $this->categories ) : array(); - while( is_array( $this->categories ) && !isset( $this->categories[$propix] ) && ( 0 < count( $this->categories )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->categories[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value']; - break; - case 'CLASS': - if( isset( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value']; - break; - case 'COMMENT': - $ak = ( is_array( $this->comment )) ? array_keys( $this->comment ) : array(); - while( is_array( $this->comment ) && !isset( $this->comment[$propix] ) && ( 0 < count( $this->comment )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->comment[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value']; - break; - case 'COMPLETED': - if( isset( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value']; - break; - case 'CONTACT': - $ak = ( is_array( $this->contact )) ? array_keys( $this->contact ) : array(); - while( is_array( $this->contact ) && !isset( $this->contact[$propix] ) && ( 0 < count( $this->contact )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->contact[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value']; - break; - case 'CREATED': - if( isset( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value']; - break; - case 'DESCRIPTION': - $ak = ( is_array( $this->description )) ? array_keys( $this->description ) : array(); - while( is_array( $this->description ) && !isset( $this->description[$propix] ) && ( 0 < count( $this->description )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->description[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value']; - break; - case 'DTEND': - if( isset( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value']; - break; - case 'DTSTAMP': - if( in_array( $this->objName, iCalUtilityFunctions::$miscComps )) - return; - if( !isset( $this->dtstamp['value'] )) - $this->_makeDtstamp(); - return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value']; - break; - case 'DTSTART': - if( isset( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value']; - break; - case 'DUE': - if( isset( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value']; - break; - case 'DURATION': - if( ! isset( $this->duration['value'] )) return FALSE; - $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value']; - $params = ( $specform && $inclParam && isset( $this->dtstart['params']['TZID'] )) ? array_merge((array) $this->duration['params'], $this->dtstart['params'] ) : $this->duration['params']; - return ( $inclParam ) ? array( 'value' => $value, 'params' => $params ) : $value; - break; - case 'EXDATE': - $ak = ( is_array( $this->exdate )) ? array_keys( $this->exdate ) : array(); - while( is_array( $this->exdate ) && !isset( $this->exdate[$propix] ) && ( 0 < count( $this->exdate )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->exdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value']; - break; - case 'EXRULE': - $ak = ( is_array( $this->exrule )) ? array_keys( $this->exrule ) : array(); - while( is_array( $this->exrule ) && !isset( $this->exrule[$propix] ) && ( 0 < count( $this->exrule )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->exrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value']; - break; - case 'FREEBUSY': - $ak = ( is_array( $this->freebusy )) ? array_keys( $this->freebusy ) : array(); - while( is_array( $this->freebusy ) && !isset( $this->freebusy[$propix] ) && ( 0 < count( $this->freebusy )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->freebusy[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value']; - break; - case 'GEO': - if( isset( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value']; - break; - case 'LAST-MODIFIED': - if( isset( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value']; - break; - case 'LOCATION': - if( isset( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value']; - break; - case 'ORGANIZER': - if( isset( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value']; - break; - case 'PERCENT-COMPLETE': - if( isset( $this->percentcomplete['value'] )) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value']; - break; - case 'PRIORITY': - if( isset( $this->priority['value'] )) return ( $inclParam ) ? $this->priority : $this->priority['value']; - break; - case 'RDATE': - $ak = ( is_array( $this->rdate )) ? array_keys( $this->rdate ) : array(); - while( is_array( $this->rdate ) && !isset( $this->rdate[$propix] ) && ( 0 < count( $this->rdate )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->rdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value']; - break; - case 'RECURRENCE-ID': - if( isset( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value']; - break; - case 'RELATED-TO': - $ak = ( is_array( $this->relatedto )) ? array_keys( $this->relatedto ) : array(); - while( is_array( $this->relatedto ) && !isset( $this->relatedto[$propix] ) && ( 0 < count( $this->relatedto )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->relatedto[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value']; - break; - case 'REPEAT': - if( isset( $this->repeat['value'] )) return ( $inclParam ) ? $this->repeat : $this->repeat['value']; - break; - case 'REQUEST-STATUS': - $ak = ( is_array( $this->requeststatus )) ? array_keys( $this->requeststatus ) : array(); - while( is_array( $this->requeststatus ) && !isset( $this->requeststatus[$propix] ) && ( 0 < count( $this->requeststatus )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->requeststatus[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value']; - break; - case 'RESOURCES': - $ak = ( is_array( $this->resources )) ? array_keys( $this->resources ) : array(); - while( is_array( $this->resources ) && !isset( $this->resources[$propix] ) && ( 0 < count( $this->resources )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->resources[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value']; - break; - case 'RRULE': - $ak = ( is_array( $this->rrule )) ? array_keys( $this->rrule ) : array(); - while( is_array( $this->rrule ) && !isset( $this->rrule[$propix] ) && ( 0 < count( $this->rrule )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->rrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value']; - break; - case 'SEQUENCE': - if( isset( $this->sequence['value'] )) return ( $inclParam ) ? $this->sequence : $this->sequence['value']; - break; - case 'STATUS': - if( isset( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value']; - break; - case 'SUMMARY': - if( isset( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value']; - break; - case 'TRANSP': - if( isset( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value']; - break; - case 'TRIGGER': - if( isset( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value']; - break; - case 'TZID': - if( isset( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value']; - break; - case 'TZNAME': - $ak = ( is_array( $this->tzname )) ? array_keys( $this->tzname ) : array(); - while( is_array( $this->tzname ) && !isset( $this->tzname[$propix] ) && ( 0 < count( $this->tzname )) && ( $propix < end( $ak ))) - $propix++; - $this->propix[$propName] = $propix; - if( !isset( $this->tzname[$propix] )) { unset( $this->propix[$propName] ); return FALSE; } - return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value']; - break; - case 'TZOFFSETFROM': - if( isset( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value']; - break; - case 'TZOFFSETTO': - if( isset( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value']; - break; - case 'TZURL': - if( isset( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value']; - break; - case 'UID': - if( in_array( $this->objName, iCalUtilityFunctions::$miscComps )) - return FALSE; - if( empty( $this->uid )) - $this->_makeuid(); - return ( $inclParam ) ? $this->uid : $this->uid['value']; - break; - case 'URL': - if( isset( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value']; - break; - default: - if( $propName != 'X-PROP' ) { - if( !isset( $this->xprop[$propName] )) return FALSE; - return ( $inclParam ) ? array( $propName, $this->xprop[$propName] ) - : array( $propName, $this->xprop[$propName]['value'] ); - } - else { - if( empty( $this->xprop )) return FALSE; - $xpropno = 0; - foreach( $this->xprop as $xpropkey => $xpropvalue ) { - if( $propix == $xpropno ) - return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] ) - : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); - else - $xpropno++; - } - return FALSE; // not found ?? - } - } - return FALSE; - } -/** - * returns calendar property unique values for 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO' or 'RESOURCES' and for each, number of occurrence - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param string $propName - * @param array $output incremented result array - * @uses iCalUtilityFunctions::$mProps1 - * @uses calendarComponent::getProperty() - * return void - */ - function _getProperties( $propName, & $output ) { - if( empty( $output )) - $output = array(); - if( !in_array( strtoupper( $propName ), iCalUtilityFunctions::$mProps1 )) - return $output; - while( FALSE !== ( $content = $this->getProperty( $propName ))) { - if( empty( $content )) - continue; - if( is_array( $content )) { - foreach( $content as $part ) { - if( FALSE !== strpos( $part, ',' )) { - $part = explode( ',', $part ); - foreach( $part as $thePart ) { - $thePart = trim( $thePart ); - if( !empty( $thePart )) { - if( !isset( $output[$thePart] )) - $output[$thePart] = 1; - else - $output[$thePart] += 1; - } - } - } - else { - $part = trim( $part ); - if( !isset( $output[$part] )) - $output[$part] = 1; - else - $output[$part] += 1; - } - } - } // end if( is_array( $content )) - elseif( FALSE !== strpos( $content, ',' )) { - $content = explode( ',', $content ); - foreach( $content as $thePart ) { - $thePart = trim( $thePart ); - if( !empty( $thePart )) { - if( !isset( $output[$thePart] )) - $output[$thePart] = 1; - else - $output[$thePart] += 1; - } - } - } // end elseif( FALSE !== strpos( $content, ',' )) - else { - $content = trim( $content ); - if( !empty( $content )) { - if( !isset( $output[$content] )) - $output[$content] = 1; - else - $output[$content] += 1; - } - } - } - ksort( $output ); - } -/** - * general component property setting - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-05 - * @param mixed $args variable number of function arguments, - * first argument is ALWAYS component name, - * second ALWAYS component value! - * @uses calendarComponent::getProperty() - * @uses calendarComponent::_notExistProp() - * @uses calendarComponent::getConfig() - * @uses calendarComponent::setAction() - * @uses calendarComponent::setAttendee() - * @uses calendarComponent::setCategories() - * @uses calendarComponent::setClass() - * @uses calendarComponent::setComment() - * @uses calendarComponent::setCompleted() - * @uses calendarComponent::setContact() - * @uses calendarComponent::setCreated() - * @uses calendarComponent::setDescription() - * @uses calendarComponent::setDtend() - * @uses calendarComponent::setDtstamp() - * @uses calendarComponent::setDtstart() - * @uses calendarComponent::setDue() - * @uses calendarComponent::setDuration() - * @uses calendarComponent::setExdate() - * @uses calendarComponent::setExrule() - * @uses calendarComponent::setFreebusy() - * @uses calendarComponent::setGeo() - * @uses calendarComponent::setLastmodified() - * @uses calendarComponent::setLocation() - * @uses calendarComponent::setOrganizer() - * @uses calendarComponent::setPercentcomplete() - * @uses calendarComponent::setPriority() - * @uses calendarComponent::setRdate() - * @uses calendarComponent::setRecurrenceid() - * @uses calendarComponent::setRelatedto() - * @uses calendarComponent::setRepeat() - * @uses calendarComponent::setRequeststatus() - * @uses calendarComponent::setResources() - * @uses calendarComponent::setRrule() - * @uses calendarComponent::setSequence() - * @uses calendarComponent::setStatus() - * @uses calendarComponent::setSummary() - * @uses calendarComponent::setTransp() - * @uses calendarComponent::setTrigger() - * @uses calendarComponent::setTzid() - * @uses calendarComponent::setTzname() - * @uses calendarComponent::setTzoffsetfrom() - * @uses calendarComponent::setTzoffsetto() - * @uses calendarComponent::setTzurl() - * @uses calendarComponent::setUid() - * @uses calendarComponent::$objName - * @uses calendarComponent::setUrl() - * @uses calendarComponent::setXprop() - * @return void - */ - function setProperty() { - $numargs = func_num_args(); - if( 1 > $numargs ) return FALSE; - $arglist = func_get_args(); - if( $this->_notExistProp( $arglist[0] )) return FALSE; - if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] ))) - return FALSE; - $arglist[0] = strtoupper( $arglist[0] ); - for( $argix=$numargs; $argix < 12; $argix++ ) { - if( !isset( $arglist[$argix] )) - $arglist[$argix] = null; - } - switch( $arglist[0] ) { - case 'ACTION': - return $this->setAction( $arglist[1], $arglist[2] ); - case 'ATTACH': - return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] ); - case 'ATTENDEE': - return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] ); - case 'CATEGORIES': - return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] ); - case 'CLASS': - return $this->setClass( $arglist[1], $arglist[2] ); - case 'COMMENT': - return $this->setComment( $arglist[1], $arglist[2], $arglist[3] ); - case 'COMPLETED': - return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); - case 'CONTACT': - return $this->setContact( $arglist[1], $arglist[2], $arglist[3] ); - case 'CREATED': - return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); - case 'DESCRIPTION': - return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] ); - case 'DTEND': - return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); - case 'DTSTAMP': - return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); - case 'DTSTART': - return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); - case 'DUE': - return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); - case 'DURATION': - return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] ); - case 'EXDATE': - return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] ); - case 'EXRULE': - return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] ); - case 'FREEBUSY': - return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] ); - case 'GEO': - return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] ); - case 'LAST-MODIFIED': - return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] ); - case 'LOCATION': - return $this->setLocation( $arglist[1], $arglist[2] ); - case 'ORGANIZER': - return $this->setOrganizer( $arglist[1], $arglist[2] ); - case 'PERCENT-COMPLETE': - return $this->setPercentComplete( $arglist[1], $arglist[2] ); - case 'PRIORITY': - return $this->setPriority( $arglist[1], $arglist[2] ); - case 'RDATE': - return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] ); - case 'RECURRENCE-ID': - return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] ); - case 'RELATED-TO': - return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] ); - case 'REPEAT': - return $this->setRepeat( $arglist[1], $arglist[2] ); - case 'REQUEST-STATUS': - return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] ); - case 'RESOURCES': - return $this->setResources( $arglist[1], $arglist[2], $arglist[3] ); - case 'RRULE': - return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] ); - case 'SEQUENCE': - return $this->setSequence( $arglist[1], $arglist[2] ); - case 'STATUS': - return $this->setStatus( $arglist[1], $arglist[2] ); - case 'SUMMARY': - return $this->setSummary( $arglist[1], $arglist[2] ); - case 'TRANSP': - return $this->setTransp( $arglist[1], $arglist[2] ); - case 'TRIGGER': - return $this->setTrigger( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8], $arglist[9], $arglist[10], $arglist[11] ); - case 'TZID': - return $this->setTzid( $arglist[1], $arglist[2] ); - case 'TZNAME': - return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] ); - case 'TZOFFSETFROM': - return $this->setTzoffsetfrom( $arglist[1], $arglist[2] ); - case 'TZOFFSETTO': - return $this->setTzoffsetto( $arglist[1], $arglist[2] ); - case 'TZURL': - return $this->setTzurl( $arglist[1], $arglist[2] ); - case 'UID': - return $this->setUid( $arglist[1], $arglist[2] ); - case 'URL': - return $this->setUrl( $arglist[1], $arglist[2] ); - default: - return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] ); - } - return FALSE; - } -/*********************************************************************************/ -/** - * parse component unparsed data into properties - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.20.3 - 2015-03-05 - * @param mixed $unparsedtext strict rfc2445 formatted, single property string or array of strings - * @uses calendarComponent::getConfig() - * @uses iCalUtilityFunctions::convEolChar() - * @uses calendarComponent::$unparsed - * @uses calendarComponent::$components - * @uses calendarComponent::copy() - * @uses iCalUtilityFunctions::_splitContent() - * @uses calendarComponent::setProperty() - * @uses iCalUtilityFunctions::_strunrep() - * @uses calendarComponent::parse() - * @return bool FALSE if error occurs during parsing - */ - function parse( $unparsedtext=null ) { - $nl = $this->getConfig( 'nl' ); - if( !empty( $unparsedtext )) { - if( is_array( $unparsedtext )) - $unparsedtext = implode( '\n'.$nl, $unparsedtext ); - $unparsedtext = iCalUtilityFunctions::convEolChar( $unparsedtext, $nl ); - } - elseif( !isset( $this->unparsed )) - $unparsedtext = array(); - else - $unparsedtext = $this->unparsed; - /* skip leading (empty/invalid) lines */ - foreach( $unparsedtext as $lix => $line ) { - if( FALSE !== ( $pos = stripos( $line, 'BEGIN:' ))) { - $unparsedtext[$lix] = substr( $unparsedtext[$lix], $pos ); - break; - } - $tst = trim( $line ); - if(( '\n' == $tst ) || empty( $tst )) - unset( $unparsedtext[$lix] ); - } - $this->unparsed = array(); - $comp = & $this; - $config = $this->getConfig(); - $compsync = $subsync = 0; - foreach ( $unparsedtext as $lix => $line ) { - if( 'END:VALARM' == strtoupper( substr( $line, 0, 10 ))) { - if( 1 != $subsync ) return FALSE; - $this->components[] = $comp->copy(); - $subsync--; - } - elseif( 'END:DAYLIGHT' == strtoupper( substr( $line, 0, 12 ))) { - if( 1 != $subsync ) return FALSE; - $this->components[] = $comp->copy(); - $subsync--; - } - elseif( 'END:STANDARD' == strtoupper( substr( $line, 0, 12 ))) { - if( 1 != $subsync ) return FALSE; - array_unshift( $this->components, $comp->copy()); - $subsync--; - } - elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) { // end: - if( 1 != $compsync ) return FALSE; - if( 0 < $subsync ) - $this->components[] = $comp->copy(); - $compsync--; - break; /* skip trailing empty lines */ - } - elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) { - $comp = new valarm( $config); - $subsync++; - } - elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) { - $comp = new vtimezone( 'standard', $config ); - $subsync++; - } - elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) { - $comp = new vtimezone( 'daylight', $config ); - $subsync++; - } - elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) // begin: - $compsync++; - else - $comp->unparsed[] = $line; - } - if( 0 < $subsync ) - $this->components[] = $comp->copy(); - unset( $config ); - /* concatenate property values spread over several lines */ - $lastix = -1; - $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed' - , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart' - , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo' - , 'last-modified', 'location', 'organizer', 'percent-complete' - , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat' - , 'request-status', 'resources', 'rrule', 'sequence', 'status' - , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom' - , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' ); - $proprows = array(); - for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines - $line = rtrim( $this->unparsed[$i], $nl ); - while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} )) - $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl ); - $proprows[] = $line; - } - /* parse each property 'line' */ - foreach( $proprows as $line ) { - if( '\n' == substr( $line, -2 )) - $line = substr( $line, 0, -2 ); - /* get propname */ - $propname = null; - $cix = 0; - while( isset( $line[$cix] )) { - if( in_array( $line[$cix], array( ':', ';' ))) - break; - else - $propname .= $line[$cix]; - $cix++; - } - if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) { - $propname2 = $propname; - $propname = 'X-'; - } - if( !in_array( strtolower( $propname ), $propnames )) // skip non standard property names - continue; - /* rest of the line is opt.params and value */ - $line = substr( $line, $cix ); - /* separate attributes from value */ - iCalUtilityFunctions::_splitContent( $line, $propAttr ); - /* call setProperty( $propname.. . */ - switch( strtoupper( $propname )) { - case 'ATTENDEE': - foreach( $propAttr as $pix => $attr ) { - if( !in_array( strtoupper( $pix ), array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' ))) - continue; - $attr2 = explode( ',', $attr ); - if( 1 < count( $attr2 )) - $propAttr[$pix] = $attr2; - } - $this->setProperty( $propname, $line, $propAttr ); - break; - case 'CATEGORIES': - case 'RESOURCES': - if( FALSE !== strpos( $line, ',' )) { - $content = array( 0 => '' ); - $cix = $lix = 0; - while( FALSE !== substr( $line, $lix, 1 )) { - if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { - $cix++; - $content[$cix] = ''; - } - else - $content[$cix] .= $line[$lix]; - $lix++; - } - if( 1 < count( $content )) { - $content = array_values( $content ); - foreach( $content as $cix => $contentPart ) - $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart ); - $this->setProperty( $propname, $content, $propAttr ); - break; - } - else - $line = reset( $content ); - } - case 'COMMENT': - case 'CONTACT': - case 'DESCRIPTION': - case 'LOCATION': - case 'SUMMARY': - if( empty( $line )) - $propAttr = null; - $this->setProperty( $propname, iCalUtilityFunctions::_strunrep( $line ), $propAttr ); - break; - case 'REQUEST-STATUS': - $values = explode( ';', $line, 3 ); - $values[1] = ( !isset( $values[1] )) ? null : iCalUtilityFunctions::_strunrep( $values[1] ); - $values[2] = ( !isset( $values[2] )) ? null : iCalUtilityFunctions::_strunrep( $values[2] ); - $this->setProperty( $propname - , $values[0] // statcode - , $values[1] // statdesc - , $values[2] // extdata - , $propAttr ); - break; - case 'FREEBUSY': - $fbtype = ( isset( $propAttr['FBTYPE'] )) ? $propAttr['FBTYPE'] : ''; // force setting default, if missing - unset( $propAttr['FBTYPE'] ); - $values = explode( ',', $line ); - foreach( $values as $vix => $value ) { - $value2 = explode( '/', $value ); - if( 1 < count( $value2 )) - $values[$vix] = $value2; - } - $this->setProperty( $propname, $fbtype, $values, $propAttr ); - break; - case 'GEO': - $value = explode( ';', $line, 2 ); - if( 2 > count( $value )) - $value[1] = null; - $this->setProperty( $propname, $value[0], $value[1], $propAttr ); - break; - case 'EXDATE': - $values = ( !empty( $line )) ? explode( ',', $line ) : null; - $this->setProperty( $propname, $values, $propAttr ); - break; - case 'RDATE': - if( empty( $line )) { - $this->setProperty( $propname, $line, $propAttr ); - break; - } - $values = explode( ',', $line ); - foreach( $values as $vix => $value ) { - $value2 = explode( '/', $value ); - if( 1 < count( $value2 )) - $values[$vix] = $value2; - } - $this->setProperty( $propname, $values, $propAttr ); - break; - case 'EXRULE': - case 'RRULE': - $values = explode( ';', $line ); - $recur = array(); - foreach( $values as $value2 ) { - if( empty( $value2 )) - continue; // ;-char in ending position ??? - $value3 = explode( '=', $value2, 2 ); - $rulelabel = strtoupper( $value3[0] ); - switch( $rulelabel ) { - case 'BYDAY': { - $value4 = explode( ',', $value3[1] ); - if( 1 < count( $value4 )) { - foreach( $value4 as $v5ix => $value5 ) { - $value6 = array(); - $dayno = $dayname = null; - $value5 = trim( (string) $value5 ); - if(( ctype_alpha( substr( $value5, -1 ))) && - ( ctype_alpha( substr( $value5, -2, 1 )))) { - $dayname = substr( $value5, -2, 2 ); - if( 2 < strlen( $value5 )) - $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 )); - } - if( $dayno ) - $value6[] = $dayno; - if( $dayname ) - $value6['DAY'] = $dayname; - $value4[$v5ix] = $value6; - } - } - else { - $value4 = array(); - $dayno = $dayname = null; - $value5 = trim( (string) $value3[1] ); - if(( ctype_alpha( substr( $value5, -1 ))) && - ( ctype_alpha( substr( $value5, -2, 1 )))) { - $dayname = substr( $value5, -2, 2 ); - if( 2 < strlen( $value5 )) - $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 )); - } - if( $dayno ) - $value4[] = $dayno; - if( $dayname ) - $value4['DAY'] = $dayname; - } - $recur[$rulelabel] = $value4; - break; - } - default: { - $value4 = explode( ',', $value3[1] ); - if( 1 < count( $value4 )) - $value3[1] = $value4; - $recur[$rulelabel] = $value3[1]; - break; - } - } // end - switch $rulelabel - } // end - foreach( $values.. . - $this->setProperty( $propname, $recur, $propAttr ); - break; - case 'X-': - $propname = ( isset( $propname2 )) ? $propname2 : $propname; - unset( $propname2 ); - case 'ACTION': - case 'CLASSIFICATION': - case 'STATUS': - case 'TRANSP': - case 'UID': - case 'TZID': - case 'RELATED-TO': - case 'TZNAME': - $line = iCalUtilityFunctions::_strunrep( $line ); - default: - $this->setProperty( $propname, $line, $propAttr ); - break; - } // end switch( $propname.. . - } // end - foreach( $proprows.. . - unset( $unparsedtext, $this->unparsed, $proprows ); - if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) { - $ckeys = array_keys( $this->components ); - foreach( $ckeys as $ckey ) { - if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) { - $this->components[$ckey]->parse(); - } - } - } - return TRUE; - } -/*********************************************************************************/ -/*********************************************************************************/ -/** - * return a copy of this component - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.4 - 2012-10-18 - * @return object - */ - function copy() { - return unserialize( serialize( $this )); - } -/*********************************************************************************/ -/*********************************************************************************/ -/** - * delete calendar subcomponent from component container - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $arg1 ordno / component type / component uid - * @param mixed $arg2 ordno if arg1 = component type - * @uses calendarComponent::$components - * @uses calendarComponent::$objName - * @uses calendarComponent::getProperty() - * @return void - */ - function deleteComponent( $arg1, $arg2=FALSE ) { - if( !isset( $this->components )) return FALSE; - $argType = $index = null; - if ( ctype_digit( (string) $arg1 )) { - $argType = 'INDEX'; - $index = (int) $arg1 - 1; - } - elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { - $argType = strtolower( $arg1 ); - $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0; - } - $cix2dC = 0; - foreach ( $this->components as $cix => $component) { - if( empty( $component )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) { - unset( $this->components[$cix] ); - return TRUE; - } - elseif( $argType == $component->objName ) { - if( $index == $cix2dC ) { - unset( $this->components[$cix] ); - return TRUE; - } - $cix2dC++; - } - elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { - unset( $this->components[$cix] ); - return TRUE; - } - } - return FALSE; - } -/** - * get calendar component subcomponent from component container - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $arg1 ordno/component type/ component uid - * @param mixed $arg2 ordno if arg1 = component type - * @uses calendarComponent::$components - * @uses calendarComponent::$compix - * @uses calendarComponent::$objName - * @uses calendarComponent::copy() - * @uses calendarComponent::getProperty() - * @return object - */ - function getComponent ( $arg1=FALSE, $arg2=FALSE ) { - if( !isset( $this->components )) return FALSE; - $index = $argType = null; - if ( !$arg1 ) { - $argType = 'INDEX'; - $index = $this->compix['INDEX'] = - ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1; - } - elseif ( ctype_digit( (string) $arg1 )) { - $argType = 'INDEX'; - $index = (int) $arg1; - unset( $this->compix ); - } - elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { - unset( $this->compix['INDEX'] ); - $argType = strtolower( $arg1 ); - if( !$arg2 ) - $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1; - else - $index = (int) $arg2; - } - $index -= 1; - $ckeys = array_keys( $this->components ); - if( !empty( $index) && ( $index > end( $ckeys ))) - return FALSE; - $cix2gC = 0; - foreach( $this->components as $cix => $component ) { - if( empty( $component )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) - return $component->copy(); - elseif( $argType == $component->objName ) { - if( $index == $cix2gC ) - return $component->copy(); - $cix2gC++; - } - elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' ))) - return $component->copy(); - } - /* not found.. . */ - unset( $this->compix ); - return false; - } -/** - * add calendar component as subcomponent to container for subcomponents - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 1.x.x - 2007-04-24 - * @param object $component calendar component - * @uses calendarComponent::setComponent( $component ) - * @return void - */ - function addSubComponent ( $component ) { - $this->setComponent( $component ); - } -/** - * create new calendar component subcomponent, already included within component - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.33 - 2011-01-03 - * @param string $compType subcomponent type - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$components - * @uses calendarComponent::calendarComponent() - * @return object (reference) - */ - function & newComponent( $compType ) { - $config = $this->getConfig(); - $keys = array_keys( $this->components ); - $ix = end( $keys) + 1; - switch( strtoupper( $compType )) { - case 'ALARM': - case 'VALARM': - $this->components[$ix] = new valarm( $config ); - break; - case 'STANDARD': - array_unshift( $this->components, new vtimezone( 'STANDARD', $config )); - $ix = 0; - break; - case 'DAYLIGHT': - $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config ); - break; - default: - return FALSE; - } - return $this->components[$ix]; - } -/** - * add calendar component as subcomponent to container for subcomponents - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param object $component calendar component - * @param mixed $arg1 ordno/component type/ component uid - * @param mixed $arg2 ordno if arg1 = component type - * @uses calendarComponent::$components - * @uses calendarComponent::setConfig() - * @uses calendarComponent::getConfig() - * @uses calendarComponent::$objName - * @uses iCalUtilityFunctions::$miscComps - * @uses calendarComponent::getProperty() - * @uses calendarComponent::copy() - * @uses iCalUtilityFunctions::$mComps - * @return bool - */ - function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) { - if( !isset( $this->components )) return FALSE; - $component->setConfig( $this->getConfig(), FALSE, TRUE ); - if( ! in_array( $component->objName, iCalUtilityFunctions::$miscComps )) { - /* make sure dtstamp and uid is set */ - $dummy = $component->getProperty( 'dtstamp' ); - $dummy = $component->getProperty( 'uid' ); - } - if( !$arg1 ) { // plain insert, last in chain - $this->components[] = $component->copy(); - return TRUE; - } - $argType = $index = null; - if ( ctype_digit( (string) $arg1 )) { // index insert/replace - $argType = 'INDEX'; - $index = (int) $arg1 - 1; - } - elseif( in_array( strtolower( $arg1 ), iCalUtilityFunctions::$mComps )) { - $argType = strtolower( $arg1 ); - $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0; - } - // else if arg1 is set, arg1 must be an UID - $cix2sC = 0; - foreach ( $this->components as $cix => $component2 ) { - if( empty( $component2 )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace - $this->components[$cix] = $component->copy(); - return TRUE; - } - elseif( $argType == $component2->objName ) { // component Type index insert/replace - if( $index == $cix2sC ) { - $this->components[$cix] = $component->copy(); - return TRUE; - } - $cix2sC++; - } - elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace - $this->components[$cix] = $component->copy(); - return TRUE; - } - } - /* arg1=index and not found.. . insert at index .. .*/ - if( 'INDEX' == $argType ) { - $this->components[$index] = $component->copy(); - ksort( $this->components, SORT_NUMERIC ); - } - else /* not found.. . insert last in chain anyway .. .*/ - $this->components[] = $component->copy(); - return TRUE; - } -/** - * creates formatted output for subcomponents - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-10 - * @param array $xcaldecl - * @uses calendarComponent::$objName - * @uses calendarComponent::$components - * @uses calendarComponent::getProperty() - * @uses iCalUtilityFunctions::$fmt - * @uses calendarComponent::copy() - * @uses calendarComponent::setConfig() - * @uses calendarComponent::getConfig() - * @uses calendarComponent::createComponent() - * @uses calendarComponent::$xcaldecl() - * @return string - */ - function createSubComponent() { - $output = null; - if( 'vtimezone' == $this->objName ) { // sort subComponents, first standard, then daylight, in dtstart order - $stdarr = $dlarr = array(); - foreach( $this->components as $component ) { - if( empty( $component )) - continue; - $dt = $component->getProperty( 'dtstart' ); - $key = (int) sprintf( iCalUtilityFunctions::$fmt['dateKey'], (int) $dt['year'], (int) $dt['month'], (int) $dt['day'], (int) $dt['hour'], (int) $dt['min'], (int) $dt['sec'] ); - if( 'standard' == $component->objName ) { - while( isset( $stdarr[$key] )) - $key += 1; - $stdarr[$key] = $component->copy(); - } - elseif( 'daylight' == $component->objName ) { - while( isset( $dlarr[$key] )) - $key += 1; - $dlarr[$key] = $component->copy(); - } - } // end foreach( $this->components as $component ) - $this->components = array(); - ksort( $stdarr, SORT_NUMERIC ); - foreach( $stdarr as $std ) - $this->components[] = $std->copy(); - unset( $stdarr ); - ksort( $dlarr, SORT_NUMERIC ); - foreach( $dlarr as $dl ) - $this->components[] = $dl->copy(); - unset( $dlarr ); - } // end if( 'vtimezone' == $this->objName ) - foreach( $this->components as $component ) { - $component->setConfig( $this->getConfig(), FALSE, TRUE ); - $output .= $component->createComponent( $this->xcaldecl ); - } - return $output; - } -} diff --git a/php/ical/lib/iCal.XML.inc.php b/php/ical/lib/iCal.XML.inc.php deleted file mode 100644 index 2d638bb..0000000 --- a/php/ical/lib/iCal.XML.inc.php +++ /dev/null @@ -1,897 +0,0 @@ - - * @since 2.18.1 - 2013-08-18 - * @param object $calendar iCalcreator vcalendar instance reference - * @uses ICALCREATOR_VERSION - * @uses vcalendar::getProperty() - * @uses _addXMLchild() - * @uses vcalendar::getConfig() - * @uses vcalendar::getComponent() - * @uses calendarComponent::$objName - * @uses calendarComponent::getProperty() - * @return string - */ -function iCal2XML( $calendar ) { - /** fix an SimpleXMLElement instance and create root element */ - $xmlstr = ''; - $xmlstr .= ''; - $xmlstr .= ''; - $xml = new SimpleXMLElement( $xmlstr ); - $vcalendar = $xml->addChild( 'vcalendar' ); - /** fix calendar properties */ - $properties = $vcalendar->addChild( 'properties' ); - $calProps = array( 'version', 'prodid', 'calscale', 'method' ); - foreach( $calProps as $calProp ) { - if( FALSE !== ( $content = $calendar->getProperty( $calProp ))) - _addXMLchild( $properties, $calProp, 'text', $content ); - } - while( FALSE !== ( $content = $calendar->getProperty( FALSE, FALSE, TRUE ))) - _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); - $langCal = $calendar->getConfig( 'language' ); - /** prepare to fix components with properties */ - $components = $vcalendar->addChild( 'components' ); - /** fix component properties */ - while( FALSE !== ( $component = $calendar->getComponent())) { - $compName = $component->objName; - $child = $components->addChild( $compName ); - $properties = $child->addChild( 'properties' ); - $langComp = $component->getConfig( 'language' ); - $props = $component->getConfig( 'setPropertyNames' ); - foreach( $props as $prop ) { - switch( strtolower( $prop )) { - case 'attach': // may occur multiple times, below - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'attendee': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); - } - break; - case 'exdate': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time'; - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'freebusy': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if( is_array( $content ) && isset( $content['value']['fbtype'] )) { - $content['params']['FBTYPE'] = $content['value']['fbtype']; - unset( $content['value']['fbtype'] ); - } - _addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] ); - } - break; - case 'request-status': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if( !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] ); - } - break; - case 'rdate': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - $type = 'date-time'; - if( isset( $content['params']['VALUE'] )) { - if( 'DATE' == $content['params']['VALUE'] ) - $type = 'date'; - elseif( 'PERIOD' == $content['params']['VALUE'] ) - $type = 'period'; - } - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'categories': - case 'comment': - case 'contact': - case 'description': - case 'related-to': - case 'resources': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); - } - break; - case 'x-prop': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); - break; - case 'created': // single occurence below, if set - case 'completed': - case 'dtstamp': - case 'last-modified': - $utcDate = TRUE; - case 'dtstart': - case 'dtend': - case 'due': - case 'recurrence-id': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time'; - unset( $content['params']['VALUE'] ); - if(( isset( $content['params']['TZID'] ) && empty( $content['params']['TZID'] )) || @is_null( $content['params']['TZID'] )) - unset( $content['params']['TZID'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - unset( $utcDate ); - break; - case 'duration': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); - break; - case 'exrule': - case 'rrule': - while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); - break; - case 'class': - case 'location': - case 'status': - case 'summary': - case 'transp': - case 'tzid': - case 'uid': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if((( 'location' == $prop ) || ( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); - } - break; - case 'geo': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] ); - break; - case 'organizer': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) { - if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); - } - break; - case 'percent-complete': - case 'priority': - case 'sequence': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); - break; - case 'tzurl': - case 'url': - if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] ); - break; - } // end switch( $prop ) - } // end foreach( $props as $prop ) - /** fix subComponent properties, if any */ - while( FALSE !== ( $subcomp = $component->getComponent())) { - $subCompName = $subcomp->objName; - $child2 = $child->addChild( $subCompName ); - $properties = $child2->addChild( 'properties' ); - $langComp = $subcomp->getConfig( 'language' ); - $subCompProps = $subcomp->getConfig( 'setPropertyNames' ); - foreach( $subCompProps as $prop ) { - switch( strtolower( $prop )) { - case 'attach': // may occur multiple times, below - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri'; - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'attendee': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] ); - } - break; - case 'comment': - case 'tzname': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - if( !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); - } - break; - case 'rdate': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - $type = 'date-time'; - if( isset( $content['params']['VALUE'] )) { - if( 'DATE' == $content['params']['VALUE'] ) - $type = 'date'; - elseif( 'PERIOD' == $content['params']['VALUE'] ) - $type = 'period'; - } - unset( $content['params']['VALUE'] ); - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'x-prop': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] ); - break; - case 'action': // single occurence below, if set - case 'description': - case 'summary': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) { - if( $langComp ) - $content['params']['LANGUAGE'] = $langComp; - elseif( $langCal ) - $content['params']['LANGUAGE'] = $langCal; - } - _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] ); - } - break; - case 'dtstart': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - unset( $content['value']['tz'], $content['params']['VALUE'] ); // always local time - _addXMLchild( $properties, $prop, 'date-time', $content['value'], $content['params'] ); - } - break; - case 'duration': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] ); - break; - case 'repeat': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] ); - break; - case 'trigger': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) { - if( isset( $content['value']['year'] ) && - isset( $content['value']['month'] ) && - isset( $content['value']['day'] )) - $type = 'date-time'; - else { - $type = 'duration'; - if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] )) - $content['params']['RELATED'] = 'END'; - } - _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] ); - } - break; - case 'tzoffsetto': - case 'tzoffsetfrom': - if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] ); - break; - case 'rrule': - while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) - _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] ); - break; - } // switch( $prop ) - } // end foreach( $subCompProps as $prop ) - } // end while( FALSE !== ( $subcomp = $component->getComponent())) - } // end while( FALSE !== ( $component = $calendar->getComponent())) - return $xml->asXML(); -} -/** - * Add children to a SimpleXMLelement - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.18.10 - 2013-09-04 - * @param object $parent reference to a SimpleXMLelement node - * @param string $name new element node name - * @param string $type content type, subelement(-s) name - * @param string $content new subelement content - * @param array $params new element 'attributes' - * @uses iCalUtilityFunctions::_duration2str() - * @uses iCalUtilityFunctions::_geo2str2() - * @uses iCalUtilityFunctions::$geoLatFmt - * @uses iCalUtilityFunctions::$geoLongFmt - * @return void - */ -function _addXMLchild( & $parent, $name, $type, $content, $params=array()) { - static $fmtYmd = '%04d-%02d-%02d'; - static $fmtYmdHis = '%04d-%02d-%02dT%02d:%02d:%02d'; - /** create new child node */ - $name = strtolower( $name ); - $child = $parent->addChild( $name ); - if( !empty( $params )) { - $parameters = $child->addChild( 'parameters' ); - foreach( $params as $param => $parVal ) { - if( 'VALUE' == $param ) - continue; - $param = strtolower( $param ); - if( 'x-' == substr( $param, 0, 2 )) { - $p1 = $parameters->addChild( $param ); - $p2 = $p1->addChild( 'unknown', htmlspecialchars( $parVal )); - } - else { - $p1 = $parameters->addChild( $param ); - switch( $param ) { - case 'altrep': - case 'dir': $ptype = 'uri'; break; - case 'delegated-from': - case 'delegated-to': - case 'member': - case 'sent-by': $ptype = 'cal-address'; break; - case 'rsvp': $ptype = 'boolean'; break ; - default: $ptype = 'text'; break; - } - if( is_array( $parVal )) { - foreach( $parVal as $pV ) - $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV )); - } - else - $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal )); - } - } - } // end if( !empty( $params )) - if(( empty( $content ) && ( '0' != $content )) || ( !is_array( $content) && ( '-' != substr( $content, 0, 1 ) && ( 0 > $content )))) - return; - /** store content */ - switch( $type ) { - case 'binary': - $v = $child->addChild( $type, $content ); - break; - case 'boolean': - break; - case 'cal-address': - $v = $child->addChild( $type, $content ); - break; - case 'date': - if( array_key_exists( 'year', $content )) - $content = array( $content ); - foreach( $content as $date ) { - $str = sprintf( $fmtYmd, (int) $date['year'], (int) $date['month'], (int) $date['day'] ); - $v = $child->addChild( $type, $str ); - } - break; - case 'date-time': - if( array_key_exists( 'year', $content )) - $content = array( $content ); - foreach( $content as $dt ) { - if( !isset( $dt['hour'] )) $dt['hour'] = 0; - if( !isset( $dt['min'] )) $dt['min'] = 0; - if( !isset( $dt['sec'] )) $dt['sec'] = 0; - $str = sprintf( $fmtYmdHis, (int) $dt['year'], (int) $dt['month'], (int) $dt['day'], (int) $dt['hour'], (int) $dt['min'], (int) $dt['sec'] ); - if( isset( $dt['tz'] ) && ( 'Z' == $dt['tz'] )) - $str .= 'Z'; - $v = $child->addChild( $type, $str ); - } - break; - case 'duration': - $output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ? '-' : ''; - $v = $child->addChild( $type, $output.iCalUtilityFunctions::_duration2str( $content ) ); - break; - case 'geo': - if( !empty( $content )) { - $v1 = $child->addChild( 'latitude', iCalUtilityFunctions::_geo2str2( $content['latitude'], iCalUtilityFunctions::$geoLatFmt )); - $v1 = $child->addChild( 'longitude', iCalUtilityFunctions::_geo2str2( $content['longitude'], iCalUtilityFunctions::$geoLongFmt )); - } - break; - case 'integer': - $v = $child->addChild( $type, (string) $content ); - break; - case 'period': - if( !is_array( $content )) - break; - foreach( $content as $period ) { - $v1 = $child->addChild( $type ); - $str = sprintf( $fmtYmdHis, (int) $period[0]['year'], (int) $period[0]['month'], (int) $period[0]['day'], (int) $period[0]['hour'], (int) $period[0]['min'], (int) $period[0]['sec'] ); - if( isset( $period[0]['tz'] ) && ( 'Z' == $period[0]['tz'] )) - $str .= 'Z'; - $v2 = $v1->addChild( 'start', $str ); - if( array_key_exists( 'year', $period[1] )) { - $str = sprintf( $fmtYmdHis, (int) $period[1]['year'], (int) $period[1]['month'], (int) $period[1]['day'], (int) $period[1]['hour'], (int) $period[1]['min'], (int) $period[1]['sec'] ); - if( isset($period[1]['tz'] ) && ( 'Z' == $period[1]['tz'] )) - $str .= 'Z'; - $v2 = $v1->addChild( 'end', $str ); - } - else - $v2 = $v1->addChild( 'duration', iCalUtilityFunctions::_duration2str( $period[1] )); - } - break; - case 'recur': - $content = array_change_key_case( $content ); - foreach( $content as $rulelabel => $rulevalue ) { - switch( $rulelabel ) { - case 'until': - if( isset( $rulevalue['hour'] )) - $str = sprintf( $fmtYmdHis, (int) $rulevalue['year'], (int) $rulevalue['month'], (int) $rulevalue['day'], (int) $rulevalue['hour'], (int) $rulevalue['min'], (int) $rulevalue['sec'] ).'Z'; - else - $str = sprintf( $fmtYmd, (int) $rulevalue['year'], (int) $rulevalue['month'], (int) $rulevalue['day'] ); - $v = $child->addChild( $rulelabel, $str ); - break; - case 'bysecond': - case 'byminute': - case 'byhour': - case 'bymonthday': - case 'byyearday': - case 'byweekno': - case 'bymonth': - case 'bysetpos': { - if( is_array( $rulevalue )) { - foreach( $rulevalue as $vix => $valuePart ) - $v = $child->addChild( $rulelabel, $valuePart ); - } - else - $v = $child->addChild( $rulelabel, $rulevalue ); - break; - } - case 'byday': { - if( isset( $rulevalue['DAY'] )) { - $str = ( isset( $rulevalue[0] )) ? $rulevalue[0] : ''; - $str .= $rulevalue['DAY']; - $p = $child->addChild( $rulelabel, $str ); - } - else { - foreach( $rulevalue as $valuePart ) { - if( isset( $valuePart['DAY'] )) { - $str = ( isset( $valuePart[0] )) ? $valuePart[0] : ''; - $str .= $valuePart['DAY']; - $p = $child->addChild( $rulelabel, $str ); - } - else - $p = $child->addChild( $rulelabel, $valuePart ); - } - } - break; - } - case 'freq': - case 'count': - case 'interval': - case 'wkst': - default: - $p = $child->addChild( $rulelabel, $rulevalue ); - break; - } // end switch( $rulelabel ) - } // end foreach( $content as $rulelabel => $rulevalue ) - break; - case 'rstatus': - $v = $child->addChild( 'code', number_format( (float) $content['statcode'], 2, '.', '')); - $v = $child->addChild( 'description', htmlspecialchars( $content['text'] )); - if( isset( $content['extdata'] )) - $v = $child->addChild( 'data', htmlspecialchars( $content['extdata'] )); - break; - case 'text': - if( !is_array( $content )) - $content = array( $content ); - foreach( $content as $part ) - $v = $child->addChild( $type, htmlspecialchars( $part )); - break; - case 'time': - break; - case 'uri': - $v = $child->addChild( $type, $content ); - break; - case 'utc-offset': - if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) { - $str = substr( $content, 0, 1 ); - $content = substr( $content, 1 ); - } - else - $str = '+'; - $str .= substr( $content, 0, 2 ).':'.substr( $content, 2, 2 ); - if( 4 < strlen( $content )) - $str .= ':'.substr( $content, 4 ); - $v = $child->addChild( $type, $str ); - break; - case 'unknown': - default: - if( is_array( $content )) - $content = implode( '', $content ); - $v = $child->addChild( 'unknown', htmlspecialchars( $content )); - break; - } -} -/** - * parse xml file into iCalcreator instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.22 - 2013-06-18 - * @param string $xmlfile - * @param array $iCalcfg iCalcreator config array (opt) - * @return mixediCalcreator instance or FALSE on error - */ -function XMLfile2iCal( $xmlfile, $iCalcfg=array()) { - if( FALSE === ( $xmlstr = file_get_contents( $xmlfile ))) - return FALSE; - return xml2iCal( $xmlstr, $iCalcfg ); -} -/** - * parse xml string into iCalcreator instance, alias of XML2iCal - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.22 - 2013-06-18 - * @param string $xmlstr - * @param array $iCalcfg iCalcreator config array (opt) - * @return mixed iCalcreator instance or FALSE on error - */ -function XMLstr2iCal( $xmlstr, $iCalcfg=array()) { - return XML2iCal( $xmlstr, $iCalcfg); -} -/** - * parse xml string into iCalcreator instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.22 - 2013-06-20 - * @param string $xmlstr - * @param array $iCalcfg iCalcreator config array (opt) - * @uses vcalendar::vcalendar() - * @uses XMLgetComps() - * @return mixed iCalcreator instance or FALSE on error - */ -function XML2iCal( $xmlstr, $iCalcfg=array()) { - $xmlstr = str_replace( array( "\r\n", "\n\r", "\n", "\r" ), '', $xmlstr ); - $xml = XMLgetTagContent1( $xmlstr, 'vcalendar', $endIx ); - $iCal = new vcalendar( $iCalcfg ); - XMLgetComps( $iCal, $xmlstr ); - unset( $xmlstr ); - return $iCal; -} -/** - * parse XML string into iCalcreator components - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param object $iCal iCalcreator vcalendar or component object instance - * @param string $xml - * @uses iCalUtilityFunctions::$allComps - * @uses XMLgetTagContent1() - * @uses XMLgetProps() - * @uses XMLgetTagContent2() - * @uses vcalendar::newComponent() - * @uses iCalUtilityFunctions::$allComps - * @uses XMLgetComps() - * @return object - */ -function XMLgetComps( $iCal, $xml ) { - $sx = 0; - while(( FALSE !== substr( $xml, ( $sx + 11 ), 1 )) && - ( '' != substr( $xml, $sx, 12 )) && ( '' != substr( $xml, $sx, 12 ))) - $sx += 1; - if( FALSE === substr( $xml, ( $sx + 11 ), 1 )) - return FALSE; - if( '' == substr( $xml, $sx, 12 )) { - $xml2 = XMLgetTagContent1( $xml, 'properties', $endIx ); - XMLgetProps( $iCal, $xml2 ); - $xml = substr( $xml, $endIx ); - } - if( '' == substr( $xml, 0, 12 )) - $xml = XMLgetTagContent1( $xml, 'components', $endIx ); - while( ! empty( $xml )) { - $xml2 = XMLgetTagContent2( $xml, $tagName, $endIx ); - if( in_array( strtolower( $tagName ), iCalUtilityFunctions::$allComps ) && - ( FALSE !== ( $subComp = $iCal->newComponent( $tagName )))) - XMLgetComps( $subComp, $xml2 ); - $xml = substr( $xml, $endIx); - } - unset( $xml ); - return $iCal; -} -/** - * parse XML into iCalcreator properties - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param array $iCal iCalcreator calendar/component instance - * @param string $xml - * @uses XMLgetTagContent2() - * @uses vcalendar::setProperty() - * @uses calendarComponent::setproperty() - * @uses XMLgetTagContent1() - * @uses vcalendar::setProperty() - * @return void - */ -function XMLgetProps( $iCal, $xml) { - while( ! empty( $xml )) { - $xml2 = XMLgetTagContent2( $xml, $propName, $endIx ); - $propName = strtoupper( $propName ); - if( empty( $xml2 ) && ( '0' != $xml2 )) { - $iCal->setProperty( $propName ); - $xml = substr( $xml, $endIx); - continue; - } - $params = array(); - if( '' == substr( $xml2, 0, 13 )) - $xml2 = substr( $xml2, 13 ); - elseif( '' == substr( $xml2, 0, 12 )) { - $xml3 = XMLgetTagContent1( $xml2, 'parameters', $endIx2 ); - while( ! empty( $xml3 )) { - $xml4 = XMLgetTagContent2( $xml3, $paramKey, $endIx3 ); - $pType = FALSE; // skip parameter valueType - $paramKey = strtoupper( $paramKey ); - static $mParams = array( 'DELEGATED-FROM', 'DELEGATED-TO', 'MEMBER' ); - if( in_array( $paramKey, $mParams )) { - while( ! empty( $xml4 )) { - if( ! isset( $params[$paramKey] )) - $params[$paramKey] = array( XMLgetTagContent1( $xml4, 'cal-address', $endIx4 )); - else - $params[$paramKey][] = XMLgetTagContent1( $xml4, 'cal-address', $endIx4 ); - $xml4 = substr( $xml4, $endIx4 ); - } - } - else { - if( ! isset( $params[$paramKey] )) - $params[$paramKey] = html_entity_decode( XMLgetTagContent2( $xml4, $pType, $endIx4 )); - else - $params[$paramKey] .= ','.html_entity_decode( XMLgetTagContent2( $xml4, $pType, $endIx4 )); - } - $xml3 = substr( $xml3, $endIx3 ); - } - $xml2 = substr( $xml2, $endIx2 ); - } // if( '' == substr( $xml2, 0, 12 )) - $valueType = FALSE; - $value = ( ! empty( $xml2 ) || ( '0' == $xml2 )) ? XMLgetTagContent2( $xml2, $valueType, $endIx3 ) : ''; - switch( $propName ) { - case 'CATEGORIES': - case 'RESOURCES': - $tValue = array(); - while( ! empty( $xml2 )) { - $tValue[] = html_entity_decode( XMLgetTagContent2( $xml2, $valueType, $endIx4 )); - $xml2 = substr( $xml2, $endIx4 ); - } - $value = $tValue; - break; - case 'EXDATE': // multiple single-date(-times) may exist - case 'RDATE': - if( 'period' != $valueType ) { - if( 'date' == $valueType ) - $params['VALUE'] = 'DATE'; - $t = array(); - while( ! empty( $xml2 ) && ( '' == substr( $xml2, 0, 8 ))) { - $xml3 = XMLgetTagContent1( $xml2, 'period', $endIx4 ); // period - $t = array(); - while( ! empty( $xml3 )) { - $t[] = XMLgetTagContent2( $xml3, $pType, $endIx5 ); // start - end/duration - $xml3 = substr( $xml3, $endIx5 ); - } - $value[] = $t; - $xml2 = substr( $xml2, $endIx4 ); - } - break; - case 'TZOFFSETTO': - case 'TZOFFSETFROM': - $value = str_replace( ':', '', $value ); - break; - case 'GEO': - $tValue = array( 'latitude' => $value ); - $tValue['longitude'] = XMLgetTagContent1( substr( $xml2, $endIx3 ), 'longitude', $endIx3 ); - $value = $tValue; - break; - case 'EXRULE': - case 'RRULE': - $tValue = array( $valueType => $value ); - $xml2 = substr( $xml2, $endIx3 ); - $valueType = FALSE; - while( ! empty( $xml2 )) { - $t = XMLgetTagContent2( $xml2, $valueType, $endIx4 ); - switch( $valueType ) { - case 'freq': - case 'count': - case 'until': - case 'interval': - case 'wkst': - $tValue[$valueType] = $t; - break; - case 'byday': - if( 2 == strlen( $t )) - $tValue[$valueType][] = array( 'DAY' => $t ); - else { - $day = substr( $t, -2 ); - $key = substr( $t, 0, ( strlen( $t ) - 2 )); - $tValue[$valueType][] = array( $key, 'DAY' => $day ); - } - break; - default: - $tValue[$valueType][] = $t; - } - $xml2 = substr( $xml2, $endIx4 ); - } - $value = $tValue; - break; - case 'REQUEST-STATUS': - $tValue = array(); - while( ! empty( $xml2 )) { - $t = html_entity_decode( XMLgetTagContent2( $xml2, $valueType, $endIx4 )); - $tValue[$valueType] = $t; - $xml2 = substr( $xml2, $endIx4 ); - } - if( ! empty( $tValue )) - $value = $tValue; - else - $value = array( 'code' => null, 'description' => null ); - break; - default: - switch( $valueType ) { - case 'binary': $params['VALUE'] = 'BINARY'; break; - case 'date': $params['VALUE'] = 'DATE'; break; - case 'date-time': $params['VALUE'] = 'DATE-TIME'; break; - case 'text': - case 'unknown': $value = html_entity_decode( $value ); break; - } - break; - } // end switch( $propName ) - if( 'FREEBUSY' == $propName ) { - $fbtype = $params['FBTYPE']; - unset( $params['FBTYPE'] ); - $iCal->setProperty( $propName, $fbtype, $value, $params ); - } - elseif( 'GEO' == $propName ) - $iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params ); - elseif( 'REQUEST-STATUS' == $propName ) { - if( !isset( $value['data'] )) - $value['data'] = FALSE; - $iCal->setProperty( $propName, $value['code'], $value['description'], $value['data'], $params ); - } - else { - if( empty( $value ) && ( is_array( $value ) || ( '0' > $value ))) - $value = ''; - $iCal->setProperty( $propName, $value, $params ); - } - $xml = substr( $xml, $endIx); - } // end while( ! empty( $xml )) -} -/** - * fetch a specific XML tag content - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.22 - 2013-06-20 - * @param string $xml - * @param string $tagName - * @param int $endIx - * @return mixed - */ -function XMLgetTagContent1( $xml, $tagName, & $endIx=0 ) { - $strlen = strlen( $tagName ); - $sx1 = 0; - while( FALSE !== substr( $xml, $sx1, 1 )) { - if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 1 ), 1 )) && - ( strtolower( "<$tagName>" ) == strtolower( substr( $xml, $sx1, ( $strlen + 2 ))))) - break; - if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 3 ), 1 )) && - ( strtolower( "<$tagName />" ) == strtolower( substr( $xml, $sx1, ( $strlen + 4 ))))) { // empty tag - $endIx = $strlen + 5; - return ''; - } - if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 2 ), 1 )) && - ( strtolower( "<$tagName/>" ) == strtolower( substr( $xml, $sx1, ( $strlen + 3 ))))) { // empty tag - $endIx = $strlen + 4; - return ''; - } - $sx1 += 1; - } - if( FALSE === substr( $xml, $sx1, 1 )) { - $endIx = ( empty( $sx )) ? 0 : $sx - 1; - return ''; - } - if( FALSE === ( $pos = stripos( $xml, "" ))) { // missing end tag?? - $endIx = strlen( $xml ) + 1; - return ''; - } - $endIx = $pos + $strlen + 3; - return substr( $xml, ( $sx1 + $strlen + 2 ), ( $pos - $sx1 - 2 - $strlen )); -} -/** - * fetch next (unknown) XML tagname AND content - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.22 - 2013-06-20 - * @param string $xml - * @param string $tagName - * @param int $endIx - * @return mixed - */ -function XMLgetTagContent2( $xml, & $tagName, & $endIx ) { - $endIx = strlen( $xml ) + 1; // just in case.. . - $sx1 = 0; - while( FALSE !== substr( $xml, $sx1, 1 )) { - if( '<' == substr( $xml, $sx1, 1 )) { - if(( FALSE !== substr( $xml, ( $sx1 + 3 ), 1 )) && ( ''; - $this->attributeDelimiter = $this->nl; - $this->valueInit = null; - break; - default: - $this->componentStart1 = 'BEGIN:'; - $this->componentStart2 = null; - $this->componentEnd1 = 'END:'; - $this->componentEnd2 = $this->nl; - $this->elementStart1 = null; - $this->elementStart2 = null; - $this->elementEnd1 = null; - $this->elementEnd2 = $this->nl; - $this->intAttrDelimiter = ''; - $this->attributeDelimiter = ';'; - $this->valueInit = ':'; - break; - } - return TRUE; - } -/** - * creates formatted output for calendar component property - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-31 - * @param string $label property name - * @param string $attributes property attributes - * @param string $content property content (optional) - * @uses iCalBase::$format - * @uses iCalBase::$elementStart1 - * @uses iCalBase::$xcaldecl - * @uses iCalBase::$intAttrDelimiter - * @uses iCalBase::$attributeDelimiter - * @uses iCalBase::_createElement() - * @uses iCalBase::$elementStart2 - * @uses iCalBase::$nl - * @uses iCalBase::$valueInit - * @uses iCalUtilityFunctions::_size75() - * @uses iCalBase::$elementEnd1 - * @uses iCalBase::$elementEnd2 - * @access protected - * @return string - */ - protected function _createElement( $label, $attributes=null, $content=FALSE ) { - switch( $this->format ) { - case 'xcal': - $label = strtolower( $label ); - break; - default: - $label = strtoupper( $label ); - break; - } - $output = $this->elementStart1.$label; - $categoriesAttrLang = null; - $attachInlineBinary = FALSE; - $attachfmttype = null; - if (( 'xcal' == $this->format) && ( 'x-' == substr( $label, 0, 2 ))) { - $this->xcaldecl[] = array( 'xmldecl' => 'ELEMENT' - , 'ref' => $label - , 'type2' => '(#PCDATA)' ); - } - if( !empty( $attributes )) { - $attributes = trim( $attributes ); - if ( 'xcal' == $this->format ) { - $attributes2 = explode( $this->intAttrDelimiter, $attributes ); - $attributes = null; - foreach( $attributes2 as $aix => $attribute ) { - $attrKVarr = explode( '=', $attribute ); - if( empty( $attrKVarr[0] )) - continue; - if( !isset( $attrKVarr[1] )) { - $attrValue = $attrKVarr[0]; - $attrKey = $aix; - } - elseif( 2 == count( $attrKVarr)) { - $attrKey = strtolower( $attrKVarr[0] ); - $attrValue = $attrKVarr[1]; - } - else { - $attrKey = strtolower( $attrKVarr[0] ); - unset( $attrKVarr[0] ); - $attrValue = implode( '=', $attrKVarr ); - } - if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) { - $attachInlineBinary = TRUE; - if( 'fmttype' == $attrKey ) - $attachfmttype = $attrKey.'='.$attrValue; - continue; - } - elseif(( 'categories' == $label ) && ( 'language' == $attrKey )) - $categoriesAttrLang = $attrKey.'='.$attrValue; - else { - $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' '; - $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null; - if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) { - $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 )); - $attrValue = str_replace( '"', '', $attrValue ); - } - $attributes .= '"'.htmlspecialchars( $attrValue ).'"'; - } - } - } - else { - $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes ); - } - } - if(( 'xcal' == $this->format) && - ((( 'attach' == $label ) && !$attachInlineBinary ) || ( in_array( $label, array( 'tzurl', 'url' ))))) { - $pos = strrpos( $content, "/" ); - $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content; - $this->xcaldecl[] = array( 'xmldecl' => 'ENTITY' - , 'uri' => $docname - , 'ref' => 'SYSTEM' - , 'external' => $content - , 'type' => 'NDATA' - , 'type2' => 'BINERY' ); - $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' '; - $attributes .= 'uri="'.$docname.'"'; - $content = null; - if( 'attach' == $label ) { - $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes ); - $content = $this->nl.$this->_createElement( 'extref', $attributes, null ); - $attributes = null; - } - } - elseif(( 'xcal' == $this->format) && ( 'attach' == $label ) && $attachInlineBinary ) { - $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute - } - $output .= $attributes; - if( !$content && ( '0' != $content )) { - switch( $this->format ) { - case 'xcal': - $output .= ' /'; - $output .= $this->elementStart2.$this->nl; - return $output; - break; - default: - $output .= $this->elementStart2.$this->valueInit; - return iCalUtilityFunctions::_size75( $output, $this->nl ); - break; - } - } - $output .= $this->elementStart2; - $output .= $this->valueInit.$content; - switch( $this->format ) { - case 'xcal': - return $output.$this->elementEnd1.$label.$this->elementEnd2; - break; - default: - return iCalUtilityFunctions::_size75( $output, $this->nl ); - break; - } - } -/** - * creates formatted output for calendar component property parameters - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-31 - * @param array $params optional - * @param array $ctrKeys optional - * @uses iCalBase::$intAttrDelimiter - * @uses vcalendar::getConfig() - * @uses calendarComponent::getConfig() - * @access protected - * @return string - */ - protected function _createParams( $params=array(), $ctrKeys=array() ) { - if( !is_array( $params ) || empty( $params )) - $params = array(); - $attrLANG = $attr1 = $attr2 = $lang = null; - $CNattrKey = ( in_array( 'CN', $ctrKeys )) ? TRUE : FALSE ; - $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ; - $CNattrExist = $LANGattrExist = FALSE; - $xparams = array(); - $params = array_change_key_case( $params, CASE_UPPER ); - foreach( $params as $paramKey => $paramValue ) { - if(( FALSE !== strpos( $paramValue, ':' )) || - ( FALSE !== strpos( $paramValue, ';' )) || - ( FALSE !== strpos( $paramValue, ',' ))) - $paramValue = '"'.$paramValue.'"'; - if( ctype_digit( (string) $paramKey )) { - $xparams[] = $paramValue; - continue; - } - if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' ))) - $xparams[$paramKey] = $paramValue; - else - $params[$paramKey] = $paramValue; - } - ksort( $xparams, SORT_STRING ); - foreach( $xparams as $paramKey => $paramValue ) { - if( ctype_digit( (string) $paramKey )) - $attr2 .= $this->intAttrDelimiter.$paramValue; - else - $attr2 .= $this->intAttrDelimiter."$paramKey=$paramValue"; - } - if( isset( $params['FMTTYPE'] ) && !in_array( 'FMTTYPE', $ctrKeys )) { - $attr1 .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2; - $attr2 = null; - } - if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING', $ctrKeys )) { - if( !empty( $attr2 )) { - $attr1 .= $attr2; - $attr2 = null; - } - $attr1 .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING']; - } - if( isset( $params['VALUE'] ) && !in_array( 'VALUE', $ctrKeys )) - $attr1 .= $this->intAttrDelimiter.'VALUE='.$params['VALUE']; - if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys )) { - $attr1 .= $this->intAttrDelimiter.'TZID='.$params['TZID']; - } - if( isset( $params['RANGE'] ) && !in_array( 'RANGE', $ctrKeys )) - $attr1 .= $this->intAttrDelimiter.'RANGE='.$params['RANGE']; - if( isset( $params['RELTYPE'] ) && !in_array( 'RELTYPE', $ctrKeys )) - $attr1 .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE']; - if( isset( $params['CN'] ) && $CNattrKey ) { - $attr1 = $this->intAttrDelimiter.'CN='.$params['CN']; - $CNattrExist = TRUE; - } - if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) { - $delim = ( FALSE !== strpos( $params['DIR'], '"' )) ? '' : '"'; - $attr1 .= $this->intAttrDelimiter.'DIR='.$delim.$params['DIR'].$delim; - } - if( isset( $params['SENT-BY'] ) && in_array( 'SENT-BY', $ctrKeys )) - $attr1 .= $this->intAttrDelimiter.'SENT-BY='.$params['SENT-BY']; - if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys )) { - $delim = ( FALSE !== strpos( $params['ALTREP'], '"' )) ? '' : '"'; - $attr1 .= $this->intAttrDelimiter.'ALTREP='.$delim.$params['ALTREP'].$delim; - } - if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) { - $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE']; - $LANGattrExist = TRUE; - } - if( !$LANGattrExist ) { - $lang = $this->getConfig( 'language' ); - if(( $CNattrExist || $LANGattrKey ) && $lang ) - $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang; - } - return $attr1.$attrLANG.$attr2; - } -} diff --git a/php/ical/lib/iCalUtilityFunctions.class.php b/php/ical/lib/iCalUtilityFunctions.class.php deleted file mode 100644 index 3825fb6..0000000 --- a/php/ical/lib/iCalUtilityFunctions.class.php +++ /dev/null @@ -1,2507 +0,0 @@ - - * @since 2.21.11 - 2015-04-03 - */ -class iCalUtilityFunctions { -/** @var string tmp line delimiter, used in convEolChar (parse) */ - private static $baseDelim = null; -/** @var array protocol prefix, used in _splitContent() */ - private static $parValPrefix = array ( 'MStz' => array( 'utc-', 'utc+', 'gmt-', 'gmt+' ) - , 'Proto3' => array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ) - , 'Proto4' => array( 'crid:', 'news:', 'pres:' ) - , 'Proto6' => array( 'mailto:' )); -/** @var string output format for geo latitude and longitude (before rtrim) */ - public static $geoLatFmt = '%09.6f'; - public static $geoLongFmt = '%8.6f'; -/** @var array date/datetime formats */ - public static $fmt = array( 'Ymd' => '%04d%02d%02d', - 'His' => '%02d%02d%02d', - 'dayOfDays' => 'day %d of %d', - 'durDHis' => '%a days, %h hours, %i min, %s sec', - 'Ymd2' => 'Y-m-d', - 'YmdHis2' => 'Y-m-d H:i:s', - 'YmdHis2e' => 'Y-m-d H:i:s e', - 'YmdHis3' => 'Y-m-d-H-i-s', - 'YmdHise' => '%04d-%02d-%02d %02d:%02d:%02d %s', - 'YmdTHisO' => 'Y-m-d\TH:i:s O', - 'dateKey' => '%04d%02d%02d%02d%02d%02d000', - ); -/** @var array component property UID value */ - public static $vComps = array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy' ); - public static $mComps = array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ); - public static $miscComps = array( 'valarm', 'vtimezone', 'standard', 'daylight' ); - public static $tzComps = array( 'vtimezone', 'standard', 'daylight' ); - public static $allComps = array( 'vtimezone', 'standard', 'daylight', 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm' ); -/** @var array component property collections */ - public static $mProps1 = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' ); - public static $mProps2 = array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE', - 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ); - public static $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' ); - public static $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' ); -/** @var object Store the single instance of iCalUtilityFunctions */ - private static $m_pInstance; -/** - * Private constructor to limit object instantiation to within the class - * - * @access private - */ - private function __construct() { - $m_pInstance = FALSE; - } -/** @var array component property UID value */ -/** - * Getter method for creating/returning the single instance of this class - * - * @uses iCalUtilityFunctions::$m_pInstance - */ - public static function getInstance() { - if (!self::$m_pInstance) - self::$m_pInstance = new iCalUtilityFunctions(); - return self::$m_pInstance; - } -/** - * ensures internal date-time/date format (keyed array) for an input date-time/date array (keyed or unkeyed) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.24 - 2013-06-26 - * @param array $datetime - * @param int $parno optional, default FALSE - * @return array - */ - public static function _chkDateArr( $datetime, $parno=FALSE ) { - $output = array(); - if(( !$parno || ( 6 <= $parno )) && isset( $datetime[3] ) && !isset( $datetime[4] )) { // Y-m-d with tz - $temp = $datetime[3]; - $datetime[3] = $datetime[4] = $datetime[5] = 0; - $datetime[6] = $temp; - } - foreach( $datetime as $dateKey => $datePart ) { - switch ( $dateKey ) { - case '0': case 'year': $output['year'] = $datePart; break; - case '1': case 'month': $output['month'] = $datePart; break; - case '2': case 'day': $output['day'] = $datePart; break; - } - if( 3 != $parno ) { - switch ( $dateKey ) { - case '0': - case '1': - case '2': break; - case '3': case 'hour': $output['hour'] = $datePart; break; - case '4': case 'min' : $output['min'] = $datePart; break; - case '5': case 'sec' : $output['sec'] = $datePart; break; - case '6': case 'tz' : $output['tz'] = $datePart; break; - } - } - } - if( 3 != $parno ) { - if( !isset( $output['hour'] )) $output['hour'] = 0; - if( !isset( $output['min'] )) $output['min'] = 0; - if( !isset( $output['sec'] )) $output['sec'] = 0; - if( isset( $output['tz'] ) && - (( '+0000' == $output['tz'] ) || ( '-0000' == $output['tz'] ) || ( '+000000' == $output['tz'] ) || ( '-000000' == $output['tz'] ))) - $output['tz'] = 'Z'; - } - return $output; - } -/** - * check date(-time) and params arrays for an opt. timezone and if it is a DATE-TIME or DATE (updates $parno and params) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.30 - 2012-01-16 - * @param array $theDate date to check - * @param int $parno no of date parts (i.e. year, month.. .) - * @param array $params property parameters - * @uses iCalUtilityFunctions::_strdate2date() - * @uses iCalUtilityFunctions::_isOffset() - * @return void - */ - public static function _chkdatecfg( $theDate, & $parno, & $params ) { - if( isset( $params['TZID'] )) - $parno = 6; - elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )) - $parno = 3; - else { - if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] )) - $parno = 7; - if( is_array( $theDate )) { - if( isset( $theDate['timestamp'] )) - $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null; - else - $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null; - if( !empty( $tzid )) { - $parno = 7; - if( !iCalUtilityFunctions::_isOffset( $tzid )) - $params['TZID'] = $tzid; // save only timezone - } - elseif( !$parno && ( 3 == count( $theDate )) && - ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))) - $parno = 3; - else - $parno = 6; - } - else { // string - $date = trim( $theDate ); - if( 'Z' == substr( $date, -1 )) - $parno = 7; // UTC DATE-TIME - elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) && - ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' )))) - $parno = 3; // DATE - $date = iCalUtilityFunctions::_strdate2date( $date, $parno ); - unset( $date['unparsedtext'] ); - if( !empty( $date['tz'] )) { - $parno = 7; - if( !iCalUtilityFunctions::_isOffset( $date['tz'] )) - $params['TZID'] = $date['tz']; // save only timezone - } - elseif( empty( $parno )) - $parno = 6; - } - if( isset( $params['TZID'] )) - $parno = 6; - } - } -/** - * vcalendar sort callback function - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-17 - * @param array $a - * @param array $b - * @uses calendarComponent::$objName - * @return int - */ - public static function _cmpfcn( $a, $b ) { - if( empty( $a )) return -1; - if( empty( $b )) return 1; - if( 'vtimezone' == $a->objName ) { - if( 'vtimezone' != $b->objName ) return -1; - elseif( $a->srtk[0] <= $b->srtk[0] ) return -1; - else return 1; - } - elseif( 'vtimezone' == $b->objName ) return 1; - $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' ); - for( $k = 0; $k < 4 ; $k++ ) { - if( empty( $a->srtk[$k] )) return -1; - elseif( empty( $b->srtk[$k] )) return 1; - if( is_array( $a->srtk[$k] )) { - if( is_array( $b->srtk[$k] )) { - foreach( $sortkeys as $key ) { - if ( !isset( $a->srtk[$k][$key] )) return -1; - elseif( !isset( $b->srtk[$k][$key] )) return 1; - if ( empty( $a->srtk[$k][$key] )) return -1; - elseif( empty( $b->srtk[$k][$key] )) return 1; - if ( $a->srtk[$k][$key] == $b->srtk[$k][$key]) - continue; - if (( (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] )) - return -1; - elseif(( (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] )) - return 1; - } - } - else return -1; - } - elseif( is_array( $b->srtk[$k] )) return 1; - elseif( $a->srtk[$k] < $b->srtk[$k] ) return -1; - elseif( $a->srtk[$k] > $b->srtk[$k] ) return 1; - } - return 0; - } -/** - * byte oriented line folding fix - * - * remove any line-endings that may include spaces or tabs - * and convert all line endings (iCal default '\r\n'), - * takes care of '\r\n', '\r' and '\n' and mixed '\r\n'+'\r', '\r\n'+'\n' - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.18.16 - 2014-04-04 - * @param string $text - * @param string $nl - * @uses iCalUtilityFunctions::$baseDelim - * @return string - */ - public static function convEolChar( & $text, $nl ) { - /* fix dummy line separator */ - if( empty( iCalUtilityFunctions::$baseDelim )) { - iCalUtilityFunctions::$baseDelim = substr( microtime(), 2, 4 ); - $base = 'aAbB!cCdD"eEfF#gGhHiIjJ%kKlL&mMnN/oOpP(rRsS)tTuU=vVxX?uUvV*wWzZ-1234_5678|90'; - $len = strlen( $base ) - 1; - for( $p = 0; $p < 6; $p++ ) - iCalUtilityFunctions::$baseDelim .= $base{mt_rand( 0, $len )}; - } - /* fix eol chars */ - $text = str_replace( array( "\r\n", "\n\r", "\n", "\r" ), iCalUtilityFunctions::$baseDelim, $text ); - /* fix empty lines */ - $text = str_replace( iCalUtilityFunctions::$baseDelim.iCalUtilityFunctions::$baseDelim, iCalUtilityFunctions::$baseDelim.str_pad( '', 75 ).iCalUtilityFunctions::$baseDelim, $text ); - /* fix line folding */ - $text = str_replace( iCalUtilityFunctions::$baseDelim, $nl, $text ); - $text = str_replace( array( $nl.' ', $nl."\t" ), '', $text ); - /* split in component/property lines */ - $text = explode( $nl, $text ); - return $text; - } -/** - * create a calendar timezone and standard/daylight components - * - * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone: - * - * BEGIN:VTIMEZONE - * TZID:Europe/Stockholm - * BEGIN:STANDARD - * DTSTART:20101031T020000 - * TZOFFSETFROM:+0200 - * TZOFFSETTO:+0100 - * TZNAME:CET - * END:STANDARD - * BEGIN:DAYLIGHT - * DTSTART:20100328T030000 - * TZOFFSETFROM:+0100 - * TZOFFSETTO:+0200 - * TZNAME:CEST - * END:DAYLIGHT - * END:VTIMEZONE - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-04-03 - * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi - * Additional changes jpirkey - * @param object $calendar iCalcreator calendar instance - * @param string $timezone PHP5 (DateTimeZone) valid timezone - * @param array $xProp *[x-propName => x-propValue], optional - * @param int $from unix timestamp - * @param int $to unix timestamp - * @uses vcalendar::getProperty() - * @uses iCalUtilityFunctions::$fmt - * @uses vcalendar::newComponent() - * @uses calendarComponent::setproperty() - * @uses iCalUtilityFunctions::offsetSec2His() - * @return bool - */ - public static function createTimezone( & $calendar, $timezone, $xProp=array(), $from=null, $to=null ) { - if( empty( $timezone )) - return FALSE; - if( !empty( $from ) && !is_int( $from )) - return FALSE; - if( !empty( $to ) && !is_int( $to )) - return FALSE; - try { - $dtz = new DateTimeZone( $timezone ); - $transitions = $dtz->getTransitions(); - $utcTz = new DateTimeZone( 'UTC' ); - } - catch( Exception $e ) { return FALSE; } - if( empty( $from ) || empty( $to )) { - $dates = array_keys( $calendar->getProperty( 'dtstart' )); - if( empty( $dates )) - $dates = array( date( 'Ymd' )); - } - if( ! empty( $from )) { - $dateFrom = new DateTime( "@$from" ); // set lowest date (UTC) - $dateFrom->modify( '-7 month' ); // set $dateFrom to seven month before the lowest date - } - else { - $from = reset( $dates ); // set lowest date to the lowest dtstart date - $dateFrom = new DateTime( $from.'T000000', $dtz ); - $dateFrom->modify( '-7 month' ); // set $dateFrom to seven month before the lowest date - $dateFrom->setTimezone( $utcTz ); // convert local date to UTC - } - $dateFromYmd = $dateFrom->format( iCalUtilityFunctions::$fmt['Ymd2'] ); - if( ! empty( $to )) - $dateTo = new DateTime( "@$to" ); // set end date (UTC) - else { - $to = end( $dates ); // set highest date to the highest dtstart date - $dateTo = new DateTime( $to.'T235959', $dtz ); - $dateTo->modify( '+18 month' ); // set $dateTo to 18 month after the highest date - $dateTo->setTimezone( $utcTz ); // convert local date to UTC - } - $dateToYmd = $dateTo->format( iCalUtilityFunctions::$fmt['Ymd2'] ); - unset( $dtz ); - $transTemp = array(); - $prevOffsetfrom = 0; - $stdIx = $dlghtIx = null; - $prevTrans = FALSE; - foreach( $transitions as $tix => $trans ) { // all transitions in date-time order!! - $date = new DateTime( "@{$trans['ts']}" ); // set transition date (UTC) - $transDateYmd = $date->format( iCalUtilityFunctions::$fmt['Ymd2'] ); - if ( $transDateYmd < $dateFromYmd ) { - $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom - $prevTrans = $trans; // save it in case we don't find any that match - $prevTrans['offsetfrom'] = ( 0 < $tix ) ? $transitions[$tix-1]['offset'] : 0; - continue; - } - if( $transDateYmd > $dateToYmd ) - break; // loop always (?) breaks here - if( !empty( $prevOffsetfrom ) || ( 0 == $prevOffsetfrom )) { - $trans['offsetfrom'] = $prevOffsetfrom; // i.e. set previous offsetto as offsetFrom - $date->modify( $trans['offsetfrom'].'seconds' ); // convert utc date to local date - $d = $date->format( iCalUtilityFunctions::$fmt['YmdHis3'] ); - $d = explode( '-', $d ); // set date to array to ease up dtstart and (opt) rdate setting - $trans['time'] = array( 'year' => (int) $d[0], 'month' => (int) $d[1], 'day' => (int) $d[2], 'hour' => (int) $d[3], 'min' => (int) $d[4], 'sec' => (int) $d[5] ); - } - $prevOffsetfrom = $trans['offset']; - if( TRUE !== $trans['isdst'] ) { // standard timezone - if( !empty( $stdIx ) && isset( $transTemp[$stdIx]['offsetfrom'] ) && // check for any repeating rdate's (in order) - ( $transTemp[$stdIx]['abbr'] == $trans['abbr'] ) && - ( $transTemp[$stdIx]['offsetfrom'] == $trans['offsetfrom'] ) && - ( $transTemp[$stdIx]['offset'] == $trans['offset'] )) { - $transTemp[$stdIx]['rdate'][] = $trans['time']; - continue; - } - $stdIx = $tix; - } // end standard timezone - else { // daylight timezone - if( !empty( $dlghtIx ) && isset( $transTemp[$dlghtIx]['offsetfrom'] ) && // check for any repeating rdate's (in order) - ( $transTemp[$dlghtIx]['abbr'] == $trans['abbr'] ) && - ( $transTemp[$dlghtIx]['offsetfrom'] == $trans['offsetfrom'] ) && - ( $transTemp[$dlghtIx]['offset'] == $trans['offset'] )) { - $transTemp[$dlghtIx]['rdate'][] = $trans['time']; - continue; - } - $dlghtIx = $tix; - } // end daylight timezone - $transTemp[$tix] = $trans; - } // end foreach( $transitions as $tix => $trans ) - $tz = $calendar->newComponent( 'vtimezone' ); - $tz->setproperty( 'tzid', $timezone ); - if( !empty( $xProp )) { - foreach( $xProp as $xPropName => $xPropValue ) - if( 'x-' == strtolower( substr( $xPropName, 0, 2 ))) - $tz->setproperty( $xPropName, $xPropValue ); - } - if( empty( $transTemp )) { // if no match found - if( $prevTrans ) { // then we use the last transition (before startdate) for the tz info - $date = new DateTime( "@{$prevTrans['ts']}" );// set transition date (UTC) - $date->modify( $prevTrans['offsetfrom'].'seconds' ); // convert utc date to local date - $d = $date->format( iCalUtilityFunctions::$fmt['YmdHis3'] ); - $d = explode( '-', $d ); // set date to array to ease up dtstart setting - $prevTrans['time'] = array( 'year' => (int) $d[0], 'month' => (int) $d[1], 'day' => (int) $d[2], 'hour' => (int) $d[3], 'min' => (int) $d[4], 'sec' => (int) $d[5] ); - $transTemp[0] = $prevTrans; - } - else { // or we use the timezone identifier to BUILD the standard tz info (?) - $date = new DateTime( 'now', new DateTimeZone( $timezone )); - $transTemp[0] = array( 'time' => $date->format( iCalUtilityFunctions::$fmt['YmdTHisO'] ), - 'offset' => $date->format( 'Z' ), - 'offsetfrom' => $date->format( 'Z' ), - 'isdst' => FALSE ); - } - } - unset( $transitions, $date, $prevTrans ); - foreach( $transTemp as $tix => $trans ) { // create standard/daylight subcomponents - $type = ( TRUE !== $trans['isdst'] ) ? 'standard' : 'daylight'; - $scomp = $tz->newComponent( $type ); - $scomp->setProperty( 'dtstart', $trans['time'] ); -// $scomp->setProperty( 'x-utc-timestamp', $tix.' : '.$trans['ts'] ); // test ### - if( !empty( $trans['abbr'] )) - $scomp->setProperty( 'tzname', $trans['abbr'] ); - if( isset( $trans['offsetfrom'] )) - $scomp->setProperty( 'tzoffsetfrom', iCalUtilityFunctions::offsetSec2His( $trans['offsetfrom'] )); - $scomp->setProperty( 'tzoffsetto', iCalUtilityFunctions::offsetSec2His( $trans['offset'] )); - if( isset( $trans['rdate'] )) - $scomp->setProperty( 'RDATE', $trans['rdate'] ); - } - return TRUE; - } -/** - * creates formatted output for calendar component property data value type date/date-time - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-10 - * @param array $datetime - * @param int $parno optional, default 6 - * @uses iCalUtilityFunctions::$fmt - * @uses iCalUtilityFunctions::_isOffset() - * @uses iCalUtilityFunctions::_tz2offset() - * @return string - */ - public static function _date2strdate( $datetime, $parno=6 ) { - if( !isset( $datetime['year'] ) && - !isset( $datetime['month'] ) && - !isset( $datetime['day'] ) && - !isset( $datetime['hour'] ) && - !isset( $datetime['min'] ) && - !isset( $datetime['sec'] )) - return; - $output = null; - foreach( $datetime as $dkey => & $dvalue ) - if( 'tz' != $dkey ) $dvalue = (int) $dvalue; - $output = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $datetime['year'], $datetime['month'], $datetime['day'] ); - if( 3 == $parno ) - return $output; - if( !isset( $datetime['hour'] )) $datetime['hour'] = 0; - if( !isset( $datetime['min'] )) $datetime['min'] = 0; - if( !isset( $datetime['sec'] )) $datetime['sec'] = 0; - $output .= 'T'.sprintf( iCalUtilityFunctions::$fmt['His'], $datetime['hour'], $datetime['min'], $datetime['sec'] ); - if( isset( $datetime['tz'] ) && ( '' < trim( $datetime['tz'] ))) { - $datetime['tz'] = trim( $datetime['tz'] ); - if( 'Z' == $datetime['tz'] ) - $parno = 7; - elseif( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) { - $parno = 7; - $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] ); - try { - $d = new DateTime( $output, new DateTimeZone( 'UTC' )); - if( 0 != $offset ) // adjust för offset - $d->modify( "$offset seconds" ); - $output = $d->format( 'Ymd\THis' ); - } - catch( Exception $e ) { - $output = date( 'Ymd\THis', mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] - $offset), $datetime['month'], $datetime['day'], $datetime['year'] )); - } - } - if( 7 == $parno ) - $output .= 'Z'; - } - return $output; - } -/** - * ensures internal duration format for input in array format - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.19.4 - 2014-03-14 - * @param array $duration - * @return array - */ - public static function _duration2arr( $duration ) { - $seconds = 0; - foreach( $duration as $durKey => $durValue ) { - if( empty( $durValue )) continue; - switch ( $durKey ) { - case '0': case 'week': - $seconds += (((int) $durValue ) * 60 * 60 * 24 * 7 ); - break; - case '1': case 'day': - $seconds += (((int) $durValue ) * 60 * 60 * 24 ); - break; - case '2': case 'hour': - $seconds += (((int) $durValue ) * 60 * 60 ); - break; - case '3': case 'min': - $seconds += (((int) $durValue ) * 60 ); - break; - case '4': case 'sec': - $seconds += (int) $durValue; - break; - } - } - $output = array(); - $output['week'] = (int) floor( $seconds / ( 60 * 60 * 24 * 7 )); - if(( 0 < $output['week'] ) && ( 0 == ( $seconds % ( 60 * 60 * 24 * 7 )))) - return $output; - unset( $output['week'] ); - $output['day'] = (int) floor( $seconds / ( 60 * 60 * 24 )); - $seconds = ( $seconds % ( 60 * 60 * 24 )); - $output['hour'] = (int) floor( $seconds / ( 60 * 60 )); - $seconds = ( $seconds % ( 60 * 60 )); - $output['min'] = (int) floor( $seconds / 60 ); - $output['sec'] = ( $seconds % 60 ); - if( empty( $output['day'] )) - unset( $output['day'] ); - if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] )) - unset( $output['hour'], $output['min'], $output['sec'] ); - return $output; - } -/** - * convert startdate+duration to a array format datetime - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param array $startdate - * @param array $duration - * @uses iCalUtilityFunctions::$fmt - * @return array, date format - */ - public static function _duration2date( $startdate, $duration ) { - $dateOnly = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE; - $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0; - $startdate['min'] = ( isset( $startdate['min'] )) ? $startdate['min'] : 0; - $startdate['sec'] = ( isset( $startdate['sec'] )) ? $startdate['sec'] : 0; - $dtend = 0; - if( isset( $duration['week'] )) $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 ); - if( isset( $duration['day'] )) $dtend += ( $duration['day'] * 24 * 60 * 60 ); - if( isset( $duration['hour'] )) $dtend += ( $duration['hour'] * 60 *60 ); - if( isset( $duration['min'] )) $dtend += ( $duration['min'] * 60 ); - if( isset( $duration['sec'] )) $dtend += $duration['sec']; - $date = date( iCalUtilityFunctions::$fmt['YmdHis3'], - mktime((int) $startdate['hour'], (int) $startdate['min'], (int) ( $startdate['sec'] + $dtend ), (int) $startdate['month'], (int) $startdate['day'], (int) $startdate['year'] )); - $d = explode( '-', $date ); - $dtend2 = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] ); - if( isset( $startdate['tz'] )) - $dtend2['tz'] = $startdate['tz']; - if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] ))) - unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] ); - return $dtend2; - } -/** - * ensures internal duration format for an input string (iCal) formatted duration - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-25 - * @param string $duration - * @uses iCalUtilityFunctions::_duration2arr() - * @return array - */ - public static function _durationStr2arr( $duration ) { - $duration = (string) trim( $duration ); - while( 'P' != strtoupper( substr( $duration, 0, 1 ))) { - if( 0 < strlen( $duration )) - $duration = substr( $duration, 1 ); - else - return false; // no leading P !?!? - } - $duration = substr( $duration, 1 ); // skip P - $duration = str_replace ( 't', 'T', $duration ); - $duration = str_replace ( 'T', '', $duration ); - $output = array(); - $val = null; - for( $ix=0; $ix < strlen( $duration ); $ix++ ) { - switch( strtoupper( substr( $duration, $ix, 1 ))) { - case 'W': - $output['week'] = $val; - $val = null; - break; - case 'D': - $output['day'] = $val; - $val = null; - break; - case 'H': - $output['hour'] = $val; - $val = null; - break; - case 'M': - $output['min'] = $val; - $val = null; - break; - case 'S': - $output['sec'] = $val; - $val = null; - break; - default: - if( !ctype_digit( substr( $duration, $ix, 1 ))) - return false; // unknown duration control character !?!? - else - $val .= substr( $duration, $ix, 1 ); - } - } - return iCalUtilityFunctions::_duration2arr( $output ); - } -/** - * creates formatted output for calendar component property data value type duration - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.8 - 2012-10-30 - * @param array $duration, array( week, day, hour, min, sec ) - * @return string - */ - public static function _duration2str( array $duration ) { - if( isset( $duration['week'] ) || - isset( $duration['day'] ) || - isset( $duration['hour'] ) || - isset( $duration['min'] ) || - isset( $duration['sec'] )) - $ok = TRUE; - else - return; - if( isset( $duration['week'] ) && ( 0 < $duration['week'] )) - return 'P'.$duration['week'].'W'; - $output = 'P'; - if( isset($duration['day'] ) && ( 0 < $duration['day'] )) - $output .= $duration['day'].'D'; - if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) || - ( isset( $duration['min']) && ( 0 < $duration['min'] )) || - ( isset( $duration['sec']) && ( 0 < $duration['sec'] ))) { - $output .= 'T'; - $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '0H'; - $output .= ( isset( $duration['min']) && ( 0 < $duration['min'] )) ? $duration['min']. 'M' : '0M'; - $output .= ( isset( $duration['sec']) && ( 0 < $duration['sec'] )) ? $duration['sec']. 'S' : '0S'; - } - if( 'P' == $output ) - $output = 'PT0H0M0S'; - return $output; - } -/** - * removes expkey+expvalue from array and returns hitval (if found) else returns elseval - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.16 - 2008-11-08 - * @param array $array iCal property parameters - * @param string $expkey expected key - * @param string $expval expected value - * @param int $hitVal return value if found - * @param int $elseVal return value if not found - * @param int $preSet return value if already preset - * @return int - */ - public static function _existRem( & $array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) { - if( $preSet ) - return $preSet; - if( !is_array( $array ) || ( 0 == count( $array ))) - return $elseVal; - foreach( $array as $key => $value ) { - if( strtoupper( $expkey ) == strtoupper( $key )) { - if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) { - unset( $array[$key] ); - return $hitVal; - } - } - } - return $elseVal; - } -/** - * check if dates are in scope - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.7 - 2015-03-25 - * @param object $start datetime - * @param object $scopeStart datetime - * @param object $end datetime - * @param object $scopeEnd datetime - * @param string $format - * @return bool - */ - public static function _inScope( $start, $scopeStart, $end, $scopeEnd, $format ) { - return (( $start->format( $format ) >= $scopeStart->format( $format )) && - ( $end->format( $format ) <= $scopeEnd->format( $format ))); -} -/** - * mgnt geo part output - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.10 - 2013-09-02 - * @param float $ll - * @param string $format - * @return string - */ - public static function _geo2str2( $ll, $format ) { - if( 0.0 < $ll ) - $sign = '+'; - else - $sign = ( 0.0 > $ll ) ? '-' : ''; - return rtrim( rtrim( $sign.sprintf( $format, abs( $ll )), '0' ), '.' ); - } -/** - * checks if input contains a (array formatted) date/time - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.24 - 2013-07-02 - * @param array $input - * @uses iCalUtilityFunctions::_strdate2date() - * @return bool - */ - public static function _isArrayDate( $input ) { - if( !is_array( $input ) || isset( $input['week'] ) || isset( $input['timestamp'] ) || ( 3 > count( $input ))) - return FALSE; - if( 7 == count( $input )) - return TRUE; - if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) - return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); - if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] )) - return FALSE; - if(( 0 == $input[0] ) || ( 0 == $input[1] ) || ( 0 == $input[2] )) - return FALSE; - if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] )) - return FALSE; - if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) && - checkdate((int) $input[1], (int) $input[2], (int) $input[0] )) - return TRUE; - $input = iCalUtilityFunctions::_strdate2date( $input[1].'/'.$input[2].'/'.$input[0], 3 ); // m - d - Y - if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] )) - return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] ); - return FALSE; - } -/** - * checks if input array contains a timestamp date - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.16 - 2008-10-18 - * @param array $input - * @return bool - */ - public static function _isArrayTimestampDate( $input ) { - return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ; - } -/** - * controls if input string contains (trailing) UTC/iCal offset - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-21 - * @param string $input - * @return bool - */ - public static function _isOffset( $input ) { - $input = trim( (string) $input ); - if( 'Z' == substr( $input, -1 )) - return TRUE; - elseif(( 5 <= strlen( $input )) && - ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) && - ( '0000' <= substr( $input, -4 )) && ( '9999' >= substr( $input, -4 ))) - return TRUE; - elseif(( 7 <= strlen( $input )) && - ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) && - ( '000000' <= substr( $input, -6 )) && ( '999999' >= substr( $input, -6 ))) - return TRUE; - return FALSE; - } -/** - * (very simple) conversion of a MS timezone to a PHP5 valid (Date-)timezone - * matching (MS) UCT offset and time zone descriptors - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.14.1 - 2012-09-16 - * @param string $timezone to convert - * @uses iCalUtilityFunctions::_tz2offset() - * @return bool - */ - public static function ms2phpTZ( & $timezone ) { - if( empty( $timezone )) - return FALSE; - $search = str_replace( '"', '', $timezone ); - $search = str_replace( array('GMT', 'gmt', 'utc' ), 'UTC', $search ); - if( '(UTC' != substr( $search, 0, 4 )) - return FALSE; - if( FALSE === ( $pos = strpos( $search, ')' ))) - return FALSE; - $pos = strpos( $search, ')' ); - $searchOffset = substr( $search, 4, ( $pos - 4 )); - $searchOffset = iCalUtilityFunctions::_tz2offset( str_replace( ':', '', $searchOffset )); - while( ' ' ==substr( $search, ( $pos + 1 ))) - $pos += 1; - $searchText = trim( str_replace( array( '(', ')', '&', ',', ' ' ), ' ', substr( $search, ( $pos + 1 )) )); - $searchWords = explode( ' ', $searchText ); - $timezone_abbreviations = DateTimeZone::listAbbreviations(); - $hits = array(); - foreach( $timezone_abbreviations as $name => $transitions ) { - foreach( $transitions as $cnt => $transition ) { - if( empty( $transition['offset'] ) || - empty( $transition['timezone_id'] ) || - ( $transition['offset'] != $searchOffset )) - continue; - $cWords = explode( '/', $transition['timezone_id'] ); - $cPrio = $hitCnt = $rank = 0; - foreach( $cWords as $cWord ) { - if( empty( $cWord )) - continue; - $cPrio += 1; - $sPrio = 0; - foreach( $searchWords as $sWord ) { - if( empty( $sWord ) || ( 'time' == strtolower( $sWord ))) - continue; - $sPrio += 1; - if( strtolower( $cWord ) == strtolower( $sWord )) { - $hitCnt += 1; - $rank += ( $cPrio + $sPrio ); - } - else - $rank += 10; - } - } - if( 0 < $hitCnt ) { - $hits[$rank][] = $transition['timezone_id']; - } - } - } - unset( $timezone_abbreviations ); - if( empty( $hits )) - return FALSE; - ksort( $hits ); - foreach( $hits as $rank => $tzs ) { - if( !empty( $tzs )) { - $timezone = reset( $tzs ); - return TRUE; - } - } - return FALSE; - } -/** - * transforms offset in seconds to [-/+]hhmm[ss] - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2011-05-02 - * @param string $seconds - * @return string - */ - public static function offsetSec2His( $seconds ) { - if( '-' == substr( $seconds, 0, 1 )) { - $prefix = '-'; - $seconds = substr( $seconds, 1 ); - } - elseif( '+' == substr( $seconds, 0, 1 )) { - $prefix = '+'; - $seconds = substr( $seconds, 1 ); - } - else - $prefix = '+'; - $output = ''; - $hour = (int) floor( $seconds / 3600 ); - if( 10 > $hour ) - $hour = '0'.$hour; - $seconds = $seconds % 3600; - $min = (int) floor( $seconds / 60 ); - if( 10 > $min ) - $min = '0'.$min; - $output = $hour.$min; - $seconds = $seconds % 60; - if( 0 < $seconds) { - if( 9 < $seconds) - $output .= $seconds; - else - $output .= '0'.$seconds; - } - return $prefix.$output; - } -/** - * updates an array with dates based on a recur pattern - * - * if missing, UNTIL is set 1 year from startdate (emergency break) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-10 - * @param array $result array to update, array([Y-m-d] => bool) - * @param array $recur pattern for recurrency (only value part, params ignored) - * @param mixed $wdate component start date, string / array / (datetime) obj - * @param mixed $fcnStart start date, string / array / (datetime) obj - * @param mixed $fcnEnd end date, string / array / (datetime) obj - * @uses iCalUtilityFunctions::_strDate2arr() - * @uses iCalUtilityFunctions::$fmt - * @uses iCalUtilityFunctions::_stepdate() - * @uses iCalUtilityFunctions::_recurIntervalIx() - * @uses iCalUtilityFunctions::_recurBYcntcheck() - * @return void - * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start OR not at all - */ - public static function _recur2date( & $result, $recur, $wdate, $fcnStart, $fcnEnd=FALSE ) { - if( is_string( $wdate )) - iCalUtilityFunctions::_strDate2arr( $wdate ); - elseif( is_a( $wdate, 'DateTime' )) { - $wdate = $wdate->format( iCalUtilityFunctions::$fmt['YmdHis2'] ); - iCalUtilityFunctions::_strDate2arr( $wdate ); - } - foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v; - $wdateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day'] ); - $wdateHis = sprintf( iCalUtilityFunctions::$fmt['His'], $wdate['hour'], $wdate['min'], $wdate['sec'] ); - $untilHis = $wdateHis; - if( is_string( $fcnStart )) - iCalUtilityFunctions::_strDate2arr( $fcnStart ); - elseif( is_a( $fcnStart, 'DateTime' )) { - $fcnStart = $fcnStart->format( iCalUtilityFunctions::$fmt['YmdHis2'] ); - iCalUtilityFunctions::_strDate2arr( $fcnStart ); - } - foreach( $fcnStart as $k => $v ) if( ctype_digit( $v )) $fcnStart[$k] = (int) $v; - $fcnStartYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $fcnStart['year'], $fcnStart['month'], $fcnStart['day'] ); - if( is_string( $fcnEnd )) - iCalUtilityFunctions::_strDate2arr( $fcnEnd ); - elseif( is_a( $fcnEnd, 'DateTime' )) { - $fcnEnd = $fcnEnd->format( iCalUtilityFunctions::$fmt['YmdHis2'] ); - iCalUtilityFunctions::_strDate2arr( $fcnEnd ); - } - if( !$fcnEnd ) { - $fcnEnd = $fcnStart; - $fcnEnd['year'] += 1; - } - foreach( $fcnEnd as $k => $v ) if( ctype_digit( $v )) $fcnEnd[$k] = (int) $v; - $fcnEndYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $fcnEnd['year'], $fcnEnd['month'], $fcnEnd['day'] ); -// echo "recur _in_ comp start ".implode('-',$wdate)." period start ".implode('-',$fcnStart)." period end ".implode('-',$fcnEnd)."
\n"; -// echo 'recur='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $recur, TRUE ))."
\n"; // test ### - if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] )) - $recur['UNTIL'] = $fcnEnd; // create break - if( isset( $recur['UNTIL'] )) { - foreach( $recur['UNTIL'] as $k => $v ) if( ctype_digit( $v )) $recur['UNTIL'][$k] = (int) $v; - unset( $recur['UNTIL']['tz'] ); - if( $fcnEnd > $recur['UNTIL'] ) { - $fcnEnd = $recur['UNTIL']; // emergency break - $fcnEndYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $fcnEnd['year'], $fcnEnd['month'], $fcnEnd['day'] ); - } - if( isset( $recur['UNTIL']['hour'] )) - $untilHis = sprintf( iCalUtilityFunctions::$fmt['His'], $recur['UNTIL']['hour'], $recur['UNTIL']['min'], $recur['UNTIL']['sec'] ); - else - $untilHis = sprintf( iCalUtilityFunctions::$fmt['His'], 23, 59, 59 ); -// echo 'recurUNTIL='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $recur['UNTIL'], TRUE )).", untilHis={$untilHis}
\n"; // test ### - } -// echo 'fcnEnd:'.$fcnEndYMD.$untilHis."
\n";//test - if( $wdateYMD > $fcnEndYMD ) { -// echo 'recur out of date, '.implode('-',$wdate).', end='.implode('-',$fcnEnd)."
\n";//test - return array(); // nothing to do.. . - } - if( !isset( $recur['FREQ'] )) // "MUST be specified.. ." - $recur['FREQ'] = 'DAILY'; // ?? - $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ?? - if( !isset( $recur['INTERVAL'] )) - $recur['INTERVAL'] = 1; - $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence - /* find out how to step up dates and set index for interval count */ - $step = array(); - if( 'YEARLY' == $recur['FREQ'] ) - $step['year'] = 1; - elseif( 'MONTHLY' == $recur['FREQ'] ) - $step['month'] = 1; - elseif( 'WEEKLY' == $recur['FREQ'] ) - $step['day'] = 7; - else - $step['day'] = 1; - if( isset( $step['year'] ) && isset( $recur['BYMONTH'] )) - $step = array( 'month' => 1 ); - if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ?? - $step = array( 'day' => 7 ); - if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] )) - $step = array( 'day' => 1 ); - $intervalarr = array(); - if( 1 < $recur['INTERVAL'] ) { - $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); - $intervalarr = array( $intervalix => 0 ); - } - if( isset( $recur['BYSETPOS'] )) { // save start date + weekno - $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array(); -// echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold
\n"; // test ### - if( is_array( $recur['BYSETPOS'] )) { - foreach( $recur['BYSETPOS'] as $bix => $bval ) - $recur['BYSETPOS'][$bix] = (int) $bval; - } - else - $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] ); - if( 'YEARLY' == $recur['FREQ'] ) { - $wdate['month'] = $wdate['day'] = 1; // start from beginning of year - $wdateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day'] ); - iCalUtilityFunctions::_stepdate( $fcnEnd, $fcnEndYMD, array( 'year' => 1 )); // make sure to count whole last year - } - elseif( 'MONTHLY' == $recur['FREQ'] ) { - $wdate['day'] = 1; // start from beginning of month - $wdateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day'] ); - iCalUtilityFunctions::_stepdate( $fcnEnd, $fcnEndYMD, array( 'month' => 1 )); // make sure to count whole last month - } - else - iCalUtilityFunctions::_stepdate( $fcnEnd, $fcnEndYMD, $step); // make sure to count whole last period -// echo "BYSETPOS endDat =".implode('-',$fcnEnd).' step='.var_export($step,TRUE)."
\n";//test### - $bysetposWold = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year'] )); - $bysetposYold = $wdate['year']; - $bysetposMold = $wdate['month']; - $bysetposDold = $wdate['day']; - } - else - iCalUtilityFunctions::_stepdate( $wdate, $wdateYMD, $step); - $year_old = null; - static $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' ); - /* MAIN LOOP */ -// echo "recur start:$wdateYMD, end:$fcnEndYMD
\n";//test - while( TRUE ) { -// echo "recur while:$wdateYMD, end:$fcnEndYMD
\n";//test - if( $wdateYMD.$wdateHis > $fcnEndYMD.$untilHis ) - break; - if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) - break; - if( $year_old != $wdate['year'] ) { - $year_old = $wdate['year']; - $daycnts = array(); - $yeardays = $weekno = 0; - $yeardaycnt = array(); - foreach( $daynames as $dn ) - $yeardaycnt[$dn] = 0; - for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters - $daycnts[$m] = array(); - $weekdaycnt = array(); - foreach( $daynames as $dn ) - $weekdaycnt[$dn] = 0; - $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); - for( $d = 1; $d <= $mcnt; $d++ ) { - $daycnts[$m][$d] = array(); - if( isset( $recur['BYYEARDAY'] )) { - $yeardays++; - $daycnts[$m][$d]['yearcnt_up'] = $yeardays; - } - if( isset( $recur['BYDAY'] )) { - $day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] )); - $day = $daynames[$day]; - $daycnts[$m][$d]['DAY'] = $day; - $weekdaycnt[$day]++; - $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day]; - $yeardaycnt[$day]++; - $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day]; - } - if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) - $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year'])); - } // end for( $d = 1; $d <= $mcnt; $d++ ) - } // end for( $m = 1; $m <= 12; $m++ ) - $daycnt = 0; - $yeardaycnt = array(); - if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) { - $weekno = null; - for( $d=31; $d > 25; $d-- ) { // get last weekno for year - if( !$weekno ) - $weekno = $daycnts[12][$d]['weekno_up']; - elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) { - $weekno = $daycnts[12][$d]['weekno_up']; - break; - } - } - } - for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters - $weekdaycnt = array(); - foreach( $daynames as $dn ) - $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0; - $monthcnt = 0; - $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] )); - for( $d = $mcnt; $d > 0; $d-- ) { - if( isset( $recur['BYYEARDAY'] )) { - $daycnt -= 1; - $daycnts[$m][$d]['yearcnt_down'] = $daycnt; - } - if( isset( $recur['BYMONTHDAY'] )) { - $monthcnt -= 1; - $daycnts[$m][$d]['monthcnt_down'] = $monthcnt; - } - if( isset( $recur['BYDAY'] )) { - $day = $daycnts[$m][$d]['DAY']; - $weekdaycnt[$day] -= 1; - $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day]; - $yeardaycnt[$day] -= 1; - $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day]; - } - if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) - $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1); - } - } // end for( $m = 12; $m > 0; $m-- ) - } // end if( $year_old != $wdate['year'] ) - /* check interval */ - if( 1 < $recur['INTERVAL'] ) { - /* create interval index */ - $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst ); - /* check interval */ - $currentKey = array_keys( $intervalarr ); - $currentKey = end( $currentKey ); // get last index - if( $currentKey != $intervalix ) - $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 )); - if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) && - ( 0 != $intervalarr[$intervalix] )) { - /* step up date */ -// echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."
\n";//test - iCalUtilityFunctions::_stepdate( $wdate, $wdateYMD, $step); - continue; - } - else // continue within the selected interval - $intervalarr[$intervalix] = 0; -// echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."
\n";//test - } // endif( 1 < $recur['INTERVAL'] ) - $updateOK = TRUE; - if( $updateOK && isset( $recur['BYMONTH'] )) - $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH'] - , $wdate['month'] - ,($wdate['month'] - 13)); - if( $updateOK && isset( $recur['BYWEEKNO'] )) - $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO'] - , $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] - , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] ); - if( $updateOK && isset( $recur['BYYEARDAY'] )) - $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY'] - , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up'] - , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] ); - if( $updateOK && isset( $recur['BYMONTHDAY'] )) - $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY'] - , $wdate['day'] - , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] ); -// echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "
\n";//test### - if( $updateOK && isset( $recur['BYDAY'] )) { - $updateOK = FALSE; - $m = $wdate['month']; - $d = $wdate['day']; - if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no - $daynoexists = $daynosw = $daynamesw = FALSE; - if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] ) - $daynamesw = TRUE; - if( isset( $recur['BYDAY'][0] )) { - $daynoexists = TRUE; - if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] )) - $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] - , $daycnts[$m][$d]['monthdayno_up'] - , $daycnts[$m][$d]['monthdayno_down'] ); - elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) - $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0] - , $daycnts[$m][$d]['yeardayno_up'] - , $daycnts[$m][$d]['yeardayno_down'] ); - } - if(( $daynoexists && $daynosw && $daynamesw ) || - ( !$daynoexists && !$daynosw && $daynamesw )) { - $updateOK = TRUE; -// echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK
\n"; // test ### - } -// echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK
\n"; // test ### - } - else { - foreach( $recur['BYDAY'] as $bydayvalue ) { - $daynoexists = $daynosw = $daynamesw = FALSE; - if( isset( $bydayvalue['DAY'] ) && - ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] )) - $daynamesw = TRUE; - if( isset( $bydayvalue[0] )) { - $daynoexists = TRUE; - if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || - isset( $recur['BYMONTH'] )) - $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] - , $daycnts[$m][$d]['monthdayno_up'] - , $daycnts[$m][$d]['monthdayno_down'] ); - elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' )) - $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0'] - , $daycnts[$m][$d]['yeardayno_up'] - , $daycnts[$m][$d]['yeardayno_down'] ); - } -// echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw
\n"; // test ### - if(( $daynoexists && $daynosw && $daynamesw ) || - ( !$daynoexists && !$daynosw && $daynamesw )) { - $updateOK = TRUE; - break; - } - } - } - } -// echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "
\n"; // test ### - /* check BYSETPOS */ - if( $updateOK ) { - if( isset( $recur['BYSETPOS'] ) && - ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) { - if( isset( $recur['WEEKLY'] )) { - if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] ) - $bysetposw1[] = $wdateYMD; - else - $bysetposw2[] = $wdateYMD; - } - else { - if(( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && - ( $bysetposYold == $wdate['year'] )) || - ( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) && - (( $bysetposYold == $wdate['year'] ) && - ( $bysetposMold == $wdate['month'] ))) || - ( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && - (( $bysetposYold == $wdate['year'] ) && - ( $bysetposMold == $wdate['month']) && - ( $bysetposDold == $wdate['day'] )))) { -// echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."
\n";//test - $bysetposymd1[] = $wdateYMD; - } - else { -// echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."
\n";//test - $bysetposymd2[] = $wdateYMD; - } - } - } - else { - if( checkdate($wdate['month'], $wdate['day'], $wdate['year'] )) { - /* update result array if BYSETPOS is not set */ - $countcnt++; - if( $fcnStartYMD <= $wdateYMD ) { // only output within period - $result[$wdateYMD] = TRUE; -// echo "recur $wdateYMD
\n";//test - } - } -// else echo "recur, no date $wdateYMD
\n";//test - $updateOK = FALSE; - } - } - /* step up date */ - iCalUtilityFunctions::_stepdate( $wdate, $wdateYMD, $step); - /* check if BYSETPOS is set for updating result array */ - if( $updateOK && isset( $recur['BYSETPOS'] )) { - $bysetpos = FALSE; - if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) && - ( $bysetposYold != $wdate['year'] )) { - $bysetpos = TRUE; - $bysetposYold = $wdate['year']; - } - elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] && - (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) { - $bysetpos = TRUE; - $bysetposYold = $wdate['year']; - $bysetposMold = $wdate['month']; - } - elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) { - $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year'])); - if( $bysetposWold != $weekno ) { - $bysetposWold = $weekno; - $bysetpos = TRUE; - } - } - elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) && - (( $bysetposYold != $wdate['year'] ) || - ( $bysetposMold != $wdate['month'] ) || - ( $bysetposDold != $wdate['day'] ))) { - $bysetpos = TRUE; - $bysetposYold = $wdate['year']; - $bysetposMold = $wdate['month']; - $bysetposDold = $wdate['day']; - } - if( $bysetpos ) { - if( isset( $recur['BYWEEKNO'] )) { - $bysetposarr1 = & $bysetposw1; - $bysetposarr2 = & $bysetposw2; - } - else { - $bysetposarr1 = & $bysetposymd1; - $bysetposarr2 = & $bysetposymd2; - } - - foreach( $recur['BYSETPOS'] as $ix ) { - if( 0 > $ix ) // both positive and negative BYSETPOS allowed - $ix = ( count( $bysetposarr1 ) + $ix + 1); - $ix--; - if( isset( $bysetposarr1[$ix] )) { - if( $fcnStartYMD <= $bysetposarr1[$ix] ) { // only output within period -// $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, (int) substr( $bysetposarr1[$ix], 4, 2 ), (int) substr( $bysetposarr1[$ix], 6, 2 ), (int) substr( $bysetposarr1[$ix], 0, 3 ))); // test ### -// echo " testYMD (weekno)=$bysetposarr1[$ix] ($testweekno)"; // test ### - $result[$bysetposarr1[$ix]] = TRUE; - } - $countcnt++; - } - if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] )) - break; - } -// echo "
\n"; // test ### - $bysetposarr1 = $bysetposarr2; - $bysetposarr2 = array(); - } // end if( $bysetpos ) - } // end if( $updateOK && isset( $recur['BYSETPOS'] )) - } // end while( TRUE ) -// echo 'output='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $result, TRUE ))."
\n"; // test ### - } -/** - * _recur2date help function, checking BYDAY (etc) hits - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.12 - 2011-01-03 - * @param array $BYvalue - * @param int $upValue - * @param int $downValue - * @return bool - */ - public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) { - if( is_array( $BYvalue ) && - ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue ))) - return TRUE; - elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue )) - return TRUE; - else - return FALSE; - } -/** - * _recur2date help function, (re-)calculate internal index - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.12 - 2011-01-03 - * @param string $freq - * @param array $date - * @param int $wkst - * @return bool - */ - public static function _recurIntervalIx( $freq, $date, $wkst ) { - /* create interval index */ - switch( $freq ) { - case 'YEARLY': - $intervalix = $date['year']; - break; - case 'MONTHLY': - $intervalix = $date['year'].'-'.$date['month']; - break; - case 'WEEKLY': - $intervalix = (int) date( 'W', mktime( 0, 0, $wkst, (int) $date['month'], (int) $date['day'], (int) $date['year'] )); - break; - case 'DAILY': - default: - $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day']; - break; - } - return $intervalix; - } -/** - * sort recur dates - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.12 - 2011-01-03 - * @param array $bydaya - * @param array $bydayb - * @return int - */ - public static function _recurBydaySort( $bydaya, $bydayb ) { - static $days = array( 'SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6 ); - return ( $days[substr( $bydaya, -2 )] < $days[substr( $bydayb, -2 )] ) ? -1 : 1; - } -/** - * convert input format for exrule and rrule to internal format - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-10 - * @param array $rexrule - * @uses iCalUtilityFunctions::_strDate2arr() - * @uses iCalUtilityFunctions::_isArrayTimestampDate() - * @uses iCalUtilityFunctions::_timestamp2date() - * @uses iCalUtilityFunctions::_chkDateArr() - * @uses iCalUtilityFunctions::_isOffset() - * @uses iCalUtilityFunctions::$fmt - * @uses iCalUtilityFunctions::_strdate2date() - * @return array - */ - public static function _setRexrule( $rexrule ) { - $input = array(); - if( empty( $rexrule )) - return $input; - $rexrule = array_change_key_case( $rexrule, CASE_UPPER ); - foreach( $rexrule as $rexrulelabel => $rexrulevalue ) { - if( 'UNTIL' != $rexrulelabel ) - $input[$rexrulelabel] = $rexrulevalue; - else { - iCalUtilityFunctions::_strDate2arr( $rexrulevalue ); - if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp, always date-time UTC - $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 7, 'UTC' ); - elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) { // date or UTC date-time - $parno = ( isset( $rexrulevalue['hour'] ) || isset( $rexrulevalue[4] )) ? 7 : 3; - $d = iCalUtilityFunctions::_chkDateArr( $rexrulevalue, $parno ); - if(( 3 < $parno ) && isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) { - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); - $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input[$rexrulelabel]['unparsedtext'] ); - } - else - $input[$rexrulelabel] = $d; - } - elseif( 8 <= strlen( trim( $rexrulevalue ))) { // ex. textual date-time 2006-08-03 10:12:18 => UTC - $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $rexrulevalue ); - unset( $input['$rexrulelabel']['unparsedtext'] ); - } - if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] )) - $input[$rexrulelabel]['tz'] = 'Z'; - } - } - /* set recurrence rule specification in rfc2445 order */ - $input2 = array(); - if( isset( $input['FREQ'] )) - $input2['FREQ'] = $input['FREQ']; - if( isset( $input['UNTIL'] )) - $input2['UNTIL'] = $input['UNTIL']; - elseif( isset( $input['COUNT'] )) - $input2['COUNT'] = $input['COUNT']; - if( isset( $input['INTERVAL'] )) - $input2['INTERVAL'] = $input['INTERVAL']; - if( isset( $input['BYSECOND'] )) - $input2['BYSECOND'] = $input['BYSECOND']; - if( isset( $input['BYMINUTE'] )) - $input2['BYMINUTE'] = $input['BYMINUTE']; - if( isset( $input['BYHOUR'] )) - $input2['BYHOUR'] = $input['BYHOUR']; - if( isset( $input['BYDAY'] )) { - if( !is_array( $input['BYDAY'] )) // ensure upper case.. . - $input2['BYDAY'] = strtoupper( $input['BYDAY'] ); - else { - foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) { - if( 'DAY' == strtoupper( $BYDAYx )) - $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv ); - elseif( !is_array( $BYDAYv )) { - $input2['BYDAY'][$BYDAYx] = $BYDAYv; - } - else { - foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) { - if( 'DAY' == strtoupper( $BYDAYx2 )) - $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 ); - else - $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2; - } - } - } - } - } - if( isset( $input['BYMONTHDAY'] )) - $input2['BYMONTHDAY'] = $input['BYMONTHDAY']; - if( isset( $input['BYYEARDAY'] )) - $input2['BYYEARDAY'] = $input['BYYEARDAY']; - if( isset( $input['BYWEEKNO'] )) - $input2['BYWEEKNO'] = $input['BYWEEKNO']; - if( isset( $input['BYMONTH'] )) - $input2['BYMONTH'] = $input['BYMONTH']; - if( isset( $input['BYSETPOS'] )) - $input2['BYSETPOS'] = $input['BYSETPOS']; - if( isset( $input['WKST'] )) - $input2['WKST'] = $input['WKST']; - return $input2; - } -/** - * convert format for input date to internal date with parameters - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param string $tz optional - * @param array $params optional - * @param string $caller optional - * @param string $objName optional - * @param string $tzid optional - * @uses iCalUtilityFunctions::$tzComps - * @uses iCalUtilityFunctions::_strDate2arr() - * @uses iCalUtilityFunctions::_isArrayDate() - * @uses iCalUtilityFunctions::_chkDateArr() - * @uses iCalUtilityFunctions::_isOffset() - * @uses iCalUtilityFunctions::_setParams() - * @uses iCalUtilityFunctions::_existRem() - * @uses iCalUtilityFunctions::$fmt - * @uses iCalUtilityFunctions::_isArrayTimestampDate() - * @uses iCalUtilityFunctions::_timestamp2date() - * @uses iCalUtilityFunctions::_strdate2date() - * @return array - */ - public static function _setDate( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE, $caller=null, $objName=null, $tzid=FALSE ) { - $input = $parno = null; - $localtime = (( 'dtstart' == $caller ) && in_array( $objName, iCalUtilityFunctions::$tzComps )) ? TRUE : FALSE; - iCalUtilityFunctions::_strDate2arr( $year ); - if( iCalUtilityFunctions::_isArrayDate( $year )) { - $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, FALSE ); //$parno ); - if( 100 > $input['value']['year'] ) - $input['value']['year'] += 2000; - if( $localtime ) - unset( $month['VALUE'], $month['TZID'] ); - elseif( !isset( $month['TZID'] ) && isset( $tzid )) - $month['TZID'] = $tzid; - if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) - unset( $month['TZID'] ); - elseif( !isset( $input['value']['tz'] ) && isset( $month['TZID'] ) && iCalUtilityFunctions::_isOffset( $month['TZID'] )) { - $input['value']['tz'] = $month['TZID']; - unset( $month['TZID'] ); - } - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - $hitval = ( isset( $input['value']['tz'] )) ? 7 : 6; - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval ); - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $input['value'] ), $parno ); - if( 6 > $parno ) - unset( $input['value']['tz'], $input['params']['TZID'], $tzid ); - if(( 6 <= $parno ) && isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { - $d = $input['value']; - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - if( isset( $input['value']['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { - $input['params']['TZID'] = $input['value']['tz']; - unset( $input['value']['tz'] ); - } - } // end if( iCalUtilityFunctions::_isArrayDate( $year )) - elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { - if( $localtime ) unset ( $month['VALUE'], $month['TZID'] ); - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); - $hitval = 7; - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno ); - if( isset( $year['tz'] ) && !empty( $year['tz'] )) { - if( !iCalUtilityFunctions::_isOffset( $year['tz'] )) { - $input['params']['TZID'] = $year['tz']; - unset( $year['tz'], $tzid ); - } - else { - if( isset( $input['params']['TZID'] ) && !empty( $input['params']['TZID'] )) { - if( !iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) - unset( $tzid ); - else - unset( $input['params']['TZID']); - } - elseif( isset( $tzid ) && !iCalUtilityFunctions::_isOffset( $tzid )) - $input['params']['TZID'] = $tzid; - } - } - elseif( isset( $input['params']['TZID'] ) && !empty( $input['params']['TZID'] )) { - if( iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { - $year['tz'] = $input['params']['TZID']; - unset( $input['params']['TZID']); - if( isset( $tzid ) && !empty( $tzid ) && !iCalUtilityFunctions::_isOffset( $tzid )) - $input['params']['TZID'] = $tzid; - } - } - elseif( isset( $tzid ) && !empty( $tzid )) { - if( iCalUtilityFunctions::_isOffset( $tzid )) { - $year['tz'] = $tzid; - unset( $input['params']['TZID']); - } - else - $input['params']['TZID'] = $tzid; - } - $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, $parno ); - } // end elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) - elseif( 8 <= strlen( trim((string) $year ))) { // ex. 2006-08-03 10:12:18 [[[+/-]1234[56]] / timezone] - if( $localtime ) - unset( $month['VALUE'], $month['TZID'] ); - elseif( !isset( $month['TZID'] ) && !empty( $tzid )) - $month['TZID'] = $tzid; - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno ); - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $year, $parno ); - if( 3 == $parno ) - unset( $input['value']['tz'], $input['params']['TZID'] ); - unset( $input['value']['unparsedtext'] ); - if( isset( $input['value']['tz'] )) { - if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { - $d = $input['value']; - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - else { - $input['params']['TZID'] = $input['value']['tz']; - unset( $input['value']['tz'] ); - } - } - elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { - $d = $input['value']; - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $input['params']['TZID'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - } // end elseif( 8 <= strlen( trim((string) $year ))) - else { - if( is_array( $params )) - $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); - elseif( is_array( $tz )) { - $input['params'] = iCalUtilityFunctions::_setParams( $tz, array( 'VALUE' => 'DATE-TIME' )); - $tz = FALSE; - } - elseif( is_array( $hour )) { - $input['params'] = iCalUtilityFunctions::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' )); - $hour = $min = $sec = $tz = FALSE; - } - if( $localtime ) - unset ( $input['params']['VALUE'], $input['params']['TZID'] ); - elseif( !isset( $tz ) && !isset( $input['params']['TZID'] ) && !empty( $tzid )) - $input['params']['TZID'] = $tzid; - elseif( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) - unset( $input['params']['TZID'] ); - elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { - $tz = $input['params']['TZID']; - unset( $input['params']['TZID'] ); - } - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 ); - $hitval = ( iCalUtilityFunctions::_isOffset( $tz )) ? 7 : 6; - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno ); - $input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day ); - if( 3 != $parno ) { - $input['value']['hour'] = ( $hour ) ? $hour : '0'; - $input['value']['min'] = ( $min ) ? $min : '0'; - $input['value']['sec'] = ( $sec ) ? $sec : '0'; - if( !empty( $tz )) - $input['value']['tz'] = $tz; - $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], $parno ); - if( !empty( $tz ) && !iCalUtilityFunctions::_isOffset( $tz )) - $strdate .= ( 'Z' == $tz ) ? $tz : ' '.$tz; - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno ); - unset( $input['value']['unparsedtext'] ); - if( isset( $input['value']['tz'] )) { - if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) { - $d = $input['value']; - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - else { - $input['params']['TZID'] = $input['value']['tz']; - unset( $input['value']['tz'] ); - } - } - elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { - $d = $input['value']; - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $input['params']['TZID'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'], $input['params']['TZID'] ); - } - } - } // end else (i.e. using all arguments) - if(( 3 == $parno ) || ( isset( $input['params']['VALUE'] ) && ( 'DATE' == $input['params']['VALUE'] ))) { - $input['params']['VALUE'] = 'DATE'; - unset( $input['value']['hour'], $input['value']['min'], $input['value']['sec'], $input['value']['tz'], $input['params']['TZID'] ); - } - elseif( isset( $input['params']['TZID'] )) { - if(( 'UTC' == strtoupper( $input['params']['TZID'] )) || ( 'GMT' == strtoupper( $input['params']['TZID'] ))) { - $input['value']['tz'] = 'Z'; - unset( $input['params']['TZID'] ); - } - else - unset( $input['value']['tz'] ); - } - elseif( isset( $input['value']['tz'] )) { - if(( 'UTC' == strtoupper( $input['value']['tz'] )) || ( 'GMT' == strtoupper( $input['value']['tz'] ))) - $input['value']['tz'] = 'Z'; - if( 'Z' != $input['value']['tz'] ) { - $input['params']['TZID'] = $input['value']['tz']; - unset( $input['value']['tz'] ); - } - else - unset( $input['params']['TZID'] ); - } - if( $localtime ) - unset( $input['value']['tz'], $input['params']['TZID'] ); - return $input; - } -/** - * convert format for input date (UTC) to internal date with parameters - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-10 - * @param mixed $year - * @param mixed $month optional - * @param int $day optional - * @param int $hour optional - * @param int $min optional - * @param int $sec optional - * @param array $params optional - * @uses iCalUtilityFunctions::_strDate2arr() - * @uses iCalUtilityFunctions::_isArrayDate() - * @uses iCalUtilityFunctions::_chkDateArr() - * @uses iCalUtilityFunctions::_setParams() - * @uses iCalUtilityFunctions::_isOffset() - * @uses iCalUtilityFunctions::$fmt - * @uses iCalUtilityFunctions::_strdate2date() - * @uses iCalUtilityFunctions::_isArrayTimestampDate() - * @uses iCalUtilityFunctions::_timestamp2date() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses iCalUtilityFunctions::_existRem() - * @return array - */ - public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) { - $input = null; - iCalUtilityFunctions::_strDate2arr( $year ); - if( iCalUtilityFunctions::_isArrayDate( $year )) { - $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, 7 ); - if( isset( $input['value']['year'] ) && ( 100 > $input['value']['year'] )) - $input['value']['year'] += 2000; - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - unset( $input['params']['VALUE'] ); - if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) - $tzid = $input['value']['tz']; - elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) - $tzid = $input['params']['TZID']; - else - $tzid = ''; - unset( $input['params']['VALUE'], $input['params']['TZID'] ); - if( !empty( $tzid ) && ( 'Z' != $tzid ) && iCalUtilityFunctions::_isOffset( $tzid )) { - $d = $input['value']; - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $tzid ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'] ); - } - } - elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { - if( isset( $year['tz'] ) && ! iCalUtilityFunctions::_isOffset( $year['tz'] )) - $year['tz'] = 'UTC'; - elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) - $year['tz'] = $input['params']['TZID']; - else - $year['tz'] = 'UTC'; - $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, 7 ); - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - unset( $input['params']['VALUE'], $input['params']['TZID'] ); - } - elseif( 8 <= strlen( trim((string) $year ))) { // ex. 2006-08-03 10:12:18 - $input['value'] = iCalUtilityFunctions::_strdate2date( $year, 7 ); - unset( $input['value']['unparsedtext'] ); - $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' )); - if(( !isset( $input['value']['tz'] ) || empty( $input['value']['tz'] )) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) { - $d = $input['value']; - $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $input['params']['TZID'] ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'] ); - } - unset( $input['params']['VALUE'], $input['params']['TZID'] ); - } - else { - $input['value'] = array( 'year' => $year - , 'month' => $month - , 'day' => $day - , 'hour' => $hour - , 'min' => $min - , 'sec' => $sec ); - if( isset( $tz )) $input['value']['tz'] = $tz; - if(( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) || - ( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))) { - if( !isset( $tz ) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) - $input['value']['tz'] = $input['params']['TZID']; - unset( $input['params']['TZID'] ); - $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], 7 ); - $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 ); - unset( $input['value']['unparsedtext'] ); - } - $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )); - unset( $input['params']['VALUE'] ); - } - $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default - if( !isset( $input['value']['hour'] )) $input['value']['hour'] = 0; - if( !isset( $input['value']['min'] )) $input['value']['min'] = 0; - if( !isset( $input['value']['sec'] )) $input['value']['sec'] = 0; - $input['value']['tz'] = 'Z'; - return $input; - } -/** - * check index and set (an indexed) content in multiple value array - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.12 - 2011-01-03 - * @param array $valArr - * @param mixed $value - * @param array $params - * @param array $defaults - * @param int $index - * @uses iCalUtilityFunctions::_setParams() - * @return void - */ - public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) { - if( !is_array( $valArr )) $valArr = array(); - if( $index ) - $index = $index - 1; - elseif( 0 < count( $valArr )) { - $keys = array_keys( $valArr ); - $index = end( $keys ) + 1; - } - else - $index = 0; - $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults )); - ksort( $valArr ); - } -/** - * set input (formatted) parameters- component property attributes - * - * default parameters can be set, if missing - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.18.10 - 2013-09-04 - * @param array $params - * @param array $defaults - * @return array - */ - public static function _setParams( $params, $defaults=FALSE ) { - if( !is_array( $params)) - $params = array(); - $input = array(); - $params = array_change_key_case( $params, CASE_UPPER ); - foreach( $params as $paramKey => $paramValue ) { - if( is_array( $paramValue )) { - foreach( $paramValue as $pkey => $pValue ) { - if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 ))) - $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 )); - } - } - elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 ))) - $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 )); - if( 'VALUE' == $paramKey ) - $input['VALUE'] = strtoupper( $paramValue ); - else - $input[$paramKey] = $paramValue; - } - if( is_array( $defaults )) { - foreach( $defaults as $paramKey => $paramValue ) { - if( !isset( $input[$paramKey] )) - $input[$paramKey] = $paramValue; - } - } - return (0 < count( $input )) ? $input : null; - } -/** - * set sort arguments/parameters in component - * - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param object $c valendar component - * @param string $sortArg - * @uses calendarComponent::$srtk - * @uses calendarComponent::$objName - * @uses calendarComponent::$getProperty() - * @uses iCalUtilityFunctions::$mProps1 - * @uses calendarComponent::_getProperties() - * @uses iCalUtilityFunctions::_date2strdate() - * @return void - */ - public static function _setSortArgs( $c, $sortArg=FALSE ) { - $c->srtk = array( '0', '0', '0', '0' ); - if( 'vtimezone' == $c->objName ) { - if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' ))) - $c->srtk[0] = 0; - return; - } - elseif( $sortArg ) { - if( in_array( $sortArg, iCalUtilityFunctions::$mProps1 )) { - $propValues = array(); - $c->_getProperties( $sortArg, $propValues ); - if( !empty( $propValues )) { - $sk = array_keys( $propValues ); - $c->srtk[0] = $sk[0]; - if( 'RELATED-TO' == $sortArg ) - $c->srtk[0] .= $c->getProperty( 'uid' ); - } - elseif( 'RELATED-TO' == $sortArg ) - $c->srtk[0] = $c->getProperty( 'uid' ); - } - elseif( FALSE !== ( $d = $c->getProperty( $sortArg ))) { - $c->srtk[0] = $d; - if( 'UID' == $sortArg ) { - if( FALSE !== ( $d = $c->getProperty( 'recurrence-id' ))) { - $c->srtk[1] = iCalUtilityFunctions::_date2strdate( $d ); - if( FALSE === ( $c->srtk[2] = $c->getProperty( 'sequence' ))) - $c->srtk[2] = PHP_INT_MAX; - } - else - $c->srtk[1] = $c->srtk[2] = PHP_INT_MAX; - } - } - return; - } // end elseif( $sortArg ) - if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) { - $c->srtk[0] = iCalUtilityFunctions::_strdate2date( $d[1] ); - unset( $c->srtk[0]['unparsedtext'] ); - } - elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' ))) - $c->srtk[0] = 0; // sortkey 0 : dtstart - if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' ))) { - $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); // sortkey 1 : dtend/due(/duration) - unset( $c->srtk[1]['unparsedtext'] ); - } - elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) { - if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' ))) { - $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); - unset( $c->srtk[1]['unparsedtext'] ); - } - elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' ))) - if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE ))) - $c->srtk[1] = 0; - } - if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' ))) // sortkey 2 : created/dtstamp - if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' ))) - $c->srtk[2] = 0; - if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' ))) // sortkey 3 : uid - $c->srtk[3] = 0; - } -/** - * break lines at pos 75 - * - * Lines of text SHOULD NOT be longer than 75 octets, excluding the line - * break. Long content lines SHOULD be split into a multiple line - * representations using a line "folding" technique. That is, a long - * line can be split between any two characters by inserting a CRLF - * immediately followed by a single linear white space character (i.e., - * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence - * of CRLF followed immediately by a single linear white space character - * is ignored (i.e., removed) when processing the content type. - * - * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where - * the reserved expression "\n" in the arg $string could be broken up by the - * folding of lines, causing ambiguity in the return string. - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @param string $string - * @param string $nl - * @return string - */ - public static function _size75( $string, $nl ) { - $tmp = $string; - $string = ''; - $cCnt = $x = 0; - while( TRUE ) { - if( !isset( $tmp[$x] )) { - $string .= $nl; // loop breakes here - break; - } - elseif(( 74 <= $cCnt ) && ( '\\' == $tmp[$x] ) && ( 'n' == $tmp[$x+1] )) { - $string .= $nl.' \n'; // don't break lines inside '\n' - $x += 2; - if( !isset( $tmp[$x] )) { - $string .= $nl; - break; - } - $cCnt = 3; - } - elseif( 75 <= $cCnt ) { - $string .= $nl.' '; - $cCnt = 1; - } - $byte = ord( $tmp[$x] ); - $string .= $tmp[$x]; - switch( TRUE ) { // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - case(( $byte >= 0x20 ) && ( $byte <= 0x7F )): // characters U-00000000 - U-0000007F (same as ASCII) - $cCnt += 1; - break; // add a one byte character - case(( $byte & 0xE0) == 0xC0 ): // characters U-00000080 - U-000007FF, mask 110XXXXX - if( isset( $tmp[$x+1] )) { - $cCnt += 1; - $string .= $tmp[$x+1]; - $x += 1; // add a two bytes character - } - break; - case(( $byte & 0xF0 ) == 0xE0 ): // characters U-00000800 - U-0000FFFF, mask 1110XXXX - if( isset( $tmp[$x+2] )) { - $cCnt += 1; - $string .= $tmp[$x+1].$tmp[$x+2]; - $x += 2; // add a three bytes character - } - break; - case(( $byte & 0xF8 ) == 0xF0 ): // characters U-00010000 - U-001FFFFF, mask 11110XXX - if( isset( $tmp[$x+3] )) { - $cCnt += 1; - $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3]; - $x += 3; // add a four bytes character - } - break; - case(( $byte & 0xFC ) == 0xF8 ): // characters U-00200000 - U-03FFFFFF, mask 111110XX - if( isset( $tmp[$x+4] )) { - $cCnt += 1; - $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4]; - $x += 4; // add a five bytes character - } - break; - case(( $byte & 0xFE ) == 0xFC ): // characters U-04000000 - U-7FFFFFFF, mask 1111110X - if( isset( $tmp[$x+5] )) { - $cCnt += 1; - $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4].$tmp[$x+5]; - $x += 5; // add a six bytes character - } - default: // add any other byte without counting up $cCnt - break; - } // end switch( TRUE ) - $x += 1; // next 'byte' to test - } // end while( TRUE ) { - return $string; - } -/** - * sort callback function for exdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-07 - * @param array $a - * @param array $b - * @uses iCalUtilityFunctions::$fmt - * @return int - */ - public static function _sortExdate1( $a, $b ) { - $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $a['year'], (int) $a['month'], (int) $a['day'] ); - $as .= ( isset( $a['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $a['hour'], (int) $a['min'], (int) $a['sec'] ) : ''; - $bs = sprintf( iCalUtilityFunctions::$fmt['His'], (int) $b['year'], (int) $b['month'], (int) $b['day'] ); - $bs .= ( isset( $b['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $b['hour'], (int) $b['min'], (int) $b['sec'] ) : ''; - return strcmp( $as, $bs ); - } -/** - * sort callback function for exdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-07 - * @param array $a - * @param array $b - * @uses iCalUtilityFunctions::$fmt - * @return int - */ - public static function _sortExdate2( $a, $b ) { - $val = reset( $a['value'] ); - $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); - $as .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; - $val = reset( $b['value'] ); - $bs = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); - $bs .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; - return strcmp( $as, $bs ); - } -/** - * sort callback function for rdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-07 - * @param array $a - * @param array $b - * @uses iCalUtilityFunctions::$fmt - * @return int - */ - public static function _sortRdate1( $a, $b ) { - $val = isset( $a['year'] ) ? $a : $a[0]; - $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); - $as .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; - $val = isset( $b['year'] ) ? $b : $b[0]; - $bs = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); - $bs .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; - return strcmp( $as, $bs ); - } -/** - * sort callback function for rdate - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-07 - * @param array $a - * @param array $b - * @uses iCalUtilityFunctions::$fmt - * @return int - */ - public static function _sortRdate2( $a, $b ) { - $val = isset( $a['value'][0]['year'] ) ? $a['value'][0] : $a['value'][0][0]; - if( empty( $val )) - $as = ''; - else { - $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); - $as .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; - } - $val = isset( $b['value'][0]['year'] ) ? $b['value'][0] : $b['value'][0][0]; - if( empty( $val )) - $bs = ''; - else { - $bs = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] ); - $bs .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : ''; - } - return strcmp( $as, $bs ); - } -/** - * separate property attributes from property value - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.18.6 - 2013-08-29 - * @param string $line property content - * @param array $propAttr property parameters - * @uses iCalUtilityFunctions::$parValPrefix - * @return void - */ - public static function _splitContent( & $line, & $propAttr=null ) { - $attr = array(); - $attrix = -1; - $clen = strlen( $line ); - $WithinQuotes = FALSE; - $cix = 0; - while( FALSE !== substr( $line, $cix, 1 )) { - if( ! $WithinQuotes && ( ':' == $line[$cix] ) && - ( substr( $line,$cix, 3 ) != '://' ) && - ( ! in_array( strtolower( substr( $line,$cix - 6, 4 )), iCalUtilityFunctions::$parValPrefix['MStz'] )) && - ( ! in_array( strtolower( substr( $line,$cix - 3, 4 )), iCalUtilityFunctions::$parValPrefix['Proto3'] )) && - ( ! in_array( strtolower( substr( $line,$cix - 4, 5 )), iCalUtilityFunctions::$parValPrefix['Proto4'] )) && - ( ! in_array( strtolower( substr( $line,$cix - 6, 7 )), iCalUtilityFunctions::$parValPrefix['Proto6'] ))) { - $attrEnd = TRUE; - if(( $cix < ( $clen - 4 )) && - ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr?? - for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) { - if( '://' == substr( $line, $c2ix - 2, 3 )) { - $attrEnd = FALSE; - break; // an URI with a portnr!! - } - } - } - if( $attrEnd) { - $line = substr( $line, ( $cix + 1 )); - break; - } - $cix++; - } - if( '"' == $line[$cix] ) - $WithinQuotes = ! $WithinQuotes; - if( ';' == $line[$cix] ) - $attr[++$attrix] = null; - else - $attr[$attrix] .= $line[$cix]; - $cix++; - } - /* make attributes in array format */ - $propAttr = array(); - foreach( $attr as $attribute ) { - $attrsplit = explode( '=', $attribute, 2 ); - if( 1 < count( $attrsplit )) - $propAttr[$attrsplit[0]] = $attrsplit[1]; - } - } -/** - * step date, return updated date, array and timpstamp - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-10 - * @param array $date date to step - * @param string $dateYMD date YMD - * @param array $step default array( 'day' => 1 ) - * @uses iCalUtilityFunctions::$fmt - * @return void - */ - public static function _stepdate( & $date, & $dateYMD, $step=array( 'day' => 1 )) { - if( !isset( $date['hour'] )) $date['hour'] = 0; - if( !isset( $date['min'] )) $date['min'] = 0; - if( !isset( $date['sec'] )) $date['sec'] = 0; - if( isset( $step['day'] )) - $mcnt = date( 't', mktime( (int) $date['hour'], (int) $date['min'], (int) $date['sec'], (int) $date['month'], (int) $date['day'], (int) $date['year'] )); - foreach( $step as $stepix => $stepvalue ) - $date[$stepix] += $stepvalue; - if( isset( $step['month'] )) { - if( 12 < $date['month'] ) { - $date['year'] += 1; - $date['month'] -= 12; - } - } - elseif( isset( $step['day'] )) { - if( $mcnt < $date['day'] ) { - $date['day'] -= $mcnt; - $date['month'] += 1; - if( 12 < $date['month'] ) { - $date['year'] += 1; - $date['month'] -= 12; - } - } - } - $dateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $date['year'], (int) $date['month'], (int) $date['day'] ); - unset( $mcnt ); - } -/** - * convert a date from specific string to array format - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.8 - 2012-01-27 - * @param mixed $input - * @return bool, TRUE on success - */ - public static function _strDate2arr( & $input ) { - if( is_array( $input )) - return FALSE; - if( 5 > strlen( (string) $input )) - return FALSE; - $work = $input; - if( 2 == substr_count( $work, '-' )) - $work = str_replace( '-', '', $work ); - if( 2 == substr_count( $work, '/' )) - $work = str_replace( '/', '', $work ); - if( !ctype_digit( substr( $work, 0, 8 ))) - return FALSE; - $temp = array( 'year' => (int) substr( $work, 0, 4 ) - , 'month' => (int) substr( $work, 4, 2 ) - , 'day' => (int) substr( $work, 6, 2 )); - if( !checkdate( $temp['month'], $temp['day'], $temp['year'] )) - return FALSE; - if( 8 == strlen( $work )) { - $input = $temp; - return TRUE; - } - if(( ' ' == substr( $work, 8, 1 )) || ( 'T' == substr( $work, 8, 1 )) || ( 't' == substr( $work, 8, 1 ))) - $work = substr( $work, 9 ); - elseif( ctype_digit( substr( $work, 8, 1 ))) - $work = substr( $work, 8 ); - else - return FALSE; - if( 2 == substr_count( $work, ':' )) - $work = str_replace( ':', '', $work ); - if( !ctype_digit( substr( $work, 0, 4 ))) - return FALSE; - $temp['hour'] = substr( $work, 0, 2 ); - $temp['min'] = substr( $work, 2, 2 ); - if((( 0 > $temp['hour'] ) || ( $temp['hour'] > 23 )) || - (( 0 > $temp['min'] ) || ( $temp['min'] > 59 ))) - return FALSE; - if( ctype_digit( substr( $work, 4, 2 ))) { - $temp['sec'] = substr( $work, 4, 2 ); - if(( 0 > $temp['sec'] ) || ( $temp['sec'] > 59 )) - return FALSE; - $len = 6; - } - else { - $temp['sec'] = 0; - $len = 4; - } - if( $len < strlen( $work)) - $temp['tz'] = trim( substr( $work, 6 )); - $input = $temp; - return TRUE; - } -/** - * ensures internal date-time/date format for input date-time/date in string fromat - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-15 - * Modified to also return original string value by Yitzchok Lavi - * @param array $datetime - * @param int $parno optional, default FALSE - * @param moxed $wtz optional, default null - * @uses iCalUtilityFunctions::_isOffset() - * @uses iCalUtilityFunctions::_strDate2arr() - * @uses iCalUtilityFunctions::_isOffset() - * @uses iCalUtilityFunctions::_tz2offset() - * @uses iCalUtilityFunctions::$fmt - * @return array - */ - public static function _strdate2date( $datetime, $parno=FALSE, $wtz=null ) { - $unparseddatetime = $datetime; - $datetime = (string) trim( $datetime ); - $tz = null; - $offset = 0; - $tzSts = FALSE; - $len = strlen( $datetime ); - if( 'Z' == substr( $datetime, -1 )) { - $tz = 'Z'; - $datetime = trim( substr( $datetime, 0, ( $len - 1 ))); - $tzSts = TRUE; - } - if( iCalUtilityFunctions::_isOffset( substr( $datetime, -5, 5 ))) { // [+/-]NNNN offset - $tz = substr( $datetime, -5, 5 ); - $datetime = trim( substr( $datetime, 0, ($len - 5))); - } - elseif( iCalUtilityFunctions::_isOffset( substr( $datetime, -7, 7 ))) { // [+/-]NNNNNN offset - $tz = substr( $datetime, -7, 7 ); - $datetime = trim( substr( $datetime, 0, ($len - 7))); - } - elseif( empty( $wtz ) && ctype_digit( substr( $datetime, 0, 4 )) && ctype_digit( substr( $datetime, -2, 2 )) && iCalUtilityFunctions::_strDate2arr( $datetime )) { - $output = $datetime; - if( !empty( $tz )) - $output['tz'] = 'Z'; - $output['unparsedtext'] = $unparseddatetime; - return $output; - } - else { - $cx = $tx = 0; // find any trailing timezone or offset - $len = strlen( $datetime ); - for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) { - $char = substr( $datetime, $cx, 1 ); - if(( ' ' == $char ) || ctype_digit( $char )) - break; // if exists, tz ends here.. . ? - else - $tx--; // tz length counter - } - if( 0 > $tx ) { // if any - $tz = substr( $datetime, $tx ); - $datetime = trim( substr( $datetime, 0, $len + $tx )); - } - if(( ctype_digit( substr( $datetime, 0, 8 )) && ( 'T' == substr( $datetime, 8, 1 )) && ctype_digit( substr( $datetime, -6, 6 ))) || - ( ctype_digit( substr( $datetime, 0, 14 )))) - $tzSts = TRUE; - } - if( empty( $tz ) && !empty( $wtz )) - $tz = $wtz; - if( 3 == $parno ) - $tz = null; - if( !empty( $tz )) { // tz set - if(( 'Z' != $tz ) && ( iCalUtilityFunctions::_isOffset( $tz ))) { - $offset = (string) iCalUtilityFunctions::_tz2offset( $tz ) * -1; - $tz = 'UTC'; - $tzSts = TRUE; - } - elseif( !empty( $wtz )) - $tzSts = TRUE; - $tz = trim( $tz ); - if(( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz ))) - $tz = 'UTC'; - if( 0 < substr_count( $datetime, '-' )) - $datetime = str_replace( '-', '/', $datetime ); - try { - $d = new DateTime( $datetime, new DateTimeZone( $tz )); - if( 0 != $offset ) // adjust for offset - $d->modify( $offset.' seconds' ); - $datestring = $d->format( iCalUtilityFunctions::$fmt['YmdHis3'] ); - unset( $d ); - } - catch( Exception $e ) { - $datestring = date( iCalUtilityFunctions::$fmt['YmdHis3'], strtotime( $datetime )); - } - } // end if( !empty( $tz )) - else - $datestring = date( iCalUtilityFunctions::$fmt['YmdHis3'], strtotime( $datetime )); - if( 'UTC' == $tz ) - $tz = 'Z'; - $d = explode( '-', $datestring ); - $output = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2] ); - if( !$parno || ( 3 != $parno )) { // parno is set to 6 or 7 - $output['hour'] = $d[3]; - $output['min'] = $d[4]; - $output['sec'] = $d[5]; - if(( $tzSts || ( 7 == $parno )) && !empty( $tz )) - $output['tz'] = $tz; - } - // return original string in the array in case strtotime failed to make sense of it - $output['unparsedtext'] = $unparseddatetime; - return $output; - } -/********************************************************************************/ -/** - * special characters management output - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @param string $string - * @param string $format - * @param string $nl - * @return string - */ - public static function _strrep( $string, $format, $nl ) { - switch( $format ) { - case 'xcal': - $string = str_replace( '\n', $nl, $string); - $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string )))); - break; - default: - $pos = 0; - $specChars = array( 'n', 'N', 'r', ',', ';' ); - while( isset( $string[$pos] )) { - if( FALSE === ( $pos = strpos( $string, "\\", $pos ))) - break; - if( !in_array( substr( $string, $pos, 1 ), $specChars )) { - $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 )); - $pos += 1; - } - $pos += 1; - } - if( FALSE !== strpos( $string, '"' )) - $string = str_replace('"', "'", $string); - if( FALSE !== strpos( $string, ',' )) - $string = str_replace(',', '\,', $string); - if( FALSE !== strpos( $string, ';' )) - $string = str_replace(';', '\;', $string); - if( FALSE !== strpos( $string, "\r\n" )) - $string = str_replace( "\r\n", '\n', $string); - elseif( FALSE !== strpos( $string, "\r" )) - $string = str_replace( "\r", '\n', $string); - elseif( FALSE !== strpos( $string, "\n" )) - $string = str_replace( "\n", '\n', $string); - if( FALSE !== strpos( $string, '\N' )) - $string = str_replace( '\N', '\n', $string); -// if( FALSE !== strpos( $string, $nl )) - $string = str_replace( $nl, '\n', $string); - break; - } - return $string; - } -/** - * special characters management input (from iCal file) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.16.2 - 2012-12-18 - * @param string $string - * @return string - */ - public static function _strunrep( $string ) { - $string = str_replace( '\\\\', '\\', $string); - $string = str_replace( '\,', ',', $string); - $string = str_replace( '\;', ';', $string); -// $string = str_replace( '\n', $nl, $string); // ?? - return $string; - } -/** - * convert timestamp to date array, default UTC or adjusted for offset/timezone - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-07 - * @param mixed $timestamp - * @param int $parno - * @param string $wtz - * @uses iCalUtilityFunctions::_isOffset() - * @uses iCalUtilityFunctions::_tz2offset() - * @uses iCalUtilityFunctions::$fmt - * @return array - */ - public static function _timestamp2date( $timestamp, $parno=6, $wtz=null ) { - if( is_array( $timestamp )) { - $tz = ( isset( $timestamp['tz'] )) ? $timestamp['tz'] : $wtz; - $timestamp = $timestamp['timestamp']; - } - $tz = ( isset( $tz )) ? $tz : $wtz; - $offset = 0; - if( empty( $tz ) || ( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz ))) - $tz = 'UTC'; - elseif( iCalUtilityFunctions::_isOffset( $tz )) { - $offset = iCalUtilityFunctions::_tz2offset( $tz ); - } - try { - $d = new DateTime( "@$timestamp" ); // set UTC date - if( 0 != $offset ) // adjust for offset - $d->modify( $offset.' seconds' ); - elseif( 'UTC' != $tz ) - $d->setTimezone( new DateTimeZone( $tz )); // convert to local date - $date = $d->format( iCalUtilityFunctions::$fmt['YmdHis3'] ); - unset( $d ); - } - catch( Exception $e ) { - $date = date( iCalUtilityFunctions::$fmt['YmdHis3'], $timestamp ); - } - $date = explode( '-', $date ); - $output = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2] ); - if( 3 != $parno ) { - $output['hour'] = $date[3]; - $output['min'] = $date[4]; - $output['sec'] = $date[5]; - if(( 'UTC' == $tz ) || ( 0 == $offset )) - $output['tz'] = 'Z'; - } - return $output; - } -/** - * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.15.1 - 2012-10-17 - * @param mixed $date date to alter - * @param string $tzFrom PHP valid 'from' timezone - * @param string $tzTo PHP valid 'to' timezone, default 'UTC' - * @param string $format date output format, default 'Ymd\THis' - * @uses iCalUtilityFunctions::_isArrayDate() - * @uses iCalUtilityFunctions::_date2strdate() - * @uses iCalUtilityFunctions::_chkDateArr() - * @return bool - */ - public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) { - if( is_array( $date ) && isset( $date['timestamp'] )) { - try { - $d = new DateTime( "@{$date['timestamp']}" ); // set UTC date - $d->setTimezone(new DateTimeZone( $tzFrom )); // convert to 'from' date - } - catch( Exception $e ) { return FALSE; } - } - else { - if( iCalUtilityFunctions::_isArrayDate( $date )) { - if( isset( $date['tz'] )) - unset( $date['tz'] ); - $date = iCalUtilityFunctions::_date2strdate( iCalUtilityFunctions::_chkDateArr( $date )); - } - if( 'Z' == substr( $date, -1 )) - $date = substr( $date, 0, ( strlen( $date ) - 2 )); - try { $d = new DateTime( $date, new DateTimeZone( $tzFrom )); } - catch( Exception $e ) { return FALSE; } - } - try { $d->setTimezone( new DateTimeZone( $tzTo )); } - catch( Exception $e ) { return FALSE; } - $date = $d->format( $format ); - return TRUE; - } -/** - * convert offset, [+/-]HHmm[ss], to seconds, used when correcting UTC to localtime or v.v. - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.4 - 2012-01-11 - * @param string $tz - * @return integer - */ - public static function _tz2offset( $tz ) { - $tz = trim( (string) $tz ); - $offset = 0; - if((( 5 != strlen( $tz )) && ( 7 != strlen( $tz ))) || - (( '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) || - (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) || - (( 7 == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 )))) - return $offset; - $hours2sec = (int) substr( $tz, 1, 2 ) * 3600; - $min2sec = (int) substr( $tz, 3, 2 ) * 60; - $sec = ( 7 == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00'; - $offset = $hours2sec + $min2sec + $sec; - $offset = ('-' == substr( $tz, 0, 1 )) ? $offset * -1 : $offset; - return $offset; - } -} diff --git a/php/ical/lib/iCaldateTime.class.php b/php/ical/lib/iCaldateTime.class.php deleted file mode 100644 index 55f7c3a..0000000 --- a/php/ical/lib/iCaldateTime.class.php +++ /dev/null @@ -1,129 +0,0 @@ - - * @since 2.21.7 - 2015-03-10 - */ -class iCaldateTime extends dateTime { -/** @var string default date[-time] format */ - public $dateFormat = 'Y-m-d H:i:s e'; -/** @var string default object instance date[-time] 'key' */ - public $key = null; -/** @var array date[-time] origin */ - public $SCbools = array(); -/** - * return time (His) array - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.7 - 2015-03-07 - * @return array - */ - public function getTime() { - return explode( ':', $this->format( 'H:i:s' )); - } -/** - * return the timezone name - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.7 - 2015-03-07 - * @return string - */ - public function getTimezoneName() { - $tz = $this->getTimezone(); - return $tz->getName(); - } -/** - * return formatted date - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.7 - 2015-03-07 - * @param string $format - * @uses iCaldateTime::$dateFormat - * @return string - */ - public function format( $format=null ) { - if( empty( $format ) && isset( $this->dateFormat )) - $format = $this->dateFormat; - return parent::format( $format ); - } -/** - * return iCaldateTime object instance based on date array and timezone(s) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-28 - * @param array $date - * @param array $params - * @param array $tz - * @param string $dtstartTz - * @uses iCalUtilityFunctions::$fmt - * @uses iCaldateTime::getTimezoneName() - * @uses iCaldateTime::$dateFormat - * @uses iCaldateTime::$key - * @return object instance - */ - public static function factory( array $date, $params=null, $tz=null, $dtstartTz=null ) { - if( isset( $params['TZID'] ) && ! empty( $params['TZID'] )) - $tz = ( 'Z' == $params['TZID'] ) ? 'UTC' : $params['TZID']; - elseif( isset( $tz['tz'] ) && ! empty( $tz['tz'] )) - $tz = ( 'Z' == $tz['tz'] ) ? 'UTC' : $tz['tz']; - else - $tz = ini_get( 'date_default_timezone_set' ); - $strdate = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $date['year'], (int) $date['month'], (int) $date['day'] ); - if( isset( $date['hour'] )) - $strdate .= 'T'.sprintf( iCalUtilityFunctions::$fmt['His'], (int) $date['hour'], (int) $date['min'], (int) $date['sec'] ); - try { - $timezone = new DateTimeZone( $tz ); - $d = new iCaldateTime( $strdate, $timezone ); - } - catch( Exception $e ) { - $d = new iCaldateTime( $strdate ); - } - if( ! empty( $dtstartTz )) { - if( 'Z' == $dtstartTz ) - $dtstartTz = 'UTC'; - if( $dtstartTz != $d->getTimezoneName()) { // set the same timezone as dtstart - try { - $timezone = new DateTimeZone( $dtstartTz ); - $d->setTimezone( $timezone ); - } - catch( Exception $e ) {} - } - } - unset( $timezone, $strdate ); - if( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )) { - $d->dateFormat = 'Y-m-d'; - $d->key = $d->format( 'Ymd' ); - } - else - $d->key = $d->format( 'YmdHis' ); - return $d; - } -} diff --git a/php/ical/lib/valarm.class.php b/php/ical/lib/valarm.class.php deleted file mode 100644 index 9264285..0000000 --- a/php/ical/lib/valarm.class.php +++ /dev/null @@ -1,135 +0,0 @@ - - * @since 2.5.1 - 2008-10-12 - */ -class valarm extends calendarComponent { -/** - * @var array $action component property value - * @var array $attach component property value - * @var array $attendee component property value - * @var array $description component property value - * @var array $duration component property value - * @var array $repeat component property value - * @var array $summary component property value - * @var array $trigger component property value - * @access protected - */ - protected $action; - protected $attach; - protected $attendee; - protected $description; - protected $duration; - protected $repeat; - protected $summary; - protected $trigger; -/** - * constructor for calendar component VALARM object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @uses valarm::calendarComponent() - * @uses valarm::$action - * @uses valarm::$attach - * @uses valarm::$attendee - * @uses valarm::$description - * @uses valarm::$duration - * @uses valarm::$repeat - * @uses valarm::$summary - * @uses valarm::$trigger - * @uses valarm::$xprop - * @uses calendarComponent::setConfig() - */ - function valarm( $config = array()) { - $this->calendarComponent(); - $this->action = ''; - $this->attach = ''; - $this->attendee = ''; - $this->description = ''; - $this->duration = ''; - $this->repeat = ''; - $this->summary = ''; - $this->trigger = ''; - $this->xprop = ''; - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - } -/** - * create formatted output for calendar component VALARM object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-22 - * @param array $xcaldecl - * @uses calendarComponent::_createFormat() - * @uses calendarComponent::$componentStart1 - * @uses calendarComponent::$componentStart2 - * @uses calendarComponent::$nl - * @uses calendarComponent::createAction() - * @uses calendarComponent::createAttach() - * @uses calendarComponent::createAttendee() - * @uses calendarComponent::createDescription() - * @uses calendarComponent::createDuration() - * @uses calendarComponent::createRepeat() - * @uses calendarComponent::createSummary() - * @uses calendarComponent::createTrigger() - * @uses calendarComponent::createXprop() - * @uses calendarComponent::$componentEnd1 - * @uses calendarComponent::$componentEnd2 - * @uses calendarComponent::$xcaldecl - * @return string - */ - function createComponent( & $xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createAction(); - $component .= $this->createAttach(); - $component .= $this->createAttendee(); - $component .= $this->createDescription(); - $component .= $this->createDuration(); - $component .= $this->createRepeat(); - $component .= $this->createSummary(); - $component .= $this->createTrigger(); - $component .= $this->createXprop(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} diff --git a/php/ical/lib/vcalendar.class.php b/php/ical/lib/vcalendar.class.php deleted file mode 100644 index 33e9dad..0000000 --- a/php/ical/lib/vcalendar.class.php +++ /dev/null @@ -1,2132 +0,0 @@ - - * @since 2.21.11 - 2015-03-31 - */ -class vcalendar extends iCalBase { -/** - * @var array $calscale calendar property variable - * @var array $method calendar property variable - * @var array $prodid calendar property variable - * @var array $version calendar property variable - * @access private - */ - private $calscale; - private $method; - private $prodid; - private $version; -/** - * @var array $directory calendar config variable - * @var array $filename calendar config variable - * @var array $url calendar config variable - * @access private - */ - private $directory; - private $filename; - private $url; -/** - * redirect headers - * - * @var array $headers - * @access private - * @static - */ - private static $headers = array( 'Content-Encoding: gzip', - 'Vary: *', - 'Content-Length: %s', - 'Content-Type: application/calendar+xml; charset=utf-8', - 'Content-Type: text/calendar; charset=utf-8', - 'Content-Disposition: attachment; filename="%s"', - 'Content-Disposition: inline; filename="%s"', - 'Cache-Control: max-age=10', - ); -/** - * constructor for calendar object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.15 - 2015-04-04 - * @param array $config - * @uses vcalendar::_makeVersion() - * @uses vcalendar::$calscale - * @uses vcalendar::$method - * @uses vcalendar::_makeUnique_id() - * @uses vcalendar::$prodid - * @uses vcalendar::$xprop - * @uses vcalendar::$language - * @uses vcalendar::$directory - * @uses vcalendar::$filename - * @uses vcalendar::$url - * @uses vcalendar::$dtzid - * @uses vcalendar::setConfig() - * @uses vcalendar::$xcaldecl - * @uses vcalendar::$components - */ - function vcalendar ( $config = array()) { - $this->_makeVersion(); - $this->calscale = null; - $this->method = null; - $this->_makeUnique_id(); - $this->prodid = null; - $this->xprop = array(); - $this->language = null; - $this->directory = '.'; - $this->filename = null; - $this->url = null; - $this->dtzid = null; -/** - * language = - */ - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - $this->xcaldecl = array(); - $this->components = array(); - } -/** - * return iCalcreator version number - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.18.5 - 2013-08-29 - * @uses ICALCREATOR_VERSION - * @return string - */ - public static function iCalcreatorVersion() { - return trim( substr( ICALCREATOR_VERSION, strpos( ICALCREATOR_VERSION, ' ' ))); - } -/*********************************************************************************/ -/** - * Property Name: CALSCALE - */ -/** - * creates formatted output for calendar property calscale - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.16 - 2011-10-28 - * @uses vcalendar::$calscale - * @uses vcalendar::$format - * @uses vcalendar::$nl - * @return string - */ - function createCalscale() { - if( empty( $this->calscale )) return FALSE; - switch( $this->format ) { - case 'xcal': - return $this->nl.' calscale="'.$this->calscale.'"'; - break; - default: - return 'CALSCALE:'.$this->calscale.$this->nl; - break; - } - } -/** - * set calendar property calscale - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-21 - * @param string $value - * @uses vcalendar::$calscale - * @return void - */ - function setCalscale( $value ) { - if( empty( $value )) return FALSE; - $this->calscale = $value; - } -/*********************************************************************************/ -/** - * Property Name: METHOD - */ -/** - * creates formatted output for calendar property method - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.16 - 2011-10-28 - * @uses vcalendar::$method - * @uses vcalendar::$format - * @uses vcalendar::$nl - * @return string - */ - function createMethod() { - if( empty( $this->method )) return FALSE; - switch( $this->format ) { - case 'xcal': - return $this->nl.' method="'.$this->method.'"'; - break; - default: - return 'METHOD:'.$this->method.$this->nl; - break; - } - } -/** - * set calendar property method - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-20-23 - * @param string $value - * @uses vcalendar::$method - * @return bool - */ - function setMethod( $value ) { - if( empty( $value )) return FALSE; - $this->method = $value; - return TRUE; - } -/*********************************************************************************/ -/** - * Property Name: PRODID - * - */ -/** - * creates formatted output for calendar property prodid - * - * @copyright copyright (c) 2007-2013 Kjell-Inge Gustafsson, kigkonsult, All rights reserved - * @license http://kigkonsult.se/downloads/dl.php?f=LGPL - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-31 - * @uses vcalendar::$prodid - * @uses vcalendar::_makeProdid() - * @uses vcalendar::$format - * @uses vcalendar::$nl - * @uses vcalendar::_createElement() - * @return string - */ - function createProdid() { - if( !isset( $this->prodid )) - $this->_makeProdid(); - switch( $this->format ) { - case 'xcal': - return $this->nl.' prodid="'.$this->prodid.'"'; - break; - default: - return $this->_createElement( 'PRODID', '', $this->prodid ); - break; - } - } -/** - * make default value for calendar prodid, do NOT alter or remove this method or invoke of this method - * - * @copyright copyright (c) 2007-2013 Kjell-Inge Gustafsson, kigkonsult, All rights reserved - * @license http://kigkonsult.se/downloads/dl.php?f=LGPL - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.6.8 - 2009-12-30 - * @uses vcalendar::$prodid - * @uses vcalendar::$unique_id - * @uses ICALCREATOR_VERSION - * @uses vcalendar::$language - * @return void - */ - function _makeProdid() { - $this->prodid = '-//'.$this->unique_id.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION.'//'.strtoupper( $this->language ); - } -/** - * Conformance: The property MUST be specified once in an iCalendar object. - * Description: The vendor of the implementation SHOULD assure that this - * is a globally unique identifier; using some technique such as an FPI - * value, as defined in [ISO 9070]. - */ -/** - * make default unique_id for calendar prodid - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 0.3.0 - 2006-08-10 - * @uses vcalendar::$unique_id - * @return void - */ - function _makeUnique_id() { - $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost'; - } -/*********************************************************************************/ -/** - * Property Name: VERSION - * - * Description: A value of "2.0" corresponds to this memo. - */ -/** - * creates formatted output for calendar property version - - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.16 - 2011-10-28 - * @uses vcalendar::$version - * @uses vcalendar::$format - * @uses vcalendar::$nl - * @return string - */ - function createVersion() { - if( empty( $this->version )) - $this->_makeVersion(); - switch( $this->format ) { - case 'xcal': - return $this->nl.' version="'.$this->version.'"'; - break; - default: - return 'VERSION:'.$this->version.$this->nl; - break; - } - } -/** - * set default calendar version - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 0.3.0 - 2006-08-10 - * @uses vcalendar::$version - * @return void - */ - function _makeVersion() { - $this->version = '2.0'; - } -/** - * set calendar version - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.4.8 - 2008-10-23 - * @param string $value - * @uses vcalendar::$version - * @return void - */ - function setVersion( $value ) { - if( empty( $value )) return FALSE; - $this->version = $value; - return TRUE; - } -/*********************************************************************************/ -/*********************************************************************************/ -/** - * delete calendar property value - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $propName bool FALSE => X-property - * @param int $propix specific property in case of multiply occurences - * @uses vcalendar::$propdelix - * @uses vcalendar::$calscale - * @uses vcalendar::$method - * @uses vcalendar::$xprop - * @return bool, if successfull delete - */ - function deleteProperty( $propName=FALSE, $propix=FALSE ) { - $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; - if( !$propix ) - $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1; - $this->propdelix[$propName] = --$propix; - $return = FALSE; - switch( $propName ) { - case 'CALSCALE': - if( isset( $this->calscale )) { - $this->calscale = null; - $return = TRUE; - } - break; - case 'METHOD': - if( isset( $this->method )) { - $this->method = null; - $return = TRUE; - } - break; - default: - $reduced = array(); - if( $propName != 'X-PROP' ) { - if( !isset( $this->xprop[$propName] )) { unset( $this->propdelix[$propName] ); return FALSE; } - foreach( $this->xprop as $k => $a ) { - if(( $k != $propName ) && !empty( $a )) - $reduced[$k] = $a; - } - } - else { - if( count( $this->xprop ) <= $propix ) return FALSE; - $xpropno = 0; - foreach( $this->xprop as $xpropkey => $xpropvalue ) { - if( $propix != $xpropno ) - $reduced[$xpropkey] = $xpropvalue; - $xpropno++; - } - } - $this->xprop = $reduced; - if( empty( $this->xprop )) { - unset( $this->propdelix[$propName] ); - return FALSE; - } - return TRUE; - } - return $return; - } -/** - * get calendar property value/params - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.09 - 2015-03-29 - * @param string $propName - * @param int $propix specific property in case of multiply occurences - * @param bool $inclParam - * @uses vcalendar::$propix - * @uses vcalendar::$components - * @uses calendarComponent::$objName - * @uses iCalUtilityFunctions::$vComps - * @uses iCalUtilityFunctions::$mProps1 - * @uses calendarComponent::_getProperties() - * @uses calendarComponent::getProperty() - * @uses iCalUtilityFunctions::_geo2str2() - * @uses iCalUtilityFunctions::$geoLatFmt - * @uses iCalUtilityFunctions::$geoLongFmt - * @uses iCalUtilityFunctions::$fmt - * @uses vcalendar::$calscale - * @uses vcalendar::$method - * @uses vcalendar::$prodid - * @uses vcalendar::_makeProdid() - * @uses vcalendar::$version - * @uses vcalendar::$xprop - * @return mixed - */ - function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) { - $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP'; - if( 'X-PROP' == $propName ) { - if( empty( $propix )) - $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1; - $this->propix[$propName] = --$propix; - } - switch( $propName ) { - case 'ATTENDEE': - case 'CATEGORIES': - case 'CONTACT': - case 'DTSTART': - case 'GEOLOCATION': - case 'LOCATION': - case 'ORGANIZER': - case 'PRIORITY': - case 'RESOURCES': - case 'STATUS': - case 'SUMMARY': - case 'RECURRENCE-ID-UID': - case 'RELATED-TO': - case 'R-UID': - case 'UID': - case 'URL': - $output = array(); - foreach ( $this->components as $cix => $component) { - if( !in_array( $component->objName, iCalUtilityFunctions::$vComps )) - continue; - if( in_array( $propName, iCalUtilityFunctions::$mProps1 )) { - $component->_getProperties( $propName, $output ); - continue; - } - elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) { - if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' ))) - $content = $component->getProperty( 'UID' ); - } - elseif( 'GEOLOCATION' == $propName ) { - $content = ( FALSE === ( $loc = $component->getProperty( 'LOCATION' ))) ? '' : $loc.' '; - if( FALSE === ( $geo = $component->getProperty( 'GEO' ))) - continue; - $content .= iCalUtilityFunctions::_geo2str2( $geo['latitude'], iCalUtilityFunctions::$geoLatFmt ). - iCalUtilityFunctions::_geo2str2( $geo['longitude'], iCalUtilityFunctions::$geoLongFmt ).'/'; - } - elseif( FALSE === ( $content = $component->getProperty( $propName ))) - continue; - if(( FALSE === $content ) || empty( $content )) - continue; - elseif( is_array( $content )) { - if( isset( $content['year'] )) { - $key = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $content['year'], (int) $content['month'], (int) $content['day'] ); - if( !isset( $output[$key] )) - $output[$key] = 1; - else - $output[$key] += 1; - } - else { - foreach( $content as $partValue => $partCount ) { - if( !isset( $output[$partValue] )) - $output[$partValue] = $partCount; - else - $output[$partValue] += $partCount; - } - } - } // end elseif( is_array( $content )) { - elseif( !isset( $output[$content] )) - $output[$content] = 1; - else - $output[$content] += 1; - } // end foreach ( $this->components as $cix => $component) - if( !empty( $output )) - ksort( $output ); - return $output; - break; - case 'CALSCALE': - return ( !empty( $this->calscale )) ? $this->calscale : FALSE; - break; - case 'METHOD': - return ( !empty( $this->method )) ? $this->method : FALSE; - break; - case 'PRODID': - if( empty( $this->prodid )) - $this->_makeProdid(); - return $this->prodid; - break; - case 'VERSION': - return ( !empty( $this->version )) ? $this->version : FALSE; - break; - default: - if( $propName != 'X-PROP' ) { - if( !isset( $this->xprop[$propName] )) return FALSE; - return ( $inclParam ) ? array( $propName, $this->xprop[$propName] ) - : array( $propName, $this->xprop[$propName]['value'] ); - } - else { - if( empty( $this->xprop )) return FALSE; - $xpropno = 0; - foreach( $this->xprop as $xpropkey => $xpropvalue ) { - if( $propix == $xpropno ) - return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] ) - : array( $xpropkey, $this->xprop[$xpropkey]['value'] ); - else - $xpropno++; - } - unset( $this->propix[$propName] ); - return FALSE; // not found ?? - } - } - return FALSE; - } -/** - * general vcalendar property setting - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.2.13 - 2007-11-04 - * @param mixed $args variable number of function arguments, - * first argument is ALWAYS component name, - * second ALWAYS component value! - * @uses vcalendar::setCalscale() - * @uses vcalendar::setMethod() - * @uses vcalendar::setVersion() - * @uses vcalendar::setXprop() - * @return bool - */ - function setProperty () { - $numargs = func_num_args(); - if( 1 > $numargs ) - return FALSE; - $arglist = func_get_args(); - $arglist[0] = strtoupper( $arglist[0] ); - switch( $arglist[0] ) { - case 'CALSCALE': - return $this->setCalscale( $arglist[1] ); - case 'METHOD': - return $this->setMethod( $arglist[1] ); - case 'VERSION': - return $this->setVersion( $arglist[1] ); - default: - if( !isset( $arglist[1] )) $arglist[1] = null; - if( !isset( $arglist[2] )) $arglist[2] = null; - return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] ); - } - return FALSE; - } -/*********************************************************************************/ -/** - * get vcalendar config values or * calendar components - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.11.7 - 2012-01-12 - * @param mixed $config - * @uses vcalendar::getConfig() - * @uses vcalendar::$allowEmpty - * @uses vcalendar::$components - * @uses calendarComponent::_getProperties() - * @uses calendarComponent::$objName - * @uses calendarComponent::getProperty() - * @uses calendarComponent::_getConfig() - * @uses vcalendar::$url - * @uses vcalendar::$delimiter - * @uses vcalendar::$directory - * @uses vcalendar::$filename - * @uses vcalendar::$format - * @uses vcalendar::$language - * @uses vcalendar::$nl - * @uses vcalendar::$dtzid - * @uses vcalendar::$unique_id - * @return value - */ - function getConfig( $config = FALSE ) { - if( !$config ) { - $return = array(); - $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' ); - $return['DELIMITER'] = $this->getConfig( 'DELIMITER' ); - $return['DIRECTORY'] = $this->getConfig( 'DIRECTORY' ); - $return['FILENAME'] = $this->getConfig( 'FILENAME' ); - $return['DIRFILE'] = $this->getConfig( 'DIRFILE' ); - $return['FILESIZE'] = $this->getConfig( 'FILESIZE' ); - $return['FORMAT'] = $this->getConfig( 'FORMAT' ); - if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' ))) - $return['LANGUAGE'] = $lang; - $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' ); - $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' ); - if( FALSE !== ( $url = $this->getConfig( 'URL' ))) - $return['URL'] = $url; - $return['TZID'] = $this->getConfig( 'TZID' ); - return $return; - } - switch( strtoupper( $config )) { - case 'ALLOWEMPTY': - return $this->allowEmpty; - break; - case 'COMPSINFO': - unset( $this->compix ); - $info = array(); - foreach( $this->components as $cix => $component ) { - if( empty( $component )) continue; - $info[$cix]['ordno'] = $cix + 1; - $info[$cix]['type'] = $component->objName; - $info[$cix]['uid'] = $component->getProperty( 'uid' ); - $info[$cix]['props'] = $component->getConfig( 'propinfo' ); - $info[$cix]['sub'] = $component->getConfig( 'compsinfo' ); - } - return $info; - break; - case 'DELIMITER': - return $this->delimiter; - break; - case 'DIRECTORY': - if( empty( $this->directory ) && ( '0' != $this->directory )) - $this->directory = '.'; - return $this->directory; - break; - case 'DIRFILE': - return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' ); - break; - case 'FILEINFO': - return array( $this->getConfig( 'directory' ) - , $this->getConfig( 'filename' ) - , $this->getConfig( 'filesize' )); - break; - case 'FILENAME': - if( empty( $this->filename ) && ( '0' != $this->filename )) { - if( 'xcal' == $this->format ) - $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. . - else - $this->filename = date( 'YmdHis' ).'.ics'; - } - return $this->filename; - break; - case 'FILESIZE': - $size = 0; - if( empty( $this->url )) { - $dirfile = $this->getConfig( 'dirfile' ); - if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile )))) - $size = 0; - clearstatcache(); - } - return $size; - break; - case 'FORMAT': - return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal'; - break; - case 'LANGUAGE': - /* get language for calendar component as defined in [RFC 1766] */ - return $this->language; - break; - case 'NL': - case 'NEWLINECHAR': - return $this->nl; - break; - case 'TZID': - return $this->dtzid; - break; - case 'UNIQUE_ID': - return $this->unique_id; - break; - case 'URL': - if( !empty( $this->url )) - return $this->url; - else - return FALSE; - break; - } - } -/** - * general vcalendar config setting - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.20.2 - 2014-05-09 - * @param mixed $config - * @param string $value - * @uses vcalendar::setConfig() - * @uses vcalendar::$allowEmpty - * @uses vcalendar::$components - * @uses vcalendar::$url - * @uses vcalendar::$delimiter - * @uses vcalendar::$directory - * @uses vcalendar::$filename - * @uses vcalendar::$format - * @uses vcalendar::$language - * @uses vcalendar::$nl - * @uses vcalendar::$dtzid - * @uses vcalendar::$unique_id - * @uses vcalendar::_makeProdid() - * @uses vcalendar::$components - * @uses calendarComponent::setConfig() - * @uses calendarComponent::copy() - * @return void - */ - function setConfig( $config, $value = FALSE) { - if( is_array( $config )) { - $config = array_change_key_case( $config, CASE_UPPER ); - if( isset( $config['DELIMITER'] )) { - if( FALSE === $this->setConfig( 'DELIMITER', $config['DELIMITER'] )) - return FALSE; - unset( $config['DELIMITER'] ); - } - if( isset( $config['DIRECTORY'] )) { - if( FALSE === $this->setConfig( 'DIRECTORY', $config['DIRECTORY'] )) - return FALSE; - unset( $config['DIRECTORY'] ); - } - foreach( $config as $cKey => $cValue ) { - if( FALSE === $this->setConfig( $cKey, $cValue )) - return FALSE; - } - return TRUE; - } - else - $res = FALSE; - $config = strtoupper( $config ); - switch( $config ) { - case 'ALLOWEMPTY': - $this->allowEmpty = $value; - $subcfg = array( 'ALLOWEMPTY' => $value ); - $res = TRUE; - break; - case 'DELIMITER': - $this->delimiter = $value; - return TRUE; - break; - case 'DIRECTORY': - if( FALSE === ( $value = realpath( rtrim( trim( $value ), $this->delimiter )))) - return FALSE; - else { - /* local directory */ - $this->directory = $value; - $this->url = null; - return TRUE; - } - break; - case 'FILENAME': - $value = trim( $value ); - $dirfile = $this->directory.$this->delimiter.$value; - if( file_exists( $dirfile )) { - /* local file exists */ - if( is_readable( $dirfile ) || is_writable( $dirfile )) { - clearstatcache(); - $this->filename = $value; - return TRUE; - } - else - return FALSE; - } - elseif( is_readable( $this->directory ) || is_writable( $this->directory )) { - /* read- or writable directory */ - clearstatcache(); - $this->filename = $value; - return TRUE; - } - else - return FALSE; - break; - case 'FORMAT': - $value = trim( strtolower( $value )); - if( 'xcal' == $value ) { - $this->format = 'xcal'; - $this->attributeDelimiter = $this->nl; - $this->valueInit = null; - } - else { - $this->format = null; - $this->attributeDelimiter = ';'; - $this->valueInit = ':'; - } - $subcfg = array( 'FORMAT' => $value ); - $res = TRUE; - break; - case 'LANGUAGE': // set language for calendar component as defined in [RFC 1766] - $value = trim( $value ); - $this->language = $value; - $this->_makeProdid(); - $subcfg = array( 'LANGUAGE' => $value ); - $res = TRUE; - break; - case 'NL': - case 'NEWLINECHAR': - $this->nl = $value; - if( 'xcal' == $value ) { - $this->attributeDelimiter = $this->nl; - $this->valueInit = null; - } - else { - $this->attributeDelimiter = ';'; - $this->valueInit = ':'; - } - $subcfg = array( 'NL' => $value ); - $res = TRUE; - break; - case 'TZID': - $this->dtzid = $value; - $subcfg = array( 'TZID' => $value ); - $res = TRUE; - break; - case 'UNIQUE_ID': - $value = trim( $value ); - $this->unique_id = $value; - $this->_makeProdid(); - $subcfg = array( 'UNIQUE_ID' => $value ); - $res = TRUE; - break; - case 'URL': - /* remote file - URL */ - $value = str_replace( array( 'HTTP://', 'WEBCAL://', 'webcal://' ), 'http://', trim( $value )); - $value = str_replace( 'HTTPS://', 'https://', trim( $value )); - if(( 'http://' != substr( $value, 0, 7 )) && ( 'https://' != substr( $value, 0, 8 ))) - return FALSE; - $this->directory = '.'; - $this->url = $value; - if( '.ics' != strtolower( substr( $value, -4 ))) - unset( $this->filename ); - else - $this->filename = basename( $value ); - return TRUE; - break; - default: // any unvalid config key.. . - return TRUE; - } - if( !$res ) return FALSE; - if( isset( $subcfg ) && !empty( $this->components )) { - foreach( $subcfg as $cfgkey => $cfgvalue ) { - foreach( $this->components as $cix => $component ) { - $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE ); - if( !$res ) - break 2; - $this->components[$cix] = $component->copy(); // PHP4 compliant - } - } - } - return $res; - } -/*********************************************************************************/ -/** - * add calendar component to container - * - * alias to setComponent - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 1.x.x - 2007-04-24 - * @param object $component calendar component - * @uses vcalendar::setComponent() - * @return void - */ - function addComponent( $component ) { - $this->setComponent( $component ); - } -/** - * delete calendar component from container - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.8 - 2011-03-15 - * @param mixed $arg1 ordno / component type / component uid - * @param mixed $arg2 optional, ordno if arg1 = component type - * @uses vcalendar::$components - * @uses calendarComponent::$objName - * @uses calendarComponent::getProperty() - * @return void - */ - function deleteComponent( $arg1, $arg2=FALSE ) { - $argType = $index = null; - if ( ctype_digit( (string) $arg1 )) { - $argType = 'INDEX'; - $index = (int) $arg1 - 1; - } - elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { - $argType = strtolower( $arg1 ); - $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0; - } - $cix1dC = 0; - foreach ( $this->components as $cix => $component) { - if( empty( $component )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) { - unset( $this->components[$cix] ); - return TRUE; - } - elseif( $argType == $component->objName ) { - if( $index == $cix1dC ) { - unset( $this->components[$cix] ); - return TRUE; - } - $cix1dC++; - } - elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { - unset( $this->components[$cix] ); - return TRUE; - } - } - return FALSE; - } -/** - * get calendar component from container - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param mixed $arg1 optional, ordno/component type/ component uid - * @param mixed $arg2 optional, ordno if arg1 = component type - * @uses vcalendar::$compix - * @uses vcalendar::$components - * @uses iCalUtilityFunctions::$dateProps - * @uses iCalUtilityFunctions::$otherProps - * @uses iCalUtilityFunctions::$mProps1 - * @uses calendarComponent::copy() - * @uses calendarComponent::$objName - * @uses calendarComponent::_getProperties() - * @uses calendarComponent::getProperty() - * @uses iCalUtilityFunctions::$fmt - * @return object - */ - function getComponent( $arg1=FALSE, $arg2=FALSE ) { - $index = $argType = null; - if ( !$arg1 ) { // first or next in component chain - $argType = 'INDEX'; - $index = $this->compix['INDEX'] = ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1; - } - elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) - $arg2 = implode( '-', array_keys( $arg1 )); - $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1; - } - elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain - $argType = 'INDEX'; - $index = (int) $arg1; - unset( $this->compix ); - } - elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name - unset( $this->compix['INDEX'] ); - $argType = strtolower( $arg1 ); - if( !$arg2 ) - $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1; - elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 )) - $index = (int) $arg2; - } - elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument - if( !$arg2 ) - $index = $this->compix[$arg1] = ( isset( $this->compix[$arg1] )) ? $this->compix[$arg1] + 1 : 1; - elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 )) - $index = (int) $arg2; - } - if( isset( $index )) - $index -= 1; - $ckeys = array_keys( $this->components ); - if( !empty( $index) && ( $index > end( $ckeys ))) - return FALSE; - $cix1gC = 0; - foreach ( $this->components as $cix => $component) { - if( empty( $component )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) - return $component->copy(); - elseif( $argType == $component->objName ) { - if( $index == $cix1gC ) - return $component->copy(); - $cix1gC++; - } - elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) - $hit = array(); - $arg1 = array_change_key_case( $arg1, CASE_UPPER ); - foreach( $arg1 as $pName => $pValue ) { - if( !in_array( $pName, iCalUtilityFunctions::$dateProps ) && !in_array( $pName, iCalUtilityFunctions::$otherProps )) - continue; - if( in_array( $pName, iCalUtilityFunctions::$mProps1 )) { // multiple occurrence - $propValues = array(); - $component->_getProperties( $pName, $propValues ); - $propValues = array_keys( $propValues ); - $hit[] = ( in_array( $pValue, $propValues )) ? TRUE : FALSE; - continue; - } // end if(.. .// multiple occurrence - if( FALSE === ( $value = $component->getProperty( $pName ))) { // single occurrence - $hit[] = FALSE; // missing property - continue; - } - if( 'SUMMARY' == $pName ) { // exists within (any case) - $hit[] = ( FALSE !== stripos( $value, $pValue )) ? TRUE : FALSE; - continue; - } - if( in_array( $pName, iCalUtilityFunctions::$dateProps )) { - $valuedate = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $value['year'], (int) $value['month'], (int) $value['day'] ); - if( 8 < strlen( $pValue )) { - if( isset( $value['hour'] )) { - if( 'T' == substr( $pValue, 8, 1 )) - $pValue = str_replace( 'T', '', $pValue ); - $valuedate .= sprintf( iCalUtilityFunctions::$fmt['His'], (int) $value['hour'], (int) $value['min'], (int) $value['sec'] ); - } - else - $pValue = substr( $pValue, 0, 8 ); - } - $hit[] = ( $pValue == $valuedate ) ? TRUE : FALSE; - continue; - } - elseif( !is_array( $value )) - $value = array( $value ); - foreach( $value as $part ) { - $part = ( FALSE !== strpos( $part, ',' )) ? explode( ',', $part ) : array( $part ); - foreach( $part as $subPart ) { - if( $pValue == $subPart ) { - $hit[] = TRUE; - continue 3; - } - } - } // end foreach( $value as $part ) - $hit[] = FALSE; // no hit in property - } // end foreach( $arg1 as $pName => $pValue ) - if( in_array( TRUE, $hit )) { - if( $index == $cix1gC ) - return $component->copy(); - $cix1gC++; - } - } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] ) - elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { // UID - if( $index == $cix1gC ) - return $component->copy(); - $cix1gC++; - } - } // end foreach ( $this->components.. . - /* not found.. . */ - unset( $this->compix ); - return FALSE; - } -/** - * create new calendar component, already included within calendar - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.20.4 - 2014-08-24 - * @param string $compType component type - * @uses vcalendar::$components - * @return object - */ - function & newComponent( $compType ) { - $config = $this->getConfig(); - $keys = array_keys( $this->components ); - $ix = ( empty( $keys )) ? 0 : end( $keys) + 1; - switch( strtoupper( $compType )) { - case 'EVENT': - case 'VEVENT': - $this->components[$ix] = new vevent( $config ); - break; - case 'TODO': - case 'VTODO': - $this->components[$ix] = new vtodo( $config ); - break; - case 'JOURNAL': - case 'VJOURNAL': - $this->components[$ix] = new vjournal( $config ); - break; - case 'FREEBUSY': - case 'VFREEBUSY': - $this->components[$ix] = new vfreebusy( $config ); - break; - case 'TIMEZONE': - case 'VTIMEZONE': - array_unshift( $this->components, new vtimezone( $config )); - $ix = 0; - break; - default: - return FALSE; - } - return $this->components[$ix]; - } -/** - * select components from calendar on date or selectOption basis - * - * Ensure DTSTART is set for every component. - * No date controls occurs. - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-31 - * @param mixed $startY optional, (int) start Year, default current Year - * ALT. (obj) start date (datetime) - * ALT. array selecOptions ( *[ => ] ) - * @param mixed $startM optional, (int) start Month, default current Month - * ALT. (obj) end date (datetime) - * @param int $startD optional, start Day, default current Day - * @param int $endY optional, end Year, default $startY - * @param int $endM optional, end Month, default $startM - * @param int $endD optional, end Day, default $startD - * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s) - * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][] - * TRUE => output : array[] (ignores split) - * @param bool $any optional, TRUE (default) - select component(-s) that occurs within period - * FALSE - only component(-s) that starts within period - * @param bool $split optional, TRUE (default) - one component copy every DAY it occurs during the - * period (implies flat=FALSE) - * FALSE - one occurance of component only in output array - * @uses vcalendar::$components - * @uses vcalendar::selectComponents2() - * @uses iCalUtilityFunctions::$vComps - * @uses calendarComponent::$objName - * @uses calendarComponent::getProperty() - * @uses iCaldateTime::factory() - * @uses iCaldateTime::getTimezoneName() - * @uses iCaldateTime::getTime() - * @uses iCalUtilityFunctions::$fmt - * @uses iCaldateTime::$SCbools - * @uses iCaldateTime::format() - * @uses iCalUtilityFunctions::_strDate2arr() - * @uses iCalUtilityFunctions::_recur2date() - * @uses iCalUtilityFunctions::_inScope() - * @uses calendarComponent::copy() - * @uses calendarComponent::setProperty() - * @uses iCalUtilityFunctions::$fmt - * @uses calendarComponent::deleteProperty() - * @uses iCalUtilityFunctions::_setSortArgs() - * @uses iCalUtilityFunctions::_cmpfcn() - * @return array or FALSE - */ - function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) { - /* check if empty calendar */ - if( 0 >= count( $this->components )) return FALSE; - if( is_array( $startY )) - return $this->selectComponents2( $startY ); - /* check default dates */ - if( is_a( $startY, 'DateTime' ) && is_a( $startM, 'DateTime' )) { - $endY = $startM->format( 'Y' ); - $endM = $startM->format( 'm' ); - $endD = $startM->format( 'd' ); - $startD = $startY->format( 'd' ); - $startM = $startY->format( 'm' ); - $startY = $startY->format( 'Y' ); - } - else { - if( ! $startY ) $startY = date( 'Y' ); - if( ! $startM ) $startM = date( 'm' ); - if( ! $startD ) $startD = date( 'd' ); - if( ! $endY ) $endY = $startY; - if( ! $endM ) $endM = $startM; - if( ! $endD ) $endD = $startD; - } -// echo "selectComp args={$startY}-{$startM}-{$startD} - {$endY}-{$endM}-{$endD}
\n"; $tcnt = 0;// test ### - /* check component types */ - if( empty( $cType )) - $cType = iCalUtilityFunctions::$vComps; - else { - if( ! is_array( $cType )) - $cType = array( $cType ); - $cType = array_map( 'strtolower', $cType ); - foreach( $cType as $cix => $theType ) { - if( !in_array( $theType, iCalUtilityFunctions::$vComps )) - $cType[$cix] = 'vevent'; - } - $cType = array_unique( $cType ); - } - if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination - $split = FALSE; - if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination - $split = FALSE; - /* iterate components */ - $result = array(); - $this->sort( 'UID' ); - $compUIDcmp = null; - $exdatelist = $recurridList = array(); - $intervalP1D = new DateInterval( 'P1D' ); - foreach ( $this->components as $cix => $component ) { - if( empty( $component )) continue; - /* deselect unvalid type components */ - if( !in_array( $component->objName, $cType )) - continue; - unset( $compStart, $compEnd ); - /* select start from dtstart or due if dtstart is missing */ - $prop = $component->getProperty( 'dtstart', FALSE, TRUE ); - if( empty( $prop ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $prop = $component->getProperty( 'due', FALSE, TRUE )))) - continue; - if( empty( $prop )) - continue; - /* get UID */ - $compUID = $component->getProperty( 'UID' ); - if( $compUIDcmp != $compUID ) { - $compUIDcmp = $compUID; - $exdatelist = $recurridList = array(); - } - $recurrid = FALSE; -// file_put_contents( '/opt/work/iCal/iCalcreator/iCalcreator-2.20.x/log/log.txt', "#$cix".PHP_EOL.var_export( $component, TRUE ).PHP_EOL.PHP_EOL, FILE_APPEND ); // test ### - $compStart = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'] ); - $dtstartTz = $compStart->getTimezoneName(); - if( isset( $prop['params']['VALUE'] ) && ( 'DATE' == $prop['params']['VALUE'] )) - $compStartHis = ''; - else { - $his = $compStart->getTime(); - $compStartHis = sprintf( iCalUtilityFunctions::$fmt['His'], (int) $his[0], (int) $his[1], (int) $his[2] ); - } - /* get end date from dtend/due/duration properties */ - if( FALSE !== ( $prop = $component->getProperty( 'dtend', FALSE, TRUE ))) { - $compEnd = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz ); - $compEnd->SCbools[ 'dtendExist'] = TRUE; - } - if( empty( $prop ) && ( $component->objName == 'vtodo' ) && ( FALSE !== ( $prop = $component->getProperty( 'due', FALSE, TRUE )))) { - $compEnd = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz ); - $compEnd->SCbools[ 'dueExist'] = TRUE; - } - if( empty( $prop ) && ( FALSE !== ( $prop = $component->getProperty( 'duration', FALSE, TRUE, TRUE )))) { // in dtend (array) format - $compEnd = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz ); - $compEnd->SCbools[ 'durationExist'] = TRUE; - } - if( ! empty( $prop ) && ! isset( $prop['value']['hour'] )) { - /* a DTEND without time part denotes an end of an event that actually ends the day before, - for an all-day event DTSTART=20071201 DTEND=20071202, taking place 20071201!!! */ - $compEnd->SCbools[ 'endAllDayEvent'] = TRUE; - $compEnd->modify( '-1 day' ); - $compEnd->setTime( 23, 59, 59 ); - } - unset( $prop ); - if( empty( $compEnd )) { - $compDuration = FALSE; - $compEnd = clone $compStart; - $compEnd->setTime( 23, 59, 59 ); // 23:59:59 the same day as start - } - else { - if( $compEnd->format( 'Ymd' ) < $compStart->format( 'Ymd' )) { // MUST be after start date!! - $compEnd = clone $compStart; - $compEnd->setTime( 23, 59, 59 ); // 23:59:59 the same day as start or ??? - } - $compDuration = $compStart->diff( $compEnd ); // DateInterval - } - /* check recurrence-id (note, a missing sequence is the same as sequence=0 so don't test for sequence), to alter when hit dtstart/recurlist */ - if( FALSE !== ( $prop = $component->getProperty( 'recurrence-id', FALSE, TRUE ))) { - $recurrid = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz ); - $rangeSet = ( isset( $prop['params']['RANGE'] ) && ( 'THISANDFUTURE' == $prop['params']['RANGE'] )) ? TRUE : FALSE; - $recurridList[$recurrid->key] = array( clone $compStart, clone $compEnd, $compDuration, $rangeSet ); // change recur this day to new YmdHis/duration/range -// echo "adding comp no:$cix with date=".$compStart->format(iCalUtilityFunctions::$fmt['YmdHis2e'])." to recurridList id={$recurrid->key}, newDate={$compStart->key}
\n"; // test ### - unset( $prop ); - continue; // ignore any other props in the component - } // end recurrence-id/sequence test -// else echo "comp no:$cix with date=".$compStart->format().", NO recurrence-id
\n"; // test ### - ksort( $recurridList, SORT_STRING ); -// echo 'recurridList='.implode(', ', array_keys( $recurridList ))."
\n"; // test ### - $fcnStart = clone $compStart; - $fcnStart->setDate((int) $startY, (int) $startM, (int) $startD ); - $fcnStart->setTime( 0, 0, 0 ); - $fcnEnd = clone $compEnd; - $fcnEnd->setDate((int) $endY, (int) $endM, (int) $endD ); - $fcnEnd->setTime( 23, 59, 59 ); -// echo 'compStart='.$compStart->format().', compEnd'.$compEnd->format(); if($compDuration)echo ', interval='.$compDuration->format( iCalUtilityFunctions::$fmt['durDHis'] ); echo "
\n"; $tcnt = 0;// test ### - /* ************************************************************* - make a list of optional exclude dates for component occurence from exrule and exdate - *********************************************************** */ - $workStart = clone $compStart; - $workStart->sub( $compDuration ? $compDuration : $intervalP1D ); - $workEnd = clone $fcnEnd; - $workEnd->add( $compDuration ? $compDuration : $intervalP1D ); - while( FALSE !== ( $prop = $component->getProperty( 'EXRULE' ))) { - $exdatelist2 = array(); - if( isset( $prop['UNTIL']['hour'] )) { // convert until date to dtstart timezone - $until = iCaldateTime::factory( $prop['UNTIL'], array( 'TZID' => 'UTC' ), null, $dtstartTz ); - $until = $until->format(); - iCalUtilityFunctions::_strDate2arr( $until ); - $prop['UNTIL'] = $until; - } - iCalUtilityFunctions::_recur2date( $exdatelist2, $prop, $compStart, $workStart, $workEnd ); - foreach( $exdatelist2 as $k => $v ) - $exdatelist[$k.$compStartHis] = $v; // point out exact every excluded ocurrence (incl. opt. His) - unset( $until, $exdatelist2 ); - } - while( FALSE !== ( $prop = $component->getProperty( 'EXDATE', FALSE, TRUE ))) { // - start check exdate - foreach( $prop['value'] as $exdate ) { - $exdate = iCaldateTime::factory( $exdate, $prop['params'], $exdate, $dtstartTz ); - $exdatelist[$exdate->key] = TRUE; - } // end - foreach( $exdate as $exdate ) - } // end - check exdate - unset( $prop, $exdate ); -// echo 'exdatelist=' .implode(', ', array_keys( $exdatelist )) ."
\n"; // test ### - /* ************************************************************* - select only components within.. . - *********************************************************** */ - $xRecurrence = 1; - if(( ! $any && iCalUtilityFunctions::_inScope( $compStart, $fcnStart, $compStart, $fcnEnd, $compStart->dateFormat )) || - ( $any && iCalUtilityFunctions::_inScope( $fcnEnd, $compStart, $fcnStart, $compEnd, $compStart->dateFormat ))) { - /* add the selected component (WITHIN valid dates) to output array */ - if( $flat ) { // any=true/false, ignores split - if( !$recurrid ) - $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id) - } - elseif( $split ) { // split the original component -// echo 'split org.:'.$compStart->format().' < '.$fcnStart->format( 'Ymd His e' )."
\n"; // test ### - if( $compStart->format( iCalUtilityFunctions::$fmt['YmdHis2'] ) < $fcnStart->format( iCalUtilityFunctions::$fmt['YmdHis2'] )) - $rstart = clone $fcnStart; - else - $rstart = clone $compStart; - if( $compEnd->format( iCalUtilityFunctions::$fmt['YmdHis2'] ) > $fcnEnd->format( iCalUtilityFunctions::$fmt['YmdHis2'] )) - $rend = clone $fcnEnd; - else - $rend = clone $compEnd; -// echo "going to test comp no:$cix, rstart=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )." (key={$rstart->key}), end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."
\n"; // test ### - if( ! isset( $exdatelist[$rstart->key] )) { // not excluded in exrule/exdate - if( isset( $recurridList[$rstart->key] )) { // change start day to new YmdHis/duration - $k = $rstart->key; -// echo "recurridList HIT, key={$k}, recur Date=".$recurridList[$k][0]->key."
\n"; // test ### - $rstart = clone $recurridList[$k][0]; - $startHis = $rstart->getTime(); - $rend = clone $rstart; - if( FALSE !== $recurridList[$k][2] ) - $rend->add( $recurridList[$k][2] ); - elseif( FALSE !== $compDuration ) - $rend->add( $compDuration ); - $endHis = $rend->getTime(); - unset( $recurridList[$k] ); - } - else { - $startHis = $compStart->getTime(); - $endHis = $compEnd->getTime(); - } -// echo "_____testing comp no:$cix, rstart=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )." (key={$rstart->key}), end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."
\n"; // test ### - $cnt = 0; // exclude any recurrence START date, found in exdatelist or recurridList but accept the reccurence-id comp itself - $occurenceDays = 1 + (int) $rstart->diff( $rend )->format( '%a' ); // count the days (incl start day) - while( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) <= $rend->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { - $cnt += 1; - if( 1 < $occurenceDays ) - $component->setProperty( 'X-OCCURENCE', sprintf( iCalUtilityFunctions::$fmt['dayOfDays'], $cnt, $occurenceDays )); - if( 1 < $cnt ) - $rstart->setTime( 0, 0, 0 ); - else { - $rstart->setTime( $startHis[0], $startHis[1], $startHis[2] ); - $exdatelist[$rstart->key] = $compDuration; // make sure to exclude start day from the recurrence pattern - } - $component->setProperty( 'X-CURRENT-DTSTART', $rstart->format( $compStart->dateFormat )); - if( FALSE !== $compDuration ) { - $propName = ( isset( $compEnd->SCbools[ 'dueExist'] )) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND'; - if( $cnt < $occurenceDays ) - $rstart->setTime( 23, 59, 59 ); - else - $rstart->setTime( $endHis[0], $endHis[1], $endHis[2] ); - $component->setProperty( $propName, $rstart->format( $compEnd->dateFormat )); - } - $result[(int)$rstart->format( 'Y' )][(int)$rstart->format( 'm' )][(int)$rstart->format( 'd' )][$compUID] = $component->copy(); // copy to output - $rstart->add( $intervalP1D ); - } // end while(( $rstart->format( 'Ymd' ) < $rend->format( 'Ymd' )) - unset( $cnt, $occurenceDays ); - } // end if( ! isset( $exdatelist[$rstart->key] )) -// else echo "skip no:$cix with date=".$compStart->format()."
\n"; // test ### - unset( $rstart, $rend ); - } // end elseif( $split ) - else use component date - else { // !$flat && !$split, i.e. no flat array and DTSTART within period - $tstart = ( isset( $recurridList[$compStart->key] )) ? clone $recurridList[$k][0] : clone $compStart; -// echo "going to test comp no:$cix with checkDate={$compStart->key} with recurridList=".implode(',',array_keys($recurridList)); // test ### - if( ! $any || ! isset( $exdatelist[$tstart->key] )) { // exclude any recurrence date, found in exdatelist -// echo " and copied to output
\n"; // test ### - $result[(int)$tstart->format( 'Y' )][(int)$tstart->format( 'm' )][(int)$tstart->format( 'd' )][$compUID] = $component->copy(); // copy to output - } - unset( $tstart ); - } - } // end (dt)start within the period OR occurs within the period - /* ************************************************************* - if 'any' components, check components with reccurrence rules, removing all excluding dates - *********************************************************** */ - if( TRUE === $any ) { - $recurlist = array(); - /* make a list of optional repeating dates for component occurence, rrule, rdate */ - while( FALSE !== ( $prop = $component->getProperty( 'RRULE' ))) { // get all rrule dates (multiple values allowed) - $recurlist2 = array(); - if( isset( $prop['UNTIL']['hour'] )) { // convert $rrule['UNTIL'] to the same timezone as DTSTART !! - $until = iCaldateTime::factory( $prop['UNTIL'], array( 'TZID' => 'UTC' ), null, $dtstartTz ); - $until = $until->format(); - iCalUtilityFunctions::_strDate2arr( $until ); - $prop['UNTIL'] = $until; - } - iCalUtilityFunctions::_recur2date( $recurlist2, $prop, $compStart, $workStart, $workEnd ); - foreach( $recurlist2 as $recurkey => $recurvalue ) { // recurkey=Ymd - $recurkey .= $compStartHis; // add opt His - if( ! isset( $exdatelist[$recurkey] )) - $recurlist[$recurkey] = $compDuration; // DateInterval or FALSE - } - unset( $prop, $until, $recurlist2 ); - } - $workStart = clone $fcnStart; - $workStart->sub( $compDuration ? $compDuration : $intervalP1D ); - $format = $compStart->dateFormat; - while( FALSE !== ( $prop = $component->getProperty( 'RDATE', FALSE, TRUE ))) { - $rdateFmt = ( isset( $prop['params']['VALUE'] )) ? $prop['params']['VALUE'] : 'DATETIME'; - $params = $prop['params']; - $prop = $prop['value']; - foreach( $prop as $theRdate ) { - if( 'PERIOD' == $rdateFmt ) { // all days within PERIOD - $rdate = iCaldateTime::factory( $theRdate[0], $params, $theRdate[0], $dtstartTz ); - if( ! iCalUtilityFunctions::_inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) || isset( $exdatelist[$rdate->key] )) - continue; - if( isset( $theRdate[1]['year'] )) // date-date period end - $recurlist[$rdate->key] = $rdate->diff( iCaldateTime::factory( $theRdate[1], $params, $theRdate[1], $dtstartTz )); - else // period duration - $recurlist[$rdate->key] = new DateInterval( iCalUtilityFunctions::_duration2str( $theRdate[1] )); - } // end if( 'PERIOD' == $rdateFmt ) - elseif( 'DATE' == $rdateFmt ) { // single recurrence, date - $rdate = iCaldateTime::factory( $theRdate, array_merge( $params, array( 'TZID' => $dtstartTz )), null, $dtstartTz ); - if( iCalUtilityFunctions::_inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) && ! isset( $exdatelist[$rdate->key] )) - $recurlist[$rdate->key.$compStartHis] = $compDuration; // set start date for recurrence + DateInterval/FALSE (+opt His) - } // end DATE - else { // start DATETIME - $rdate = iCaldateTime::factory( $theRdate, $params, $theRdate, $dtstartTz ); - if( iCalUtilityFunctions::_inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) && ! isset( $exdatelist[$rdate->key] )) - $recurlist[$rdate->key] = $compDuration; // set start datetime for recurrence DateInterval/FALSE - } // end DATETIME - } // end foreach( $prop as $theRdate ) - } // end while( FALSE !== ( $prop = $component->getProperty( 'rdate', FALSE, TRUE ))) - unset( $prop, $workStart, $format, $theRdate, $rdate, $rend ); - foreach( $recurridList as $rKey => $rVal ) { // check for recurrence-id, i.e. alter recur Ymd[His] and duration - if( isset( $recurlist[$rKey] )) { - unset( $recurlist[$rKey] ); - $recurlist[$rVal[0]->key] = ( FALSE !== $rVal[2] ) ? $rVal[2] : $compDuration; -// echo "alter recurfrom {$rKey} to {$rVal[0]->key} ";if(FALSE!==$dur)echo " ({$dur->format( '%a days, %h-%i-%s' )})";echo "
\n"; // test ### - } - } - ksort( $recurlist, SORT_STRING ); -// echo 'recurlist=' .implode(', ', array_keys( $recurlist )) ."
\n"; // test ### -// echo 'recurridList=' .implode(', ', array_keys( $recurridList )) ."
\n"; // test ### - /* ************************************************************* - output all remaining components in recurlist - *********************************************************** */ - if( 0 < count( $recurlist )) { - $component2 = $component->copy(); - $compUID = $component2->getProperty( 'UID' ); - $workStart = clone $fcnStart; - $workStart->sub( $compDuration ? $compDuration : $intervalP1D ); - $YmdOld = null; - foreach( $recurlist as $recurkey => $durvalue ) { - if( $YmdOld == substr( $recurkey, 0, 8 )) // skip overlapping recur the same day, i.e. RDATE before RRULE - continue; - $YmdOld = substr( $recurkey, 0, 8 ); - $rstart = clone $compStart; - $rstart->setDate((int) substr( $recurkey, 0, 4 ), (int) substr( $recurkey, 4, 2 ), (int) substr( $recurkey, 6, 2 )); - $rstart->setTime((int) substr( $recurkey, 8, 2 ), (int) substr( $recurkey, 10, 2 ), (int) substr( $recurkey, 12, 2 )); -// echo "recur start=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."
\n"; // test ###; - /* add recurring components within valid dates to output array, only start date set */ - if( $flat ) { - if( !isset( $result[$compUID] )) // only one comp - $result[$compUID] = $component2->copy(); // copy to output - } - /* add recurring components within valid dates to output array, split for each day */ - elseif( $split ) { - $rend = clone $rstart; - if( FALSE !== $durvalue ) - $rend->add( $durvalue ); - if( $rend->format( iCalUtilityFunctions::$fmt['Ymd2'] ) > $fcnEnd->format( iCalUtilityFunctions::$fmt['Ymd2'] )) - $rend = clone $fcnEnd; -// echo "recur 1={$recurkey}, start=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] ).", end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] );if($durvalue) echo ", duration=".$durvalue->format( iCalUtilityFunctions::$fmt['durDHis'] );echo "
\n"; // test ### - $xRecurrence += 1; - $cnt = 0; - $occurenceDays = 1 + (int) $rstart->diff( $rend )->format( '%a' ); // count the days (incl start day) - while( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) <= $rend->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { // iterate.. . - $cnt += 1; - if( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) < $fcnStart->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { // date before dtstart -// echo "recur 3, start=".$rstart->format( 'Y-m-d H:i:s' )." >= fcnStart=".$fcnStart->format( 'Y-m-d H:i:s' )."
\n"; // test ### - $rstart->add( $intervalP1D ); - $rstart->setTime( 0, 0, 0 ); - continue; - } - elseif( 2 == $cnt ) - $rstart->setTime( 0, 0, 0 ); - $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); - if( 1 < $occurenceDays ) - $component2->setProperty( 'X-OCCURENCE', sprintf( iCalUtilityFunctions::$fmt['dayOfDays'], $cnt, $occurenceDays )); - else - $component2->deleteProperty( 'X-OCCURENCE' ); - $component2->setProperty( 'X-CURRENT-DTSTART', $rstart->format( $compStart->dateFormat )); - $propName = ( isset( $compEnd->SCbools[ 'dueExist'] )) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND'; - if( FALSE !== $durvalue ) { - if( $cnt < $occurenceDays ) - $rstart->setTime( 23, 59, 59 ); - else { - $His = $rend->getTime(); // set end time - $rstart->setTime( $His[0], $His[1], $His[2] ); - } - $component2->setProperty( $propName, $rstart->format( $compEnd->dateFormat )); -// echo "checking date, (day {$cnt} of {$occurenceDays}), _end_=".$rstart->format( 'Y-m-d H:i:s e' )."
"; // test ###; - } - else - $component2->deleteProperty( $propName ); - $result[(int)$rstart->format( 'Y' )][(int)$rstart->format( 'm' )][(int)$rstart->format( 'd' )][$compUID] = $component2->copy(); // copy to output - $rstart->add( $intervalP1D ); - } // end while( $rstart->format( 'Ymd' ) <= $rend->format( 'Ymd' )) - unset( $rstart, $rend ); - } // end elseif( $split ) - elseif( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) >= $fcnStart->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { - $xRecurrence += 1; // date within period //* flat=FALSE && split=FALSE => one comp every recur startdate *// - $component2->setProperty( 'X-RECURRENCE', $xRecurrence ); - $component2->setProperty( 'X-CURRENT-DTSTART', $rstart->format( $compStart->dateFormat )); - $propName = ( isset( $compEnd->SCbools[ 'dueExist'] )) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND'; - if( FALSE !== $durvalue ) { - $rstart->add( $durvalue ); - $component2->setProperty( $propName, $rstart->format( $compEnd->dateFormat )); - } - else - $component2->deleteProperty( $propName ); - $result[(int)$rstart->format( 'Y' )][(int)$rstart->format( 'm' )][(int)$rstart->format( 'd' )][$compUID] = $component2->copy(); // copy to output - } // end elseif( $rstart >= $fcnStart ) - unset( $rstart ); - } // end foreach( $recurlist as $recurkey => $durvalue ) - unset( $component2, $xRecurrence, $compUID, $workStart, $rstart ); - } // end if( 0 < count( $recurlist )) - } // end if( TRUE === $any ) - unset( $component ); - } // end foreach ( $this->components as $cix => $component ) - unset( $recurrid, $recurridList, $fcnStart, $fcnEnd, $compStart, $compEnd, $exdatelist, $recurlist ); // clean up - if( 0 >= count( $result )) - return FALSE; - elseif( !$flat ) { - foreach( $result as $y => $yeararr ) { - foreach( $yeararr as $m => $montharr ) { - foreach( $montharr as $d => $dayarr ) { - if( empty( $result[$y][$m][$d] )) - unset( $result[$y][$m][$d] ); - else { - $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index - if( 1 < count( $result[$y][$m][$d] )) { - foreach( $result[$y][$m][$d] as & $c ) // sort - iCalUtilityFunctions::_setSortArgs( $c ); - usort( $result[$y][$m][$d], array( 'iCalUtilityFunctions', '_cmpfcn' )); - } - } - } // end foreach( $montharr as $d => $dayarr ) - if( empty( $result[$y][$m] )) - unset( $result[$y][$m] ); - else - ksort( $result[$y][$m] ); - } // end foreach( $yeararr as $m => $montharr ) - if( empty( $result[$y] )) - unset( $result[$y] ); - else - ksort( $result[$y] ); - }// end foreach( $result as $y => $yeararr ) - if( empty( $result )) - unset( $result ); - else - ksort( $result ); - } // end elseif( !$flat ) - if( 0 >= count( $result )) - return FALSE; - return $result; - } -/** - * select components from calendar on based on specific property value(-s) - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param array $selectOptions (string) key => (mixed) value, (key=propertyName) - * @uses vcalendar::$components - * @uses calendarComponent::$objName - * @uses iCalUtilityFunctions::$vComps - * @uses calendarComponent::getProperty() - * @uses iCalUtilityFunctions::$otherProps - * @uses calendarComponent::copy() - * @uses iCalUtilityFunctions::$mProps1 - * @uses calendarComponent::_getProperties() - * @return array - */ - function selectComponents2( $selectOptions ) { -// $output = array(); - $selectOptions = array_change_key_case( $selectOptions, CASE_UPPER ); - foreach( $this->components as $cix => $component3 ) { - if( !in_array( $component3->objName, iCalUtilityFunctions::$vComps )) - continue; - $uid = $component3->getProperty( 'UID' ); - foreach( $selectOptions as $propName => $pvalue ) { - if( !in_array( $propName, iCalUtilityFunctions::$otherProps )) - continue; - if( !is_array( $pvalue )) - $pvalue = array( $pvalue ); - if(( 'UID' == $propName ) && in_array( $uid, $pvalue )) { - $output[$uid][] = $component3->copy(); - continue; - } - elseif( in_array( $propName, iCalUtilityFunctions::$mProps1 )) { - $propValues = array(); - $component3->_getProperties( $propName, $propValues ); - $propValues = array_keys( $propValues ); - foreach( $pvalue as $theValue ) { - if( in_array( $theValue, $propValues )) { // && !isset( $output[$uid] )) { - $output[$uid][] = $component3->copy(); - break; - } - } - continue; - } // end elseif( // multiple occurrence? - elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single occurrence - continue; - if( is_array( $d )) { - foreach( $d as $part ) { - if( in_array( $part, $pvalue ) && !isset( $output[$uid] )) - $output[$uid][] = $component3->copy(); - } - } - elseif(( 'SUMMARY' == $propName ) && !isset( $output[$uid] )) { - foreach( $pvalue as $pval ) { - if( FALSE !== stripos( $d, $pval )) { - $output[$uid][] = $component3->copy(); - break; - } - } - } - elseif( in_array( $d, $pvalue ) && !isset( $output[$uid] )) - $output[$uid][] = $component3->copy(); - } // end foreach( $selectOptions as $propName => $pvalue ) { - } // end foreach( $this->components as $cix => $component3 ) { - if( !empty( $output )) { - ksort( $output ); // uid order - $output2 = array(); - foreach( $output as $uid => $components ) { - foreach( $components as $component ) - $output2[] = $component; - } - $output = $output2; - } - return $output; - } -/** - * replace calendar component in vcalendar - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param object $component calendar component - * @uses calendarComponent::$objName - * @uses iCalUtilityFunctions::$vComps - * @uses vcalendar::setComponent() - * @uses calendarComponent::getProperty() - * @return bool - */ - function replaceComponent( $component ) { - if( in_array( $component->objName, iCalUtilityFunctions::$vComps )) - return $this->setComponent( $component, $component->getProperty( 'UID' )); - if(( 'vtimezone' != $component->objName ) || ( FALSE === ( $tzid = $component->getProperty( 'TZID' )))) - return FALSE; - foreach( $this->components as $cix => $comp ) { - if( 'vtimezone' != $component->objName ) - continue; - if( $tzid == $comp->getComponent( 'TZID' )) { - unset( $component->propix, $component->compix ); - $this->components[$cix] = $component; - return TRUE; - } - } - return FALSE; - } -/** - * add calendar component to calendar - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param object $component calendar component - * @param mixed $arg1 optional, ordno/component type/ component uid - * @param mixed $arg2 optional, ordno if arg1 = component type - * @uses calendarComponent::setConfig() - * @uses vcalendar::getConfig() - * @uses calendarComponent::$objName - * @uses calendarComponent::getProperty() - * @uses vcalendar::$components - * @uses iCalUtilityFunctions::$mComps - * @uses calendarComponent::copy() - * @return bool - */ - function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) { - $component->setConfig( $this->getConfig(), FALSE, TRUE ); - if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) { - /* make sure dtstamp and uid is set */ - $dummy1 = $component->getProperty( 'dtstamp' ); - $dummy2 = $component->getProperty( 'uid' ); - } - unset( $component->propix, $component->compix ); - if( !$arg1 ) { // plain insert, last in chain - $this->components[] = $component->copy(); - return TRUE; - } - $argType = $index = null; - if ( ctype_digit( (string) $arg1 )) { // index insert/replace - $argType = 'INDEX'; - $index = (int) $arg1 - 1; - } - elseif( in_array( strtolower( $arg1 ), iCalUtilityFunctions::$mComps )) { - $argType = strtolower( $arg1 ); - $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0; - } - // else if arg1 is set, arg1 must be an UID - $cix1sC = 0; - foreach ( $this->components as $cix => $component2) { - if( empty( $component2 )) continue; - if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace - $this->components[$cix] = $component->copy(); - return TRUE; - } - elseif( $argType == $component2->objName ) { // component Type index insert/replace - if( $index == $cix1sC ) { - $this->components[$cix] = $component->copy(); - return TRUE; - } - $cix1sC++; - } - elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace - $this->components[$cix] = $component->copy(); - return TRUE; - } - } - /* arg1=index and not found.. . insert at index .. .*/ - if( 'INDEX' == $argType ) { - $this->components[$index] = $component->copy(); - ksort( $this->components, SORT_NUMERIC ); - } - else /* not found.. . insert last in chain anyway .. .*/ - $this->components[] = $component->copy(); - return TRUE; - } -/** - * sort iCal compoments - * - * ascending sort on properties (if exist) x-current-dtstart, dtstart, - * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid if called without arguments, - * otherwise sorting on specific (argument) property values - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.11 - 2015-03-21 - * @param string $sortArg - * @uses vcalendar::$components - * @uses iCalUtilityFunctions::$otherProps - * @uses iCalUtilityFunctions::_setSortArgs() - * @uses iCalUtilityFunctions::_cmpfcn() - * @return void - */ - function sort( $sortArg=FALSE ) { - if( ! is_array( $this->components ) || ( 2 > count( $this->components ))) - return; - if( $sortArg ) { - $sortArg = strtoupper( $sortArg ); - if( ! in_array( $sortArg, iCalUtilityFunctions::$otherProps ) && ( 'DTSTAMP' != $sortArg )) - $sortArg = FALSE; - } - foreach( $this->components as & $c ) - iCalUtilityFunctions::_setSortArgs( $c, $sortArg ); - usort( $this->components, array( 'iCalUtilityFunctions', '_cmpfcn' )); - } -/** - * parse iCal text/file into vcalendar, components, properties and parameters - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.12 - 2014-03-10 - * @param mixed $unparsedtext strict rfc2445 formatted, single property string or array of property strings - * @param resource $context PHP resource context - * @uses iCalUtilityFunctions::convEolChar() - * @uses vcalendar::getConfig() - * @uses vcalendar::$components - * @uses calendarComponent::copy() - * @uses vevent::construct() - * @uses vfreebusy::construct() - * @uses vjournal::construct() - * @uses vtodo::construct() - * @uses vtimezone::construct() - * @uses vcalendar::$unparsed - * @uses iCalUtilityFunctions::_splitContent() - * @uses iCalUtilityFunctions::_strunrep() - * @uses vcalendar::setProperty() - * @uses calendarComponent::$unparsed - * @uses calendarComponent::parse() - * @return bool FALSE if error occurs during parsing - */ - function parse( $unparsedtext=FALSE, $context=null ) { - $nl = $this->getConfig( 'nl' ); - if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) { - /* directory+filename is set previously via setConfig url or directory+filename */ - if( FALSE === ( $file = $this->getConfig( 'url' ))) { - if( FALSE === ( $file = $this->getConfig( 'dirfile' ))) - return FALSE; /* err 1 */ - if( ! is_file( $file )) - return FALSE; /* err 2 */ - if( ! is_readable( $file )) - return FALSE; /* err 3 */ - } - /* READ FILE */ - if( ! empty( $context ) && filter_var( $file, FILTER_VALIDATE_URL ) && - ( FALSE === ( $rows = file_get_contents( $file, FALSE, $context )))) - return FALSE; /* err 6 */ - elseif( FALSE === ( $rows = file_get_contents( $file ))) - return FALSE; /* err 5 */ - } // end if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) - elseif( is_array( $unparsedtext )) - $rows = implode( '\n'.$nl, $unparsedtext ); - else - $rows = & $unparsedtext; - /* fix line folding */ - $rows = iCalUtilityFunctions::convEolChar( $rows, $nl ); - /* skip leading (empty/invalid) lines (and remove leading BOM chars etc) */ - foreach( $rows as $lix => $line ) { - if( FALSE !== stripos( $line, 'BEGIN:VCALENDAR' )) { - $rows[$lix] = 'BEGIN:VCALENDAR'; - break; - } - unset( $rows[$lix] ); - } - $rcnt = count( $rows ); - if( 3 > $rcnt ) /* err 10 */ - return FALSE; - /* skip trailing empty lines and ensure an end row */ - $lix = array_keys( $rows ); - $lix = end( $lix ); - while( 3 < $lix ) { - $tst = trim( $rows[$lix] ); - if(( '\n' == $tst ) || empty( $tst )) { - unset( $rows[$lix] ); - $lix--; - continue; - } - if( FALSE === stripos( $rows[$lix], 'END:VCALENDAR' )) - $rows[] = 'END:VCALENDAR'; - else - $rows[$lix] = 'END:VCALENDAR'; - break; - } - $comp = & $this; - $calsync = $compsync = 0; - /* identify components and update unparsed data within component */ - $config = $this->getConfig(); - $endtxt = array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' ); - foreach( $rows as $lix => $line ) { - if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) { - $calsync++; - continue; - } - elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) { - if( 0 < $compsync ) - $this->components[] = $comp->copy(); - $compsync--; - $calsync--; - break; - } - elseif( 1 != $calsync ) - return FALSE; /* err 20 */ - elseif( in_array( strtoupper( substr( $line, 0, 6 )), $endtxt )) { - $this->components[] = $comp->copy(); - $compsync--; - continue; - } - if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) { - $comp = new vevent( $config ); - $compsync++; - } - elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) { - $comp = new vfreebusy( $config ); - $compsync++; - } - elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) { - $comp = new vjournal( $config ); - $compsync++; - } - elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) { - $comp = new vtodo( $config ); - $compsync++; - } - elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) { - $comp = new vtimezone( $config ); - $compsync++; - } - else { /* update component with unparsed data */ - $comp->unparsed[] = $line; - } - } // end foreach( $rows as $line ) - unset( $config, $endtxt ); - /* parse data for calendar (this) object */ - if( isset( $this->unparsed ) && is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) { - /* concatenate property values spread over several lines */ - $propnames = array( 'calscale','method','prodid','version','x-' ); - $proprows = array(); - for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines - $line = rtrim( $this->unparsed[$i], $nl ); - while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} )) - $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl ); - $proprows[] = $line; - } - foreach( $proprows as $line ) { - if( '\n' == substr( $line, -2 )) - $line = substr( $line, 0, -2 ); - /* get property name */ - $propname = ''; - $cix = 0; - while( FALSE !== ( $char = substr( $line, $cix, 1 ))) { - if( in_array( $char, array( ':', ';' ))) - break; - else - $propname .= $char; - $cix++; - } - /* skip non standard property names */ - if(( 'x-' != strtolower( substr( $propname, 0, 2 ))) && !in_array( strtolower( $propname ), $propnames )) - continue; - /* ignore version/prodid properties */ - if( in_array( strtolower( $propname ), array( 'version', 'prodid' ))) - continue; - /* rest of the line is opt.params and value */ - $line = substr( $line, $cix); - /* separate attributes from value */ - iCalUtilityFunctions::_splitContent( $line, $propAttr ); - /* update Property */ - if( FALSE !== strpos( $line, ',' )) { - $content = array( 0 => '' ); - $cix = $lix = 0; - while( FALSE !== substr( $line, $lix, 1 )) { - if(( 0 < $lix ) && ( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) { - $cix++; - $content[$cix] = ''; - } - else - $content[$cix] .= $line[$lix]; - $lix++; - } - if( 1 < count( $content )) { - foreach( $content as $cix => $contentPart ) - $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart ); - $this->setProperty( $propname, $content, $propAttr ); - continue; - } - else - $line = reset( $content ); - $line = iCalUtilityFunctions::_strunrep( $line ); - } - $this->setProperty( $propname, rtrim( $line, "\x00..\x1F" ), $propAttr ); - } // end - foreach( $this->unparsed.. . - } // end - if( is_array( $this->unparsed.. . - unset( $unparsedtext, $rows, $this->unparsed, $proprows ); - /* parse Components */ - if( is_array( $this->components ) && ( 0 < count( $this->components ))) { - $ckeys = array_keys( $this->components ); - foreach( $ckeys as $ckey ) { - if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) { - $this->components[$ckey]->parse(); - } - } - } - else - return FALSE; /* err 91 or something.. . */ - return TRUE; - } -/*********************************************************************************/ -/** - * creates formatted output for calendar object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.07 - 2015-03-31 - * @uses vcalendar::$format - * @uses vcalendar::$nl - * @uses vcalendar::createVersion() - * @uses vcalendar::createProdid() - * @uses vcalendar::createCalscale() - * @uses vcalendar::createMethod() - * @uses vcalendar::createXprop() - * @uses vcalendar::$components - * @uses calendarComponent::setConfig() - * @uses vcalendar::getConfig() - * @uses calendarComponent::createComponent() - * @uses vcalendar::$xcaldecl - * @return string - */ - function createCalendar() { - parent::_createFormat(); - $calendarInit = $calendarxCaldecl = $calendarStart = $calendar = ''; - switch( $this->format ) { - case 'xcal': - $calendarInit = ''.$this->nl. - 'nl. - '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"'; - $calendarStart = '>'.$this->nl.'nl; - break; - } - $calendarStart .= $this->createVersion(); - $calendarStart .= $this->createProdid(); - $calendarStart .= $this->createCalscale(); - $calendarStart .= $this->createMethod(); - if( 'xcal' == $this->format ) - $calendarStart .= '>'.$this->nl; - $calendar .= $this->createXprop(); - foreach( $this->components as $component ) { - if( empty( $component )) continue; - $component->setConfig( $this->getConfig(), FALSE, TRUE ); - $calendar .= $component->createComponent( $this->xcaldecl ); - } - if(( 'xcal' == $this->format ) && ( 0 < count( $this->xcaldecl ))) { // xCal only - $calendarInit .= ' ['; - $old_xcaldecl = array(); - foreach( $this->xcaldecl as $declix => $declPart ) { - if(( 0 < count( $old_xcaldecl)) && - isset( $declPart['uri'] ) && isset( $declPart['external'] ) && - isset( $old_xcaldecl['uri'] ) && isset( $old_xcaldecl['external'] ) && - ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) && - ( in_array( $declPart['external'], $old_xcaldecl['external'] ))) - continue; // no duplicate uri and ext. references - if(( 0 < count( $old_xcaldecl)) && - !isset( $declPart['uri'] ) && !isset( $declPart['uri'] ) && - isset( $declPart['ref'] ) && isset( $old_xcaldecl['ref'] ) && - ( in_array( $declPart['ref'], $old_xcaldecl['ref'] ))) - continue; // no duplicate element declarations - $calendarxCaldecl .= $this->nl.' $declValue ) { - switch( $declKey ) { // index - case 'xmldecl': // no 1 - $calendarxCaldecl .= $declValue.' '; - break; - case 'uri': // no 2 - $calendarxCaldecl .= $declValue.' '; - $old_xcaldecl['uri'][] = $declValue; - break; - case 'ref': // no 3 - $calendarxCaldecl .= $declValue.' '; - $old_xcaldecl['ref'][] = $declValue; - break; - case 'external': // no 4 - $calendarxCaldecl .= '"'.$declValue.'" '; - $old_xcaldecl['external'][] = $declValue; - break; - case 'type': // no 5 - $calendarxCaldecl .= $declValue.' '; - break; - case 'type2': // no 6 - $calendarxCaldecl .= $declValue; - break; - } - } - $calendarxCaldecl .= '>'; - } - $calendarxCaldecl .= $this->nl.']'; - } // end if(( 'xcal'... - switch( $this->format ) { - case 'xcal': - $calendar .= ''.$this->nl; - break; - default: - $calendar .= 'END:VCALENDAR'.$this->nl; - break; - } - return $calendarInit.$calendarxCaldecl.$calendarStart.$calendar; - } -/** - * a HTTP redirect header is sent with created, updated and/or parsed calendar - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.5 - 2015-03-29 - * @param bool $utf8Encode - * @param bool $gzip - * @param bool $cdType TRUE : Content-Disposition: attachment... (default), FALSE : ...inline... - * @uses vcalendar::getConfig() - * @uses vcalendar::createCalendar() - * @uses vcalendar::$headers - * @uses vcalendar::$format - * @return bool TRUE on success, FALSE on error - */ - function returnCalendar( $utf8Encode=FALSE, $gzip=FALSE, $cdType=TRUE ) { - $filename = $this->getConfig( 'filename' ); - $output = $this->createCalendar(); - if( $utf8Encode ) - $output = utf8_encode( $output ); - $fsize = null; - if( $gzip ) { - $output = gzencode( $output, 9 ); - $fsize = strlen( $output ); - header( self::$headers[0] ); - header( self::$headers[1] ); - } - else { - if( FALSE !== ( $temp = tempnam( sys_get_temp_dir(), 'iCr' ))) { - if( FALSE !== file_put_contents( $temp, $output )) - $fsize = @filesize( $temp ); - unlink( $temp ); - } - } - if( ! empty( $fsize )) - header( sprintf( self::$headers[2], $fsize )); - if( 'xcal' == $this->format ) - header( self::$headers[3] ); - else - header( self::$headers[4] ); - $cdType = ( $cdType ) ? 5 : 6; - header( sprintf( self::$headers[$cdType], $filename )); - header( self::$headers[7] ); - echo $output; - return TRUE; - } -/** - * save content in a file - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.5 - 2015-03-29 - * @uses vcalendar::createComponent() - * @uses vcalendar::getConfig() - * @return bool TRUE on success, FALSE on error - */ - function saveCalendar() { - $output = $this->createCalendar(); - if( FALSE === ( $dirfile = $this->getConfig( 'url' ))) - $dirfile = $this->getConfig( 'dirfile' ); - return ( FALSE === file_put_contents( $dirfile, $output, LOCK_EX )) ? FALSE : TRUE; - } -/** - * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent - * else FALSE is returned - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.21.5 - 2015-03-29 - * @param int $timeout default 3600 sec - * @param bool $cdType TRUE : Content-Disposition: attachment... (default), FALSE : ...inline... - * @uses vcalendar::getConfig() - * @uses vcalendar::$headers - * @uses vcalendar::$format - * @return bool TRUE on success, FALSE on error - */ - function useCachedCalendar( $timeout=3600, $cdType=TRUE ) { - if( FALSE === ( $dirfile = $this->getConfig( 'url' ))) - $dirfile = $this->getConfig( 'dirfile' ); - if( ! is_file( $dirfile ) || ! is_readable( $dirfile )) - return FALSE; - if( time() - filemtime( $dirfile ) > $timeout ) - return FALSE; - clearstatcache(); - $dirfile = $this->getConfig( 'dirfile' ); - $fsize = @filesize( $dirfile ); - $filename = $this->getConfig( 'filename' ); - if( 'xcal' == $this->format ) - header( self::$headers[3] ); - else - header( self::$headers[4] ); - if( ! empty( $fsize )) - header( sprintf( self::$headers[2], $fsize )); - $cdType = ( $cdType ) ? 5 : 6; - header( sprintf( self::$headers[$cdType], $filename )); - header( self::$headers[7] ); - if( FALSE === ( $fp = @fopen( $dirfile, 'r' ))) - return FALSE; - fpassthru( $fp ); - fclose( $fp ); - return TRUE; - } -} diff --git a/php/ical/lib/vevent.class.php b/php/ical/lib/vevent.class.php deleted file mode 100644 index fc17dde..0000000 --- a/php/ical/lib/vevent.class.php +++ /dev/null @@ -1,269 +0,0 @@ - - * @since 2.5.1 - 2008-10-12 - */ -class vevent extends calendarComponent { -/** - * @var array $attach component property value - * @var array $attendee component property value - * @var array $categories component property value - * @var array $comment component property value - * @var array $contact component property value - * @var array $class component property value - * @var array $created component property value - * @var array $description component property value - * @var array $dtend component property value - * @var array $dtstart component property value - * @var array $duration component property value - * @var array $exdate component property value - * @var array $exrule component property value - * @var array $geo component property value - * @var array $lastmodified component property value - * @var array $location component property value - * @var array $organizer component property value - * @var array $priority component property value - * @var array $rdate component property value - * @var array $recurrenceid component property value - * @var array $relatedto component property value - * @var array $requeststatus component property value - * @var array $resources component property value - * @var array $rrule component property value - * @var array $sequence component property value - * @var array $status component property value - * @var array $summary component property value - * @var array $transp component property value - * @var array $url component property value - * @access protected - */ - protected $attach; - protected $attendee; - protected $categories; - protected $comment; - protected $contact; - protected $class; - protected $created; - protected $description; - protected $dtend; - protected $dtstart; - protected $duration; - protected $exdate; - protected $exrule; - protected $geo; - protected $lastmodified; - protected $location; - protected $organizer; - protected $priority; - protected $rdate; - protected $recurrenceid; - protected $relatedto; - protected $requeststatus; - protected $resources; - protected $rrule; - protected $sequence; - protected $status; - protected $summary; - protected $transp; - protected $url; -/** - * constructor for calendar component VEVENT object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @uses vevent::calendarComponent() - * @uses vevent::$attach - * @uses vevent::$attendee - * @uses vevent::$categories - * @uses vevent::$class - * @uses vevent::$comment - * @uses vevent::$contact - * @uses vevent::$created - * @uses vevent::$description - * @uses vevent::$dtstart - * @uses vevent::$dtend - * @uses vevent::$duration - * @uses vevent::$exdate - * @uses vevent::$exrule - * @uses vevent::$geo - * @uses vevent::$lastmodified - * @uses vevent::$location - * @uses vevent::$organizer - * @uses vevent::$priority - * @uses vevent::$rdate - * @uses vevent::$recurrenceid - * @uses vevent::$relatedto - * @uses vevent::$requeststatus - * @uses vevent::$resources - * @uses vevent::$rrule - * @uses vevent::$sequence - * @uses vevent::$status - * @uses vevent::$summary - * @uses vevent::$transp - * @uses vevent::$url - * @uses vevent::$xprop - * @uses vevent::$components - * @uses calendarComponent::setConfig() - */ - function vevent( $config = array()) { - $this->calendarComponent(); - $this->attach = ''; - $this->attendee = ''; - $this->categories = ''; - $this->class = ''; - $this->comment = ''; - $this->contact = ''; - $this->created = ''; - $this->description = ''; - $this->dtstart = ''; - $this->dtend = ''; - $this->duration = ''; - $this->exdate = ''; - $this->exrule = ''; - $this->geo = ''; - $this->lastmodified = ''; - $this->location = ''; - $this->organizer = ''; - $this->priority = ''; - $this->rdate = ''; - $this->recurrenceid = ''; - $this->relatedto = ''; - $this->requeststatus = ''; - $this->resources = ''; - $this->rrule = ''; - $this->sequence = ''; - $this->status = ''; - $this->summary = ''; - $this->transp = ''; - $this->url = ''; - $this->xprop = ''; - $this->components = array(); - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - } -/** - * create formatted output for calendar component VEVENT object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.10.16 - 2011-10-28 - * @param array $xcaldecl - * @uses calendarComponent::_createFormat() - * @uses calendarComponent::$componentStart1 - * @uses calendarComponent::$componentStart2 - * @uses calendarComponent::$nl; - * @uses calendarComponent::createUid() - * @uses calendarComponent::createDtstamp() - * @uses calendarComponent::createAttach() - * @uses calendarComponent::createAttendee() - * @uses calendarComponent::createCategories() - * @uses calendarComponent::createComment() - * @uses calendarComponent::createContact() - * @uses calendarComponent::createClass() - * @uses calendarComponent::createCreated() - * @uses calendarComponent::createDescription() - * @uses calendarComponent::createDtstart() - * @uses calendarComponent::createDtend() - * @uses calendarComponent::createDuration() - * @uses calendarComponent::createExdate() - * @uses calendarComponent::createExrule() - * @uses calendarComponent::createGeo() - * @uses calendarComponent::createLastModified() - * @uses calendarComponent::createLocation() - * @uses calendarComponent::createOrganizer() - * @uses calendarComponent::createPriority() - * @uses calendarComponent::createRdate() - * @uses calendarComponent::createRrule() - * @uses calendarComponent::createRelatedTo() - * @uses calendarComponent::createRequestStatus() - * @uses calendarComponent::createRecurrenceid() - * @uses calendarComponent::createResources() - * @uses calendarComponent::createSequence() - * @uses calendarComponent::createStatus() - * @uses calendarComponent::createSummary() - * @uses calendarComponent::createTransp() - * @uses calendarComponent::createUrl() - * @uses calendarComponent::createXprop() - * @uses calendarComponent::createSubComponent() - * @uses calendarComponent::$componentEnd1 - * @uses calendarComponent::$componentEnd2 - * @uses calendarComponent::$xcaldecl - * @return string - */ - function createComponent( & $xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createUid(); - $component .= $this->createDtstamp(); - $component .= $this->createAttach(); - $component .= $this->createAttendee(); - $component .= $this->createCategories(); - $component .= $this->createComment(); - $component .= $this->createContact(); - $component .= $this->createClass(); - $component .= $this->createCreated(); - $component .= $this->createDescription(); - $component .= $this->createDtstart(); - $component .= $this->createDtend(); - $component .= $this->createDuration(); - $component .= $this->createExdate(); - $component .= $this->createExrule(); - $component .= $this->createGeo(); - $component .= $this->createLastModified(); - $component .= $this->createLocation(); - $component .= $this->createOrganizer(); - $component .= $this->createPriority(); - $component .= $this->createRdate(); - $component .= $this->createRrule(); - $component .= $this->createRelatedTo(); - $component .= $this->createRequestStatus(); - $component .= $this->createRecurrenceid(); - $component .= $this->createResources(); - $component .= $this->createSequence(); - $component .= $this->createStatus(); - $component .= $this->createSummary(); - $component .= $this->createTransp(); - $component .= $this->createUrl(); - $component .= $this->createXprop(); - $component .= $this->createSubComponent(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} diff --git a/php/ical/lib/vfreebusy.class.php b/php/ical/lib/vfreebusy.class.php deleted file mode 100644 index 0ba0bdd..0000000 --- a/php/ical/lib/vfreebusy.class.php +++ /dev/null @@ -1,149 +0,0 @@ - - * @since 2.5.1 - 2008-10-12 - */ -class vfreebusy extends calendarComponent { -/** - * @var array $attendee component property value - * @var array $comment component property value - * @var array $contact component property value - * @var array $dtend component property value - * @var array $dtstart component property value - * @var array $duration component property value - * @var array $freebusy component property value - * @var array $organizer component property value - * @var array $requeststatus component property value - * @var array $url component property value - * @access protected - */ - protected $attendee; - protected $comment; - protected $contact; - protected $dtend; - protected $dtstart; - protected $duration; - protected $freebusy; - protected $organizer; - protected $requeststatus; - protected $url; -/** - * constructor for calendar component VFREEBUSY object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @uses vjournal::calendarComponent() - * @uses vjournal::$attendee - * @uses vjournal::$comment - * @uses vjournal::$contact - * @uses vjournal::$dtend - * @uses vjournal::$dtstart - * @uses vjournal::$dtduration - * @uses vjournal::$organizer - * @uses vjournal::$requeststatus - * @uses vjournal::$url - * @uses vjournal::$xprop - * @uses calendarComponent::setConfig() - */ - function vfreebusy( $config = array()) { - $this->calendarComponent(); - $this->attendee = ''; - $this->comment = ''; - $this->contact = ''; - $this->dtend = ''; - $this->dtstart = ''; - $this->duration = ''; - $this->freebusy = ''; - $this->organizer = ''; - $this->requeststatus = ''; - $this->url = ''; - $this->xprop = ''; - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - } -/** - * create formatted output for calendar component VFREEBUSY object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.3.1 - 2007-11-19 - * @param array $xcaldecl - * @uses calendarComponent::_createFormat() - * @uses calendarComponent::createUid() - * @uses calendarComponent::createDtstamp() - * @uses calendarComponent::createAttendee() - * @uses calendarComponent::createComment() - * @uses calendarComponent::createContact() - * @uses calendarComponent::createDtstart() - * @uses calendarComponent::createDtend() - * @uses calendarComponent::createDuration() - * @uses calendarComponent::createFreebusy() - * @uses calendarComponent::createOrganizer() - * @uses calendarComponent::createRequestStatus() - * @uses calendarComponent::createUrl() - * @uses calendarComponent::createXprop() - * @uses calendarComponent::createUrl() - * @uses calendarComponent::createXprop() - * @uses calendarComponent::$componentEnd1 - * @uses calendarComponent::$componentEnd2 - * @uses calendarComponent::$xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createUid(); - $component .= $this->createDtstamp(); - $component .= $this->createAttendee(); - $component .= $this->createComment(); - $component .= $this->createContact(); - $component .= $this->createDtstart(); - $component .= $this->createDtend(); - $component .= $this->createDuration(); - $component .= $this->createFreebusy(); - $component .= $this->createOrganizer(); - $component .= $this->createRequestStatus(); - $component .= $this->createUrl(); - $component .= $this->createXprop(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} diff --git a/php/ical/lib/vjournal.class.php b/php/ical/lib/vjournal.class.php deleted file mode 100644 index f74adee..0000000 --- a/php/ical/lib/vjournal.class.php +++ /dev/null @@ -1,222 +0,0 @@ - - * @since 2.5.1 - 2008-10-12 - */ -class vjournal extends calendarComponent { -/** - * @var array $attach component property value - * @var array $attendee component property value - * @var array $categories component property value - * @var array $comment component property value - * @var array $contact component property value - * @var array $class component property value - * @var array $created component property value - * @var array $description component property value - * @var array $dtstart component property value - * @var array $exdate component property value - * @var array $exrule component property value - * @var array $lastmodified component property value - * @var array $organizer component property value - * @var array $rdate component property value - * @var array $recurrenceid component property value - * @var array $relatedto component property value - * @var array $requeststatus component property value - * @var array $rrule component property value - * @var array $sequence component property value - * @var array $status component property value - * @var array $summary component property value - * @var array $url component property value - * @access protected - */ - protected $attach; - protected $attendee; - protected $categories; - protected $comment; - protected $contact; - protected $class; - protected $created; - protected $description; - protected $dtstart; - protected $exdate; - protected $exrule; - protected $lastmodified; - protected $organizer; - protected $rdate; - protected $recurrenceid; - protected $relatedto; - protected $requeststatus; - protected $rrule; - protected $sequence; - protected $status; - protected $summary; - protected $url; -/** - * constructor for calendar component VJOURNAL object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @uses vjournal::calendarComponent() - * @uses vjournal::$attach - * @uses vjournal::$attendee - * @uses vjournal::$categories - * @uses vjournal::$class - * @uses vjournal::$comment - * @uses vjournal::$contact - * @uses vjournal::$created - * @uses vjournal::$description - * @uses vjournal::$dtstart - * @uses vjournal::$exdate - * @uses vjournal::$exrule - * @uses vjournal::$lastmodified - * @uses vjournal::$organizer - * @uses vjournal::$rdate - * @uses vjournal::$recurrenceid - * @uses vjournal::$relatedto - * @uses vjournal::$requeststatus - * @uses vjournal::$rrule - * @uses vjournal::$sequence - * @uses vjournal::$status - * @uses vjournal::$summary - * @uses vjournal::$url - * @uses vjournal::$xprop - * @uses calendarComponent::setConfig() - */ - function vjournal( $config = array()) { - $this->calendarComponent(); - $this->attach = ''; - $this->attendee = ''; - $this->categories = ''; - $this->class = ''; - $this->comment = ''; - $this->contact = ''; - $this->created = ''; - $this->description = ''; - $this->dtstart = ''; - $this->exdate = ''; - $this->exrule = ''; - $this->lastmodified = ''; - $this->organizer = ''; - $this->rdate = ''; - $this->recurrenceid = ''; - $this->relatedto = ''; - $this->requeststatus = ''; - $this->rrule = ''; - $this->sequence = ''; - $this->status = ''; - $this->summary = ''; - $this->url = ''; - $this->xprop = ''; - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - } -/** - * create formatted output for calendar component VJOURNAL object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-12 - * @param array $xcaldecl - * @uses calendarComponent::_createFormat() - * @uses calendarComponent::$componentStart1 - * @uses calendarComponent::$componentStart2 - * @uses calendarComponent::$nl - * @uses calendarComponent::createUid() - * @uses calendarComponent::createDtstamp() - * @uses calendarComponent::createAttach() - * @uses calendarComponent::createAttendee() - * @uses calendarComponent::createCategories() - * @uses calendarComponent::createClass() - * @uses calendarComponent::createComment() - * @uses calendarComponent::createContact() - * @uses calendarComponent::createDescription() - * @uses calendarComponent::createDtstart() - * @uses calendarComponent::createExdate() - * @uses calendarComponent::createExrule() - * @uses calendarComponent::createLastModified() - * @uses calendarComponent::createOrganizer() - * @uses calendarComponent::createRdate() - * @uses calendarComponent::createRelatedTo() - * @uses calendarComponent::createRequestStatus() - * @uses calendarComponent::createRecurrenceid() - * @uses calendarComponent::createRrule() - * @uses calendarComponent::createSequence() - * @uses calendarComponent::createStatus() - * @uses calendarComponent::createSummary() - * @uses calendarComponent::createUrl() - * @uses calendarComponent::createXprop() - * @uses calendarComponent::$componentEnd1 - * @uses calendarComponent::$componentEnd2 - * @uses calendarComponent::$xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createUid(); - $component .= $this->createDtstamp(); - $component .= $this->createAttach(); - $component .= $this->createAttendee(); - $component .= $this->createCategories(); - $component .= $this->createClass(); - $component .= $this->createComment(); - $component .= $this->createContact(); - $component .= $this->createCreated(); - $component .= $this->createDescription(); - $component .= $this->createDtstart(); - $component .= $this->createExdate(); - $component .= $this->createExrule(); - $component .= $this->createLastModified(); - $component .= $this->createOrganizer(); - $component .= $this->createRdate(); - $component .= $this->createRequestStatus(); - $component .= $this->createRecurrenceid(); - $component .= $this->createRelatedTo(); - $component .= $this->createRrule(); - $component .= $this->createSequence(); - $component .= $this->createStatus(); - $component .= $this->createSummary(); - $component .= $this->createUrl(); - $component .= $this->createXprop(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} diff --git a/php/ical/lib/vtimezone.class.php b/php/ical/lib/vtimezone.class.php deleted file mode 100644 index d65bf7c..0000000 --- a/php/ical/lib/vtimezone.class.php +++ /dev/null @@ -1,164 +0,0 @@ - - * @since 2.5.1 - 2008-10-12 - */ -class vtimezone extends calendarComponent { - /** @var array $timezonetype component property value */ - public $timezonetype; -/** - * @var array $comment component property value - * @var array $dtstart component property value - * @var array $lastmodified component property value - * @var array $rdate component property value - * @var array $rrule component property value - * @var array $tzid component property value - * @var array $tzname component property value - * @var array $tzoffsetfrom component property value - * @var array $tzoffsetto component property value - * @var array $tzurl component property value - * @access protected - */ - protected $comment; - protected $dtstart; - protected $lastmodified; - protected $rdate; - protected $rrule; - protected $tzid; - protected $tzname; - protected $tzoffsetfrom; - protected $tzoffsetto; - protected $tzurl; -/** - * constructor for calendar component VTIMEZONE object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param mixed $timezonetype default FALSE ( STANDARD / DAYLIGHT ) - * @param array $config - * @uses vtimezone::$timezonetype - * @uses vtimezone::calendarComponent() - * @uses vtimezone::$comment - * @uses vtimezone::$dtstart - * @uses vtimezone::$lastmodified - * @uses vtimezone::$rdate - * @uses vtimezone::$rrule - * @uses vtimezone::$tzid - * @uses vtimezone::$tzname - * @uses vtimezone::$tzoffsetfrom - * @uses vtimezone::$tzoffsetto - * @uses vtimezone::$tzurl - * @uses vtimezone::$xprop - * @uses vtimezone::$components - * @uses calendarComponent::setConfig() - */ - function vtimezone( $timezonetype=FALSE, $config = array()) { - if( is_array( $timezonetype )) { - $config = $timezonetype; - $timezonetype = FALSE; - } - if( !$timezonetype ) - $this->timezonetype = 'VTIMEZONE'; - else - $this->timezonetype = strtoupper( $timezonetype ); - $this->calendarComponent(); - $this->comment = ''; - $this->dtstart = ''; - $this->lastmodified = ''; - $this->rdate = ''; - $this->rrule = ''; - $this->tzid = ''; - $this->tzname = ''; - $this->tzoffsetfrom = ''; - $this->tzoffsetto = ''; - $this->tzurl = ''; - $this->xprop = ''; - $this->components = array(); - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - } -/** - * create formatted output for calendar component VTIMEZONE object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-10-25 - * @param array $xcaldecl - * @uses calendarComponent::_createFormat() - * @uses calendarComponent::$componentStart1 - * @uses calendarComponent::$componentStart2 - * @uses calendarComponent::$nl - * @uses calendarComponent::createTzid() - * @uses calendarComponent::createLastModified() - * @uses calendarComponent::createTzurl() - * @uses calendarComponent::createDtstart() - * @uses calendarComponent::createTzoffsetfrom() - * @uses calendarComponent::createTzoffsetto() - * @uses calendarComponent::createComment() - * @uses calendarComponent::createRdate() - * @uses calendarComponent::createRrule() - * @uses calendarComponent::createTzname() - * @uses calendarComponent::createXprop() - * @uses calendarComponent::createSubComponent() - * @uses calendarComponent::createXprop() - * @uses calendarComponent::$componentEnd1 - * @uses calendarComponent::$componentEnd2 - * @uses calendarComponent::$xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createTzid(); - $component .= $this->createLastModified(); - $component .= $this->createTzurl(); - $component .= $this->createDtstart(); - $component .= $this->createTzoffsetfrom(); - $component .= $this->createTzoffsetto(); - $component .= $this->createComment(); - $component .= $this->createRdate(); - $component .= $this->createRrule(); - $component .= $this->createTzname(); - $component .= $this->createXprop(); - $component .= $this->createSubComponent(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} diff --git a/php/ical/lib/vtodo.class.php b/php/ical/lib/vtodo.class.php deleted file mode 100644 index 8e373e9..0000000 --- a/php/ical/lib/vtodo.class.php +++ /dev/null @@ -1,273 +0,0 @@ - - * @since 2.5.1 - 2008-10-12 - */ -class vtodo extends calendarComponent { -/** - * @var array $attach component property value - * @var array $attendee component property value - * @var array $categories component property value - * @var array $comment component property value - * @var array $completed component property value - * @var array $contact component property value - * @var array $class component property value - * @var array $created component property value - * @var array $description component property value - * @var array $dtstart component property value - * @var array $due component property value - * @var array $duration component property value - * @var array $exdate component property value - * @var array $exrule component property value - * @var array $geo component property value - * @var array $lastmodified component property value - * @var array $location component property value - * @var array $organizer component property value - * @var array $percentcomplete component property value - * @var array $priority component property value - * @var array $rdate component property value - * @var array $recurrenceid component property value - * @var array $relatedto component property value - * @var array $requeststatus component property value - * @var array $resources component property value - * @var array $rrule component property value - * @var array $sequence component property value - * @var array $status component property value - * @var array $summary component property value - * @var arrayr $url; - * @access protected - */ - protected $attach; - protected $attendee; - protected $categories; - protected $comment; - protected $completed; - protected $contact; - protected $class; - protected $created; - protected $description; - protected $dtstart; - protected $due; - protected $duration; - protected $exdate; - protected $exrule; - protected $geo; - protected $lastmodified; - protected $location; - protected $organizer; - protected $percentcomplete; - protected $priority; - protected $rdate; - protected $recurrenceid; - protected $relatedto; - protected $requeststatus; - protected $resources; - protected $rrule; - protected $sequence; - protected $status; - protected $summary; - protected $url; -/** - * constructor for calendar component VTODO object - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.8.2 - 2011-05-01 - * @param array $config - * @uses vtodo::calendarComponent() - * @uses vtodo::$attach - * @uses vtodo::$attendee - * @uses vtodo::$categories - * @uses vtodo::$class - * @uses vtodo::$comment - * @uses vtodo::$completed - * @uses vtodo::$contact - * @uses vtodo::$created - * @uses vtodo::$description - * @uses vtodo::$dtstart - * @uses vtodo::$due - * @uses vtodo::$duration - * @uses vtodo::$exdate - * @uses vtodo::$exrule - * @uses vtodo::$geo - * @uses vtodo::$lastmodified - * @uses vtodo::$location - * @uses vtodo::$organizer - * @uses vtodo::$percentcomplete - * @uses vtodo::$priority - * @uses vtodo::$rdate - * @uses vtodo::$recurrenceid - * @uses vtodo::$relatedto - * @uses vtodo::$requeststatus - * @uses vtodo::$resources - * @uses vtodo::$rrule - * @uses vtodo::$sequence - * @uses vtodo::$status - * @uses vtodo::$summary - * @uses vtodo::$url - * @uses vtodo::$xprop - * @uses vtodo::$components - * @uses calendarComponent::setConfig() - */ - function vtodo( $config = array()) { - $this->calendarComponent(); - $this->attach = ''; - $this->attendee = ''; - $this->categories = ''; - $this->class = ''; - $this->comment = ''; - $this->completed = ''; - $this->contact = ''; - $this->created = ''; - $this->description = ''; - $this->dtstart = ''; - $this->due = ''; - $this->duration = ''; - $this->exdate = ''; - $this->exrule = ''; - $this->geo = ''; - $this->lastmodified = ''; - $this->location = ''; - $this->organizer = ''; - $this->percentcomplete = ''; - $this->priority = ''; - $this->rdate = ''; - $this->recurrenceid = ''; - $this->relatedto = ''; - $this->requeststatus = ''; - $this->resources = ''; - $this->rrule = ''; - $this->sequence = ''; - $this->status = ''; - $this->summary = ''; - $this->url = ''; - $this->xprop = ''; - $this->components = array(); - if( defined( 'ICAL_LANG' ) && !isset( $config['language'] )) - $config['language'] = ICAL_LANG; - if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE; - if( !isset( $config['nl'] )) $config['nl'] = "\r\n"; - if( !isset( $config['format'] )) $config['format'] = 'iCal'; - if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR; - $this->setConfig( $config ); - } -/** - * create formatted output for calendar component VTODO object instance - * - * @author Kjell-Inge Gustafsson, kigkonsult - * @since 2.5.1 - 2008-11-07 - * @param array $xcaldecl - * @uses calendarComponent::_createFormat() - * @uses calendarComponent::$componentStart1 - * @uses calendarComponent::$componentStart2 - * @uses calendarComponent::$nl; - * @uses calendarComponent::createUid() - * @uses calendarComponent::createDtstamp() - * @uses calendarComponent::createAttach() - * @uses calendarComponent::createAttendee() - * @uses calendarComponent::createCategories() - * @uses calendarComponent::createClass() - * @uses calendarComponent::createComment() - * @uses calendarComponent::createCompleted() - * @uses calendarComponent::createContact() - * @uses calendarComponent::createDescription() - * @uses calendarComponent::createDtstart() - * @uses calendarComponent::createDtend() - * @uses calendarComponent::createDuration() - * @uses calendarComponent::createExdate() - * @uses calendarComponent::createExrule() - * @uses calendarComponent::createGeo() - * @uses calendarComponent::createLastModified() - * @uses calendarComponent::createLocation() - * @uses calendarComponent::createOrganizer() - * @uses calendarComponent::createPriority() - * @uses calendarComponent::createRdate() - * @uses calendarComponent::createRelatedTo() - * @uses calendarComponent::createRequestStatus() - * @uses calendarComponent::createRecurrenceid() - * @uses calendarComponent::createResources() - * @uses calendarComponent::createRrule() - * @uses calendarComponent::createSequence() - * @uses calendarComponent::createStatus() - * @uses calendarComponent::createSummary() - * @uses calendarComponent::createUrl() - * @uses calendarComponent::createXprop() - * @uses calendarComponent::createSubComponent() - * @uses calendarComponent::$componentEnd1 - * @uses calendarComponent::$componentEnd2 - * @uses calendarComponent::$xcaldecl - * @return string - */ - function createComponent( &$xcaldecl ) { - $objectname = $this->_createFormat(); - $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl; - $component .= $this->createUid(); - $component .= $this->createDtstamp(); - $component .= $this->createAttach(); - $component .= $this->createAttendee(); - $component .= $this->createCategories(); - $component .= $this->createClass(); - $component .= $this->createComment(); - $component .= $this->createCompleted(); - $component .= $this->createContact(); - $component .= $this->createCreated(); - $component .= $this->createDescription(); - $component .= $this->createDtstart(); - $component .= $this->createDue(); - $component .= $this->createDuration(); - $component .= $this->createExdate(); - $component .= $this->createExrule(); - $component .= $this->createGeo(); - $component .= $this->createLastModified(); - $component .= $this->createLocation(); - $component .= $this->createOrganizer(); - $component .= $this->createPercentComplete(); - $component .= $this->createPriority(); - $component .= $this->createRdate(); - $component .= $this->createRelatedTo(); - $component .= $this->createRequestStatus(); - $component .= $this->createRecurrenceid(); - $component .= $this->createResources(); - $component .= $this->createRrule(); - $component .= $this->createSequence(); - $component .= $this->createStatus(); - $component .= $this->createSummary(); - $component .= $this->createUrl(); - $component .= $this->createXprop(); - $component .= $this->createSubComponent(); - $component .= $this->componentEnd1.$objectname.$this->componentEnd2; - if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) { - foreach( $this->xcaldecl as $localxcaldecl ) - $xcaldecl[] = $localxcaldecl; - } - return $component; - } -} diff --git a/php/module.calendar.php b/php/module.calendar.php index a5529a1..c490a5d 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -1,4 +1,4 @@ -DEBUG) { + + if (!$this->DEBUG) { /* disable error printing - otherwise json communication might break... */ ini_set('display_errors', '0'); } - - foreach($this->data as $actionType => $actionData) { - if(isset($actionType)) { + + foreach ($this->data as $actionType => $actionData) { + if (isset($actionType)) { try { - if($this->DEBUG) { + if ($this->DEBUG) { error_log("exec: " . $actionType); } - switch($actionType) { + switch ($actionType) { + case "load": + $result = $this->loadCalendar($actionType, $actionData); + break; case "export": $result = $this->exportCalendar($actionType, $actionData); break; @@ -71,30 +76,31 @@ class CalendarModule extends Module { } } catch (MAPIException $e) { - if($this->DEBUG) { + if ($this->DEBUG) { error_log("mapi exception: " . $e->getMessage()); } } catch (Exception $e) { - if($this->DEBUG) { + if ($this->DEBUG) { error_log("exception: " . $e->getMessage()); } } } } - + return $result; } - + /** * Generates a random string with variable length. * @param $length the lenght of the generated string * @return string a random string */ - private function randomstring($length = 6) { + private function randomstring($length = 6) + { // $chars - all allowed charakters $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; - srand((double)microtime()*1000000); + srand((double)microtime() * 1000000); $i = 0; $pass = ""; while ($i < $length) { @@ -105,45 +111,35 @@ class CalendarModule extends Module { } return $pass; } - - /** - * Generates the secid file (used to verify the download path) - * @param $secid the secid, a random security token - */ - private function createSecIDFile($secid) { - $lockFile = TMP_PATH . "/secid." . $secid; - $fh = fopen($lockFile, 'w') or die("can't open secid file"); - $stringData = date(DATE_RFC822); - fwrite($fh, $stringData); - fclose($fh); - } - + /** * Generates the secid file (used to verify the download path) * @param $time a timestamp * @param $incl_time true if date should include time * @ return date object */ - private function getIcalDate($time, $incl_time = true) { + private function getIcalDate($time, $incl_time = true) + { return $incl_time ? date('Ymd\THis', $time) : date('Ymd', $time); } - + /** * adds an event to the exported calendar) * @param $vevent pointer to the eventstore * @param $event the event to add */ - private function addEvent(&$vevent, $event) { - + private function addEvent(&$vevent, $event) + { + $busystate = array("FREE", "TENTATIVE", "BUSY", "OOF"); $zlabel = array("NONE", "IMPORTANT", "WORK", "PERSONAL", "HOLIDAY", "REQUIRED", "TRAVEL REQUIRED", "PREPARATION REQUIERED", "BIRTHDAY", "SPECIAL DATE", "PHONE INTERVIEW"); - + $vevent->setProperty("LOCATION", $event["location"]); // property name - case independent $vevent->setProperty("SUMMARY", $event["subject"]); - $vevent->setProperty("DESCRIPTION", str_replace("\n", "\\n",$event["description"])); - $vevent->setProperty("COMMENT", "Exported from Zarafa" ); + $vevent->setProperty("DESCRIPTION", str_replace("\n", "\\n", $event["description"])); + $vevent->setProperty("COMMENT", "Exported from Zarafa"); $vevent->setProperty("ORGANIZER", $event["sent_representing_email_address"]); - $vevent->setProperty("DTSTART", $this->getIcalDate($event["commonstart"]) . "Z"); + $vevent->setProperty("DTSTART", $this->getIcalDate($event["commonstart"]) . "Z"); $vevent->setProperty("DTEND", $this->getIcalDate($event["commonend"]) . "Z"); $vevent->setProperty("DTSTAMP", $this->getIcalDate($event["creation_time"]) . "Z"); $vevent->setProperty("CREATED", $this->getIcalDate($event["creation_time"]) . "Z"); @@ -152,49 +148,50 @@ class CalendarModule extends Module { $vevent->setProperty("X-ZARAFA-LABEL", $zlabel[$event["label"]]); $vevent->setProperty("PRIORITY", $event["importance"]); $vevent->setProperty("CLASS", $event["private"] ? "PRIVATE" : "PUBLIC"); - + // ATTENDEES - if(count($event["attendees"]) > 0) { - foreach($event["attendees"] as $attendee) { + if (count($event["attendees"]) > 0) { + foreach ($event["attendees"] as $attendee) { $vevent->setProperty("ATTENDEE", $attendee["props"]["smtp_address"]); } - } - + } + // REMINDERS - if($event["reminder"]) { - $valarm = & $vevent->newComponent("valarm"); // create an event alarm - $valarm->setProperty("action", "DISPLAY" ); - $valarm->setProperty("description", $vevent->getProperty("SUMMARY")); // reuse the event summary - $valarm->setProperty("trigger", $this->getIcalDate($event["reminder_time"]) . "Z"); // create alarm trigger (in UTC datetime) + if ($event["reminder"]) { + $valarm = &$vevent->newComponent("valarm"); // create an event alarm + $valarm->setProperty("action", "DISPLAY"); + $valarm->setProperty("description", $vevent->getProperty("SUMMARY")); // reuse the event summary + $valarm->setProperty("trigger", $this->getIcalDate($event["reminder_time"]) . "Z"); // create alarm trigger (in UTC datetime) } } - + /** * Loads the descriptiontext of an event * @param $event * @return array with event description/body */ - private function loadEventDescription($event) { + private function loadEventDescription($event) + { $entryid = $this->getActionEntryID($event); $store = $this->getActionStore($event); - + $basedate = null; - + $properties = $GLOBALS['properties']->getAppointmentProperties(); $plaintext = true; - + $data = array(); - - if($store && $entryid) { + + if ($store && $entryid) { $message = $GLOBALS['operations']->openMessage($store, $entryid); - - + + // add all standard properties from the series/normal message $data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $properties, (isset($plaintext) && $plaintext)); - + // if appointment is recurring then only we should get properties of occurence if basedate is supplied - if($data['item']['props']['recurring'] === true) { - if(isset($basedate) && $basedate) { + if ($data['item']['props']['recurring'] === true) { + if (isset($basedate) && $basedate) { $recur = new Recurrence($store, $message); $exceptionatt = $recur->getExceptionAttachment($basedate); @@ -202,7 +199,7 @@ class CalendarModule extends Module { // Single occurences are never recurring $data['item']['props']['recurring'] = false; - if($exceptionatt) { + if ($exceptionatt) { // Existing exception (open existing item, which includes basedate) $exceptionattProps = mapi_getprops($exceptionatt, array(PR_ATTACH_NUM)); $exception = mapi_attach_openobj($exceptionatt, 0); @@ -229,11 +226,11 @@ class CalendarModule extends Module { // @FIXME getMessageProps should not return empty string if exception doesn't contain body // by this change we can handle a situation where user has set empty string in the body explicitly if (!empty($exceptionProps['props']['body']) || !empty($exceptionProps['props']['html_body'])) { - if(!empty($exceptionProps['props']['body'])) { + if (!empty($exceptionProps['props']['body'])) { $data['item']['props']['body'] = $exceptionProps['props']['body']; } - if(!empty($exceptionProps['props']['html_body'])) { + if (!empty($exceptionProps['props']['html_body'])) { $data['item']['props']['html_body'] = $exceptionProps['props']['html_body']; } @@ -284,12 +281,12 @@ class CalendarModule extends Module { $tz = $recur->tz; // no function to do this at the moment // Add the recurrence pattern to the data - if(isset($recurpattern) && is_array($recurpattern)) { + if (isset($recurpattern) && is_array($recurpattern)) { $data['item']['props'] += $recurpattern; } // Add the timezone information to the data - if(isset($tz) && is_array($tz)) { + if (isset($tz) && is_array($tz)) { $data['item']['props'] += $tz; } } @@ -298,154 +295,160 @@ class CalendarModule extends Module { return $data['item']['props']['body']; } - + /** * Loads the attendees of an event * @param $event * @return array with event attendees */ - private function loadAttendees($event) { + private function loadAttendees($event) + { $entryid = $this->getActionEntryID($event); $store = $this->getActionStore($event); - + $basedate = null; - + $properties = $GLOBALS['properties']->getAppointmentProperties(); $plaintext = true; - + $data = array(); - - if($store && $entryid) { + + if ($store && $entryid) { $message = $GLOBALS['operations']->openMessage($store, $entryid); - - + + // add all standard properties from the series/normal message $data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $properties, (isset($plaintext) && $plaintext)); - + } return $data['item']['recipients']['item']; } - + /** * The main export function, creates the ics file for download * @param $actionType * @param $actionData */ - private function exportCalendar($actionType, $actionData) { - $secid = $this->randomstring(); + private function exportCalendar($actionType, $actionData) + { + $secid = $this->randomstring(); $this->createSecIDFile($secid); $tmpname = stripslashes($actionData["calendar"] . ".ics." . $this->randomstring(8)); $filename = TMP_PATH . "/" . $tmpname . "." . $secid; - - if(!is_writable(TMP_PATH . "/")) { + + if (!is_writable(TMP_PATH . "/")) { error_log("could not write to export tmp directory!"); } - - $tz = date("e"); // use php timezone (maybe set up in php.ini, date.timezone) - - if($this->DEBUG) { - error_log("PHP Timezone: " . $tz); - } - - $config = array( - "language" => substr($GLOBALS["settings"]->get("zarafa/v1/main/language"),0,2), - "directory" => TMP_PATH . "/", - "filename" => $tmpname . "." . $secid, - "unique_id" => "zarafa-export-plugin", - "TZID" => $tz - ); - - $v = new vcalendar($config); - $v->setProperty("method", "PUBLISH"); // required of some calendar software - $v->setProperty("x-wr-calname", $actionData["calendar"]); // required of some calendar software - $v->setProperty("X-WR-CALDESC", "Exported Zarafa Calendar"); // required of some calendar software - $v->setProperty("X-WR-TIMEZONE", $tz); - $xprops = array("X-LIC-LOCATION" => $tz); // required of some calendar software - iCalUtilityFunctions::createTimezone($v, $tz, $xprops); // create timezone object in calendar - - - foreach($actionData["data"] as $event) { + $tz = date("e"); // use php timezone (maybe set up in php.ini, date.timezone) + + if ($this->DEBUG) { + error_log("PHP Timezone: " . $tz); + } + + $config = array( + "language" => substr($GLOBALS["settings"]->get("zarafa/v1/main/language"), 0, 2), + "directory" => TMP_PATH . "/", + "filename" => $tmpname . "." . $secid, + "unique_id" => "zarafa-export-plugin", + "TZID" => $tz + ); + + $v = new vcalendar($config); + $v->setProperty("method", "PUBLISH"); // required of some calendar software + $v->setProperty("x-wr-calname", $actionData["calendar"]); // required of some calendar software + $v->setProperty("X-WR-CALDESC", "Exported Zarafa Calendar"); // required of some calendar software + $v->setProperty("X-WR-TIMEZONE", $tz); + + $xprops = array("X-LIC-LOCATION" => $tz); // required of some calendar software + iCalUtilityFunctions::createTimezone($v, $tz, $xprops); // create timezone object in calendar + + + foreach ($actionData["data"] as $event) { $event["props"]["description"] = $this->loadEventDescription($event); $event["props"]["attendees"] = $this->loadAttendees($event); - - $vevent = & $v->newComponent("vevent"); // create a new event object + + $vevent = &$v->newComponent("vevent"); // create a new event object $this->addEvent($vevent, $event["props"]); } - + $v->saveCalendar(); - - $response['status'] = true; - $response['fileid'] = $tmpname; // number of entries that will be exported - $response['basedir'] = TMP_PATH; - $response['secid'] = $secid; - $response['realname'] = $actionData["calendar"]; + + $response['status'] = true; + $response['fileid'] = $tmpname; // number of entries that will be exported + $response['basedir'] = TMP_PATH; + $response['secid'] = $secid; + $response['realname'] = $actionData["calendar"]; $this->addActionData($actionType, $response); $GLOBALS["bus"]->addData($this->getResponseData()); - - if($this->DEBUG) { + + if ($this->DEBUG) { error_log("export done, bus data written!"); } } - + /** * The main import function, parses the uploaded ics file * @param $actionType * @param $actionData */ - private function importCalendar($actionType, $actionData) { - if($this->DEBUG) { + private function importCalendar($actionType, $actionData) + { + if ($this->DEBUG) { error_log("PHP Timezone: " . $tz); } - - if(is_readable ($actionData["ics_filepath"])) { + + if (is_readable($actionData["ics_filepath"])) { $ical = new ICal($actionData["ics_filepath"], $GLOBALS["settings"]->get("zarafa/v1/plugins/calendarimporter/default_timezone"), $actionData["timezone"], $actionData["ignore_dst"]); // Parse it! - - if(isset($ical->errors)) { - $response['status'] = false; - $response['message']= $ical->errors; - } else if(!$ical->hasEvents()) { - $response['status'] = false; - $response['message']= "No events in ics file"; + + if (isset($ical->errors)) { + $response['status'] = false; + $response['message'] = $ical->errors; } else { - $response['status'] = true; - $response['parsed_file']= $actionData["ics_filepath"]; - $response['parsed'] = array ( - 'timezone' => $ical->timezone(), - 'calendar' => $ical->calendar(), - 'events' => $ical->events() - ); + if (!$ical->hasEvents()) { + $response['status'] = false; + $response['message'] = "No events in ics file"; + } else { + $response['status'] = true; + $response['parsed_file'] = $actionData["ics_filepath"]; + $response['parsed'] = array( + 'timezone' => $ical->timezone(), + 'calendar' => $ical->calendar(), + 'events' => $ical->events() + ); + } } } else { - $response['status'] = false; - $response['message']= "File could not be read by server"; + $response['status'] = false; + $response['message'] = "File could not be read by server"; } - + $this->addActionData($actionType, $response); $GLOBALS["bus"]->addData($this->getResponseData()); - - if($this->DEBUG) { + + if ($this->DEBUG) { error_log("parsing done, bus data written!"); } } - + /** * Store the file to a temporary directory, prepare it for oc upload * @param $actionType * @param $actionData * @private */ - private function getAttachmentPath($actionType, $actionData) { + private function getAttachmentPath($actionType, $actionData) + { // Get store id $storeid = false; - if(isset($actionData["store"])) { + if (isset($actionData["store"])) { $storeid = $actionData["store"]; } // Get message entryid $entryid = false; - if(isset($actionData["entryid"])) { + if (isset($actionData["entryid"])) { $entryid = $actionData["entryid"]; } @@ -454,42 +457,41 @@ class CalendarModule extends Module { // Get number of attachment which should be opened. $attachNum = false; - if(isset($actionData["attachNum"])) { + if (isset($actionData["attachNum"])) { $attachNum = $actionData["attachNum"]; } // Check if storeid and entryid isset - if($storeid && $entryid) { + if ($storeid && $entryid) { // Open the store $store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid)); - - if($store) { + + if ($store) { // Open the message $message = mapi_msgstore_openentry($store, hex2bin($entryid)); - - if($message) { + + if ($message) { $attachment = false; // Check if attachNum isset - if($attachNum) { + if ($attachNum) { // Loop through the attachNums, message in message in message ... - for($i = 0; $i < (count($attachNum) - 1); $i++) - { + for ($i = 0; $i < (count($attachNum) - 1); $i++) { // Open the attachment - $tempattach = mapi_message_openattach($message, (int) $attachNum[$i]); - if($tempattach) { + $tempattach = mapi_message_openattach($message, (int)$attachNum[$i]); + if ($tempattach) { // Open the object in the attachment $message = mapi_attach_openobj($tempattach); } } // Open the attachment - $attachment = mapi_message_openattach($message, (int) $attachNum[(count($attachNum) - 1)]); + $attachment = mapi_message_openattach($message, (int)$attachNum[(count($attachNum) - 1)]); } // Check if the attachment is opened - if($attachment) { - + if ($attachment) { + // Get the props of the attachment $props = mapi_attach_getprops($attachment, array(PR_ATTACH_LONG_FILENAME, PR_ATTACH_MIME_TAG, PR_DISPLAY_NAME, PR_ATTACH_METHOD)); // Content Type @@ -498,29 +500,33 @@ class CalendarModule extends Module { $filename = "ERROR"; // Set filename - if(isset($props[PR_ATTACH_LONG_FILENAME])) { + if (isset($props[PR_ATTACH_LONG_FILENAME])) { $filename = $props[PR_ATTACH_LONG_FILENAME]; - } else if(isset($props[PR_ATTACH_FILENAME])) { - $filename = $props[PR_ATTACH_FILENAME]; - } else if(isset($props[PR_DISPLAY_NAME])) { - $filename = $props[PR_DISPLAY_NAME]; - } - + } else { + if (isset($props[PR_ATTACH_FILENAME])) { + $filename = $props[PR_ATTACH_FILENAME]; + } else { + if (isset($props[PR_DISPLAY_NAME])) { + $filename = $props[PR_DISPLAY_NAME]; + } + } + } + // Set content type - if(isset($props[PR_ATTACH_MIME_TAG])) { + if (isset($props[PR_ATTACH_MIME_TAG])) { $contentType = $props[PR_ATTACH_MIME_TAG]; } else { // Parse the extension of the filename to get the content type - if(strrpos($filename, ".") !== false) { + if (strrpos($filename, ".") !== false) { $extension = strtolower(substr($filename, strrpos($filename, "."))); $contentType = "application/octet-stream"; - if (is_readable("mimetypes.dat")){ - $fh = fopen("mimetypes.dat","r"); + if (is_readable("mimetypes.dat")) { + $fh = fopen("mimetypes.dat", "r"); $ext_found = false; - while (!feof($fh) && !$ext_found){ + while (!feof($fh) && !$ext_found) { $line = fgets($fh); preg_match("/(\.[a-z0-9]+)[ \t]+([^ \t\n\r]*)/i", $line, $result); - if ($extension == $result[1]){ + if ($extension == $result[1]) { $ext_found = true; $contentType = $result[2]; } @@ -529,24 +535,24 @@ class CalendarModule extends Module { } } } - - + + $tmpname = tempnam(TMP_PATH, stripslashes($filename)); // Open a stream to get the attachment data $stream = mapi_openpropertytostream($attachment, PR_ATTACH_DATA_BIN); $stat = mapi_stream_stat($stream); // File length = $stat["cb"] - - $fhandle = fopen($tmpname,'w'); + + $fhandle = fopen($tmpname, 'w'); $buffer = null; - for($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) { + for ($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) { // Write stream $buffer = mapi_stream_read($stream, BLOCK_SIZE); - fwrite($fhandle,$buffer,strlen($buffer)); + fwrite($fhandle, $buffer, strlen($buffer)); } fclose($fhandle); - + $response = array(); $response['tmpname'] = $tmpname; $response['filename'] = $filename; @@ -568,6 +574,71 @@ class CalendarModule extends Module { $GLOBALS["bus"]->addData($this->getResponseData()); } } -}; + + /** + * Function that parses the uploaded ics file and posts it via json + * @param $actionType + * @param $actionData + */ + private function loadCalendar($actionType, $actionData) + { + $error = false; + $error_msg = ""; + + if (is_readable($actionData["ics_filepath"])) { + $parser = null; + + try { + $parser = new vcalendar([ + "unique_id" => md5($actionData["ics_filepath"]), + "filename" => $actionData["ics_filepath"] + ]); + $parser->parse(); + } catch (Exception $e) { + $error = true; + $error_msg = $e->getMessage(); + } + if ($error) { + $response['status'] = false; + $response['message'] = $error_msg; + } else { + if (iterator_count($parser) == 0) { + $response['status'] = false; + $response['message'] = "No event in ics file"; + } else { + $response['status'] = true; + $response['parsed_file'] = $actionData["ics_filepath"]; + $response['parsed'] = array( + 'contacts' => $this->parseCalendarToArray($parser) + ); + } + } + } else { + $response['status'] = false; + $response['message'] = "File could not be read by server"; + } + + $this->addActionData($actionType, $response); + $GLOBALS["bus"]->addData($this->getResponseData()); + + if ($this->DEBUG) { + error_log("parsing done, bus data written!"); + } + } + + /** + * Create a array with contacts + * + * @param calendar ics parser object + * @return array parsed events + * @private + */ + private function parseCalendarToArray($calendar) + { + return false; + } +} + +; ?> diff --git a/php/plugin.calendarimporter.php b/php/plugin.calendarimporter.php index fcb688c..29168ef 100644 --- a/php/plugin.calendarimporter.php +++ b/php/plugin.calendarimporter.php @@ -19,24 +19,31 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * + */ +require_once __DIR__ . "/download.php"; + +/** * calendarimporter Plugin * * With this plugin you can import a ics file to your zarafa calendar * */ -class Plugincalendarimporter extends Plugin { +class Plugincalendarimporter extends Plugin +{ /** * Constructor */ - function Plugincalendarimporter() {} + function Plugincalendarimporter() + { + } /** * Function initializes the Plugin and registers all hooks * * @return void */ - function init() { + function init() + { $this->registerHook('server.core.settings.init.before'); } @@ -47,11 +54,17 @@ class Plugincalendarimporter extends Plugin { * @param mixed $data object(s) related to the hook * @return void */ - function execute($eventID, &$data) { - switch($eventID) { + function execute($eventID, &$data) + { + switch ($eventID) { case 'server.core.settings.init.before' : $this->injectPluginSettings($data); break; + case 'server.index.load.custom': + if ($data['name'] == 'download_ics') { + DownloadHandler::doDownload(); + } + break; } } @@ -60,16 +73,17 @@ class Plugincalendarimporter extends Plugin { * settings. * @param Array $data Reference to the data of the triggered hook */ - function injectPluginSettings(&$data) { + function injectPluginSettings(&$data) + { $data['settingsObj']->addSysAdminDefaults(Array( 'zarafa' => Array( 'v1' => Array( 'plugins' => Array( 'calendarimporter' => Array( - 'enable' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE, + 'enable' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE, 'enable_sync' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_SYNC, - 'default_calendar' => PLUGIN_CALENDARIMPORTER_DEFAULT, - 'default_timezone' => PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE + 'default_calendar' => PLUGIN_CALENDARIMPORTER_DEFAULT, + 'default_timezone' => PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE ) ) ) @@ -77,4 +91,5 @@ class Plugincalendarimporter extends Plugin { )); } } + ?> From 2c18071de43145dac8384c00393a7bffa8ff1ca9 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 20 Jun 2016 18:02:57 +0200 Subject: [PATCH 04/17] new ics lib --- js/dialogs/ImportPanel.js | 4 ++-- php/download.php | 2 ++ php/module.calendar.php | 10 ++++------ php/plugin.calendarimporter.php | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/js/dialogs/ImportPanel.js b/js/dialogs/ImportPanel.js index cf3a3cc..c12743a 100644 --- a/js/dialogs/ImportPanel.js +++ b/js/dialogs/ImportPanel.js @@ -507,8 +507,8 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { waitMsg: 'Uploading and parsing calendar...', url: 'plugins/calendarimporter/php/upload.php', failure: function(file, action) { - Ext.getCmp('submitButton').disable(); - Ext.getCmp('submitAllButton').disable(); + this.submitButton.disable(); + this.submitAllButton.disable(); Zarafa.common.dialogs.MessageBox.show({ title : _('Error'), msg : _(action.result.error), diff --git a/php/download.php b/php/download.php index 215fddb..8f64548 100644 --- a/php/download.php +++ b/php/download.php @@ -21,6 +21,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ +namespace calendarimporter; + class DownloadHandler { /** diff --git a/php/module.calendar.php b/php/module.calendar.php index c490a5d..feab76a 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -395,10 +395,6 @@ class CalendarModule extends Module */ private function importCalendar($actionType, $actionData) { - if ($this->DEBUG) { - error_log("PHP Timezone: " . $tz); - } - if (is_readable($actionData["ics_filepath"])) { $ical = new ICal($actionData["ics_filepath"], $GLOBALS["settings"]->get("zarafa/v1/plugins/calendarimporter/default_timezone"), $actionData["timezone"], $actionData["ignore_dst"]); // Parse it! @@ -591,9 +587,11 @@ class CalendarModule extends Module try { $parser = new vcalendar([ "unique_id" => md5($actionData["ics_filepath"]), - "filename" => $actionData["ics_filepath"] + "directory" => dirname($actionData["ics_filepath"]), + "filename" => basename($actionData["ics_filepath"]) ]); $parser->parse(); + error_log(print_r($parser, true)); } catch (Exception $e) { $error = true; $error_msg = $e->getMessage(); @@ -602,7 +600,7 @@ class CalendarModule extends Module $response['status'] = false; $response['message'] = $error_msg; } else { - if (iterator_count($parser) == 0) { + if (count($parser->components) == 0) { $response['status'] = false; $response['message'] = "No event in ics file"; } else { diff --git a/php/plugin.calendarimporter.php b/php/plugin.calendarimporter.php index 29168ef..27712cb 100644 --- a/php/plugin.calendarimporter.php +++ b/php/plugin.calendarimporter.php @@ -62,7 +62,7 @@ class Plugincalendarimporter extends Plugin break; case 'server.index.load.custom': if ($data['name'] == 'download_ics') { - DownloadHandler::doDownload(); + calendarimporter\DownloadHandler::doDownload(); } break; } From 2606bf75b13dfb00ec7f1f88a1cc9d8a60caf8b5 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 20 Jun 2016 19:42:07 +0200 Subject: [PATCH 05/17] switched to sabre / vobject library --- js/data/ResponseHandler.js | 2 +- js/dialogs/ImportPanel.js | 71 ++++++++++++++---------- manifest.xml | 4 +- php/composer.json | 2 +- php/module.calendar.php | 52 +++++++++++++---- resources/css/calendarimporter-main.css | 28 +++++++++- resources/images/download.png | Bin 0 -> 224 bytes resources/images/download.xcf | Bin 0 -> 93773 bytes resources/images/upload.png | Bin 0 -> 213 bytes resources/images/upload.xcf | Bin 0 -> 930 bytes 10 files changed, 113 insertions(+), 46 deletions(-) create mode 100755 resources/images/download.png create mode 100755 resources/images/download.xcf create mode 100644 resources/images/upload.png create mode 100755 resources/images/upload.xcf diff --git a/js/data/ResponseHandler.js b/js/data/ResponseHandler.js index 1d338c1..0a2b823 100644 --- a/js/data/ResponseHandler.js +++ b/js/data/ResponseHandler.js @@ -52,7 +52,7 @@ Zarafa.plugins.calendarimporter.data.ResponseHandler = Ext.extend(Zarafa.core.da * Call the successCallback callback function. * @param {Object} response Object contained the response data. */ - doList: function (response) { + doLoad: function (response) { this.successCallback(response); }, diff --git a/js/dialogs/ImportPanel.js b/js/dialogs/ImportPanel.js index c12743a..a317efc 100644 --- a/js/dialogs/ImportPanel.js +++ b/js/dialogs/ImportPanel.js @@ -77,17 +77,19 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { // create the data store this.store = new Ext.data.ArrayStore({ fields: [ - {name: 'title'}, - {name: 'start'}, - {name: 'end'}, + {name: 'subject'}, + {name: 'startdate'}, + {name: 'enddate'}, {name: 'location'}, - {name: 'description'}, + {name: 'body'}, {name: 'priority'}, {name: 'label'}, {name: 'busy'}, - {name: 'privatestate'}, + {name: 'class'}, {name: 'organizer'}, - {name: 'trigger'} + {name: 'alarms'}, + {name: 'timezone'}, + {name: 'record'} ] }); @@ -285,13 +287,21 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { parsedData = new Array(eventdata.events.length); var i = 0; for(i = 0; i < eventdata.events.length; i++) { - var trigger = null; - - if(eventdata.events[i]["VALARM"]) { - trigger = eventdata.events[i]["VALARM"]["TRIGGER"]; - trigger = new Date(parseInt(trigger) + local_tz_offset); - } - parsedData[i] = new Array(eventdata.events[i]["SUMMARY"], new Date(parseInt(eventdata.events[i]["DTSTART"]) + local_tz_offset), new Date(parseInt(eventdata.events[i]["DTEND"]) + local_tz_offset), eventdata.events[i]["LOCATION"], eventdata.events[i]["DESCRIPTION"],eventdata.events[i]["PRIORITY"],eventdata.events[i]["X-ZARAFA-LABEL"],eventdata.events[i]["X-MICROSOFT-CDO-BUSYSTATUS"],eventdata.events[i]["CLASS"],eventdata.events[i]["ORGANIZER"],trigger); + parsedData[i] = [ + eventdata.events[i]["subject"], + new Date(parseInt(eventdata.events[i]["startdate"]) * 1000 + local_tz_offset), + new Date(parseInt(eventdata.events[i]["enddate"]) * 1000 + local_tz_offset), + eventdata.events[i]["location"], + eventdata.events[i]["body"], + eventdata.events[i]["priority"], + eventdata.events[i]["label"], + eventdata.events[i]["busy"], + eventdata.events[i]["class"], + eventdata.events[i]["organizer"], + eventdata.events[i]["alarms"], + eventdata.events[i]["timezone"], + eventdata.events[i] + ]; } } else { return null; @@ -325,17 +335,18 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { sortable: true }, columns: [ - {id: 'Summary', header: 'Title', width: 200, sortable: true, dataIndex: 'title'}, - {header: 'Start', width: 200, sortable: true, dataIndex: 'start', renderer : Zarafa.common.ui.grid.Renderers.datetime}, - {header: 'End', width: 200, sortable: true, dataIndex: 'end', renderer : Zarafa.common.ui.grid.Renderers.datetime}, + {id: 'Summary', header: 'Title', width: 200, sortable: true, dataIndex: 'subject'}, + {header: 'Start', width: 200, sortable: true, dataIndex: 'startdate', renderer : Zarafa.common.ui.grid.Renderers.datetime}, + {header: 'End', width: 200, sortable: true, dataIndex: 'enddate', renderer : Zarafa.common.ui.grid.Renderers.datetime}, {header: 'Location', width: 150, sortable: true, dataIndex: 'location'}, - {header: 'Description', sortable: true, dataIndex: 'description'}, + {header: 'Description', sortable: true, dataIndex: 'body'}, {header: "Priority", dataIndex: 'priority', hidden: true}, {header: "Label", dataIndex: 'label', hidden: true}, {header: "Busystatus", dataIndex: 'busy', hidden: true}, - {header: "Privacystatus", dataIndex: 'privatestate', hidden: true}, + {header: "Privacystatus", dataIndex: 'class', hidden: true}, {header: "Organizer", dataIndex: 'organizer', hidden: true}, - {header: "Alarm", dataIndex: 'trigger', hidden: true, renderer : Zarafa.common.ui.grid.Renderers.datetime} + {header: "Alarm", dataIndex: 'alarms', hidden: true, renderer : Zarafa.common.ui.grid.Renderers.datetime}, + {header: "Timezone", dataIndex: 'timezone', hidden: true} ] }), sm: new Ext.grid.RowSelectionModel({multiSelect:true}) @@ -430,7 +441,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { createSubmitButton: function() { return { xtype: "button", - ref: "submitButton", + ref: "../submitButton", disabled: true, width: 100, border: false, @@ -444,7 +455,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { createSubmitAllButton: function() { return { xtype: "button", - ref: "submitAllButton", + ref: "../submitAllButton", disabled: true, width: 100, border: false, @@ -548,20 +559,22 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { }, handleParsingResult: function(response) { - this.loadMask.hide(); + var self = this.scope; + + self.loadMask.hide(); if(response["status"] == true) { - this.submitButton.enable(); - this.submitAllButton.enable(); + self.submitButton.enable(); + self.submitAllButton.enable(); if(typeof response.parsed.calendar["X-WR-TIMEZONE"] !== "undefined") { - this.timezone = response.parsed.calendar["X-WR-TIMEZONE"]; - this.timezoneselector.setValue(Zarafa.plugins.calendarimporter.data.Timezones.unMap(this.timezone)); + self.timezone = response.parsed.calendar["X-WR-TIMEZONE"]; + self.timezoneselector.setValue(Zarafa.plugins.calendarimporter.data.Timezones.unMap(this.timezone)); } - this.reloadGridStore(response.parsed); + self.reloadGridStore(response.parsed); } else { - this.submitButton.disable(); - this.submitAllButton.disable(); + self.submitButton.disable(); + self.submitAllButton.disable(); Zarafa.common.dialogs.MessageBox.show({ title : _('Parser Error'), msg : _(response["message"]), diff --git a/manifest.xml b/manifest.xml index 207e798..00e0852 100644 --- a/manifest.xml +++ b/manifest.xml @@ -20,7 +20,7 @@ php/module.calendar.php - js/calendarimporter.js + js/calendarimporter-debug.js js/calendarimporter-debug.js js/data/timezones.js @@ -39,7 +39,7 @@ js/plugin.calendarimporter.js - resources/css/calendarimporter-min.css + resources/css/calendarimporter.css resources/css/calendarimporter.css resources/css/calendarimporter-main.css diff --git a/php/composer.json b/php/composer.json index 7579244..aa0fd8e 100644 --- a/php/composer.json +++ b/php/composer.json @@ -1,5 +1,5 @@ { "require": { - "kigkonsult/icalcreator": "dev-master" + "sabre/vobject": "4.1" } } \ No newline at end of file diff --git a/php/module.calendar.php b/php/module.calendar.php index feab76a..040d447 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -23,6 +23,8 @@ include_once('vendor/autoload.php'); +use Sabre\VObject; + class CalendarModule extends Module { @@ -585,13 +587,10 @@ class CalendarModule extends Module $parser = null; try { - $parser = new vcalendar([ - "unique_id" => md5($actionData["ics_filepath"]), - "directory" => dirname($actionData["ics_filepath"]), - "filename" => basename($actionData["ics_filepath"]) - ]); - $parser->parse(); - error_log(print_r($parser, true)); + $parser = VObject\Reader::read( + fopen($actionData["ics_filepath"],'r') + ); + error_log(print_r($parser->VTIMEZONE, true)); } catch (Exception $e) { $error = true; $error_msg = $e->getMessage(); @@ -600,14 +599,16 @@ class CalendarModule extends Module $response['status'] = false; $response['message'] = $error_msg; } else { - if (count($parser->components) == 0) { + if (count($parser->VEVENT) == 0) { $response['status'] = false; $response['message'] = "No event in ics file"; } else { $response['status'] = true; $response['parsed_file'] = $actionData["ics_filepath"]; $response['parsed'] = array( - 'contacts' => $this->parseCalendarToArray($parser) + 'events' => $this->parseCalendarToArray($parser), + 'timezone' => $parser->VTIMEZONE->TZID, + 'calendar' => $parser ); } } @@ -627,13 +628,42 @@ class CalendarModule extends Module /** * Create a array with contacts * - * @param calendar ics parser object + * @param {VObject} $calendar ics parser object * @return array parsed events * @private */ private function parseCalendarToArray($calendar) { - return false; + $events = array(); + foreach ($calendar->VEVENT as $Index => $vEvent) { + // Sabre\VObject\Parser\XML\Element\VEvent + $properties = array(); + + //uid - used for front/backend communication + $properties["internal_fields"] = array(); + $properties["internal_fields"]["event_uid"] = base64_encode($Index . $vEvent->UID); + + $properties["startdate"] = (string)$vEvent->DTSTART->getDateTime()->getTimestamp(); + $properties["enddate"] = (string)$vEvent->DTEND->getDateTime()->getTimestamp(); + $properties["location"] = (string)$vEvent->LOCATION; + $properties["subject"] = (string)$vEvent->SUMMARY; + $properties["body"] = (string)$vEvent->DESCRIPTION; + $properties["comment"] = (string)$vEvent->COMMENT; + $properties["timezone"] = (string)$vEvent->DTSTART["TZID"]; + $properties["organizer"] = (string)$vEvent->ORGANIZER; + $properties["busy"] = (string)$vEvent->{'X-MICROSOFT-CDO-INTENDEDSTATUS'}; // X-MICROSOFT-CDO-BUSYSTATUS + $properties["transp"] = (string)$vEvent->TRANSP; + //$properties["trigger"] = (string)$vEvent->COMMENT; + $properties["priority"] = (string)$vEvent->PRIORITY; + $properties["class"] = (string)$vEvent->CLASS; + $properties["label"] = (string)$vEvent->COMMENT; + $properties["lastmodified"] = (string)$vEvent->{'LAST-MODIFIED'}; + $properties["created"] = (string)$vEvent->CREATED; + + array_push($events, $properties); + } + + return $events; } } diff --git a/resources/css/calendarimporter-main.css b/resources/css/calendarimporter-main.css index d3d595a..d101e01 100644 --- a/resources/css/calendarimporter-main.css +++ b/resources/css/calendarimporter-main.css @@ -2,5 +2,29 @@ background: url(../images/import_icon.png) no-repeat !important; background-repeat: no-repeat; background-position: center; - background-size: 18px!important; -} \ No newline at end of file +} + +.icon_calendarimporter_export { + background: url(../images/download.png) no-repeat; + background-repeat: no-repeat; + background-position: center; +} + +.icon_calendarimporter_import { + background: url(../images/upload.png) no-repeat; + background-repeat: no-repeat; + background-position: center; +} + +.zarafa-caiplg-container { + width: 100%; + height: 50px; +} + +.zarafa-caiplg-button .x-btn-small { + width: 80%; + height: 30px; + margin-left: 10%; + margin-right: 10%; + margin-top: 10px; +} diff --git a/resources/images/download.png b/resources/images/download.png new file mode 100755 index 0000000000000000000000000000000000000000..38a828115f79b5f70942577589b71ab86af6f3a5 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xawj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&pIsjaN{SdHo4NN1#xyr;B5V#`(RM4)QiA@U%V*k8D?r zY1-bmgHPw7(3xO=l@6AREB>$b-5<@`JEu76oT1ywd^MXl^%^RBSLtn^a3U+nYxdSK zhBJ#bS|0E+^gQZeIPm1DsNO}!Zw0-68(O1RNPGHUdi-|DztiXK)7hu%8tz?h+vo(e On8DN4&t;ucLK6VmSxpZB literal 0 HcmV?d00001 diff --git a/resources/images/download.xcf b/resources/images/download.xcf new file mode 100755 index 0000000000000000000000000000000000000000..9a5b62c6c719e6dbcbe61eccdbd24838d52c18c6 GIT binary patch literal 93773 zcmeEv2S8L;*8hDoL$Rc8$)=l_sEO;Y`>nfQcGE~P(I_w|15?moBWf(ff`VlRdl!r# zSR(eWLh|v;oqaqhu|mha2)>p3XkXeQJUrS0RA-|4IUMq-#TI9`wjS?!|&st zA=yqthWGnyP^ZsE4H*a$LpaAej2;s{Y*3rgBZmzc_*%;jA)gHxIc(&pHXY=jPNP2S z->*&QKXjJ=Jp}lZGw3x$?f;OduJAVB4N%naqeJ@*7&79sHh*&E1!DNah)!Cku_K0z zX)|QRfWd=CQ~Z%3A)^P4X(}Ma{x(UWJQ$Vq;QYtUlmTw|Iw#?eQ1e*8q{rV588C9h z=uV^m^2UIWKlcym{O8Vp2p#bmF_OmVpF1A1fP}}>2~*4#P8dIoEI~ZDP|A`*mO%zt@?{@ApjfV`g>XgXjgKc0};&pmzX!J47?9I{t)_#nY0TB%Wrk zGO<8XOJV_Q&p_jd_8Q}hrD7CV%u{5MAescxB%qKb4?!}=YfVuQ*_jLybS7<9z0SFV_X0p5WFjQ#;2Jj3ihT7R9t9@UjcfL@~)40`r7bAYNxP2mDO^uJ{`J!AUbdW5(! z0Y54vAAr`g4s=6cZ~s6eg>VLyo;L*gqnT}_2>Jk30H=3nB9(^^?5#&bOaTU?vwRxn zfrR`)cIcVjsCUzI1_xwe`j+}9*&}+6wZbnK*vbVNJduH(3R@Sb4-C{BbiD%%>`B&A zA)~l@!K6p?jpFGkZ9QuP8VW#3zsyv6(SW=;V_Se~L!;hc3NnF(Wa7XrsR1@MkWCE&GmuUFd&8!Iy#oRQ4Qd0DAe$O=0sgX0{R0i|2G}&9 z-Vhk*6F9&Gf(8N@U_iT%pMQWqsvv_&?vr6xXbg%9^f17#23W&iZ!nNu4Mt=kn#imm z8}+^Y^(G1s4MxuBFz5|^VAxuUVSw(9D2p3&Gw{Z~2C^$fp#%iD0tskv1K5X5Ykx>D^hndM3f_c-^~L}yp~B;a*i;A@)M!E)BpM8A5RFKlGeWTj zs^VBsa7itjWQem8xI8pnm2lPW7DEd#|VSZ&K=LaGPn$+^8fpNKPBQYBSAHhp(5=!@~3n}x{p zJgD*g@0s1)VPt{pNVHH6YO`?|iA2_Yse;)A)kMl{W(%@4=$!kb3+~L-AjGE8#vDyg zFsP}jQjDJFh0`P2QKb5}k}g!bUwN5QV>UtPS+>*jN-o_(a8C!q{DS zaS#>u19$c^tKn47v}ya7|NiT1Pd%vyYc=Qj{>q&A{((bdwoK|wZMRDB+KVojzjCph zekT5ggNryg^?Tc;7LqXLS*rh!eQ?2*)UA=DQ^yQQ&3K*33xyPQ;CKyfGg`o43`{sZHRCj95KZXm z#Xt;OJoI7+dxd@s_(2jfy*+by8IZkM^A~1MkC`!L&K~gAo&R#}@q&}7=J6-U4(I3X zE#1E^VMqF$kZk1EOj5Kjkksy!lp0u|imYlwlb~pOP?$E5)L;lgsnPrNm+LFK5y+*- z&x`0`NJJ;7X_71mO)^sLfOc4rWwYy{9j9#Q5m1>if}*}b(35cx48Q1h4YuLA6|X&LCMf!+_$08#F?6G7zCtAGzXeq zMYJHa$Pmrs6b)^UVn7qj6GWI1<`nxPGIc<5Yo$hG#P?M!=w#^tBN|OcGNPy_UNrgb zEmV3r=IhLuywyZ{i~L4znw2h7s-m=E#RY$v67U9$h^l_ZiGHDY3ZNH zl243^Y7cQ6V{Q~&dIZwgpQx53(HkNPABZ$6UP>a4LhB{&0ny&>sw6cGC0Z2G&XMoF z&`E=02<>J55ioJ1_nvbjJRywzPbowFSA9;BKUz#JVB;HP<#=klF1V506*LGhH%oEb(s(EQ=8*0L+RN$pq*sV!?Ip+8_XU+ykm@#TWpm<78heTLFk9bIxZVexE9 znm%!Ia^MCOqADh3$GjQgk~DPO==eqbKygexuw(}EbA(Rb+?{Z9(ws;cpS0yeiZLrL zHX?M8B!z~KS~6cpl+?ky!oL^?%7`($K3Il)YQiS%95*_2)H&QV}3a*$CPtkz;=$KSG+pp{iwg+tXS4}anq4>hZ-d_#I6G|%(a zt1=E76{i#7p7k!GtE?)=eGLmAL_Ad-@*jLNCPw{KZh zRyW2MB2V~5fz_%MsSrhi#19l1PIX@%n2WztC18d`-A~QIqN;NzataDgm8PvB6BTFP zJ%2h&l1?1Sy!6#EQt!c%OUIAxm!z%x_T;C31&Sm2O5rgwN$R13g@o@c$))bik$RwD z9>qw_Ih}E6izKC{rsW)nB}(Czl0ygffs(wtXyG9;(blvJ`}U+JC+|MLc_9_JG-20; zoPB$apWi&AkW6$jA^vb)?t%I93&})T%NNd%Ii#4V)@rRK6D4**`FYJ-N6AFjwd}bu zYnO*U&IZ;dH-SN4JOp!YMRLmT+B!z!Ia%+b0bLg zh9N87C4A_nNn>UFv*mA6jB#^je?CAbNf_NupWscD9lbXX{4^L8ebD+hmXIkv8@e$# z2!n9bI^+9P;0)csjWa_A&RD1aOB$KtXyAL3Vq?eu^}RGQMMA*4Z}p7(zioYN-SBkY15P)Drq4#8ycd{38XGgi)jPqWr6pFiw+U zv?Hbcy9Pr;a7unR7g)rmvY8X0{N*ia{di7!_(}Eqk@b>H^Ve_ z$A^qrvwi!jp}IpbTg~yHsNE~3PF;~a_ss)j){v;=h_F#(#)j{j{XV(Zw$R-Xqe4fF z_~O9i^IxbedtRTaG{}>% zSO*u|{g+-#w`_^+*(HTsEc)HoUVAqhF4l=X>W!(Q^AXMAt-?h!s_CN^P`~prt6o(! zQ+C{X;7BoP=1$h(v&F?{(vr$ZGq;bNynOED$#a*pR$W4A8q-f+&dJW=vK+@Q9bHAL zxpeSyPF806(W7OVmo{aQNa<(K9?Lj-fg&7J!eN%@Bjqe2o-4M>$7^M(lUj-l1#cu{?q$2mo1z6 zcIRZ$&B8Zdef7 z2Aphhw_xXDvKj?^Oh2<2(Vob3at*17k>G;hsHy6gFS@DuhLrjftBi&{Ho}d z93bJXMmVS-e1u9G#Oz*&9Jiu8X|`;j#Rst!%`nj8N#ZipZhmW31FeD7rNL*! zFmm!ayBY3Nyn-JOQJ*)kC^am@ydQ{@ z&c>+d97)Q^n6FSaAHJFU|q`#c$qH&bWFoM{#dloMYjRQ@AM zS!hvt&Z2~Sj*BC&D0zt$gQgsw`_;|)1J2nLig5evm)*i<8NNDks#2!-hkib&zxLIR zeTNMEY=l3?l>*~dVtT(4g5nS6WJbYa!V;huX;c~cNFy!ZRT_tfeKBrQWSDuv*pXqw zouuKT!_CuXOqm!KI$S0_YJ|>HDq2;DDHJEA;>4*Fr;g_y%gM~iEO(OfD{?BbDvlmL zSYbWnBxM~xl78SoYTBONdoDOhCo7Is9In`3k+yetg)6PRtm8+s4jtZmaBne@%w(?U ze%r}h+xtb6xuTaFqsd&+#^r?9L@&QGZ`ii&BNr5GjwW*r4J31YI*7~_IFyXDpt-pg zD3oUAa#EU^%SE|wuKN_1xxS0i+*}F|%r%wFwE*T)C}ghgVY7^6E}0TMaoot^#^3+f zkkKPYg$F;(T*Cy*@L}9AF>;t{7#}%o7^!|(MA+nMv*(A+n+fYgI7t!a>2sqNEu1}d zQiM!;blAhql?8K^I<>Bt%lh_`ie+zptr*p5t}i=|?YQ$B<_dpt)HlqP@cZ$fJt zD#({fk8b-PHW$rLRG7)!pI3a>K<8nzAhTLzLW3vf-`HwJ(;~s7kD^ijJ`WlZw~B`H zLq!!AKj{`dvO;T52xS-Znu zXJk5eor4(>@Eo%T=yZ$)fWsbuT`=%G!}t*VaLhx8sg~~9hp=GH`hjGy?DAkfZ5}8F z=I*ZYnKc#E8bXlEf8Sn_^f#(L5X3!2+>yei2s?fe=cl#~xuSTTh6LAT*Rs7n_1u-* zeXIWt->T7_DxLG^XFGp5wD`b#@UBYDf=lnQ$KuP&;~&%XzZ4B)-b+Y+f~hkksav-U zHGAUh2=cN1r*UfL6Rfgw-5Mku>S$BmlibYhwlCF$QCN2oM*-r4uepQnV%JD^Coht4 zCaaR{&SanecJ7$?JW#;4?0(_Rn%(R%tQCk1!=L6=(-&1)HBTdP^Jvk5AQd3a3898U z9nw5brgM@ndt@?Q&rNsQ{rcGUP1Gc236R!gNN~ zO^;ld1)%)k=erAY5rw%Z7Y2|D5DFth&ca;eWK1s1a%A1~$b~rx$`5|1Fl4G9_n6hk ze`HKoj%L*qjD5o&vWNv~9`K!q;j-LK{sE5f4V$^Y|Eu{nxdQV2-tfO6-zKXg-|r18 z`F8(Tt@W?ln`?=AbQRTpBYYe+89pvUP9Mj(pXju_sL*MFQK6{;iAGGqD1cTS=@&a^ zFH)-lkV9(!a=PSFNCc_lFp2yt4;`psLaT{9L2WDl(#V8xT1ll}XPNLzE1`trsFD2p z&r^DlIhC>iDWOF{&NAdw$^uTtWTnWEtP~lNm9l`80#sHCenlF&D~B1rwxz^@#%Zf1?fw6>7{PpxASE@kx(J)jzU*}- z`ucD_IE&-!qw+zm^uZ>X6Xtz=d@#F{u{KQYgHsd4^b!-j@q@t+VHmU%Cc~(oKL?2% zzTVzGw8QP|LjcB2-Vmw@<9$KYxNvNr$y_QI0Ru5_nZlIlSaFtd1;ZxKPb{L37DjVu zeSNhMk<)U%a2AN@3lW`fobxI3LfO%toIz#oQYNh@vvjGF0HDlW$t*664*?NoQE-&GfVf~5g@7_YDN~q& z!z?ncU}P5fAxD15`Lt)ew~yl14*dEcbHT?KbN2$A^T4Jp{-C|md*FGIY4q5%lYiCt zMc1Q8w}f*;E%bDZr#%h@b-;rKao{aLSHg*p`0vM8(nIBN=3rKzVh&{PO9v~weSI`O zg0DALIOUq6@}(++QplK_FBKdbQ862Q%}4g%cFgXCIxvSf`7ESBjr}R4@J7B5O3_GV z^zr3Taw=3Cx%PmdX(o#7Q{-k4#Ygcph~i7W2~qG@7R5*gF_J-yFbEbLH8?A&k$<6J zBNT0fL2$OXg%JiZ!XQQ%1iv1@TY#>F6Cd&4kFTT$gE(`L4I=wH)}DQ|?pm~UFSACX zX#F@pPD@2X@1^C5pz%|oiG@MP%NofQp@1h#=(G(vvEu_?DkL#J*sX}shQw%t_8*oD_HoL0ke(?rRvP*fxI79!sZenj#DyOeO zPeO$PDmIqv%es*CU8>3?95<}$0#WUZs-cgso3G$Q8(wN3v{vLJDxVja#>Yectp^V= z`I0xGR(R9qfsd~WjwII$C$91JP{MeyqNURKYLEyj7%&wuR}J!Y(^6C%$#4b{CRa59 z1ZPcyZnS78izt`D8*2uPaVue&=4eMd= zk3Fl9jvX%eY3Z1uA(xyHD~cipk6I|jj~+VqoRgG)VfCQlv!yRbezu&_QnI{W zHu1Cd7q$-$zp{^LsVSMu!669Gs}5@+j6kB(hxM+PW16sd&3Sy_znXieYpwzQ3m8tO3vG?!C8w3&G&=W z(qs1@KXWW;ioBMt8CEwlXUm)kb2gorP0Q_0^HnSu6A>Fec6ND_`7Ul7GhwMTcjDL$ zP39|^J>I<08XXxv?Yv@`W5-J)!l$g3)=ixdc3B~vtSFfs9=S|fJ2hfjk&~2Pyftjn z5^1G*{D#v_SzfQ0J3jH!?(muA&CFN4Y1V{U8;YIgtGagMTBXZ;zP^5FzTEr-EvZxO z%lV1anqf%bOXkyxzJBg#+g@PxzFI#&oS?;dQVs;v5mY>_pNd%hv~EoV4HEE0I!y#m zKMkT|s3~(IlU8Uvsp0fQGB{VPrSvf_^rHa~c?VJs-MvN+wuO+qS^nsiy&+PTQowu|t}!;Ari`W~gVN8*vh_(<(#ju|B9o3L!{G$rsS+ zfC5a_1Pqlt<(3(*<^V>MjaC}XciZ*oU4->*$x2UloiU^9ld_e5>{@^GMOZX0Pfprb zbkKrhOK4oo>>!g{;||Kehx117<&B{c8U`FU@bz)?!Dtr!n6Hn!k4$h^MszfEf-Euv z4W;CPlNv)0rK5oFV(Owr2OZg*0lB-5p&DrG~hap&Ztf z6SY>D*vO@jVf>NHCYP7SL!7rfh@&YK6o%pVgH4aI+F}~JEvMr6wlT)0P1kWdiWVAt zbaN-H9_QM0-Im@xGP7`Z&$q^GbCSm9_1tcqFla*R1fO$bsqbh^sfk-qU!ksOthe2* zQTFI;y-%K{z1S0{>+5X_snOb)OM9^u>5aP@om}|A5 zGGuSXxHj3iU~^JvL_y>1X6A(f)jkez(64M{jStr>SL83}!^uVRc2i18LvZ?^Osw{% z%)ZOQ!>TRAr={dq*0fJMZg+8%!6#tegiYgI`?Sd!pG?U*o7(fOvB^%-v{T-R znX|)YADrl0G>y8K{}mRdsU|E;y#dC~FryEKUMgQ0*-r-0(7%EnM&tXc{18pWaKQMwU3^%PDV!+DI$T|Egk0a~>da|l2BeUZ6i zLVG_Ol{~#gD}bVDG1imIV{0}If%~BDYw_+VCLiCfvD>PrY~6dNyrTR}+P0>R-0+H9 zOH4lcMUe<pPLNr+UmPC_CcwRz$gzw71wNwRC^d{xcSB`CjUZ{#V$utJnqhq~SYy zp!6qUQQ^rd^fu^BpusTeaoSRAJcFRFE~ufau0Sfo$RF0|f(C#u3!}+^+{cI^fEugb zuPQ4eQV1I1y0$ZBggx0+l0ZU_H3KAjNJ{$pF4XUFWikmVt*2hDUlnMMGD!+SN5^-S z(vgTz6-GrWKz5E9l1yq|f*F)(I{lFg7}A7N$>qr#=5I?-mF-WaOHvgrL_lgX{=cJt zdzm$Kwroz{Y@wHlhhfqA7*lIrWA<)6eSEri!hvx55gMY2qib>SiF!rQxhN79A2(8EL4#Ty zf0$c5d}RGE98LN6=)$4)@K8Zi`Atq8yKkP={@weZ(?UbaJB;IN%-;Gq# zQ<6pzUMl5NJV!f8@*&(u!pVCq!g#9i*Reucr2-Z0jvIdXaZ-e=lOI#h>8P7>JniVg zBL`)F#z^0J4(|Tpm5%?@^-!nl?dR*=qx;7np*ch$Q4a?!iLR3*d-O~i?bCun#j&Aw z>*r^#g>K6+DI|z8RCFBrJY>h^Y9c`tRYl!UA!qH8l;|vlBrgjLS)XZdsIN<2L9gT0 z1r8fFbns_`2K4QQ!TWLcXbgr>F(v8}{Bgq{O6Vb|XEQcci6f?@ggAe!LjTjX`TskG z{->LS|FsHr9b>zHeyHQWIz(uV_jX#~ot>PAo{@7^p8J*LA5qi~tyaz$OSmOm47a3t zoxH$Z5M$&&&FU#fb(jA90NSH5W)0qS?|}vP9$0Yi@d|VA!Ta>+ffj=6A!>VYJsjg# z%V?e1wjRR4?W z@AqQOYsddZuXnX@|M-_`yP!?PYsX?JhGSwdrg&l$CR4)Wm9$QXO)RYKdeOwMPVxfG zdr1wbc8=@yGTJy0uZY)!Ak+_}W}uC9kD_6F0nsXHRg+$?MzVl{X-*kM8CYn>*#^x) zL!}9W!cr~HkBcX14p~CMTFe?T(~|Dqm8cM6DNbfKwy~62y|B9ZEQ&^(VU5gETWdkO z&oVI&N!h)aB(duX|I?WBqlIxio|pGHO=oAKF+kR!c$MaP5ye6jD^X@il7%Q%Oz5`C z$J$yiKyen-y45x|*z0TQH8o*D?RNEc`>_>a#dU=9z>jms8*=Uhe2`X8lxm_JKT*2l z!-3c2C`2f{R9M~TqkR`#g!E%;?(FF{prO!3xH$Zy+>lRp*B848CoX>6?UUe!+RH9N z#?86idnDTHD_n%^qD%9ls&C)AArnwcuGUd!Z>XzP)#Bh%8`gS(+0$JSVr(UP}7lO7vWg$ z_PdArjD;9Zj?&4df^nZ7tS@m9axVw<{dAPQ_KJ&;bvwRqa2ko>jB>o_O8knNI}n2i zC}uM;h|+rDKNKTPoo3IoZn^3bBdw4tY&cW3Vo{z;jI_eSGZndu)?Ihym{w9=T)S%V znIH`Y^WrNTmK4@sbP@7z#KbOH+jw7$?8?1M;_{n`k#ph3p8fTA zZr}XpV&Gs~>mT&c{-p3_ET%xV(AndYlEzORLG>mwNs}bb24ss!+T!Q8DQObwRb6aS zk}qT4Nl9O#zSTt~CE=zy8N<5BgrrTxH_K2xSbKJ~fNMBeI1?LZf*PLPZ zQWi~!hzJiGJ9<>;$YH}QDYOeQV^QlxXdrO+(*!K@fDnNyFNU8}pA*BW+71BC9xml% zho1}J0vgVRCzg0g!NeHuVnnc)m(-US!=$VXnNc_w?!|ct65S?+cO0EbDx5~*94#HT zx{~|~N$QDlq>E9>+n*RmNC)<~7-OQNrQXCi>_J99(C*C}95S%qr$I)2V1R$`-hT5i zY5WvZOKfXG4{lmaPHE**-o(P{BOqVs%0t^uencHnbZ98;J!sLg2Ov&s$F3R{F=akz z*pChy_@Oc6d_Ay4iG_1NttU}lVnPpJ1PZ=A5Gr_4MuMLg*3+rkf##3w+OU4@>V&UW zERBy{y!fjlB*V6})@foiphq`|bBVB)gj#b&eMMYDnx6@peGN3f=E@9iM#Gggi6vpu zbYfiXVnlFQm=sBj3DCUEC|p?+#)S#eWa9klLC%T9xg45zCHWPSj3CA(E=DEqvBbC- zns+gh=0_9bq6ZmAfp%}zg1NJ%Pn|q*0{PbHFIN4WG=Je@adFYP!h%z|CyyUHmU)3> zD7oJHx_AiCqs8bOK)66cov&Bdi|0x6`$4mxhvv`MmviL}_2&~y%A`HSc+SO$V621g zCdNW&US<^P&zEs!f|N|01rKuWB+h(j-j(E6NHUQab6ku{-dl+=8=7}9lIAxPW7dO= zn?S3{IAT4pH*MF>ueWd8vWeRyX8ep)|HF^Yn>E4A@y@hq@1VlsT~t^imPRgm>iPB5 z#BCsG13}3I(Tn5aB!VUZ5?af*-W0c{O8i93*|RZE0J1&4ZA`m%W41j`BOfji%t9hZ z1DCNha2ZS0^?4gW!S*~2RR&{gRUAwe`iV@UW7wax~2 zc72O_F&lkaLw0@nrtyM5;TJP2roW16FvtS{+el$1QT?thXu3qho zdq3IM*=CK2w%%IOJ||xN#nBf!A|oGop?!Yka15udM*!|c#d!}9ao&^8($IMi9L;ua zkv0(1jD}q|&xc8H7o#<4H#s_Z z7+EvDpqXByTZ|TaK@os7qaoR7C-S4nt$UFk+_l&tqqCy|PJn3WyohoDlsZLD>*iXJ7Xz*g z4r#{FP&Q);_KEI?cV!HFVon@$f^;hR427Vh9MWgZSg+(Lpd!f?&a6Jk#G{AZ4+{qu zpcxQXCb%aEV@Ct5jsIftZam5HqZKrEcTPM#PISl7lkxP(sAoJky6umRl8jt-xsv@x zE{R7@#?#X*7gf9}L?!3vg6QOL%LFb;m4J;mOK3t~qI;sl>ZJ|FR19Yi-6XD3uy#g^ zSCb%BCU_-C)+mb$wd4auhm!hINqr4MRyR^#qEtF7@4947h;!i^Ee)j& zm6g|BxFz^{mOeEh&MNb7Z0Pnz>iBNgua^QR^-<;1;!GZD)jv|-%#9iGGdHHY1lyc- zK7Mo7ITva@RTNK8Gr?-X{v)EKBid0{vfs=lAGzRZmWxYi-%y`bmDj?GQ)UT5Ua8gH z>L?8jrT4KhvB&AsykJsXo>W;1zss{G%DCMsmF8(J=>DDIgm-Sktj_QTs|1I8ybXp! z#DD`)sQGf+L0S{qC&MU0=^3i9SIgbY1-usXGHZ#KlYYq4g6^ewncx+p&bU2Q(0a*# zJ7dSJ!5C}|#I@#cac`N`xoDGHIeR57PCT{3X&27B6&zk7Mc(-z18zp!*6B>Ok(M7FyYy315*cs5oj7z3)X%1vnA~ciL11%w)uddWo^4Iee zSs{<(dTHt`SqGI|rTu#8xx9@eZCwItFOaN+@xUOiRjN#EgQX{@D++K$k31}YLDXxO zV2@xijYPEslaNh+A?kR`%t*#Y58;N$qBfZdXJ&j``9c;S7*D>%6lY{w(v%XWLWe2` z3OH5+!&5Nl*Xo;rt_TQJ5}}x`WR;A_$R1Igg%;`ZGU6BE#u$rzVs z2NtCzv@QxVCPZ06y$Hi?)UvCSyW^r%+|sT*bd?#0hlCPBx}>*E$C(V|-?nNz)yS?? z;wM{m73qwL(6#N1mNRe=g~Fw#aG~v!U0W&~T-#O;33o+nO6tM%o3bS#TgV$Vv{`fi z5zwt^oDK_nO4da5XjYP{F{;uMYAxkT1sN5iV0%?5pC@C2rLt62%H;`(!K57v*wjvh zx0IIVad{3>9m4FP&>a(oDd)}J(DZZ~@Zc-m^-4(ycGU4-< z;Hdjz*~&R%N1Mm=eHTqkjUh-sAj;rBKh*y(ZD?eu!#%aOkPk*%KG*$sdm_-qwKn5h zV@CATzW-McG{8(fKh=HG`$G!uL&3N`_rt$;HGV|xso|5}!F^1I_j{N={R9j(xCvfs z2pTwYT*Me1MbP0E+s1xlCxnvU;vV=9JEBmy;?{^Ml1+!Z1aaj#U2eNrN${f zQCewDu%;$PDOld%!5b4dZk#okWSW(QfQ<yGbTX?cspqswn?^Oo;TPf3_Y_hf4fwt?{n)3z>~F@S=N6g+%W z!sdjq2i?hjfMRE6hPUG~ixelcGe6Qj>zcNHukq1UOi*)~mCEMQMWx zX+v0}MGoLP3vEaj4jEH9F(K5ev&IRFp`F#8Rc%BsaPh&NJGap&Fc+-Ws=Z|2>z#H! z|G;QjcX4;ltXs8W87@GRmwgcJGw{hskWHf~Idb0s zqqq+tyzN7Gy zY6rKq1keGd03CZohnMYjX4MsliHg9{7aGX*1tW>7!Y3ne5j6=)KXfTI6$mxX%~@0W z7<5#bK+{o$QDC(JB4QbrM8vJvPKH1i1JXdT=^F=%l+L7@SGxbKnDBUrDc?O!QaW&} zKh+CaP6ymw4he#wd>>q=uQ8EvgM#Vv0k1H-4&k`|ny%!Bcp4LKt`3qVRpFL@&cp|q zh)&7Kw^Wm?o=D-Fbp)>)|HlmJ2svF4L`!gQ(-8YaYsgr?Op-?U9V!iP4Y3;gNfpZd z(-FWNt+pU8Pmi#Kt_HMM5qc%)|U{?w3J?OFh>EtI4U zs9bArCv$MQ#y@Vq^f#(pxN43T>ZoQ3w&wtUN5!IH2RymyflF9u%t37oAXH@wc`|Jy z0zxwM0RcepWSUw4f+t(BK}eo#fhS`e|A<7MY-YEZrw&IXbQnZ@$Ae0diTdzO2D9c( zcrun>b*f?7Oj?Co!4P?1((QbsVT&o z4Hh`GA|21*gJlehF)~{)HY9LQ4Ynqb0*4P4fXy2q4qziut)C8=fW)X6^u~^S)Ec8l zoPx&&g*U8;@nJ9QOXU3`>VpRSKV(&m%gcF5>Qw3;wARaECl}+Qi`WE|_a*WMB^SF%cTLxsti6;p0 z+(XE;+$UuuZ8?z0{9h5m?cW5%eCpR!19}#Z8N??B)Kg9sfNsYE6>p;OGfWpyu@xbQw3J2mh&*M-Oc)Lp0u0NlG!Yyh5JDBI?MM;B~G5=GO6QU##ug6SEO z??NvGG)tcQ7`woVoa;9^)&Y=EXUvvejLU`j}sm5_3b`}!i)6RqBLoV9cuMLySgQqu^5`oGfE>Ga_FCJaJpWcK}U1 zD%@PqES;u`%!Q$*p=8M@Su#qNjFKgzWXUKQe$~k$OV%Xg^kgzhmW+~tL6eM0Feb~ z0#LF5lq>)x1A`_2B@1wsEP#gpRE_{O!ezh>%rf8^pp-1&+dvbKDnKcjGo5diOwj&` zj{koo*?*6G^En>JrcNmCA0orFZ zNYYiB?h1dpLG!}$U&AwnXKxZ)#cz>hBj9VlrMC;(r@e^v-2&Om;CbX_aAsi-e6q=z z`STNBraD0$iqAFNOZeasm;b_@owOMxNPhP7T}CHbuiWH=Ckio2O0D@I!zm9+geeurr+|4a?0g&!VH9$w#ZB zmE@z{qLt*MJ)@Q69|AP-sG^nRo#`|s-%Ns-7-tqS>KG-97`GTDix|%sC5wjuO+2a? zB@1UdO<6c4=+MELd^de+baTqw1g}?@XsD`DPNl@Pado7t}8( zS-jx(f|A7xo-Zg_JOpUsQN5sK;Y_C~3#SA#XF8Lgsh+7MKhtfdlKf21nM(2x0h)MJ zGnM3>=`$A# zQ%dqrc|N5i{}7;wNA;8l->a?*tF5|wGwg-v4iN67xSXOaUVH6&+3B^!Ws{cexNtUcnS?Ct zG3!^Z+OdK%vnz85AXQLE)L)w1sj~ z()6Xr4lkQ7o5K<{d*0GnvN@*jPEXoBZ3dacGIMH7;h95QHcg9^&0(3k6KORRrfuD6 ziL}5Rwkgw=T2Gxgw0ash4eJV)DKn=`n>ls*!>!RtFn8|6brK^xsXFnIojQ>*EhU?D`HV=$#KoOc~k#gAYiZ z9}HdhWw`%I3F$hKkWYtD4B?~Ugy@HTL{Z-BId$5^ ze%@Y4*8X8YM5wMS#@zPzy}Ny)`$)FMhr>b*{XTe`Y~l6M2fe0F8>;Q|;roAs=DGL9 z!AQw5wV&UB_qg|P)atzt14c(q8e{n2gLkNAcz^iO2Ooa?;oDAI{Ft|bT`$fwy&_1h z#4GzMEBC!drF*_gz?yZ<>m=6VMoeY}VLf2+BCR0bC8(Yt2SGOpsv+nQK^cGq-x`Wj zO(#}uTt1~$P9HrLEb=!SPY|d&A+!7j0yzfmudSsSbK^C9JQ7o!ztWbKP!j-{|MSgy z3bNNPd!A%(IkmukCx6=0B)fVJ-F|`ZrPDh#?poZLhs&IB7EmFy)bg9b^&)Gf#iso~ zd2rxhGZGYRhEV+%c>-M>O_kgsM=zi9FclYx)L+R!U6xK$p2$nt{}&#U^p8!x)j&d38kZP z+)-13!-e9&tU`kpLc7h!&`mifouWP^MCd_E9|n`wSnVW=%1 zJH~ws*VLL%T)SqKdR60^^^@dEWw%6Hmz3BAPOv-5mhPKHb*s`c`CiSTZF%Cw?R)Q4 z&rnF_+je{XfjQf%?Do4diMN2n*?FN}pr2+$55lPOf#rIwM=gIt#yCq$GlzQdd)8_V z*6Y?_`CFuq*Ye?`2Nt>)3N>q}<6BdG=APyrf2Nv(90)o`=(#Ei6xLLocRw!xR$^Un zzaUg90ryJWON4u_fQ#-Ig}X|?ol^Hw;f^cdvioI1D5=|J?q$MlSHM;GtHLb`5ZB!F zyoqw*lc8(O>XCA9_%qaC95fhr$McRD=hVes_r1dHI24hjZZ1-%T!WEF?o+Fei0atU~bA_v}fK%?Lgfb=I3R&%n zE8v{_IpHz|h;gN!r6?CP822;Q;1FnVNTFw;NUONygGP6waB2u?a7b>yVofpMFPDNy zgM$ejoI`=aknEA}BZX{NtT6X5AyWxBI>~*KaMTqr&3&41NC`MF*L|*Vz!k8_eUY$F z2}p}~j~CKh0SS1?AcX?NAxWM|C>Jz{W1}Pgqu-pw9aYK^PkNP=j(L9HU*e_0^~d}; z4htC#G0WnX#xGlfJ)GM3r3oi)R;8pbi(5ilLGj{^My}C*YVU(@xq-= zmn(Jye~DEuUQtuEvu5XotfbT>G0=JSlH_Z9&)05CODxYhwM-@)+LwCp#>O4xSJoAi z$5k)eSJilYRmT49M>eM|CqjJe!4t>N7MxAr7Ppkrs9lAQrrOXIgD ztzWxo^`7jd7*W?PS`r((yy(Kg_*mK%h>we38jG<+&;xz;w?AFWS-6?C(=5|w&ah0K zITJHRYAt8obsU(GBVCHvZmW)?cCXSr%O)M3kS|q)e=RxUWs+s;wxsyUD|c<2^ySw( z=1@eWWmZ{4GsYSm8b+M%QMt$d7=G9=gjjx;a zd(<@H1v`#m^7fjxgzOdw5%1CgWtt#khRf5TL!pkRvsz^U@ljBm#k3P~TO)Rfc?Yh2 z0n$Mvq<%$bLRL@+CgC_S7EoPn7ik)e&+MWmKWdt^2{%^ob$_6m{JH(^ra#Cv`G<{^ z{-fXZrB6DY`DW9i3Fjlmj0;yOLo;`aWAeJWljMm6XVI8#5&NZ0Q)q@mZ*NUnzi%(M z*Opc+NoQi^2@r3II1Od;jhm=>0Y~ETY%bfDbrr8^Bu-M=7jg0=)g;^aeNxsuWweJ( zChlIP%w9Y!+$0T#V}=LH_3xkjf4_+jH~1G7{ga9AFDm+{YXMFb{d{V^M*jmRZ2!q- zh_s_RBvo|7vWMv^o-N+nxe~Ipe#NGAsfn7h`5lkmnaVSKG7uyBD5H$FlpToBo zUPi_#S6`qSt=$g?Y;jT+AF#Ff;5K}fwvpitDsLj_7^7pkg0FlkR)?O1)7t!e5eqAb?;p!*(bYETN775^iA%OSW((dFDr<2y>)}N9eeAhG=e6ss`o}LtB@MPnYU!? zNCY**7imT5GZru9xudnIzEZa88D^5|UTal^dvwr{c-keY`eU|6%2oB2a==oh?zm05 z^WtN-q|Tnz`2H<5HnW~hl9sW}(onWb>Pd~wZyTjmU8KuwgtV5;k>t;Zd)$yL+?C2- z{Z%^6{2kK~Pk8(dYHHNmS#@pkn?WNP<7=+hNi~#Y?a^2Hdsl~FtHC?hR{29GwMT!q zQgF9boTk5s%oRM*Y zNJuT=3ny)D$Bti}k}9oHc?s)Y2hEYnHdI1mVlG~=Y}8s4-YO=}SuAKrNu88inV1)v zkkFwPw7OZi+iJv)l4Jb>*KzBFPY#pJM`sO`sVla3OiunyEl5?h#i=sUXX1+FUll{) zH7<(ZBDY!?tlote!>A2}%sN^Nm7HSs*$WrUo=tCrsN!t1@7zk7vqWBS8jNz1&O6uVHdH2dJD2!3r%l$KzHOH>VZ6bKiLW); zWXI{-nUliyaC?OA2k+Zt$Lai%!B?(4{eVrD?ajaP)B`qIam@Yu+Qeoto5?7%&72uq zqvmy+6`O2YCtpQs$x>``h%mvDO*T2AgycAFl1^x?<)qUlSqhrxv`Myt<~wbYW4>>b zt=8j=o$K~a_8;C`UMy%OFb z{quRDbUjzyo1jS&Q9-QF3uWN+0SU)|tS9LH6x1kYTfO=)UMa_226<_J15HR}m_XNO zg5*`1_XjeGFiR#Kl=7H1FYi^dh=Xbg^5Ct}^Q@dz@S=~^*p+I6^cz=8;&WsYo2wGg zB>9;KOd?4h-`6Cnc5*eD^;)}j(5$0fS0C9Vv$b6b=iALwOfrEmq1^&9$s{?XeU#HA z7D8*=f9W*IR0Un^G|3DFjdhx2Ry(IjGM<>RhNyqf2wa>B5(P8ztTz?X6EP`E;Hi#Tx!mLoc|y6%BG|9}90U0`>#TY}CX zFI@`$I=GzjE+y8Ty+PFIcz+!YT{(YFC+JNepx?338=CTaZNc=0rhM(DAHAVzKr<%> z;*B&h5IvXt&gEoOpn<+M|}lS<_{abaNFyTjrE*0ik^RuN3K`2ou`NH(-3J zz+X&un=E`mu-LA>M|-i|&r`tfoKFtJ2-eGE{p>sw|;p#H#D1wPnog8OsJR zNB#BN^r=Yx#`O~9iu+@$`Nm5~s-gb!ovL#ej>upwyPL!IAY8b75-D(3ImVwpxe8%I zVF{|6kWC*k5eiNdoKB|_8?p{EmUwU>f%{U^lGE0Q0pzwdCT?66J(S9mFm-awfPMtU z4EMJL^dxBR0I$)0ZvkrT>;JC*2XYzB>?jjkGjq#EDsD^NYhF}beNS&H?w@x_*RGDH zOrEY?elziEG=Z(3XUoD-5)}8pvXNNBA61NTXJ0~?5dI7mG0Q~oCmsY_<>C%-$4l*X zJ1Kfd{?~cD6i*z#x&u3l{i$dRZtmZoKaHTNr((nNwh=V#!urV5O93_d3~SN74~&{m z-=ig)Hh*wxMWUlC62rB3O0bNXRd=v3*n@EkOiZ&tk6)4D{xwpI>(zzN+hOTzID7Onk3yI(TJG=#n+-)~;E-dR1Jg7yMc0xIulU1>bc^-g|+1 zfqik@9Ft4(-f?bmj_5vLJ0%Z757pp5cwS(}gQ9x}G!xzYvk-mfw?y|IeqZ$XDUsn3 zg9q;$&?I``DfKCParv>}V@{(7qVIQ{8@kgedS6^~V4r;Q)654Y_cb(=e98lov)gFs z(5*{M&3J0*a2GIsB#iy&e~e)y9OI%FbOn&PxuuA}SOw-fB&?t4N@23GuE5 zE-nh#*%1U!0v!bM4+X`?n;67}3W)xQDwPsTYy%MIOdxB6h$*|Mp*~D>^W;43tsBZN zte~$i@gobyW0|c3D$ND7SgR5=3BoEX^dhXRx~sV>RILQeuc$wntIFk1)~^6ox3n_* zcuvmo>`K}a=9k{g$WmqT88-=Y#FZU9bc8#?AHvZGpi3^;_8sI7^7~8ZWP`nOOOW4= zRP4#{d(On7bXCHs5o?b78CZ^ zjvPOCZ$j4{`<4*b+|gq;?_M%kKR7(7XJX27BvpB9rE&3=Uhnz(_Ui5LA7}*0p7ZtR zef(o_D8sDzBO1Kk-e`GiLxTGR^iL1=^YWr*u6Eq&6>GL^+O#ru8J!#ubknJW7pHbd zeBRqI2PSgx_hNehZPW#gEukH>#_9`4KKlriD&6q9S=O3{hLW5GQ!AYmo|rPj9&wF3uE(hHMv6mSip5(TwA()#fs%iuYEyS z=*8IhrMOM`VkluF3KlPki(3*?ID)X@#}+PHy!gxL?BT#1!w#k{h~}dC1&4-1AvN{K zm(5!^VCv zYZvYKKW;J0hwp3N7e2H=+x+`Gwry8!=eO;6pDgiCfw52DzI}`ZbacD!?F>WTPe1Kz z$e@GYb#JEy2EhXY(+K0=*`~*(C=O%5R+;tPRc(xVePHjE?~u)I&wr-XZ*i};u2=Ls zWHUa!=cne!pMTHm!>-z??~patg#XuwZtRuLuebfpW4>c$rp~cXGxtBg+YOf>^&Ipz zm>MUWI(*U@1DOWZ%g;R7y*JJCYi}+8%U`@&weQ~D+qW;*SL`Dj?d!l_Jre$Z+h`BJ zR*9}nqZ2*%r`^i*x(%k_^8F~(?zCUsf!ZB6&sGy{f-SN&K1zUvfrHm@FA}-~(Q)l4 zH9YDPbO6Ft4=9vxd6jwtLA(Y;_?-jO4D=AcvwhFs2ANO%orIG{9EP{5ddBs}g(m2Y zbpHJhH4XL_xdwY!fc1J{w1*;eH_W_0+=DM@UE@7lr99q)4`~P0&P!{o&ERjuPNe~# zOloYrSD+sul-BTk?p=`TwS#!^tbUaDaBQjzt3{A(+CkD$=oq|p$Lt_2 z?K5GbW+ERZ6KvXnDfYymar)j|Z-<|+g_!Df0j81V+qIuoVf@G0=#?_z{0dB+@YzlD zx#tC#mjX`mW8sm^Sf+7N9oN%5{Q4vuD8I&2kO!uC1&;Nbwy*tP`~J2sY4Ps; ztv#-__u6X@XKx7~z)5#A6X(xgpB|Qug81;c}byTxX%A0Adv5Y~;+=4YPn)@^uZ zV(jq5;qMqJtxqpV{CQ$}Y-AWx5}~S3$0nwyC&tFUlPJZ8iKXha$k^ERk?*WpA6ZZk z8;l~=r^gO|Co)~d8mkoX50}vKZ%Js#geD2qQbJincLh#}m?_WHHn>D|bmXDa2h)@^ zb=wq|h(4Qo;xMsqP3nPvP*V~C_8EO>g^Xc z=1Y>@P7$5lexau3>Yb z&2EIMKD+qjh3u25J4bJp7LUHc5_}!CIW=|N=+*0{<(I7BEz|W!7mpnsb;w@#KO&>U z-;z=PF-mX2PO=;}o|i3LE0hsLgo^yo$F zgS%xWu5$IFtv2?e^*IIXMWUxwt+KHf*$N8Si_%!8s>IU;nLp386(oj5+Hypw>eJ|= z2$SjAi_!}U(nT+_trxxMbZl7jUZg(vrLej!)9PJT_lud$tZr+L+v;>tjz34>OfrflXC2(-Ymw zPMu#+pvr2UEipE@N!z8_)JzB^5^^%Mojr5CT9BD8r#C`SkztwaN{N~2>(@udHdlAU z4Rwbn3s7RTq#S%F3sy3=@PxXvQ>6y*db0!d|UNo#Q zLp>+5$)Grq!(9bdGch*E>2fR!9Q%$@;O6=nYiMLFbkx%AT=JFePf67=v(DC(oLrnF zZJ!onXs8P-X00fx*%?C(m2YUMr0nczi&soLx_!mjVbe?vRg*n>^o0v2Jv3BFNzAML zrkzafm+IC~U(fo@iq!uzamBP*E)8{I)(ijV(8*~JKtsh$%qClty1ts4x}slm4K>!# zP-JU%($Za@(v;bD=(PbaQ-wb-pTwV+!!=nPNAQV{m@pARo6 z2u^b=mbBpy#ileHJQ*!J^h)}MwZOhZ33G$Fdz zGw2VAV1c0t)y!1|xEHD3P=!W=O^a-%3JuPBVIfzBIf@HBxxQg+1N~Q8#QG*ycrA$c zS1BI6OXz94cs+t^j7Q>@=DT=HXxPx#@tDwn;C|S0lkcjw1*bWa&KS`TZhh;%RgOX*EGkPSr@8<5Uhg1Y--hpxfL~YmtDDAD;`d`E8FCT+KMkH zy|?muO-)n4a+~ecSBLi}#8*_5y8_;=N=dCKE-E{=y7-Pe#ez-8N=xqCzFmAV_J$GA zUh&z5;^HD}k$OG0ubJ^kvj%u-mPeMtt-Dy+CL`c~L1ml9(|`ZUVg&ViR8m)-vFmMg z4Ce1%$1nDy|9(KYH4&!c$5QKEYGOtP?1AcC1%*Y$#kcR=DJeaE>@p{Jy9$ROX+iRZD@dlQDVgGnd(wH6ggc$0wAM98G@1Z@)%M13<9Pgne)mDp@#~m@-&R-wzVSOFKm!+FL1=hI^qv3u z;X)o0coX{!gUvly*M8j#*r*D(ek$4L-EiEPhe@z=Ge&9LB!E4OI=Z;c>cX(`=MHui z+kVcg$o-d+cSI+tWyCZ8p!7e5t?SCc5~1jp2s{FUKPAE<+%D{!zel`%iyI7La?sECe`34rqoNv%|oUY?^ zHPY4C9CL?jij9=qsp7U%S7tl{)9~D?d^La7bIf@_BXe#Xz?`4sw>etlb4+^`b6G`K zK3(~AsdTB$F?YD8*htBpDspyZ_7`%tMmTkRd5@cDO?eo+#`$d*;mIq&~g9#CKQG8TI;&a{d+X8KxgcWiLHEUhn?f6Vgmqlylz;&aL@iWUw^a zM4Y(WPikDG;{dIZiUUPyY>*r3RGcB;L4r~qvqfQB>00SqMyEB$1nL#6Y~o4M-?6*! z2!?o<)UnV!fgzqI6_@Obr%BhdLyD(KchM!DCY=J8`84TeA6P`&=>xbY8 zf&O^&5j2GJ$46E|Sw11zv0#9==k`O^l)E4| z-c^NETH+N1XHZ|cFF(q{QB~_wRsv&m5RSB2gYfEOKZFsQ6thL7?-Qg6MbD4C{E!2R z&T7@;5l&Pl$X_UDuONO}Ms19Kz8WsAH^H=5$_x>Z)6NfAE5-&r#1He7>$Hw>kva(KuWn_Sw4rn;2ox zcSf(VKXprQodXTx90x1F;XUg9QhKJ7G;{CR2X@fMC5nbVP#%?THOaPZU57wzxZgCfG`L@MKy+&2auT1g9YY-kz=?kGjKSGUgSvaVgpMvr^ zU|M4)hE;1msViP)VJwT}@DB>|mx$Bfy4i0tgVA#QyK?`J^BWU6yEj^o=NBf<4Ol&O zZa{;L{bqyxYs?xRg?|_=$MNu1%u3NvbhY25H`sLExYW-?s-1g%w$F10SgddH^tr#)q!v(2xbGigM@*WA5RMh`aT-H4=jULLE+XBt{lzo zBnSBzBXy9}+907sE%7Zj;-Eb6w17o{_mP+|gdT!n`3F!goL*t4!iwI8fZr#()4t>BcRc;qX{VfGwbC8EXg|;;c2Lpx-4IrQ1xeP?_w`;< zFG$x@0XDgxlAKTA>HAXS0~xxG4lGM`-#dp{+*noy?6+T$Y|M+je-$8Y=h4A`W4{NPi)(?y8Pm$=OE|m0ii>O4IA^N9^L(w|MOyJA^{>i;{{mF~ZJxjy(7r%bx z6`IP9%;!2ii-Od?{L`Pl9X4_No6kOrA}TM~pX=P|*=JbrXPwVrH%`aHpV6(OoiX4~ zRrHCICr=zbkeaw-`}Q3>q5;)MpE|fZejB{<4kg}6NR2*~NG|T!Nnd01$@m@d@dV@<=onvd3pJ_ZWR<18j;}7r@xSU%m!2?$VJAUvjLR~^6lGqN=iyg36ZxJ z>(>z)J!j5b z>s(8;=rPVvocz@$&yBW5YccG7&eucbQ2Q_-(J_`7qldow`ji>3Po`g<2L#iqA+Jv% zFwaUqrd2~;hj*SAefXSa$m_3#db7ueTTZd}ccA-0sUkFH@h6`wTC{NCf(7`9Vg9^1 zk(Nk>THrybpvCLF*VV6H9=kARHk5&v0oR6pvTmITS|WW6&;f8&q*r9C*-Dg%YzMIP zqmlQIbbbVFARtHRqIHXA`^@%nc|Tb<4~e#lQm8l{lt_=7tpM^>ylUv8 zWl=5Q6Po2gD5XVdc2CYBkr(@H#{1 zDDbs(`J!7y%X3>r%jEBac#k!@U9^|!el64*>U=ZWJG!L-sG+Y-o}tV@7$v%u!T*O) z8i4W)H55W>86$-7JxPA8aq^5Agsd?wDTxkJ-4KK$5;Lzgk%y&5AOPmwNADw&J@hrV z?@@eL_$@#Q3CkCJTahm9MQ4bDBuan>}ewwoc<=(NL7 zwv1^_wC)zM)#$xn?^Hw}%Wg}OiKV6NGQwC5jmren5}}0^#Iq(@_h`vXP>W6$hy{q1 z;vtlXXV)0Sl59yap&i@3w<`!?O_5VgD1L`^hrHba$eOC{WlTuX8tquPWBZ;I1)!B; zM44meZ{Dr!wny*So}7|uNww^?P?%_aB*?oPK}bnW-D}xvO_M3d?@4vhpzH=t(Oc7m zbUR4(^MFRjZ%;|Hq_t$ghXn*AHEmyO0*`>{^XcE$hF-M9IrF01iuQn(Se0_S7+Ml1 zbxO<1$}6ZTwUlewO14}km)k2UE2{`O_om1xwu9N_6_r(0)zvjZ$L`rzWZRovSyf$A zbJu1|K})Pn*;lkTyXr2ywQ{X9g>5l)-`?z+S}*$07L(IbQ@m4ftXQly|MFh8XXi$4u^TgB~L*K@AR*OeP`F4B8YvZY|r zo_Kza5kbD8%>i1&HTjx?bQA$Ad4gb1DbcI5*eb7JAYn3%KuR$z8L^c-C11WJ z7np%d^}-t%hLoGcm0L=ITnt%#tJr=+FiT&=2YLoXUR zylq88LnC*M)v%q~VEER?I`PdA7CGN_!YIdVWsDQE6)r!OV*9rvUSUruQq(XdZoNlUG>>0U{#~GZd|=`#fp`yR(-a5 z^%_FzsI9A~wV0-^{oD-j87XVme!gzq7wZk^D|$a)_r(|MH*DBwK$`@4{f3PjH+}h~ z0c~7Q@}^CA|8?^gLe5be<&8GbzuLSRkEL!iu%*h<+9g}IY{lrXV~2nyO_V3vK3uXD zS|6%=r%3JH$y?IGmTZs5r~vIdimGGmexx6 zM|qEOzGFyd%%~3J1Dfur)$8PS%IC^jWsSVru@YZ@f+6xML?ZDE=;($mU#|tWto>Yvi>eo*kr(%ctEx&Atju zV_+g*tXw4{oV;3DgK~T>uhYIT()nWh+RvCS$_89nr+lG->*$H2)^A_k6kPd2Subx8 z8Gy274YOl#8Mv}R*(fLtqc(gcB!%jV@XAJIlLqmyz#Abl*&SZqqv7D18S|>J4 zTe;***Az>^yvfLF+~lwDVU}a!$|YaHJdj~laE7I!!8~a6hJ^Um@y-M^#@cwh!(p$l zDJ#l@M%MD;fi=dfHO2YV&`O@1?0$q zW&3h&m&j1tT3I};xTLRd?#n4Dl}jyU2!oL``Rjc-rDb09VdUJMnh@_D@7!rL|AhDs ztT?Q=_@eUivNADx^5{1zoO{!H6LHY`<0~+3+8wo3rG+=Wzy*QcjW5R{iNHiquRJZE zp&h^*b1k{nn_hWVvI|WcP)!aSdlqI~u0ZABs8^m>zSk`g$Pr(r7w1_4SZ`U#MvSM3 zN6uOi#;ZUpB&Nof+4C&|u#&eTbsGj?D}q=#bQ+<`D{m=4yb8Sx1jQ=3RbUZ-g)C=M z<%JLegIJ5@+XB<#?-W`Acokb+KmjCYEpQeUTZ+95S&$_+a=dfYLX3mf+g_9pv3wL? zy`ED9nRyu^g58$7*Sgn!Fc;_@%N;L{x+3qSeYs^ynPV>qkf&FP)xd>v?$)(+SFFpR z9d(^ESDsrtd*<}_Cr_U8-qfzZs=7wc!fPAwzBl##X&(?$yGFmy<~V(Z5n%3AQa+qM zeMWf1OaqGg0DgEQX3qL3(tsjokUZ<7k0NJB%`u>lW|BNRDr(N$=omuIu6QiV20Evj z^X7kSVB?kXwd3HO|M7x_iv;A?TkdTO9yfo%!i9@IS^TL;t%v{YIKOcVKUuu^)7T|T zyHYs=@*DR_?2;u*=jq{=pt+S`!AG>;Y#a5!Q>$-LAKt4cHU8l~N zE>BlJls{0W$?vO^!@?%ZljSMedtJ%+t~2oTfqiad#E0(-Sbk5Qs=ZHA9Z2&?m>MRB zDTqOm{JuO5*`cX+ojxyWk~~QcV`2sg>3^VoNU9ywSz&Co~SaIowQc#=H-v8tFHYln4znU{S*7O=GMNK6J&xmHad1S=vV=S9XnZfnPbm^N|MlFh`q42X&9C|I}k zj71!GOv;HZ3BtMs2y6>h2U`(=bvu6Y#C5B-?QTOa?8A2_jcQB#C){#Cx}PU^JU7$K zZ8hv|^}|UTb*O7!t!caMZ**CMS~3TWn7rJW8+7nJjvX)!2Oxz9YyaHo7H%x{24eRw z+;UoSCMUH{awbzDC6y|rwRegO@@`_(xtRp0F{uorl`*%H^PGen64k3L)N65xrNoPK znt1TfmJGa%N*C*SmQrt`VBQ{}0E>~f521&p20(jn<`kE;p&yJ(+PC*s?rp;h#wG2I zZ)=RA|J;n56xKS-ISDMO3R{cIsa7ufXzKWJ6DCa#6VS#_rj37R?D&acQ>IS)kdQ-S zzmntX(XZx;J|%xD$I46OrCMB9@UNWYI1r=F-nMjUoQ1l(gH+uMq~24<#jq0s_9l!r z2$(34Rz$!`U;p0W?|mHChF-9;N1v{3spkLL+u+WG)(Nni&;}Fm@UvEspJ#!|L#wed zp$I->sxIFWhNLQCiuN~5&5A|!oj0koZZZ#@yV~YH`we@nfpNoDyFEh5z1e0D)C zsZ?0Zw2F;BnRExHoUv-56^dS(R1CY#Sgz2Dh^R?Lw@kBGE*7!Wq(axK1uQg%aVDKt z&Z`;b3WNcrB*{sRuQTwen2Z$+fi>(-twyl zX`Her$(w41^?Ni4x;QB(kMjVm+T2jCt2tM%WGPw7RfNW{mXufSU0z>ObSwAT)vT5Nsfn3pmRNZpl`z35o-hJ6Gf@mMjYbTd<-eV0+Td@`6hd_eVG{Vok~&GEXFF zxn>dTPHy-**3=Y$7O@y5U=`_JV{(@37GvFt1#~3kmt4!?s+Hx2MJzj+Y2+8@TqmgH zTEtqE3EwKXenSxVefDxmt6Z(6WsWr4YlX^z2^d?9oT zgl;LYU=d2d&ZImpdNH6WT-#y{H7O+*n%j=_@;P6!7>ip;Tqq(*1}W=u{_Q&ojB=$^ zrnoVbCWBN;!krSO#8PS{jOF+}fQgnM$(F>pN|cfd2y2UFt%z_qNFl)CAc;B*-nOFU z$S+B1*oODn4`xLgN_PF^DN{d~9zOG<*>j@j8PKW^XGF}3jG8;|n8_|=weI}!+4 z!ffwHeYJIa{LaLrWCAd6C)p-P?bx+DIW_I;17WnpCxuO#9F??ppSXR@8HUc#h&e^n z-b072hv9`O4lkbcbG~Qv_DNwK!fam$rC0{;@sr@ zFi^Fyd0V`R$2|dKlnTRX=yeY0EFpJ?F26L8q^9}0!Es>Z2nMr^oCNG$*7}N!u z!$}eo9}($eF>8r6sEgJclOThFNSM%I!m`YuF2)X&k&{8e)Fy5jR>}-&Y@92nPdMp` z!-HQdv4*zRpf3586DA}Fl1W*E6|^r5YMeGN?-t$(0;Qq+rQxi%Auv z&zd5EvK32bq&mZ5Tf|nbHN$V+wganYyGU?^EyYN!ER|y`*O}o#O5B~aM?{+vW(_Nw zwlsD<(qR-5uwO))I(f_LU6Zm0o!VgQefWwx>Zbu4-eF!~*R2)Ed z9BxA|I^d)!VQocYWd}?s6a!IS0S9LdJ{h`4EV!G17<7bsBH&CYP+UtfhF@qT>KHMW zI}@-xZV~I?rXM4amO*`j!A8VL#Kw`DDn=Visel^dcNf;~%D4mVqm*go2^@cR>@M7e z@kTDQlp_qrpIy6mWt5hC(T5%vpSUx@JHffj=y5v}I?&vJm=bbwb8lE~Sg*@DTJ}|; z{}BP5Eg`qayU1RUfAhw*Y}pu4h}(d}Ah~%XM}+1Of^mfu2Pn7PyjA<6p;^!hyzu;X}(~V0*&NB5c2NdxW526$!Win;}hOigwEg zop%d6^&C?;&?vVwhzU(9AunHSj}YWYAus9%RGasW1Q6XD&pIr&o1jnAN zEBRt*v2X=Pq#d7l745{4aP`V9F|t@VvLvwmlOh+|1qEY^f~7c_GRB~@6Qn{CIJ{WO ztb{S>BmgE_iZvlTx0!%OUk?mWL0Nfg0vK}=66r5*Lob>8oT!sN%I)1|Np1*K0(||4t^7#vymo8t)GNBBT>A#wtV?ddv_ZkkH zTkp27&jG;MlA>Y}anIiD!jyCQ^xwWyn#=(>X>WGQxk8*P z;bs|ZPH2PFy(#DJ&>BsqHb_oQN%l_0wkEC7N63K(rMt;TaFOUK94$I={P;2Xm<@G5 za1gaWoSw`koO~Lql9gE(&z?MT=pdPte&oneQtCj-$+~#%w9HtbfNLj7wI`p-Iln*d{y6&)++*qre?}w$wi^LYfum>6o;9dva!w#)@csA+ za2}CBI(IGu>_Om|U`}Qvi1uoB_BB$q6x)Wf!xsyu-^1tBmJ(Ce%DBaLjF#Zey2(Qdz$pGJ0$5cb^d3~o5JL#;Q=ED3<2}l zX&%P`M>0nkYewZ&b8xr`vjAJ3t^r2D08^fEE*thQC?w%bP9dqz)MRUN*}jYCa?B_^ zmkMivG&OlIEzPpzi|4NM#41Tx{(|k0Oly-=8Pv>kH_S-pIjm$DoMoh<(C2QNH!1Oe z78b*kIosL;TJoiHd1iR5h9`^Fj=fibVgZD!9m%vjQ}!lbJ||W?l3{r|l6crp7-d|u zZCaeKN>XuT*j`stQOaz%T33~%)m4`A@GOpnl~)*xd|Cz9N6M;<0IHzyh!~E98Bik* z0~sg8YVO`OpnCdoLX2m^Y7MB??XRn=Hz1oJ!&~3b&`3y2x(#!_j^0MQ-9gC7)k53B zVgWc*Rm9wlvtcR4c6c?-i8Y*CPV^KM+dc=m*RLOzM7iqJ6PZe2Zh(rq9TQseT^vSFM) zN1qm#e@Z?jpRP9zIwR6`mnli3(15O*<|B;t%u5Dz8K%42Tr;4Hf@~Uggq(4wEvMxx z4TcFQkQLbZu&p~ylaL+Th`8d+%@wD!&Hfz6hOunLnH8sV*oSdMkk78!arEc(bsUX= z4-I4K%H`bsSpHqZ!1Cz56vh)BTX)233upu#XJHv}`h>S$4GO@Ac^w55HE~E^KYw`p z;$;FtYRB1w(JthHP9wlaeMt!!FmMpA!8f4y4L@$h4}I-*0~$XNRN;SP=$i)gw#z$g z*q??Q&>OG44l=xNjd*(`A+6(tiOR$};U6`63?b)}9p#R;A59zy|2yxzJJyK#)N7NT zo;U{HaagwO$SGaNr(Sz{qVVEO0m_Po@x!N{o;Y!m7ky|LKYZ%Rj@})ePl|@&c^vS+ zcdXCcJNnj!jBA9v*RMHhI>+K9BR({oa9V+Zf)GD(>FJ zsY)EUtWqk~ic+qeE2U6}m_H@m#UY`3wV}4UocdmzKO(^ff-c~aJ2PpZ-Din$+Bh!dgikatCr45d_Bs@!m;SWZ(aEaL2_R%2`k+||o@ z0F|Hxr$aRiA{a-LY-$BhrCM;9)Pl32n$3h8D-|=OMVuow;M%$>o=0U!i%qH3>P*;H z%|ogTX{lA}Gz>kgeq9X%8knVCYcP|pxl2%~wbUu~mIe)j5(^^Epfa|lUTLs2YIY-y z$_noa22&a>b`66S)2&7!$wxcEzEnvL=|KCZ(2P z#A-~ctl)?zJ1nY#ugO5;b_i(rA_z2`bHjDT7EN(tCy@Igwq(%sq^b%##z0xKIPpn8 zH$-ap00)e(ft<1vpWhohBB^D>FgW7lKVwHE6@bd`g^eANRPKmya6F1{VT{{S+u}pL z_P9yuK^#r}9*(O%jH9c6%^%v}^0*Gvs5s&scji)N=EaK_E}TD~k#X+a*^|eP%g2=y zeE16Nin~6UQ;yC@1Ap(8_3rahZjM#LpZzA}A*nj4p!a04*155(?5G ztejTP2!b7it4G-skDis!3aAm#B`jUYW;o@na!$)&4y0sYNkbO#i7Oe}c~Y3n=~Jg= zBx}I51I-LB0V|gTL5;hTnIXFa zE0-*n1+K+i!Fsikf^tc@tX$FXvM70#i5rNSu7O&zw5tYw;iBwDx#1~in+csg<9$ZC zLWrA?W^o{d3?&0qFkx(KG8yNJIBT4J&Dk?pK9doeJCWKAQq(0Y&tT}Z$DKWc6&BaY zdJQj_qJ@$s-*m*CJCiMr?(@h!R-fb0LhIr{yGe>TyRYDgA#k+NWxxS*NS1S362zVz zKw{XYY()fCY~0GYjI-BowxJj6r*SLd+KN%y8X`F#c+UEqvpqgQ(>j;%Mu_5P@oAdY z=?-+7cAIZT(v6SS3O8*k#IJUz6UT~YZ=S zm_LUk4G5j;IZuSw@^~O8B(`%DN5{A4&FCZdk^L$}VkQaC3Wbnt?>J)K!~pIt3?49G zppo3$^WHX-M;Z(m0uqLEgF0efsFAvhLJ|gZGj2?OGZNyMlp#YxLrv<;=8;Hw4Ufba z)RFHoBacWRz46A-p$2u-yMhF(!Hh)Gn{N&?sH2Co3?|8>4Eqxe2{LGO=r`-KjQmC^b7Fc1>FtrY+nJHIjv>L`!Se8GMiA)e(PLnjkTQ6X zbx?UoFl-ltFoyQYyQDdv>S*m))_Fki+en5HNEkbI9BC>Hl}XB^vW^3SM>bK$Vf(cs zdmyPYr~`vXiAdu}nE*4UBj=N(P6BN-5_1t0+BcX_k_OTkGrTup^hCfw-$4qkh@dAv z{#3_7A!8@Cp%*>z@uxes6=u=Ddr$o5`W6HuvvVph)R_wX1{X&1sWf+|auA=&58kJ8 z>T9pJ);sNVO_d%qu}0`c7xsu7LsDkA4z9RNw9$zBJ$wTTI?^W40r%~|cR&A;4PQNd zd^B3n{ysjB91Kh2VOSoI!UFj|ED>HwfQNBmo#5#oONONO#nZS7pT_MAb6x8)fRE=6 z=t7gCJVhy81hbTXwqB@#2S-FK4~~FeMnkPPHx zN(`g4TaK85c}O^1;XN=~=R4%NYYe~rEXQ?Ytj`V)=T(6m>pA}4ra0k-UGQu%*r=6s zhP~iyPs`x}T%z#0q@KTcDO1T*FI_y3_T${2*(%eypRMO&W}8gsMYNt4nQ~T(OjraL zGcB3*dDV7&c37PUH>jxBe#F+U@QNy}>I&S5{jg*K#6IGkM!kCCmV~Sp|rG~Z6lA!Mba+ORZUuBd3ghA)kH3_-DILWf*=wE!Hxvc zLNc9MY&QZ$yU9ZEKZo8s1&=&kNxSp(D65OsRMtOR#|}+=5wTo+F}ZjbT+|&ln**)K zWxs}M(xVS7LTZ!Bx zB4Po#Ngeh8r6~|dQ&_0qKoI5|v0N#;(h(_(++9c@%kzyA;v-3;9wLxY58MfXdg{0L z@5fzhsO!@nJirCZ4}>eXI8ronq~LJk8cP~EmV9e`!I)!?FOaV>UV(=$%r#zlj$x>q zIl`g5=BOveKQR)9Th2+f9r}dtEGpU=-lqeXZLHJnNK&;=w^BDee-h=;PCgGlYOmLW zk+O=b>gr#|XrMjMK})ND9(`G}o`l@6%z{)kO7NtWW59F+L`?|d20eVd*};yFH8pz& z_RT){j?>wPu5h}->Do!xPPiOJSV=(+4qVGobPs=1NRQa=6r1t^N554Ld^~!O5~CjO zI-!2e>Wlijd4WYb4=z2~jDzeq;qp#NF2Yr*w_lVT_`2*iZmF>pKQDO|cg7K9d}K^e z$5p2F*ruqhEiNvCYOT#JF2=xGT3noqMpBCqjhfcT$aroUpI%iuif`)`#l?jXl3ZMS zTcuw`FbS9W71!73^y_z#`r}fiUR->~sjEbsBCj8UoyEm`>`E&ZV{#pH{Q=P1#auwq zDyrGrN{eq7VZw&;-KG*&opp7D?JUM)lH*QsF*UHrntBttz@n9lwcEU3MODko*$p+6 z0sAJ%uSRR5Ick^_gpgbOP_Z+YA}*qfB7L|x+?pi$i0eruaa9-^V8DnV!;NM-6n6#U zJT||Z&_?jmXyHLij=)WWuFKfOMb(A}{gz)*7@qGNf{1`1Vjzfl_$G&fh{3Hu^J0`3 z=R95;yE!Ii(W01`&6Ea0WMnWDq_Hq2W|h;qDkf$jq@lz(@@%&J7&+!?=`QZB*ijzy zvA#VfhBrmPKfj9pD%=YuBq|KEe36I3{K})RYp_ zcnQ}k&0EcWaR~`SK#2G`*r;twBhS|`GVbAy(dHFX6b%1A4?r(!D z=H+FO!gHF|iI+C*E55xSw=y*bUcrSie0sOuipQK==($!(;sc#-{e4Q}A)9^^rmJpy z5Rz#80t21pMRM&6+9p5cGntzIyZqPr6WSqEL~7NVb>ISCUqc%jLVhqgG|j=(X)TI~ z5f5BKWvk8X{v&>BAiAD*nQf0BH_m4;h+J5?8!w+s1TIj=X? z>*p=tgY#`vhF2B6O-8gU)$73qy_BPfa{(3q0`S1m*{(Mrwxd?J^ZHUXNq0a|s}7yp zcn)7QGY)_-zI;)8hgTL-@zx!6dX^WfK7k|81ge6ED)l-jaEDE=dhUUWBQQL?yk6_VyGY#{!q@FVl0C555)@($k*QdCudz@nC`4ahQ}ckmrYqd*2a*!l z2@@9F(A)`EGopnJ#rI*nrICNA1ihpn5(D8Wg}`u&`dt~C_+U+N3yR$@%`HO+qxCVp z)09Unk2t(}?Hr`vF2f~#NTfpxvn7DJri&6$U#+aB#CN-oglU`UkcO{J~%C2=EC&J2N6SOT;J8jR*b>ZgI7)S)f21)64m5E7Heb z_SgJ+`uq698pC|e-%s(wKR)>9@8|tS%H)_UqgH+}Q_W2OYPYMwj%c1N;?# zXFpby`M3A~!MFa}86+?NR(>K3{C)`fmwudPsvafL(Rjok8jp^~qoeV-G0}KJ*2-(O zIghUOSqqH_R?OuPmh~L8m~-ym&)@NY&jII%ru15M@#*4xrpYhzTlDwvd(`JuqwOhB z?!4^zztctjrvB7CaPz#wt@*SKaoH~akpsk~_P7$0ck>4Ywnnf0|Kx;~N=CU_^FOo; z{vCVsy^?BH2Qs{czud>$&?ydoKfgXuE3&`0p;i86fyWGi{nwi0UfRWlxm1QwL?){O zz$$7b3p9>@YpAAwxWjCF9$ohzsEYpHs_oCG*Q`Edu6_90wLM;?!tE2X*2_@1_O-e` zei4=K)VZ`5sC55*F)U|@HmCSj+AAe0}BS{OO>1MV#;u;^U@R3w9&d>u%GbUd!a%fHotzzF#8!>2}+K-^9o z7~l`Yfeq0B$!h*$v5#*#Ir||=dta6Z1#t3-CX5~dLC6Ev8-Be5F*giBiIGVFrV)cB zr{wSLQZhz;{QUj=`}nawuqeF!RR2DGm{=daat@NepT!T}Zcs&vWc7o+gTnOj^J8ta z5QY-qWDTApOA8}6RmM;QMr8v0u{A9!lP3$#I_u^sOEzD%5()wnGQMns`5J{-4)5ExrD$K_N)Crkhi* zoIB*T`DK`8zJ;vqZS(cRVkY-yH9Dywrqd}~9!jw`TX)~D!m2)_M(D}au0z2Y&}@xD^5FCe4)a`kjf@+5nQBYMewD9hb0 znqIwa`H)4$x_uxQ8GfIQl4^H;hf>%3OvL_G@B1sDVqYtT(hJU~SMS~Lz03s`306q8z3}6^AfInaWr9fVeOpEkKvhlhFcn zwg8(Ef6xLLpDjTAq6LUwv;gso7NDaAV19-BrqKn|p%G8uCT)Af+IT$z4(H2M z6GO4QR`YUI)ytCfazwop;UE%&*I@&#TX^n_i>mgKRwF5(>kO9F1hKNLJhXw~rK{$(qcz9Q6Ab#=x;Gs-^1h)P0 zTq`%^pOw70%Z`C}1kWwukM4mXcTnqRPa%J^Vljl&AhzxQc2nn>e~*h$CFy}@vA z7V5WR$E_}_`eO9l>w3{^z_d5AAFVIIRck<-9U2?v$j5I=iP&0Q@3Rf6(Mgx zY7ND2z7p_$zm(!nq z8`!m(oMyZ|?2T{AX?E0jPii(-rkQBDUAXwS-5*1_^eG7NI20KXHesz5|>MncU#e zzhX7KRC`2EO%J2{>GMC8q$r)$)9&}89<&~7M?Kg<=t2AO^aHpG#Cp(A zZ+7F?ao7B<#V-zn@dwXmIW4-Hm1`58I*oha`mHOZwb1LU=6|I-manKWe50jJdiA^q zR>G&gGiu~0WmMgtezpx8WcqFnAll=Xp&qroACcb={pVX_{_u!w@Y|9jwa4>N3SLiB z@GA%1hrb_4<>J*fzP#r83B{lO8Bm7@PJ!ARsStxgCx6f4` zSNYoGL99U%Ht$}P)R|q@!lcK+uUD{pWUvb_NK6NlFd2ZbKpuMbQ#MEVjKVT)t;d5D!f6Muk}Jd(78 zmAtv@+M;u;O~LT#gJl|O-k7)YB}Y=P1wZti#zhOMxvJshxRX)g;?8P89Sd3l@k%FF$v z0aD|(O}jR(Jx1eqna|6m!%)XZDwqmx1YlpeM3S-) zMC~E{68^?Ri%v>X0)b>{6Aa#l+$HaQ`FYeP#u|_Qbhddmcbc+!-bnogSDKyOi9z$Y zQh7mgo`T%9tlpIV^%})G z?qR;`$(hkzCFvNm>@NL+oLSywgCvC!*e$ImXEI)SWy+}EgfZ4wgEOAlt$0LF-t^38VUY7SeuV=$S6NS;D4bP8zkK@3^L7HdGG1 zDy4KeMb5CC;LP-1+a=#FuOo~oCpfcz(Q#?XqQ&IQ(gmM=JZ7z!FiFy&Uoz8ApV-7q z$1asV=PNqusi4viQIB;SRuAnN^~5UCH3hWrO!URk&xIywRqWMx@>bb0Bnn7CzeFyS1_ z4jhigjlYhVg9qqWmtEDPEzznYlRjPp;w`nPq)u9d~_8qAOC0NYkfp`I2juqE{8vj710fw4ZeQ0 zA)IxLIBxLuAbncIN!IbOK{;*m^$5MTh_fbNj~m_$ldmTY?|GB2rw#8#ldoqD?`4y( z=jl}=vJAdnq)&^;W}cS}_la{}=QNR|PXx}0{uA?6=_K0g+~+&-;Jl+#_W;4y=%DWO ztDWW>eEmQitM9acb(}6JofnyWjiA@o`BRgx9~s_Qldn;Rcd5zOxrTSS$=7*?ccsbK z1@x+&R~vj?M4#4qE%W@;a8H=wHA9;~x3g2{7M-22nT;mf{)f;3jn5QfJxrz!SePnF zt^3jSb=|MzDUs;H4ZpmBv*y}!PzMdatkTP}(){4G!zotYzUe?H`Ju%j4y?R3p+e3Ouy?eoneG+d; z_EmdHzV!71P4UHGK$@dhNH1S7v!R!-Z-|8VLGb~7ukLjB^!4q@v5Sy~sIsp^LM9xS z2-V|wo3Agjb56x`47`v$fQ)O_i71n`w_Y&Q0lBr`U6z>Pjch0Be31)8Y|9Q%QN zS-5ve*5zky(9NFb77p&S^gg1B59gw0O)Re$UydIu`qlXY3p*loG zF#e&yMCyAWaF3nRGZpx7exLvC^(BaPqzT+MUzM0 z15tD>aLY?-?16w1iH@Ejy@UjN_3Q-+5~}Tq=T9L)aFOLERM%5tjGjG3-!tMtU_F(d z;2pj7NDi6z5aJ^M6!s(%rNBG^Awf5wK|#1A2;owKl&*%neR`tTieA?T8?fH~Dt3EG z0yA7Pq#teH?0KSJPjtIWRM9+FX7-aJ}xr=pb%WTzJP*b2m=^e|A-qniV&Jl z1O;~#0koYzW`^LGr2Tj&0QDzHM_H|-i9a?3_@nqk2fPochjzRuf@5198^Ms%3k>Z% zM3MCoD*F)q2k8HNEemwO@I!=Nl@-uC2BIB!9Lm!%RoAEDS9_eH)B`O(;iox^ThM1e z0G(HX(bHavUTLO+GWO?y+K;7mV~tC6jFfRMtQs-pqTFBqI=2`bsSH(pP?BExvaKYw z{qnbxbP$MR+Y3@V)Yj2XdSM&$elg)7>vwR+3+#{9G5`K!;Vn_kf7>y`sNTP^Ry8a5 zzd`@pg4+0|;Rc0*BU%3F^rrEKgBq~rT*e#7JZMFIV;WmPU@mdd$phi>*ycz?^H{VE zF^DxqvhcK5hzEdlad_+-NV9Y~+OaeuZ`yGh>^SJp04T}kb{r28ecK3k8AUK|TNxIv zFoC^%Q7X4VY^8+^t!M%`V$j={_LmDlxoI*tMM2FNr3nC!83H^s(?Ah@;cfyjs=%D) zAR1n&6KtVWLr8JpuC;*S8As5rU0<+8mYaR#=s?iScK!S`zZ5~Jj z53@CeK?Qk`?Hhm)R8vkNeXuIi6oie$GbiS0Vc85@qe*Orz0qXEg}A=tW* znlN}N53e4`&Fxcq!eDk=*}x$;*xi(y8HRF$71tCA)#`ya)tUgxt;q~WJODj=2)ViQ z5s5->0_e#C(SG9$&(*k^S=>Ty>bHzG58<)?=ttmJAWrbHG^XuF>G3EX5D`Xj(BI@B zAkc&S{e@WEbVo$<7^tFo%FhfU03oNQIIz%~BbMg5@GPXO6sE-~Ivi7Q8*vodx5o(b zMA)~z$SKj*DI>3*sFd68_4HX`oP)REk4dJ~U~M;rM-6%!QZqwPY6t^spGtFHVd>E|8N8?q!?n6{gQU+|~6B3JM^-332Uw-y5k8=!3z} zGYk(U%Yk+O4^UvUh%BKUg$d%vDUJScARCC%wSK`*blWU=5?_7Vd~EpY6JNP4ux+<) zx9>>YYok|%_mpy~G2_y;g94}_aO%v3tn2wl-N2cQ%xigtw@pu&NW`lx_(#yH3ZI`zjQ74)-gA5KJ&`; z{G!`uT)>{JoIG;*k^mYO+V_IMjYEi=*-eOxk@rTb{XLn@Fq%K@5Vwr}TTm0*Q3Jttj{)W$JjW~P_SAg`1$V&n zluOE`#;ly&BQAEO;LU>U8+irC-N2Qr5P=8J({gfek>{612=IJ=p2_nYt~iGZzs2)! zjSgtuy}HOh0EwCNBQ(k4Olk3zX zmqs|5dF^HquDHGCWBRbzH=(FO| zGL(ftQ@?GqP?QCm8!acGEEAB=1e9e0$}$0E5q~HP{H+C!vWPG!3&OXYfU-;wWm%50 zEN5Akqb$o=7C2azqu(ma=}S2*OIgXi%aVe!q@XM*C`$^;l7h0Npe*7KWr4r7z)=U6pl z&%OPAO65u2@y2;Q4O=Glzt;1P9sik6Ji_9Vk%_wA@bt*tb3=4J9o%euEx5L>paW=* zxv34$Udlcoe9q8AnYd<-Ma#-Ni zw)2(@yCYP0bm9TM@?>PC9$e5Bdq5fz;^FU1Nt@t{7jzxI5&C^d5ymF;<&(M-OI&Ce zoHsO14Ycb!@2B@MsK}O&HFwC84-oY+NqUTq){kkp_26;$Z>z^-#(Ny0VCm4&@-Y}R z%^WQrg8@Ut`!OgEI_`Tc+)~d&(fAy=?~f#sqZ?fv+Y69K&sXJz!zp3r}o9HL)Pn^-icLD=~yX-a_k{VIqy!}+z;2- zZI0>!r2coWt=ILsCuXo=KPvWk)V=x!`06MBQP+zt0+RK+>(FZb=9c4@CSxPWsgKch zOaxvDp+U^aW8FFZq3*3G=z1YaVjxF;inF5+==%LGMAN@yMm1lEHGIa>^fl~0tQ{mg&(p&2_0Ox4xaA6l3k)~(jAPjodMeNWbLJp43;jn53_D4;hNizZ> zSh)2*>GwQj*%9Y4U>+L|99#*)nY17jkwBXuO+0tp2Hyd-#l_@*{*MPSaXz(HtwnjC zl4=?>huwZ+$O+uNtkt7yX?NF6XfZ)sT|>84Th*dUv#D^`=#@hT4d~xDppS2lVqE}@ zL;f_ie|X<91AEl+sLNgLjfuPZrw`aL+80M;o|J42?~Y7^f7`IZ_0$Qr4~8CrclWEK zYv`?s8GI@veZancA65zKvO$MmOCONl`(r_E+&C!p?exKieAhIRs_z+;Fg|_gUf-R% z@McbacVNh?Bj3$5ytTEBn$uo)_bv>fq5-{MYY>}D1f1el!Ujx- zt+=F9T%v}4NlF{>nG}D~FLm_GKlA5I<|RIO$`>a}Z7o~f_dvQM{ek;hwL~|nd0(^J z8*BAiT#v5JLi={^nDGT z{P5qylH`L!CgQ)=`0DYx>Gr}0fMZQJ_NwGxnr<1tD@`|luovn6uaoJzAHV#&?+=qF zz4zwGciw$-(wMhMv+^JO=Z!zQ!|~VBbTeTe(~Tcoi_{dJi7`CGw8NhTfB5eq{JCHO cNb=Zc;c(@Tx$4+o8w3-R$NJJAE~V@L7hg0BH2?qr literal 0 HcmV?d00001 diff --git a/resources/images/upload.png b/resources/images/upload.png new file mode 100644 index 0000000000000000000000000000000000000000..9e3158a4f4363faf5cf39ad03980820bc814009d GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xawj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&pIsjaN)jHlV|5IZ!Cc)5S4Fi9Qd7{WOD*hM zLXX~YP<+N0a`aB8o`m@YxdzFt5C5ncAI?7iQ*K>mpY_V4&*BdQZDa6s^>bP0l+XkK DM?FXf literal 0 HcmV?d00001 diff --git a/resources/images/upload.xcf b/resources/images/upload.xcf new file mode 100755 index 0000000000000000000000000000000000000000..e89500d6498ef0f592c4e42e5c72a00ef6cb91dc GIT binary patch literal 930 zcmd6k%~HZJ5XaL}q0|p_P%nDO5e{|m6?g`ddnrxKOlg_6&cH1*zJL$lo5~P(A;B`@ z-JSVof8CF?yOnCOq^nd=q4JD?U0EUDfIJV5At;=F1fMA2TToziZ#~xCLH#56YYErJ zYLUn+mX(@Ah(O*Wqqgg}gEjBn5qP(B z+~MG*N{U*P)gozBSzwiq2Ur(`Yd4_KGPd+BgCn?ptU-%Fj?Y_s(c;S%4}kGrXFUMd z-~Qz`?@ukb&xcx{AIE%w&kt-!JRXrR&KM^>vgOQYTTaf90;CR77s*2k*@hF&qJ65v y`s7E!f4|Pauo{Nd#%LT?8{cd-4y#Ro1jB06MS@|q3C%_mZK8duV_?{W0`d(*c)UOW literal 0 HcmV?d00001 From 988d2d7b9f07144c26252a6ac637ee43f8ea6132 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 20 Jun 2016 20:00:44 +0200 Subject: [PATCH 06/17] display parsed data to user --- js/dialogs/ImportPanel.js | 9 ++------- php/module.calendar.php | 32 +++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/js/dialogs/ImportPanel.js b/js/dialogs/ImportPanel.js index a317efc..c191a46 100644 --- a/js/dialogs/ImportPanel.js +++ b/js/dialogs/ImportPanel.js @@ -278,19 +278,14 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { reloadGridStore: function(eventdata) { var parsedData = []; - // this is done to get rid of the local browser timezone.... - // because all timezone specific stuff is done via php - var local_tz_offset = new Date().getTimezoneOffset() * 60000; // getTimezoneOffset returns minutes... we need milliseconds - - if(eventdata !== null) { parsedData = new Array(eventdata.events.length); var i = 0; for(i = 0; i < eventdata.events.length; i++) { parsedData[i] = [ eventdata.events[i]["subject"], - new Date(parseInt(eventdata.events[i]["startdate"]) * 1000 + local_tz_offset), - new Date(parseInt(eventdata.events[i]["enddate"]) * 1000 + local_tz_offset), + new Date(parseInt(eventdata.events[i]["startdate"]) * 1000), + new Date(parseInt(eventdata.events[i]["enddate"]) * 1000), eventdata.events[i]["location"], eventdata.events[i]["body"], eventdata.events[i]["priority"], diff --git a/php/module.calendar.php b/php/module.calendar.php index 040d447..fc2d3fd 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -607,8 +607,8 @@ class CalendarModule extends Module $response['parsed_file'] = $actionData["ics_filepath"]; $response['parsed'] = array( 'events' => $this->parseCalendarToArray($parser), - 'timezone' => $parser->VTIMEZONE->TZID, - 'calendar' => $parser + 'timezone' => isset($parser->VTIMEZONE->TZID) ? (string)$parser->VTIMEZONE->TZID : (string)$parser->{'X-WR-TIMEZONE'}, + 'calendar' => (string)$parser->PRODID ); } } @@ -656,9 +656,35 @@ class CalendarModule extends Module //$properties["trigger"] = (string)$vEvent->COMMENT; $properties["priority"] = (string)$vEvent->PRIORITY; $properties["class"] = (string)$vEvent->CLASS; - $properties["label"] = (string)$vEvent->COMMENT; + //$properties["label"] = (string)$vEvent->COMMENT; $properties["lastmodified"] = (string)$vEvent->{'LAST-MODIFIED'}; $properties["created"] = (string)$vEvent->CREATED; + $properties["rrule"] = (string)$vEvent->RRULE; + + // Attendees + $properties["attendees"] = array(); + if(isset($vEvent->ATTENDEE) && count($vEvent->ATTENDEE) > 0) { + foreach($vEvent->ATTENDEE as $attendee) { + $properties["attendees"][] = array( + "name" => (string)$attendee["CN"], + "mail" => (string)$attendee, + "status" => (string)$attendee["PARTSTAT"], + "role" => (string)$attendee["ROLE"] + ); + } + } + + // Alarms + $properties["alarms"] = array(); + if(isset($vEvent->VALARM) && count($vEvent->VALARM) > 0) { + foreach($vEvent->VALARM as $alarm) { + $properties["alarms"][] = array( + "description" => (string)$alarm->DESCRIPTION, + "trigger" => (string)$alarm->TRIGGER, + "type" => (string)$alarm->TRIGGER["VALUE"] + ); + } + } array_push($events, $properties); } From 02b235a1edd4ce4b405537ef5b94bed58021c3df Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 20 Jun 2016 20:10:37 +0200 Subject: [PATCH 07/17] clean up code for reimplementing import/export --- php/module.calendar.php | 300 +--------------------------------------- 1 file changed, 2 insertions(+), 298 deletions(-) diff --git a/php/module.calendar.php b/php/module.calendar.php index fc2d3fd..49d1fa5 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -114,219 +114,6 @@ class CalendarModule extends Module return $pass; } - /** - * Generates the secid file (used to verify the download path) - * @param $time a timestamp - * @param $incl_time true if date should include time - * @ return date object - */ - private function getIcalDate($time, $incl_time = true) - { - return $incl_time ? date('Ymd\THis', $time) : date('Ymd', $time); - } - - /** - * adds an event to the exported calendar) - * @param $vevent pointer to the eventstore - * @param $event the event to add - */ - private function addEvent(&$vevent, $event) - { - - $busystate = array("FREE", "TENTATIVE", "BUSY", "OOF"); - $zlabel = array("NONE", "IMPORTANT", "WORK", "PERSONAL", "HOLIDAY", "REQUIRED", "TRAVEL REQUIRED", "PREPARATION REQUIERED", "BIRTHDAY", "SPECIAL DATE", "PHONE INTERVIEW"); - - $vevent->setProperty("LOCATION", $event["location"]); // property name - case independent - $vevent->setProperty("SUMMARY", $event["subject"]); - $vevent->setProperty("DESCRIPTION", str_replace("\n", "\\n", $event["description"])); - $vevent->setProperty("COMMENT", "Exported from Zarafa"); - $vevent->setProperty("ORGANIZER", $event["sent_representing_email_address"]); - $vevent->setProperty("DTSTART", $this->getIcalDate($event["commonstart"]) . "Z"); - $vevent->setProperty("DTEND", $this->getIcalDate($event["commonend"]) . "Z"); - $vevent->setProperty("DTSTAMP", $this->getIcalDate($event["creation_time"]) . "Z"); - $vevent->setProperty("CREATED", $this->getIcalDate($event["creation_time"]) . "Z"); - $vevent->setProperty("LAST-MODIFIED", $this->getIcalDate($event["last_modification_time"]) . "Z"); - $vevent->setProperty("X-MICROSOFT-CDO-BUSYSTATUS", $busystate[$event["busystatus"]]); - $vevent->setProperty("X-ZARAFA-LABEL", $zlabel[$event["label"]]); - $vevent->setProperty("PRIORITY", $event["importance"]); - $vevent->setProperty("CLASS", $event["private"] ? "PRIVATE" : "PUBLIC"); - - // ATTENDEES - if (count($event["attendees"]) > 0) { - foreach ($event["attendees"] as $attendee) { - $vevent->setProperty("ATTENDEE", $attendee["props"]["smtp_address"]); - } - } - - // REMINDERS - if ($event["reminder"]) { - $valarm = &$vevent->newComponent("valarm"); // create an event alarm - $valarm->setProperty("action", "DISPLAY"); - $valarm->setProperty("description", $vevent->getProperty("SUMMARY")); // reuse the event summary - $valarm->setProperty("trigger", $this->getIcalDate($event["reminder_time"]) . "Z"); // create alarm trigger (in UTC datetime) - } - } - - /** - * Loads the descriptiontext of an event - * @param $event - * @return array with event description/body - */ - private function loadEventDescription($event) - { - $entryid = $this->getActionEntryID($event); - $store = $this->getActionStore($event); - - $basedate = null; - - $properties = $GLOBALS['properties']->getAppointmentProperties(); - $plaintext = true; - - $data = array(); - - if ($store && $entryid) { - $message = $GLOBALS['operations']->openMessage($store, $entryid); - - - // add all standard properties from the series/normal message - $data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $properties, (isset($plaintext) && $plaintext)); - - // if appointment is recurring then only we should get properties of occurence if basedate is supplied - if ($data['item']['props']['recurring'] === true) { - if (isset($basedate) && $basedate) { - $recur = new Recurrence($store, $message); - - $exceptionatt = $recur->getExceptionAttachment($basedate); - - // Single occurences are never recurring - $data['item']['props']['recurring'] = false; - - if ($exceptionatt) { - // Existing exception (open existing item, which includes basedate) - $exceptionattProps = mapi_getprops($exceptionatt, array(PR_ATTACH_NUM)); - $exception = mapi_attach_openobj($exceptionatt, 0); - - // overwrite properties with the ones from the exception - $exceptionProps = $GLOBALS['operations']->getMessageProps($store, $exception, $properties, (isset($plaintext) && $plaintext)); - - /** - * If recurring item has set reminder to true then - * all occurrences before the 'flagdueby' value(of recurring item) - * should not show that reminder is set. - */ - if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) { - $flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']); - - if ($flagDueByDay > $basedate) { - $exceptionProps['props']['reminder'] = false; - } - } - - // The properties must be merged, if the recipients or attachments are present in the exception - // then that list should be used. Otherwise the list from the series must be applied (this - // corresponds with OL2007). - // @FIXME getMessageProps should not return empty string if exception doesn't contain body - // by this change we can handle a situation where user has set empty string in the body explicitly - if (!empty($exceptionProps['props']['body']) || !empty($exceptionProps['props']['html_body'])) { - if (!empty($exceptionProps['props']['body'])) { - $data['item']['props']['body'] = $exceptionProps['props']['body']; - } - - if (!empty($exceptionProps['props']['html_body'])) { - $data['item']['props']['html_body'] = $exceptionProps['props']['html_body']; - } - - $data['item']['props']['isHTML'] = $exceptionProps['props']['isHTML']; - } - // remove properties from $exceptionProps so array_merge will not overwrite it - unset($exceptionProps['props']['html_body']); - unset($exceptionProps['props']['body']); - unset($exceptionProps['props']['isHTML']); - - $data['item']['props'] = array_merge($data['item']['props'], $exceptionProps['props']); - if (isset($exceptionProps['recipients'])) { - $data['item']['recipients'] = $exceptionProps['recipients']; - } - - if (isset($exceptionProps['attachments'])) { - $data['item']['attachments'] = $exceptionProps['attachments']; - } - - // Make sure we are using the passed basedate and not something wrong in the opened item - $data['item']['props']['basedate'] = $basedate; - } else { - // opening an occurence of a recurring series (same as normal open, but add basedate, startdate and enddate) - $data['item']['props']['basedate'] = $basedate; - $data['item']['props']['startdate'] = $recur->getOccurrenceStart($basedate); - $data['item']['props']['duedate'] = $recur->getOccurrenceEnd($basedate); - $data['item']['props']['commonstart'] = $data['item']['props']['startdate']; - $data['item']['props']['commonend'] = $data['item']['props']['duedate']; - unset($data['item']['props']['reminder_time']); - - /** - * If recurring item has set reminder to true then - * all occurrences before the 'flagdueby' value(of recurring item) - * should not show that reminder is set. - */ - if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) { - $flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']); - - if ($flagDueByDay > $basedate) { - $exceptionProps['props']['reminder'] = false; - } - } - } - } else { - // Opening a recurring series, get the recurrence information - $recur = new Recurrence($store, $message); - $recurpattern = $recur->getRecurrence(); - $tz = $recur->tz; // no function to do this at the moment - - // Add the recurrence pattern to the data - if (isset($recurpattern) && is_array($recurpattern)) { - $data['item']['props'] += $recurpattern; - } - - // Add the timezone information to the data - if (isset($tz) && is_array($tz)) { - $data['item']['props'] += $tz; - } - } - } - } - - return $data['item']['props']['body']; - } - - /** - * Loads the attendees of an event - * @param $event - * @return array with event attendees - */ - private function loadAttendees($event) - { - $entryid = $this->getActionEntryID($event); - $store = $this->getActionStore($event); - - $basedate = null; - - $properties = $GLOBALS['properties']->getAppointmentProperties(); - $plaintext = true; - - $data = array(); - - if ($store && $entryid) { - $message = $GLOBALS['operations']->openMessage($store, $entryid); - - - // add all standard properties from the series/normal message - $data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $properties, (isset($plaintext) && $plaintext)); - - } - - return $data['item']['recipients']['item']; - } - /** * The main export function, creates the ics file for download * @param $actionType @@ -334,60 +121,7 @@ class CalendarModule extends Module */ private function exportCalendar($actionType, $actionData) { - $secid = $this->randomstring(); - $this->createSecIDFile($secid); - $tmpname = stripslashes($actionData["calendar"] . ".ics." . $this->randomstring(8)); - $filename = TMP_PATH . "/" . $tmpname . "." . $secid; - - if (!is_writable(TMP_PATH . "/")) { - error_log("could not write to export tmp directory!"); - } - - $tz = date("e"); // use php timezone (maybe set up in php.ini, date.timezone) - - if ($this->DEBUG) { - error_log("PHP Timezone: " . $tz); - } - - $config = array( - "language" => substr($GLOBALS["settings"]->get("zarafa/v1/main/language"), 0, 2), - "directory" => TMP_PATH . "/", - "filename" => $tmpname . "." . $secid, - "unique_id" => "zarafa-export-plugin", - "TZID" => $tz - ); - - $v = new vcalendar($config); - $v->setProperty("method", "PUBLISH"); // required of some calendar software - $v->setProperty("x-wr-calname", $actionData["calendar"]); // required of some calendar software - $v->setProperty("X-WR-CALDESC", "Exported Zarafa Calendar"); // required of some calendar software - $v->setProperty("X-WR-TIMEZONE", $tz); - - $xprops = array("X-LIC-LOCATION" => $tz); // required of some calendar software - iCalUtilityFunctions::createTimezone($v, $tz, $xprops); // create timezone object in calendar - - - foreach ($actionData["data"] as $event) { - $event["props"]["description"] = $this->loadEventDescription($event); - $event["props"]["attendees"] = $this->loadAttendees($event); - - $vevent = &$v->newComponent("vevent"); // create a new event object - $this->addEvent($vevent, $event["props"]); - } - - $v->saveCalendar(); - - $response['status'] = true; - $response['fileid'] = $tmpname; // number of entries that will be exported - $response['basedir'] = TMP_PATH; - $response['secid'] = $secid; - $response['realname'] = $actionData["calendar"]; - $this->addActionData($actionType, $response); - $GLOBALS["bus"]->addData($this->getResponseData()); - - if ($this->DEBUG) { - error_log("export done, bus data written!"); - } + // TODO: implement } /** @@ -397,37 +131,7 @@ class CalendarModule extends Module */ private function importCalendar($actionType, $actionData) { - if (is_readable($actionData["ics_filepath"])) { - $ical = new ICal($actionData["ics_filepath"], $GLOBALS["settings"]->get("zarafa/v1/plugins/calendarimporter/default_timezone"), $actionData["timezone"], $actionData["ignore_dst"]); // Parse it! - - if (isset($ical->errors)) { - $response['status'] = false; - $response['message'] = $ical->errors; - } else { - if (!$ical->hasEvents()) { - $response['status'] = false; - $response['message'] = "No events in ics file"; - } else { - $response['status'] = true; - $response['parsed_file'] = $actionData["ics_filepath"]; - $response['parsed'] = array( - 'timezone' => $ical->timezone(), - 'calendar' => $ical->calendar(), - 'events' => $ical->events() - ); - } - } - } else { - $response['status'] = false; - $response['message'] = "File could not be read by server"; - } - - $this->addActionData($actionType, $response); - $GLOBALS["bus"]->addData($this->getResponseData()); - - if ($this->DEBUG) { - error_log("parsing done, bus data written!"); - } + // TODO: implement } /** From 3f3f624b668ed3cc3d812b7d614db8ac71159eac Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Tue, 21 Jun 2016 11:15:18 +0200 Subject: [PATCH 08/17] exporting implemented. TODO: add timezone to ics --- php/module.calendar.php | 217 +++++++++++++++++++++++++++++++- php/plugin.calendarimporter.php | 1 + 2 files changed, 217 insertions(+), 1 deletion(-) diff --git a/php/module.calendar.php b/php/module.calendar.php index 49d1fa5..54c22af 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -30,6 +30,12 @@ class CalendarModule extends Module private $DEBUG = true; // enable error_log debugging + private $busystates = null; + + private $labels = null; + + private $attendeetype = null; + /** * @constructor * @param $id @@ -38,6 +44,38 @@ class CalendarModule extends Module public function __construct($id, $data) { parent::Module($id, $data); + + // init default timezone + date_default_timezone_set(PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE); + + // init mappings + $this->busystates = array( + "FREE", + "TENTATIVE", + "BUSY", + "OOF" + ); + + $this->labels = array( + "NONE", + "IMPORTANT", + "WORK", + "PERSONAL", + "HOLIDAY", + "REQUIRED", + "TRAVEL REQUIRED", + "PREPARATION REQUIERED", + "BIRTHDAY", + "SPECIAL DATE", + "PHONE INTERVIEW" + ); + + $this->attendeetype = array( + "NON-PARTICIPANT", // needed as zarafa starts counting at 1 + "REQ-PARTICIPANT", + "OPT-PARTICIPANT", + "NON-PARTICIPANT" + ); } /** @@ -114,6 +152,63 @@ class CalendarModule extends Module return $pass; } + /** + * Get a property from the array. + * @param $props + * @param $propname + * @return string + */ + private function getProp($props, $propname) + { + if (isset($props["props"][$propname])) { + return $props["props"][$propname]; + } + return ""; + } + + private function getDurationStringFromMintues($minutes, $pos = false) { + $pos = $pos === true ? "+" : "-"; + $str = $pos . "P"; + + + + // variables for holding values + $mins = intval($minutes); + $hours = 0; + $days = 0; + $weeks = 0; + + // calculations + if ( $mins >= 60 ) { + $hours = (int)($mins / 60); + $mins = $mins % 60; + } + if ( $hours >= 24 ) { + $days = (int)($hours / 24); + $hours = $hours % 60; + } + if ( $days >= 7 ) { + $weeks = (int)($days / 7); + $days = $days % 7; + } + + // format result + if ( $weeks ) { + $str .= "{$weeks}W"; + } + if ( $days ) { + $str .= "{$days}D"; + } + if ( $hours ) { + $str .= "{$hours}H"; + } + if ( $mins ) { + $str .= "{$mins}M"; + } + + return $str; + } + /** * The main export function, creates the ics file for download * @param $actionType @@ -121,7 +216,127 @@ class CalendarModule extends Module */ private function exportCalendar($actionType, $actionData) { - // TODO: implement + // Get store id + $storeid = false; + if (isset($actionData["storeid"])) { + $storeid = $actionData["storeid"]; + } + + // Get records + $records = array(); + if (isset($actionData["records"])) { + $records = $actionData["records"]; + } + + // Get folders + $folder = false; + if (isset($actionData["folder"])) { + $folder = $actionData["folder"]; + } + + $response = array(); + $error = false; + $error_msg = ""; + + // write csv + $token = $this->randomstring(16); + $file = PLUGIN_CALENDARIMPORTER_TMP_UPLOAD . "ics_" . $token . ".ics"; + file_put_contents($file, ""); + + $store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid)); + if ($store) { + // load folder first + if ($folder !== false) { + $mapifolder = mapi_msgstore_openentry($store, hex2bin($folder)); + + $table = mapi_folder_getcontentstable($mapifolder); + $list = mapi_table_queryallrows($table, array(PR_ENTRYID)); + + foreach ($list as $item) { + $records[] = bin2hex($item[PR_ENTRYID]); + } + } + + $vcalendar = new VObject\Component\VCalendar(); + for ($index = 0, $count = count($records); $index < $count; $index++) { + $message = mapi_msgstore_openentry($store, hex2bin($records[$index])); + + // get message properties. + $properties = $GLOBALS['properties']->getAppointmentProperties(); + $plaintext = true; + $messageProps = $GLOBALS['operations']->getMessageProps($store, $message, $properties, $plaintext); + + $vevent = $vcalendar->add('VEVENT', [ + 'SUMMARY' => $this->getProp($messageProps, "subject"), + 'DTSTART' => date_timestamp_set(new DateTime(), $this->getProp($messageProps, "startdate")), + 'DTEND' => date_timestamp_set(new DateTime(), $this->getProp($messageProps, "duedate")), + 'CREATED' => date_timestamp_set(new DateTime(), $this->getProp($messageProps, "creation_time")), + 'LAST-MODIFIED' => date_timestamp_set(new DateTime(), $this->getProp($messageProps, "last_modification_time")), + 'PRIORITY' => $this->getProp($messageProps, "importance"), + 'X-MICROSOFT-CDO-INTENDEDSTATUS' => $this->busystates[intval($this->getProp($messageProps, "busystatus"))], // both seem to be valid... + 'X-MICROSOFT-CDO-BUSYSTATUS' => $this->busystates[intval($this->getProp($messageProps, "busystatus"))], // both seem to be valid... + 'X-ZARAFA-LABEL' => $this->labels[intval($this->getProp($messageProps, "label"))], + 'CLASS' => $this->getProp($messageProps, "private") ? "PRIVATE" : "PUBLIC", + 'COMMENT' => "eid:" . $records[$index] + ]); + + // Add organizer + $vevent->add('ORGANIZER','mailto:' . $this->getProp($messageProps, "sender_email_address")); + $vevent->ORGANIZER['CN'] = $this->getProp($messageProps, "sender_name"); + + // Add Attendees + if(isset($messageProps["recipients"]) && count($messageProps["recipients"]["item"]) > 0) { + foreach($messageProps["recipients"]["item"] as $attendee) { + $att = $vevent->add('ATTENDEE', "mailto:" . $this->getProp($attendee, "email_address")); + $att["CN"] = $this->getProp($attendee, "display_name"); + $att["ROLE"] = $this->attendeetype[intval($this->getProp($attendee, "recipient_type"))]; + } + } + + // Add alarms + if(!empty($this->getProp($messageProps, "reminder")) && $this->getProp($messageProps, "reminder") == 1) { + $valarm = $vevent->add('VALARM', [ + 'ACTION' => 'DISPLAY', + 'DESCRIPTION' => $this->getProp($messageProps, "subject") // reuse the event summary + ]); + + // Add trigger + $durationValue = $this->getDurationStringFromMintues($this->getProp($messageProps, "reminder_minutes"), false); + $valarm->add('TRIGGER', $durationValue); // default trigger type is duration (see 4.8.6.3) + + /* + $valarm->add('TRIGGER', date_timestamp_set(new DateTime(), $this->getProp($messageProps, "reminder_time"))); // trigger type "DATE-TIME" + $valarm->TRIGGER['VALUE'] = 'DATE-TIME'; + */ + } + + // Add location + if(!empty($this->getProp($messageProps, "location"))) { + $vevent->add('LOCATION',$this->getProp($messageProps, "location")); + } + + // Add description + $body = $this->getProp($messageProps, "isHTML") ? $this->getProp($messageProps, "html_body") : $this->getProp($messageProps, "body"); + if(!empty($body)) { + $vevent->add('DESCRIPTION',$body); + } + } + + // write combined ics file + file_put_contents($file, file_get_contents($file) . $vcalendar->serialize()); + } + + if (count($records) > 0) { + $response['status'] = true; + $response['download_token'] = $token; + $response['filename'] = count($records) . "events.ics"; + } else { + $response['status'] = false; + $response['message'] = "No events found. Export skipped!"; + } + + $this->addActionData($actionType, $response); + $GLOBALS["bus"]->addData($this->getResponseData()); } /** diff --git a/php/plugin.calendarimporter.php b/php/plugin.calendarimporter.php index 27712cb..1b9d947 100644 --- a/php/plugin.calendarimporter.php +++ b/php/plugin.calendarimporter.php @@ -45,6 +45,7 @@ class Plugincalendarimporter extends Plugin function init() { $this->registerHook('server.core.settings.init.before'); + $this->registerHook('server.index.load.custom'); } /** From c0debc99fcc25c25bb17d544db6b80cc11e4f2bf Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Tue, 21 Jun 2016 11:25:48 +0200 Subject: [PATCH 09/17] exporting implemented --- php/module.calendar.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/php/module.calendar.php b/php/module.calendar.php index 54c22af..0691ccd 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -258,6 +258,14 @@ class CalendarModule extends Module } $vcalendar = new VObject\Component\VCalendar(); + + // Add static stuff to vcalendar + $vcalendar->add('METHOD', 'PUBLISH'); + $vcalendar->add('X-WR-CALDESC', 'Exported Zarafa Calendar'); + $vcalendar->add('X-WR-TIMEZONE', date_default_timezone_get()); + + // TODO: add VTIMEZONE object to ical. + for ($index = 0, $count = count($records); $index < $count; $index++) { $message = mapi_msgstore_openentry($store, hex2bin($records[$index])); From 746278f8910afc5a3dfc870b03e2b6c4f30bf82d Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Thu, 23 Jun 2016 22:50:36 +0200 Subject: [PATCH 10/17] started implementing import - not working atm --- js/dialogs/ImportPanel.js | 16 ++++--- php/module.calendar.php | 94 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 8 deletions(-) diff --git a/js/dialogs/ImportPanel.js b/js/dialogs/ImportPanel.js index c191a46..7271717 100644 --- a/js/dialogs/ImportPanel.js +++ b/js/dialogs/ImportPanel.js @@ -620,7 +620,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { buttons : Zarafa.common.dialogs.MessageBox.OK }); } else { - var calendarFolder = this.getContactFolderByEntryid(folderValue); + var calendarFolder = this.getCalendarFolderByEntryid(calValue); this.loadMask.show(); var uids = []; @@ -630,7 +630,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { uids.push(newRecord.data.record.internal_fields.event_uid); }, this); - var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({ + var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({ successCallback: this.importEventsDone, scope: this }); @@ -639,10 +639,10 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { 'calendarmodule', 'import', { - storeid : contactFolder.store_entryid, - folderid : contactFolder.entryid, + storeid : calendarFolder.store_entryid, + folderid : calendarFolder.entryid, uids : uids, - vcf_filepath: this.vcffile + ics_filepath: this.icsfile }, responseHandler ); @@ -655,8 +655,10 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { * @param {Object} response */ importEventsDone: function (response) { - this.loadMask.hide(); - this.dialog.close(); + var self = this.scope; + + self.loadMask.hide(); + self.dialog.close(); if (response.status == true) { container.getNotifier().notify('info', 'Imported', 'Imported ' + response.count + ' events. Please reload your calendar!'); } else { diff --git a/php/module.calendar.php b/php/module.calendar.php index 0691ccd..bb7fcac 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -354,7 +354,99 @@ class CalendarModule extends Module */ private function importCalendar($actionType, $actionData) { - // TODO: implement + // Get uploaded vcf path + $icsfile = false; + if (isset($actionData["ics_filepath"])) { + $icsfile = $actionData["ics_filepath"]; + } + + // Get store id + $storeid = false; + if (isset($actionData["storeid"])) { + $storeid = $actionData["storeid"]; + } + + // Get folder entryid + $folderid = false; + if (isset($actionData["folderid"])) { + $folderid = $actionData["folderid"]; + } + + // Get uids + $uids = array(); + if (isset($actionData["uids"])) { + $uids = $actionData["uids"]; + } + + $response = array(); + $error = false; + $error_msg = ""; + + // parse the ics file a last time... + $parser = null; + try { + $parser = VObject\Reader::read( + fopen($icsfile,'r') + ); + } catch (Exception $e) { + $error = true; + $error_msg = $e->getMessage(); + } + + $events = array(); + if (count($parser->VEVENT) > 0) { + $events = $this->parseCalendarToArray($parser); + $store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid)); + $folder = mapi_msgstore_openentry($store, hex2bin($folderid)); + + $importall = false; + if (count($uids) == count($events)) { + $importall = true; + } + + $propValuesMAPI = array(); + $properties = $GLOBALS['properties']->getAppointmentProperties(); + $count = 0; + + // iterate through all events and import them :) + foreach ($events as $event) { + if (isset($event["startdate"]) && ($importall || in_array($event["internal_fields"]["event_uid"], $uids))) { + // parse the arraykeys + // TODO: this is very slow... + foreach ($events as $key => $value) { + if ($key !== "internal_fields") { + if(isset($properties[$key])) { + $propValuesMAPI[$properties[$key]] = $value; + } + } + } + + $propValuesMAPI[$properties["message_class"]] = "IPM.Appointment"; + $propValuesMAPI[$properties["icon_index"]] = "1024"; + $message = mapi_folder_createmessage($folder); + + + mapi_setprops($message, $propValuesMAPI); + mapi_savechanges($message); + if ($this->DEBUG) { + error_log("New event added: \"" . $propValuesMAPI[$properties["startdate"]] . "\".\n"); + } + $count++; + } + } + + $response['status'] = true; + $response['count'] = $count; + $response['message'] = ""; + + } else { + $response['status'] = false; + $response['count'] = 0; + $response['message'] = $error ? $error_msg : "ICS file empty!"; + } + + $this->addActionData($actionType, $response); + $GLOBALS["bus"]->addData($this->getResponseData()); } /** From 557ea6b6da4db3e4eac50cf724c33a96f190a691 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Sat, 25 Jun 2016 15:17:19 +0200 Subject: [PATCH 11/17] basic import working --- js/dialogs/ImportPanel.js | 6 +++--- php/module.calendar.php | 27 +++++++++++++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/js/dialogs/ImportPanel.js b/js/dialogs/ImportPanel.js index 7271717..a4f6684 100644 --- a/js/dialogs/ImportPanel.js +++ b/js/dialogs/ImportPanel.js @@ -285,13 +285,13 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { parsedData[i] = [ eventdata.events[i]["subject"], new Date(parseInt(eventdata.events[i]["startdate"]) * 1000), - new Date(parseInt(eventdata.events[i]["enddate"]) * 1000), + new Date(parseInt(eventdata.events[i]["duedate"]) * 1000), eventdata.events[i]["location"], eventdata.events[i]["body"], eventdata.events[i]["priority"], eventdata.events[i]["label"], - eventdata.events[i]["busy"], - eventdata.events[i]["class"], + eventdata.events[i]["busystatus"], + eventdata.events[i]["private"], eventdata.events[i]["organizer"], eventdata.events[i]["alarms"], eventdata.events[i]["timezone"], diff --git a/php/module.calendar.php b/php/module.calendar.php index bb7fcac..2e72cff 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -406,14 +406,18 @@ class CalendarModule extends Module $propValuesMAPI = array(); $properties = $GLOBALS['properties']->getAppointmentProperties(); + error_log("properties: " . print_r($properties, true)); $count = 0; // iterate through all events and import them :) foreach ($events as $event) { if (isset($event["startdate"]) && ($importall || in_array($event["internal_fields"]["event_uid"], $uids))) { + + error_log("Importing: " . print_r($event, true)); + // parse the arraykeys // TODO: this is very slow... - foreach ($events as $key => $value) { + foreach ($event as $key => $value) { if ($key !== "internal_fields") { if(isset($properties[$key])) { $propValuesMAPI[$properties[$key]] = $value; @@ -421,6 +425,11 @@ class CalendarModule extends Module } } + error_log("MAPI: " . print_r($propValuesMAPI, true)); + + $propValuesMAPI[$properties["commonstart"]] = $propValuesMAPI[$properties["startdate"]]; + $propValuesMAPI[$properties["commonend"]] = $propValuesMAPI[$properties["duedate"]]; + $propValuesMAPI[$properties["message_class"]] = "IPM.Appointment"; $propValuesMAPI[$properties["icon_index"]] = "1024"; $message = mapi_folder_createmessage($folder); @@ -429,7 +438,7 @@ class CalendarModule extends Module mapi_setprops($message, $propValuesMAPI); mapi_savechanges($message); if ($this->DEBUG) { - error_log("New event added: \"" . $propValuesMAPI[$properties["startdate"]] . "\".\n"); + error_log("New event added: \"" . $event["startdate"] . "\".\n"); } $count++; } @@ -663,21 +672,23 @@ class CalendarModule extends Module $properties["internal_fields"]["event_uid"] = base64_encode($Index . $vEvent->UID); $properties["startdate"] = (string)$vEvent->DTSTART->getDateTime()->getTimestamp(); - $properties["enddate"] = (string)$vEvent->DTEND->getDateTime()->getTimestamp(); + $properties["duedate"] = (string)$vEvent->DTEND->getDateTime()->getTimestamp(); $properties["location"] = (string)$vEvent->LOCATION; $properties["subject"] = (string)$vEvent->SUMMARY; $properties["body"] = (string)$vEvent->DESCRIPTION; $properties["comment"] = (string)$vEvent->COMMENT; $properties["timezone"] = (string)$vEvent->DTSTART["TZID"]; $properties["organizer"] = (string)$vEvent->ORGANIZER; - $properties["busy"] = (string)$vEvent->{'X-MICROSOFT-CDO-INTENDEDSTATUS'}; // X-MICROSOFT-CDO-BUSYSTATUS + $properties["busystatus"] = array_search((string)$vEvent->{'X-MICROSOFT-CDO-INTENDEDSTATUS'}, $this->busystates); // X-MICROSOFT-CDO-BUSYSTATUS $properties["transp"] = (string)$vEvent->TRANSP; //$properties["trigger"] = (string)$vEvent->COMMENT; $properties["priority"] = (string)$vEvent->PRIORITY; - $properties["class"] = (string)$vEvent->CLASS; - //$properties["label"] = (string)$vEvent->COMMENT; - $properties["lastmodified"] = (string)$vEvent->{'LAST-MODIFIED'}; - $properties["created"] = (string)$vEvent->CREATED; + $properties["private"] = ((string)$vEvent->CLASS) == "PRIVATE" ? true : false; + if(!empty((string)$vEvent->{'X-ZARAFA-LABEL'})) { + $properties["label"] = array_search((string)$vEvent->{'X-ZARAFA-LABEL'}, $this->labels); + } + $properties["last_modification_time"] = (string)$vEvent->{'LAST-MODIFIED'}->getDateTime()->getTimestamp(); + $properties["creation_time"] = (string)$vEvent->CREATED->getDateTime()->getTimestamp(); $properties["rrule"] = (string)$vEvent->RRULE; // Attendees From cfe069dbd0e41bff0833221525c55bdfcd6bdfe3 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Sat, 25 Jun 2016 15:35:59 +0200 Subject: [PATCH 12/17] basic import working --- php/module.calendar.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/php/module.calendar.php b/php/module.calendar.php index 2e72cff..1620295 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -406,17 +406,18 @@ class CalendarModule extends Module $propValuesMAPI = array(); $properties = $GLOBALS['properties']->getAppointmentProperties(); - error_log("properties: " . print_r($properties, true)); + // extend properties... + $properties["body"] = PR_BODY; + $count = 0; // iterate through all events and import them :) foreach ($events as $event) { if (isset($event["startdate"]) && ($importall || in_array($event["internal_fields"]["event_uid"], $uids))) { - error_log("Importing: " . print_r($event, true)); + $message = mapi_folder_createmessage($folder); // parse the arraykeys - // TODO: this is very slow... foreach ($event as $key => $value) { if ($key !== "internal_fields") { if(isset($properties[$key])) { @@ -425,20 +426,20 @@ class CalendarModule extends Module } } - error_log("MAPI: " . print_r($propValuesMAPI, true)); - $propValuesMAPI[$properties["commonstart"]] = $propValuesMAPI[$properties["startdate"]]; $propValuesMAPI[$properties["commonend"]] = $propValuesMAPI[$properties["duedate"]]; + $propValuesMAPI[$properties["duration"]] = ($propValuesMAPI[$properties["duedate"]] - $propValuesMAPI[$properties["startdate"]]) / 60; // Minutes needed + $propValuesMAPI[$properties["reminder"]] = false; // needed, overwritten if there is a timer $propValuesMAPI[$properties["message_class"]] = "IPM.Appointment"; $propValuesMAPI[$properties["icon_index"]] = "1024"; - $message = mapi_folder_createmessage($folder); + // TODO: set attendees and alarms mapi_setprops($message, $propValuesMAPI); mapi_savechanges($message); if ($this->DEBUG) { - error_log("New event added: \"" . $event["startdate"] . "\".\n"); + error_log("New event added: \"" . $event["subject"] . "\".\n"); } $count++; } From 7746bdd5627f70e1be875570a07fff56892e0987 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 31 Oct 2016 23:01:52 +0100 Subject: [PATCH 13/17] Fixed import on Kopano Webapp 3.1.1 --- .idea/calendarimporter.iml | 10 - .idea/copyright/profiles_settings.xml | 3 + .idea/workspace.xml | 435 ++++++++++++++++++++++++++ Makefile | 2 + config.php | 2 +- js/plugin.calendarimporter.js | 4 +- php/module.calendar.php | 4 +- php/plugin.calendarimporter.php | 4 +- 8 files changed, 446 insertions(+), 18 deletions(-) create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 .idea/workspace.xml create mode 100644 Makefile diff --git a/.idea/calendarimporter.iml b/.idea/calendarimporter.iml index 0c76797..c956989 100644 --- a/.idea/calendarimporter.iml +++ b/.idea/calendarimporter.iml @@ -4,15 +4,5 @@ - - - - - - - - - -
\ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..27afebc --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + project + + + true + + + + DIRECTORY + + false + + + + + + + + + 1477949602474 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fd5d35c --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +default: + ant deploy; cp -r /home/osboxes/Documents/kopano-webapp-3.2.0.285/deploy/plugins/calendarimporter /usr/share/kopano-webapp/plugins/ diff --git a/config.php b/config.php index 6796ccf..a1b8f1b 100644 --- a/config.php +++ b/config.php @@ -9,5 +9,5 @@ define('PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE', "Europe/Vienna"); /** Tempory path for uploaded files... */ - define('PLUGIN_CALENDARIMPORTER_TMP_UPLOAD', "/var/lib/zarafa-webapp/tmp/"); + define('PLUGIN_CALENDARIMPORTER_TMP_UPLOAD', "/var/lib/kopano-webapp/tmp/"); ?> diff --git a/js/plugin.calendarimporter.js b/js/plugin.calendarimporter.js index 4d634e7..bb4579d 100644 --- a/js/plugin.calendarimporter.js +++ b/js/plugin.calendarimporter.js @@ -95,7 +95,7 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, { recordIds.push(btn.records[i].get("entryid")); } - var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({ + var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({ successCallback: Zarafa.plugins.calendarimporter.data.Actions.downloadICS, scope : this }); @@ -164,7 +164,7 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, { */ gotAttachmentFileName: function (response) { if (response.status == true) { - this.openImportDialog(response.tmpname); + this.scope.openImportDialog(response.tmpname); } else { Zarafa.common.dialogs.MessageBox.show({ title : _('Error'), diff --git a/php/module.calendar.php b/php/module.calendar.php index 1620295..ea1f07f 100644 --- a/php/module.calendar.php +++ b/php/module.calendar.php @@ -43,7 +43,7 @@ class CalendarModule extends Module */ public function __construct($id, $data) { - parent::Module($id, $data); + parent::__construct($id, $data); // init default timezone date_default_timezone_set(PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE); @@ -619,7 +619,7 @@ class CalendarModule extends Module $parser = VObject\Reader::read( fopen($actionData["ics_filepath"],'r') ); - error_log(print_r($parser->VTIMEZONE, true)); + //error_log(print_r($parser->VTIMEZONE, true)); } catch (Exception $e) { $error = true; $error_msg = $e->getMessage(); diff --git a/php/plugin.calendarimporter.php b/php/plugin.calendarimporter.php index 1b9d947..bd8aec5 100644 --- a/php/plugin.calendarimporter.php +++ b/php/plugin.calendarimporter.php @@ -33,9 +33,7 @@ class Plugincalendarimporter extends Plugin /** * Constructor */ - function Plugincalendarimporter() - { - } + function __construct() {} /** * Function initializes the Plugin and registers all hooks From 6f3561722ccdea270571b89bc597cfadef92cb99 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 31 Oct 2016 23:42:42 +0100 Subject: [PATCH 14/17] Fixed settings panel - webapp reloading if a value has changed --- .idea/workspace.xml | 261 ++++++++++++++++++++-------------- js/data/Actions.js | 113 +++++++++++++++ js/dialogs/ImportPanel.js | 119 +--------------- js/settings/SettingsWidget.js | 148 ++++++++++++++----- 4 files changed, 384 insertions(+), 257 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 27afebc..7608394 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,11 +2,11 @@ - - - - - + + + + + @@ -27,46 +27,33 @@ - - + + - - + + - - + + - - + + - - - - + - - - - - - - - - - - - + + @@ -74,50 +61,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -128,6 +80,10 @@
@@ -198,7 +154,7 @@
- - + - + @@ -338,7 +338,9 @@ - + + @@ -366,14 +368,6 @@ - - - - - - - - @@ -382,16 +376,6 @@ - - - - - - - - - - @@ -402,14 +386,6 @@ - - - - - - - - @@ -420,16 +396,91 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/js/data/Actions.js b/js/data/Actions.js index 513f9a4..68d6e5a 100644 --- a/js/data/Actions.js +++ b/js/data/Actions.js @@ -61,5 +61,118 @@ Zarafa.plugins.calendarimporter.data.Actions = { downloadFrame.dom.contentWindow.location = link; } + }, + + /** + * Get all calendar folders. + * @param {boolean} asDropdownStore If true, a simple array store will be returned. + * @returns {*} + */ + getAllCalendarFolders: function (asDropdownStore) { + asDropdownStore = Ext.isEmpty(asDropdownStore) ? false : asDropdownStore; + + var allFolders = []; + + var inbox = container.getHierarchyStore().getDefaultStore(); + var pub = container.getHierarchyStore().getPublicStore(); + + if (!Ext.isEmpty(inbox.subStores) && inbox.subStores.folders.totalLength > 0) { + for (var i = 0; i < inbox.subStores.folders.totalLength; i++) { + var folder = inbox.subStores.folders.getAt(i); + if (folder.get("container_class") == "IPF.Appointment") { + if (asDropdownStore) { + allFolders.push([ + folder.get("entryid"), + folder.get("display_name") + ]); + } else { + allFolders.push({ + display_name : folder.get("display_name"), + entryid : folder.get("entryid"), + store_entryid: folder.get("store_entryid"), + is_public : false + }); + } + } + } + } + + if (!Ext.isEmpty(pub.subStores) && pub.subStores.folders.totalLength > 0) { + for (var j = 0; j < pub.subStores.folders.totalLength; j++) { + var folder = pub.subStores.folders.getAt(j); + if (folder.get("container_class") == "IPF.Appointment") { + if (asDropdownStore) { + allFolders.push([ + folder.get("entryid"), + folder.get("display_name") + " (Public)" + ]); + } else { + allFolders.push({ + display_name : folder.get("display_name"), + entryid : folder.get("entryid"), + store_entryid: folder.get("store_entryid"), + is_public : true + }); + } + } + } + } + + if (asDropdownStore) { + return allFolders.sort(Zarafa.plugins.calendarimporter.data.Actions.dynamicSort(1)); + } else { + return allFolders; + } + }, + + /** + * Return a calendar folder element by name. + * @param {string} name + * @returns {*} + */ + getCalendarFolderByName: function (name) { + var folders = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(false); + + for (var i = 0; i < folders.length; i++) { + if (folders[i].display_name == name) { + return folders[i]; + } + } + + return container.getHierarchyStore().getDefaultFolder('calendar'); + }, + + /** + * Return a calendar folder element by entryid. + * @param {string} entryid + * @returns {*} + */ + getCalendarFolderByEntryid: function (entryid) { + var folders = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(false); + + for (var i = 0; i < folders.length; i++) { + if (folders[i].entryid == entryid) { + return folders[i]; + } + } + + return container.getHierarchyStore().getDefaultFolder('calendar'); + }, + + /** + * Dynamic sort function, sorts by property name. + * @param {string|int} property + * @returns {Function} + */ + dynamicSort: function (property) { + var sortOrder = 1; + if (property[0] === "-") { + sortOrder = -1; + property = property.substr(1); + } + return function (a, b) { + var result = (a[property].toLowerCase() < b[property].toLowerCase()) ? -1 : (a[property].toLowerCase() > b[property].toLowerCase()) ? 1 : 0; + return result * sortOrder; + } } }; \ No newline at end of file diff --git a/js/dialogs/ImportPanel.js b/js/dialogs/ImportPanel.js index a4f6684..abdc201 100644 --- a/js/dialogs/ImportPanel.js +++ b/js/dialogs/ImportPanel.js @@ -158,119 +158,6 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { }; }, - /** - * Get all calendar folders. - * @param {boolean} asDropdownStore If true, a simple array store will be returned. - * @returns {*} - */ - getAllCalendarFolders: function (asDropdownStore) { - asDropdownStore = Ext.isEmpty(asDropdownStore) ? false : asDropdownStore; - - var allFolders = []; - - var inbox = container.getHierarchyStore().getDefaultStore(); - var pub = container.getHierarchyStore().getPublicStore(); - - if (!Ext.isEmpty(inbox.subStores) && inbox.subStores.folders.totalLength > 0) { - for (var i = 0; i < inbox.subStores.folders.totalLength; i++) { - var folder = inbox.subStores.folders.getAt(i); - if (folder.get("container_class") == "IPF.Appointment") { - if (asDropdownStore) { - allFolders.push([ - folder.get("entryid"), - folder.get("display_name") - ]); - } else { - allFolders.push({ - display_name : folder.get("display_name"), - entryid : folder.get("entryid"), - store_entryid: folder.get("store_entryid"), - is_public : false - }); - } - } - } - } - - if (!Ext.isEmpty(pub.subStores) && pub.subStores.folders.totalLength > 0) { - for (var j = 0; j < pub.subStores.folders.totalLength; j++) { - var folder = pub.subStores.folders.getAt(j); - if (folder.get("container_class") == "IPF.Appointment") { - if (asDropdownStore) { - allFolders.push([ - folder.get("entryid"), - folder.get("display_name") + " (Public)" - ]); - } else { - allFolders.push({ - display_name : folder.get("display_name"), - entryid : folder.get("entryid"), - store_entryid: folder.get("store_entryid"), - is_public : true - }); - } - } - } - } - - if (asDropdownStore) { - return allFolders.sort(this.dynamicSort(1)); - } else { - return allFolders; - } - }, - - /** - * Dynamic sort function, sorts by property name. - * @param {string|int} property - * @returns {Function} - */ - dynamicSort: function (property) { - var sortOrder = 1; - if (property[0] === "-") { - sortOrder = -1; - property = property.substr(1); - } - return function (a, b) { - var result = (a[property].toLowerCase() < b[property].toLowerCase()) ? -1 : (a[property].toLowerCase() > b[property].toLowerCase()) ? 1 : 0; - return result * sortOrder; - } - }, - - /** - * Return a calendar folder element by name. - * @param {string} name - * @returns {*} - */ - getCalendarFolderByName: function (name) { - var folders = this.getAllCalendarFolders(false); - - for (var i = 0; i < folders.length; i++) { - if (folders[i].display_name == name) { - return folders[i]; - } - } - - return container.getHierarchyStore().getDefaultFolder('calendar'); - }, - - /** - * Return a calendar folder element by entryid. - * @param {string} entryid - * @returns {*} - */ - getCalendarFolderByEntryid: function (entryid) { - var folders = this.getAllCalendarFolders(false); - - for (var i = 0; i < folders.length; i++) { - if (folders[i].entryid == entryid) { - return folders[i]; - } - } - - return container.getHierarchyStore().getDefaultFolder('calendar'); - }, - /** * Reloads the data of the grid * @private @@ -349,14 +236,14 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { }, createSelectBox: function() { - var myStore = this.getAllCalendarFolders(true); + var myStore = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(true); return { xtype: "selectbox", ref: 'calendarselector', editable: false, name: "choosen_calendar", - value: Ext.isEmpty(this.folder) ? this.getCalendarFolderByName(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar")).entryid : this.folder, + value: Ext.isEmpty(this.folder) ? Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByName(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar")).entryid : this.folder, width: 100, fieldLabel: "Select folder", store: myStore, @@ -620,7 +507,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { buttons : Zarafa.common.dialogs.MessageBox.OK }); } else { - var calendarFolder = this.getCalendarFolderByEntryid(calValue); + var calendarFolder = Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(calValue); this.loadMask.show(); var uids = []; diff --git a/js/settings/SettingsWidget.js b/js/settings/SettingsWidget.js index 6d3c034..784c357 100644 --- a/js/settings/SettingsWidget.js +++ b/js/settings/SettingsWidget.js @@ -40,43 +40,14 @@ Zarafa.plugins.calendarimporter.settings.SettingsWidget = Ext.extend(Zarafa.sett }, createSelectBox: function() { - var defaultFolder = container.getHierarchyStore().getDefaultFolder('calendar'); // @type: Zarafa.hierarchy.data.MAPIFolderRecord - var subFolders = defaultFolder.getChildren(); - var myStore = []; - - /* add all local calendar folders */ - var i = 0; - myStore.push(new Array(defaultFolder.getDefaultFolderKey(), defaultFolder.getDisplayName())); - for(i = 0; i < subFolders.length; i++) { - /* Store all subfolders */ - myStore.push(new Array(subFolders[i].getDisplayName(), subFolders[i].getDisplayName(), false)); // 3rd field = isPublicfolder - } - - /* add all shared calendar folders */ - var pubStore = container.getHierarchyStore().getPublicStore(); - - if(typeof pubStore !== "undefined") { - try { - var pubFolder = pubStore.getDefaultFolder("publicfolders"); - var pubSubFolders = pubFolder.getChildren(); - - for(i = 0; i < pubSubFolders.length; i++) { - if(pubSubFolders[i].isContainerClass("IPF.Appointment")){ - myStore.push(new Array(pubSubFolders[i].getDisplayName(), pubSubFolders[i].getDisplayName() + " [Shared]", true)); // 3rd field = isPublicfolder - } - } - } catch (e) { - console.log("Error opening the shared folder..."); - console.log(e); - } - } - + var myStore = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(true); + return { xtype: "selectbox", ref : 'defaultCalendar', editable: false, name: "zarafa/v1/plugins/calendarimporter/default_calendar", - value: container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar"), + value: Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByName(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar")).entryid, width: 100, fieldLabel: "Default calender", store: myStore, @@ -128,10 +99,115 @@ Zarafa.plugins.calendarimporter.settings.SettingsWidget = Ext.extend(Zarafa.sett * @param {Zarafa.settings.SettingsModel} settingsModel The settings to update */ updateSettings : function(settingsModel) { - settingsModel.set(this.enableSync.name, this.enableSync.getValue()); - settingsModel.set(this.defaultCalendar.name, this.defaultCalendar.getValue()); - settingsModel.set(this.defaultTimezone.name, this.defaultTimezone.getValue()); - } + // check if the user changed a value + var changed = false; + + if(settingsModel.get(this.enableSync.name) != this.enableSync.getValue()) { + changed = true; + } else if(settingsModel.get(this.defaultCalendar.name) != this.defaultCalendar.getValue()) { + changed = true; + } else if(settingsModel.get(this.defaultTimezone.name) != this.defaultTimezone.getValue()) { + changed = true; + } + + if(changed) { + // Really save changes + settingsModel.set(this.enableSync.name, this.enableSync.getValue()); + settingsModel.set(this.defaultCalendar.name, this.defaultCalendar.getValue()); + settingsModel.set(this.defaultTimezone.name, this.defaultTimezone.getValue()); + + this.onUpdateSettings(); + } + }, + + /** + * Called after the {@link Zarafa.settings.SettingsModel} fires the {@link Zarafa.settings.SettingsModel#save save} + * event to indicate the settings were successfully saved and it will forcefully realod the webapp. + * settings which were saved to the server. + * @private + */ + onUpdateSettings : function() + { + var message = _('Your WebApp needs to be reloaded to make the changes visible!'); + message += '

'; + message += _('WebApp will automatically restart in order for these changes to take effect'); + message += '
'; + + Zarafa.common.dialogs.MessageBox.addCustomButtons({ + title: _('Restart WebApp'), + msg : message, + icon: Ext.MessageBox.QUESTION, + fn : this.restartWebapp, + customButton : [{ + text : _('Restart'), + name : 'restart' + }, { + text : _('Cancel'), + name : 'cancel' + }], + scope : this + }); + + }, + + /** + * Event handler for {@link #onResetSettings}. This will check if the user + * wishes to reset the default settings or not. + * @param {String} button The button which user pressed. + * @private + */ + restartWebapp : function(button) + { + if (button === 'restart') { + var contextModel = this.ownerCt.settingsContext.getModel(); + var realModel = contextModel.getRealSettingsModel(); + + realModel.save(); + + this.loadMask = new Zarafa.common.ui.LoadMask(Ext.getBody(), { + msg : '' + _('Webapp is reloading, Please wait.') + '' + }); + this.loadMask.show(); + + this.mon(realModel, 'save', this.onSettingsSave, this); + this.mon(realModel, 'exception', this.onSettingsException, this); + } + + }, + + /** + * Called when the {@link Zarafa.settings.} fires the {@link Zarafa.settings.SettingsModel#save save} + * event to indicate the settings were successfully saved and it will forcefully realod the webapp. + * @param {Zarafa.settings.SettingsModel} model The model which fired the event. + * @param {Object} parameters The key-value object containing the action and the corresponding + * settings which were saved to the server. + * @private + */ + onSettingsSave : function(model, parameters) + { + this.mun(model, 'save', this.onSettingsSave, this); + Zarafa.core.Util.reloadWebapp(); + }, + + /** + * Called when the {@link Zarafa.settings.SettingsModel} fires the {@link Zarafa.settings.SettingsModel#exception exception} + * event to indicate the settings were not successfully saved. + * @param {Zarafa.settings.SettingsModel} model The settings model which fired the event + * @param {String} type The value of this parameter will be either 'response' or 'remote'. + * @param {String} action Name of the action (see {@link Ext.data.Api#actions}). + * @param {Object} options The object containing a 'path' and 'value' field indicating + * respectively the Setting and corresponding value for the setting which was being saved. + * @param {Object} response The response object as received from the PHP-side + * @private + */ + onSettingsException : function(model, type, action, options, response) + { + this.loadMask.hide(); + + // Remove event handlers + this.mun(model, 'save', this.onSettingsSave, this); + this.mun(model, 'exception', this.onSettingsException, this); + } }); Ext.reg('calendarimporter.settingswidget', Zarafa.plugins.calendarimporter.settings.SettingsWidget); From 6b9e371b70008e16c0156080ae3fb5ba9d8bacae Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Tue, 1 Nov 2016 00:34:32 +0100 Subject: [PATCH 15/17] Settings widget for sync is functional now --- .idea/workspace.xml | 210 ++++++++++++++---- js/settings/SettingsCalSyncWidget.js | 8 +- js/settings/SettingsWidget.js | 6 +- .../dialogs/CalSyncEditContentPanel.js | 2 +- js/settings/dialogs/CalSyncEditPanel.js | 47 +--- js/settings/ui/CalSyncGrid.js | 12 +- 6 files changed, 200 insertions(+), 85 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 7608394..35f199c 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,11 +2,11 @@ - - - - + + + + @@ -27,24 +27,62 @@ - - + + - - + + - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -52,8 +90,8 @@ - - + + @@ -83,7 +121,11 @@ @@ -158,6 +200,24 @@
- @@ -381,19 +477,11 @@ - + - - - - - - - - @@ -428,20 +516,12 @@ - - - - - - - - - + @@ -466,8 +546,8 @@ - - + + @@ -476,8 +556,56 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/settings/SettingsCalSyncWidget.js b/js/settings/SettingsCalSyncWidget.js index aacaed0..1c5efac 100644 --- a/js/settings/SettingsCalSyncWidget.js +++ b/js/settings/SettingsCalSyncWidget.js @@ -27,6 +27,7 @@ Zarafa.plugins.calendarimporter.settings.SettingsCalSyncWidget = Ext.extend(Zara { name : 'pass' }, { name : 'intervall', type : 'int' }, { name : 'calendar' }, + { name : 'calendarname' }, { name : 'lastsync' } ], sortInfo : { @@ -68,7 +69,9 @@ Zarafa.plugins.calendarimporter.settings.SettingsCalSyncWidget = Ext.extend(Zara var icslinks = settingsModel.get('zarafa/v1/contexts/calendar/icssync', true); var syncArray = []; for (var key in icslinks) { - syncArray.push(Ext.apply({}, icslinks[key], { id : key })); + if(icslinks.hasOwnProperty(key)) { // skip inherited props + syncArray.push(Ext.apply({}, icslinks[key], {id: key})); + } } // Load all icslinks into the GridPanel @@ -98,7 +101,8 @@ Zarafa.plugins.calendarimporter.settings.SettingsCalSyncWidget = Ext.extend(Zara 'user' : icslink.get('user'), 'pass' : icslink.get('pass'), 'lastsync' : icslink.get('lastsync'), - 'calendar' : icslink.get('calendar') + 'calendar' : icslink.get('calendar'), + 'calendarname' : Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(icslink.get('calendar')).display_name }; } settingsModel.set('zarafa/v1/contexts/calendar/icssync', icslinkData); diff --git a/js/settings/SettingsWidget.js b/js/settings/SettingsWidget.js index 784c357..5d3e7e1 100644 --- a/js/settings/SettingsWidget.js +++ b/js/settings/SettingsWidget.js @@ -88,7 +88,7 @@ Zarafa.plugins.calendarimporter.settings.SettingsWidget = Ext.extend(Zarafa.sett */ update : function(settingsModel) { this.enableSync.setValue(settingsModel.get(this.enableSync.name)); - this.defaultCalendar.setValue(settingsModel.get(this.defaultCalendar.name)); + this.defaultCalendar.setValue(Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByName(settingsModel.get(this.defaultCalendar.name)).entryid); this.defaultTimezone.setValue(settingsModel.get(this.defaultTimezone.name)); }, @@ -104,7 +104,7 @@ Zarafa.plugins.calendarimporter.settings.SettingsWidget = Ext.extend(Zarafa.sett if(settingsModel.get(this.enableSync.name) != this.enableSync.getValue()) { changed = true; - } else if(settingsModel.get(this.defaultCalendar.name) != this.defaultCalendar.getValue()) { + } else if(settingsModel.get(this.defaultCalendar.name) != Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(this.defaultCalendar.getValue()).display_name) { changed = true; } else if(settingsModel.get(this.defaultTimezone.name) != this.defaultTimezone.getValue()) { changed = true; @@ -113,7 +113,7 @@ Zarafa.plugins.calendarimporter.settings.SettingsWidget = Ext.extend(Zarafa.sett if(changed) { // Really save changes settingsModel.set(this.enableSync.name, this.enableSync.getValue()); - settingsModel.set(this.defaultCalendar.name, this.defaultCalendar.getValue()); + settingsModel.set(this.defaultCalendar.name, Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(this.defaultCalendar.getValue()).display_name); // store name settingsModel.set(this.defaultTimezone.name, this.defaultTimezone.getValue()); this.onUpdateSettings(); diff --git a/js/settings/dialogs/CalSyncEditContentPanel.js b/js/settings/dialogs/CalSyncEditContentPanel.js index 09a328f..ce480b0 100644 --- a/js/settings/dialogs/CalSyncEditContentPanel.js +++ b/js/settings/dialogs/CalSyncEditContentPanel.js @@ -23,7 +23,7 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditContentPanel = Ext.e model : true, autoSave : false, width : 400, - height : 350, + height : 400, title : _('ICAL Sync'), items : [{ xtype : 'calendarimporter.calsynceditpanel', diff --git a/js/settings/dialogs/CalSyncEditPanel.js b/js/settings/dialogs/CalSyncEditPanel.js index 1c664c9..a2d78ac 100644 --- a/js/settings/dialogs/CalSyncEditPanel.js +++ b/js/settings/dialogs/CalSyncEditPanel.js @@ -59,8 +59,7 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditPanel = Ext.extend(E var store = this.dialog.store; var id = 0; var record = undefined; - - console.log(this); + if(!this.currentItem) { record = new store.recordType({ id: this.hashCode(this.icsurl.getValue()), @@ -69,6 +68,7 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditPanel = Ext.extend(E user: this.user.getValue(), pass: Ext.util.base64.encode(this.pass.getValue()), calendar: this.calendar.getValue(), + calendarname : Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(this.calendar.getValue()).display_name, lastsync: "never" }); } @@ -82,6 +82,7 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditPanel = Ext.extend(E this.currentItem.set('user', this.user.getValue()); this.currentItem.set('pass', Ext.util.base64.encode(this.pass.getValue())); this.currentItem.set('calendar', this.calendar.getValue()); + this.currentItem.set('calendarname', Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(this.calendar.getValue()).display_name); } this.dialog.close(); } @@ -95,48 +96,20 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditPanel = Ext.extend(E createPanelItems : function(config) { var icsurl = ""; - var intervall = ""; + var intervall = "15"; var user = ""; var pass = ""; - var calendar = ""; - - var defaultFolder = container.getHierarchyStore().getDefaultFolder('calendar'); // @type: Zarafa.hierarchy.data.MAPIFolderRecord - var subFolders = defaultFolder.getChildren(); - var myStore = []; - - if(config.item){ + var calendarname = ""; + var calendar = Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByName(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar")).entryid; + var myStore = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(true); + + if(config.item){ icsurl = config.item.get('icsurl'); intervall = config.item.get('intervall'); user = config.item.get('user'); pass = Ext.util.base64.decode(config.item.get('pass')); calendar = config.item.get('calendar'); - } - - /* add all local calendar folders */ - var i = 0; - myStore.push(new Array(defaultFolder.getDefaultFolderKey(), defaultFolder.getDisplayName())); - for(i = 0; i < subFolders.length; i++) { - /* Store all subfolders */ - myStore.push(new Array(subFolders[i].getDisplayName(), subFolders[i].getDisplayName(), false)); // 3rd field = isPublicfolder - } - - /* add all shared calendar folders */ - var pubStore = container.getHierarchyStore().getPublicStore(); - - if(typeof pubStore !== "undefined") { - try { - var pubFolder = pubStore.getDefaultFolder("publicfolders"); - var pubSubFolders = pubFolder.getChildren(); - - for(i = 0; i < pubSubFolders.length; i++) { - if(pubSubFolders[i].isContainerClass("IPF.Appointment")){ - myStore.push(new Array(pubSubFolders[i].getDisplayName(), pubSubFolders[i].getDisplayName() + " [Shared]", true)); // 3rd field = isPublicfolder - } - } - } catch (e) { - console.log("Error opening the shared folder..."); - console.log(e); - } + calendarname = config.item.get('calendarname'); } diff --git a/js/settings/ui/CalSyncGrid.js b/js/settings/ui/CalSyncGrid.js index 7535659..6295ac6 100644 --- a/js/settings/ui/CalSyncGrid.js +++ b/js/settings/ui/CalSyncGrid.js @@ -58,6 +58,16 @@ Zarafa.plugins.calendarimporter.settings.ui.CalSyncGrid = Ext.extend(Ext.grid.Gr return value ? "true" : "false"; }, + /** + * Render function + * @return {String} + * @private + */ + renderCalendarColumn : function(value, p, record) + { + return Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(value).display_name; + }, + /** * Creates a column model object, used in {@link #colModel} config * @return {Ext.grid.ColumnModel} column model object @@ -71,7 +81,7 @@ Zarafa.plugins.calendarimporter.settings.ui.CalSyncGrid = Ext.extend(Ext.grid.Gr renderer : Zarafa.common.ui.grid.Renderers.text }, { - dataIndex : 'calendar', + dataIndex : 'calendarname', header : _('Destination Calender'), renderer : Zarafa.common.ui.grid.Renderers.text }, From daeb871aeeced637874742f1f17c984948ece404 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Tue, 1 Nov 2016 00:54:31 +0100 Subject: [PATCH 16/17] Fixed backend sync script --- .idea/workspace.xml | 95 ++++++++++++++++++++++++++++++++++--------- backend/functions.php | 2 +- backend/sync.php | 4 +- 3 files changed, 79 insertions(+), 22 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 35f199c..1494732 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,11 +2,8 @@ - - - - - + + @@ -27,7 +24,7 @@ - + @@ -67,11 +64,43 @@ - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -126,6 +155,8 @@ @@ -392,12 +423,12 @@ - @@ -562,14 +593,6 @@ - - - - - - - - @@ -610,5 +633,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/functions.php b/backend/functions.php index f6a9625..b4df684 100644 --- a/backend/functions.php +++ b/backend/functions.php @@ -3,7 +3,7 @@ * functions.php, zarafa calender to ics im/exporter backend * * Author: Christoph Haas - * Copyright (C) 2012-2014 Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/backend/sync.php b/backend/sync.php index 647429b..90e2322 100644 --- a/backend/sync.php +++ b/backend/sync.php @@ -4,7 +4,7 @@ * sync.php, zarafa calender to ics im/exporter backend * * Author: Christoph Haas - * Copyright (C) 2012-2014 Christoph Haas + * Copyright (C) 2012-2016 Christoph Haas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -129,7 +129,7 @@ foreach($userList as $userName => $userData) { if($icsData != NULL) { file_put_contents($tmpFilename, $icsData); echo "Got valid data for " . $syncItem["icsurl"] . " stored in " . $tmpFilename . "\n"; - $result = upload_ics_to_caldav($tmpFilename, $CALDAVURL, $userName, $syncItem["calendar"], $ADMINUSERNAME, $ADMINPASSWORD); + $result = upload_ics_to_caldav($tmpFilename, $CALDAVURL, $userName, $syncItem["calendarname"], $ADMINUSERNAME, $ADMINPASSWORD); if(intval($result) == 200) { echo "Import completed: $result\n"; $result = update_last_sync_date($userStore, $syncItemName); From 5b01fae9eb616eaef8a61e0b5f3e77c16d597f60 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Tue, 1 Nov 2016 00:57:04 +0100 Subject: [PATCH 17/17] Updated changelog --- .idea/workspace.xml | 76 ++++++++++++++++++++++++--------------------- changelog.txt | 7 +++++ 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 1494732..6488bc3 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,8 +2,7 @@ - - + @@ -44,16 +43,6 @@ - - - - - - - - - - @@ -64,7 +53,7 @@ - + @@ -76,6 +65,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -116,18 +125,6 @@ - - - - - - - - - - - - @@ -157,6 +154,7 @@ @@ -423,12 +421,12 @@
- @@ -479,14 +477,6 @@ - - - - - - - - @@ -667,5 +657,21 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/changelog.txt b/changelog.txt index 3854a97..1e2a725 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +calendarimporter 2.2.0: + - support for Kopano Webapp 3.1.1 + - Code rework + - Calendar export improved + - Calendar import improved + - GUI improvements + calendarimporter 2.1.0: - ics sync is now implemented