iCalcreator.class.php 428 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458
  1. <?php
  2. /*********************************************************************************/
  3. /**
  4. * iCalcreator v2.16.12
  5. * copyright (c) 2007-2013 Kjell-Inge Gustafsson kigkonsult
  6. * kigkonsult.se/iCalcreator/index.php
  7. * ical@kigkonsult.se
  8. *
  9. * Description:
  10. * This file is a PHP implementation of rfc2445/rfc5545.
  11. *
  12. * This library is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU Lesser General Public
  14. * License as published by the Free Software Foundation; either
  15. * version 2.1 of the License, or (at your option) any later version.
  16. *
  17. * This library is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. * Lesser General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Lesser General Public
  23. * License along with this library; if not, write to the Free Software
  24. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  25. */
  26. /*********************************************************************************/
  27. /*********************************************************************************/
  28. /* A little setup */
  29. /*********************************************************************************/
  30. /* your local language code */
  31. // define( 'ICAL_LANG', 'sv' );
  32. // alt. autosetting
  33. /*
  34. $langstr = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
  35. $pos = strpos( $langstr, ';' );
  36. if ($pos !== false) {
  37. $langstr = substr( $langstr, 0, $pos );
  38. $pos = strpos( $langstr, ',' );
  39. if ($pos !== false) {
  40. $pos = strpos( $langstr, ',' );
  41. $langstr = substr( $langstr, 0, $pos );
  42. }
  43. define( 'ICAL_LANG', $langstr );
  44. }
  45. */
  46. /*********************************************************************************/
  47. /* version, do NOT remove!! */
  48. define( 'ICALCREATOR_VERSION', 'iCalcreator 2.16.12' );
  49. /*********************************************************************************/
  50. /*********************************************************************************/
  51. /**
  52. * vcalendar class
  53. *
  54. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  55. * @since 2.9.6 - 2011-05-14
  56. */
  57. class vcalendar {
  58. // calendar property variables
  59. var $calscale;
  60. var $method;
  61. var $prodid;
  62. var $version;
  63. var $xprop;
  64. // container for calendar components
  65. var $components;
  66. // component config variables
  67. var $allowEmpty;
  68. var $unique_id;
  69. var $language;
  70. var $directory;
  71. var $filename;
  72. var $url;
  73. var $delimiter;
  74. var $nl;
  75. var $format;
  76. var $dtzid;
  77. // component internal variables
  78. var $attributeDelimiter;
  79. var $valueInit;
  80. // component xCal declaration container
  81. var $xcaldecl;
  82. /**
  83. * constructor for calendar object
  84. *
  85. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  86. * @since 2.9.6 - 2011-05-14
  87. * @param array $config
  88. * @return void
  89. */
  90. function vcalendar ( $config = array()) {
  91. $this->_makeVersion();
  92. $this->calscale = null;
  93. $this->method = null;
  94. $this->_makeUnique_id();
  95. $this->prodid = null;
  96. $this->xprop = array();
  97. $this->language = null;
  98. $this->directory = null;
  99. $this->filename = null;
  100. $this->url = null;
  101. $this->dtzid = null;
  102. /**
  103. * language = <Text identifying a language, as defined in [RFC 1766]>
  104. */
  105. if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
  106. $config['language'] = ICAL_LANG;
  107. if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
  108. if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
  109. if( !isset( $config['format'] )) $config['format'] = 'iCal';
  110. if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
  111. $this->setConfig( $config );
  112. $this->xcaldecl = array();
  113. $this->components = array();
  114. }
  115. /*********************************************************************************/
  116. /**
  117. * Property Name: CALSCALE
  118. */
  119. /**
  120. * creates formatted output for calendar property calscale
  121. *
  122. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  123. * @since 2.10.16 - 2011-10-28
  124. * @return string
  125. */
  126. function createCalscale() {
  127. if( empty( $this->calscale )) return FALSE;
  128. switch( $this->format ) {
  129. case 'xcal':
  130. return $this->nl.' calscale="'.$this->calscale.'"';
  131. break;
  132. default:
  133. return 'CALSCALE:'.$this->calscale.$this->nl;
  134. break;
  135. }
  136. }
  137. /**
  138. * set calendar property calscale
  139. *
  140. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  141. * @since 2.4.8 - 2008-10-21
  142. * @param string $value
  143. * @return void
  144. */
  145. function setCalscale( $value ) {
  146. if( empty( $value )) return FALSE;
  147. $this->calscale = $value;
  148. }
  149. /*********************************************************************************/
  150. /**
  151. * Property Name: METHOD
  152. */
  153. /**
  154. * creates formatted output for calendar property method
  155. *
  156. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  157. * @since 2.10.16 - 2011-10-28
  158. * @return string
  159. */
  160. function createMethod() {
  161. if( empty( $this->method )) return FALSE;
  162. switch( $this->format ) {
  163. case 'xcal':
  164. return $this->nl.' method="'.$this->method.'"';
  165. break;
  166. default:
  167. return 'METHOD:'.$this->method.$this->nl;
  168. break;
  169. }
  170. }
  171. /**
  172. * set calendar property method
  173. *
  174. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  175. * @since 2.4.8 - 2008-20-23
  176. * @param string $value
  177. * @return bool
  178. */
  179. function setMethod( $value ) {
  180. if( empty( $value )) return FALSE;
  181. $this->method = $value;
  182. return TRUE;
  183. }
  184. /*********************************************************************************/
  185. /**
  186. * Property Name: PRODID
  187. *
  188. * The identifier is RECOMMENDED to be the identical syntax to the
  189. * [RFC 822] addr-spec. A good method to assure uniqueness is to put the
  190. * domain name or a domain literal IP address of the host on which.. .
  191. */
  192. /**
  193. * creates formatted output for calendar property prodid
  194. *
  195. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  196. * @since 2.12.11 - 2012-05-13
  197. * @return string
  198. */
  199. function createProdid() {
  200. if( !isset( $this->prodid ))
  201. $this->_makeProdid();
  202. switch( $this->format ) {
  203. case 'xcal':
  204. return $this->nl.' prodid="'.$this->prodid.'"';
  205. break;
  206. default:
  207. $toolbox = new calendarComponent();
  208. $toolbox->setConfig( $this->getConfig());
  209. return $toolbox->_createElement( 'PRODID', '', $this->prodid );
  210. break;
  211. }
  212. }
  213. /**
  214. * make default value for calendar prodid
  215. *
  216. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  217. * @since 2.6.8 - 2009-12-30
  218. * @return void
  219. */
  220. function _makeProdid() {
  221. $this->prodid = '-//'.$this->unique_id.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION.'//'.strtoupper( $this->language );
  222. }
  223. /**
  224. * Conformance: The property MUST be specified once in an iCalendar object.
  225. * Description: The vendor of the implementation SHOULD assure that this
  226. * is a globally unique identifier; using some technique such as an FPI
  227. * value, as defined in [ISO 9070].
  228. */
  229. /**
  230. * make default unique_id for calendar prodid
  231. *
  232. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  233. * @since 0.3.0 - 2006-08-10
  234. * @return void
  235. */
  236. function _makeUnique_id() {
  237. $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
  238. }
  239. /*********************************************************************************/
  240. /**
  241. * Property Name: VERSION
  242. *
  243. * Description: A value of "2.0" corresponds to this memo.
  244. */
  245. /**
  246. * creates formatted output for calendar property version
  247. *
  248. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  249. * @since 2.10.16 - 2011-10-28
  250. * @return string
  251. */
  252. function createVersion() {
  253. if( empty( $this->version ))
  254. $this->_makeVersion();
  255. switch( $this->format ) {
  256. case 'xcal':
  257. return $this->nl.' version="'.$this->version.'"';
  258. break;
  259. default:
  260. return 'VERSION:'.$this->version.$this->nl;
  261. break;
  262. }
  263. }
  264. /**
  265. * set default calendar version
  266. *
  267. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  268. * @since 0.3.0 - 2006-08-10
  269. * @return void
  270. */
  271. function _makeVersion() {
  272. $this->version = '2.0';
  273. }
  274. /**
  275. * set calendar version
  276. *
  277. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  278. * @since 2.4.8 - 2008-10-23
  279. * @param string $value
  280. * @return void
  281. */
  282. function setVersion( $value ) {
  283. if( empty( $value )) return FALSE;
  284. $this->version = $value;
  285. return TRUE;
  286. }
  287. /*********************************************************************************/
  288. /**
  289. * Property Name: x-prop
  290. */
  291. /**
  292. * creates formatted output for calendar property x-prop, iCal format only
  293. *
  294. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  295. * @since 2.16.2 - 2012-12-18
  296. * @return string
  297. */
  298. function createXprop() {
  299. if( empty( $this->xprop ) || !is_array( $this->xprop )) return FALSE;
  300. $output = null;
  301. $toolbox = new calendarComponent();
  302. $toolbox->setConfig( $this->getConfig());
  303. foreach( $this->xprop as $label => $xpropPart ) {
  304. if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) {
  305. $output .= $toolbox->_createElement( $label );
  306. continue;
  307. }
  308. $attributes = $toolbox->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
  309. if( is_array( $xpropPart['value'] )) {
  310. foreach( $xpropPart['value'] as $pix => $theXpart )
  311. $xpropPart['value'][$pix] = iCalUtilityFunctions::_strrep( $theXpart, $this->format, $this->nl );
  312. $xpropPart['value'] = implode( ',', $xpropPart['value'] );
  313. }
  314. else
  315. $xpropPart['value'] = iCalUtilityFunctions::_strrep( $xpropPart['value'], $this->format, $this->nl );
  316. $output .= $toolbox->_createElement( $label, $attributes, $xpropPart['value'] );
  317. if( is_array( $toolbox->xcaldecl ) && ( 0 < count( $toolbox->xcaldecl ))) {
  318. foreach( $toolbox->xcaldecl as $localxcaldecl )
  319. $this->xcaldecl[] = $localxcaldecl;
  320. }
  321. }
  322. return $output;
  323. }
  324. /**
  325. * set calendar property x-prop
  326. *
  327. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  328. * @since 2.11.9 - 2012-01-16
  329. * @param string $label
  330. * @param string $value
  331. * @param array $params optional
  332. * @return bool
  333. */
  334. function setXprop( $label, $value, $params=FALSE ) {
  335. if( empty( $label ))
  336. return FALSE;
  337. if( 'X-' != strtoupper( substr( $label, 0, 2 )))
  338. return FALSE;
  339. if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  340. $xprop = array( 'value' => $value );
  341. $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
  342. if( !is_array( $this->xprop )) $this->xprop = array();
  343. $this->xprop[strtoupper( $label )] = $xprop;
  344. return TRUE;
  345. }
  346. /*********************************************************************************/
  347. /**
  348. * delete calendar property value
  349. *
  350. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  351. * @since 2.8.8 - 2011-03-15
  352. * @param mixed $propName, bool FALSE => X-property
  353. * @param int $propix, optional, if specific property is wanted in case of multiply occurences
  354. * @return bool, if successfull delete
  355. */
  356. function deleteProperty( $propName=FALSE, $propix=FALSE ) {
  357. $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
  358. if( !$propix )
  359. $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
  360. $this->propdelix[$propName] = --$propix;
  361. $return = FALSE;
  362. switch( $propName ) {
  363. case 'CALSCALE':
  364. if( isset( $this->calscale )) {
  365. $this->calscale = null;
  366. $return = TRUE;
  367. }
  368. break;
  369. case 'METHOD':
  370. if( isset( $this->method )) {
  371. $this->method = null;
  372. $return = TRUE;
  373. }
  374. break;
  375. default:
  376. $reduced = array();
  377. if( $propName != 'X-PROP' ) {
  378. if( !isset( $this->xprop[$propName] )) { unset( $this->propdelix[$propName] ); return FALSE; }
  379. foreach( $this->xprop as $k => $a ) {
  380. if(( $k != $propName ) && !empty( $a ))
  381. $reduced[$k] = $a;
  382. }
  383. }
  384. else {
  385. if( count( $this->xprop ) <= $propix ) return FALSE;
  386. $xpropno = 0;
  387. foreach( $this->xprop as $xpropkey => $xpropvalue ) {
  388. if( $propix != $xpropno )
  389. $reduced[$xpropkey] = $xpropvalue;
  390. $xpropno++;
  391. }
  392. }
  393. $this->xprop = $reduced;
  394. if( empty( $this->xprop )) {
  395. unset( $this->propdelix[$propName] );
  396. return FALSE;
  397. }
  398. return TRUE;
  399. }
  400. return $return;
  401. }
  402. /**
  403. * get calendar property value/params
  404. *
  405. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  406. * @since 2.13.4 - 2012-08-08
  407. * @param string $propName, optional
  408. * @param int $propix, optional, if specific property is wanted in case of multiply occurences
  409. * @param bool $inclParam=FALSE
  410. * @return mixed
  411. */
  412. function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) {
  413. $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
  414. if( 'X-PROP' == $propName ) {
  415. if( !$propix )
  416. $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
  417. $this->propix[$propName] = --$propix;
  418. }
  419. else
  420. $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' );
  421. switch( $propName ) {
  422. case 'ATTENDEE':
  423. case 'CATEGORIES':
  424. case 'CONTACT':
  425. case 'DTSTART':
  426. case 'GEOLOCATION':
  427. case 'LOCATION':
  428. case 'ORGANIZER':
  429. case 'PRIORITY':
  430. case 'RESOURCES':
  431. case 'STATUS':
  432. case 'SUMMARY':
  433. case 'RECURRENCE-ID-UID':
  434. case 'RELATED-TO':
  435. case 'R-UID':
  436. case 'UID':
  437. case 'URL':
  438. $output = array();
  439. foreach ( $this->components as $cix => $component) {
  440. if( !in_array( $component->objName, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' )))
  441. continue;
  442. if( in_array( strtoupper( $propName ), $mProps )) {
  443. $component->_getProperties( $propName, $output );
  444. continue;
  445. }
  446. elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) {
  447. if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' )))
  448. $content = $component->getProperty( 'UID' );
  449. }
  450. elseif( 'GEOLOCATION' == $propName ) {
  451. $content = $component->getProperty( 'LOCATION' );
  452. $content = ( !empty( $content )) ? $content.' ' : '';
  453. if(( FALSE === ( $geo = $component->getProperty( 'GEO' ))) || empty( $geo ))
  454. continue;
  455. if( 0.0 < $geo['latitude'] )
  456. $sign = '+';
  457. else
  458. $sign = ( 0.0 > $geo['latitude'] ) ? '-' : '';
  459. $content .= ' '.$sign.sprintf( "%09.6f", abs( $geo['latitude'] ));
  460. $content = rtrim( rtrim( $content, '0' ), '.' );
  461. if( 0.0 < $geo['longitude'] )
  462. $sign = '+';
  463. else
  464. $sign = ( 0.0 > $geo['longitude'] ) ? '-' : '';
  465. $content .= $sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/';
  466. }
  467. elseif( FALSE === ( $content = $component->getProperty( $propName )))
  468. continue;
  469. if(( FALSE === $content ) || empty( $content ))
  470. continue;
  471. elseif( is_array( $content )) {
  472. if( isset( $content['year'] )) {
  473. $key = sprintf( '%04d%02d%02d', $content['year'], $content['month'], $content['day'] );
  474. if( !isset( $output[$key] ))
  475. $output[$key] = 1;
  476. else
  477. $output[$key] += 1;
  478. }
  479. else {
  480. foreach( $content as $partValue => $partCount ) {
  481. if( !isset( $output[$partValue] ))
  482. $output[$partValue] = $partCount;
  483. else
  484. $output[$partValue] += $partCount;
  485. }
  486. }
  487. } // end elseif( is_array( $content )) {
  488. elseif( !isset( $output[$content] ))
  489. $output[$content] = 1;
  490. else
  491. $output[$content] += 1;
  492. } // end foreach ( $this->components as $cix => $component)
  493. if( !empty( $output ))
  494. ksort( $output );
  495. return $output;
  496. break;
  497. case 'CALSCALE':
  498. return ( !empty( $this->calscale )) ? $this->calscale : FALSE;
  499. break;
  500. case 'METHOD':
  501. return ( !empty( $this->method )) ? $this->method : FALSE;
  502. break;
  503. case 'PRODID':
  504. if( empty( $this->prodid ))
  505. $this->_makeProdid();
  506. return $this->prodid;
  507. break;
  508. case 'VERSION':
  509. return ( !empty( $this->version )) ? $this->version : FALSE;
  510. break;
  511. default:
  512. if( $propName != 'X-PROP' ) {
  513. if( !isset( $this->xprop[$propName] )) return FALSE;
  514. return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
  515. : array( $propName, $this->xprop[$propName]['value'] );
  516. }
  517. else {
  518. if( empty( $this->xprop )) return FALSE;
  519. $xpropno = 0;
  520. foreach( $this->xprop as $xpropkey => $xpropvalue ) {
  521. if( $propix == $xpropno )
  522. return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
  523. : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
  524. else
  525. $xpropno++;
  526. }
  527. unset( $this->propix[$propName] );
  528. return FALSE; // not found ??
  529. }
  530. }
  531. return FALSE;
  532. }
  533. /**
  534. * general vcalendar property setting
  535. *
  536. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  537. * @since 2.2.13 - 2007-11-04
  538. * @param mixed $args variable number of function arguments,
  539. * first argument is ALWAYS component name,
  540. * second ALWAYS component value!
  541. * @return bool
  542. */
  543. function setProperty () {
  544. $numargs = func_num_args();
  545. if( 1 > $numargs )
  546. return FALSE;
  547. $arglist = func_get_args();
  548. $arglist[0] = strtoupper( $arglist[0] );
  549. switch( $arglist[0] ) {
  550. case 'CALSCALE':
  551. return $this->setCalscale( $arglist[1] );
  552. case 'METHOD':
  553. return $this->setMethod( $arglist[1] );
  554. case 'VERSION':
  555. return $this->setVersion( $arglist[1] );
  556. default:
  557. if( !isset( $arglist[1] )) $arglist[1] = null;
  558. if( !isset( $arglist[2] )) $arglist[2] = null;
  559. return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
  560. }
  561. return FALSE;
  562. }
  563. /*********************************************************************************/
  564. /**
  565. * get vcalendar config values or * calendar components
  566. *
  567. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  568. * @since 2.11.7 - 2012-01-12
  569. * @param mixed $config
  570. * @return value
  571. */
  572. function getConfig( $config = FALSE ) {
  573. if( !$config ) {
  574. $return = array();
  575. $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' );
  576. $return['DELIMITER'] = $this->getConfig( 'DELIMITER' );
  577. $return['DIRECTORY'] = $this->getConfig( 'DIRECTORY' );
  578. $return['FILENAME'] = $this->getConfig( 'FILENAME' );
  579. $return['DIRFILE'] = $this->getConfig( 'DIRFILE' );
  580. $return['FILESIZE'] = $this->getConfig( 'FILESIZE' );
  581. $return['FORMAT'] = $this->getConfig( 'FORMAT' );
  582. if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' )))
  583. $return['LANGUAGE'] = $lang;
  584. $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
  585. $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' );
  586. if( FALSE !== ( $url = $this->getConfig( 'URL' )))
  587. $return['URL'] = $url;
  588. $return['TZID'] = $this->getConfig( 'TZID' );
  589. return $return;
  590. }
  591. switch( strtoupper( $config )) {
  592. case 'ALLOWEMPTY':
  593. return $this->allowEmpty;
  594. break;
  595. case 'COMPSINFO':
  596. unset( $this->compix );
  597. $info = array();
  598. foreach( $this->components as $cix => $component ) {
  599. if( empty( $component )) continue;
  600. $info[$cix]['ordno'] = $cix + 1;
  601. $info[$cix]['type'] = $component->objName;
  602. $info[$cix]['uid'] = $component->getProperty( 'uid' );
  603. $info[$cix]['props'] = $component->getConfig( 'propinfo' );
  604. $info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
  605. }
  606. return $info;
  607. break;
  608. case 'DELIMITER':
  609. return $this->delimiter;
  610. break;
  611. case 'DIRECTORY':
  612. if( empty( $this->directory ) && ( '0' != $this->directory ))
  613. $this->directory = '.';
  614. return $this->directory;
  615. break;
  616. case 'DIRFILE':
  617. return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' );
  618. break;
  619. case 'FILEINFO':
  620. return array( $this->getConfig( 'directory' )
  621. , $this->getConfig( 'filename' )
  622. , $this->getConfig( 'filesize' ));
  623. break;
  624. case 'FILENAME':
  625. if( empty( $this->filename ) && ( '0' != $this->filename )) {
  626. if( 'xcal' == $this->format )
  627. $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. .
  628. else
  629. $this->filename = date( 'YmdHis' ).'.ics';
  630. }
  631. return $this->filename;
  632. break;
  633. case 'FILESIZE':
  634. $size = 0;
  635. if( empty( $this->url )) {
  636. $dirfile = $this->getConfig( 'dirfile' );
  637. if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile ))))
  638. $size = 0;
  639. clearstatcache();
  640. }
  641. return $size;
  642. break;
  643. case 'FORMAT':
  644. return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal';
  645. break;
  646. case 'LANGUAGE':
  647. /* get language for calendar component as defined in [RFC 1766] */
  648. return $this->language;
  649. break;
  650. case 'NL':
  651. case 'NEWLINECHAR':
  652. return $this->nl;
  653. break;
  654. case 'TZID':
  655. return $this->dtzid;
  656. break;
  657. case 'UNIQUE_ID':
  658. return $this->unique_id;
  659. break;
  660. case 'URL':
  661. if( !empty( $this->url ))
  662. return $this->url;
  663. else
  664. return FALSE;
  665. break;
  666. }
  667. }
  668. /**
  669. * general vcalendar config setting
  670. *
  671. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  672. * @since 2.16.7 - 2013-01-11
  673. * @param mixed $config
  674. * @param string $value
  675. * @return void
  676. */
  677. function setConfig( $config, $value = FALSE) {
  678. if( is_array( $config )) {
  679. $ak = array_keys( $config );
  680. foreach( $ak as $k ) {
  681. if( 'DIRECTORY' == strtoupper( $k )) {
  682. if( FALSE === $this->setConfig( 'DIRECTORY', $config[$k] ))
  683. return FALSE;
  684. unset( $config[$k] );
  685. }
  686. elseif( 'NEWLINECHAR' == strtoupper( $k )) {
  687. if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] ))
  688. return FALSE;
  689. unset( $config[$k] );
  690. }
  691. }
  692. foreach( $config as $cKey => $cValue ) {
  693. if( FALSE === $this->setConfig( $cKey, $cValue ))
  694. return FALSE;
  695. }
  696. return TRUE;
  697. }
  698. $res = FALSE;
  699. switch( strtoupper( $config )) {
  700. case 'ALLOWEMPTY':
  701. $this->allowEmpty = $value;
  702. $subcfg = array( 'ALLOWEMPTY' => $value );
  703. $res = TRUE;
  704. break;
  705. case 'DELIMITER':
  706. $this->delimiter = $value;
  707. return TRUE;
  708. break;
  709. case 'DIRECTORY':
  710. $value = trim( $value );
  711. $del = $this->getConfig('delimiter');
  712. if( $del == substr( $value, ( 0 - strlen( $del ))))
  713. $value = substr( $value, 0, ( strlen( $value ) - strlen( $del )));
  714. if( is_dir( $value )) {
  715. /* local directory */
  716. clearstatcache();
  717. $this->directory = $value;
  718. $this->url = null;
  719. return TRUE;
  720. }
  721. else
  722. return FALSE;
  723. break;
  724. case 'FILENAME':
  725. $value = trim( $value );
  726. if( !empty( $this->url )) {
  727. /* remote directory+file -> URL */
  728. $this->filename = $value;
  729. return TRUE;
  730. }
  731. $dirfile = $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$value;
  732. if( file_exists( $dirfile )) {
  733. /* local file exists */
  734. if( is_readable( $dirfile ) || is_writable( $dirfile )) {
  735. clearstatcache();
  736. $this->filename = $value;
  737. return TRUE;
  738. }
  739. else
  740. return FALSE;
  741. }
  742. elseif( is_readable($this->getConfig( 'directory' ) ) || is_writable( $this->getConfig( 'directory' ) )) {
  743. /* read- or writable directory */
  744. $this->filename = $value;
  745. return TRUE;
  746. }
  747. else
  748. return FALSE;
  749. break;
  750. case 'FORMAT':
  751. $value = trim( strtolower( $value ));
  752. if( 'xcal' == $value ) {
  753. $this->format = 'xcal';
  754. $this->attributeDelimiter = $this->nl;
  755. $this->valueInit = null;
  756. }
  757. else {
  758. $this->format = null;
  759. $this->attributeDelimiter = ';';
  760. $this->valueInit = ':';
  761. }
  762. $subcfg = array( 'FORMAT' => $value );
  763. $res = TRUE;
  764. break;
  765. case 'LANGUAGE': // set language for calendar component as defined in [RFC 1766]
  766. $value = trim( $value );
  767. $this->language = $value;
  768. $this->_makeProdid();
  769. $subcfg = array( 'LANGUAGE' => $value );
  770. $res = TRUE;
  771. break;
  772. case 'NL':
  773. case 'NEWLINECHAR':
  774. $this->nl = $value;
  775. if( 'xcal' == $value ) {
  776. $this->attributeDelimiter = $this->nl;
  777. $this->valueInit = null;
  778. }
  779. else {
  780. $this->attributeDelimiter = ';';
  781. $this->valueInit = ':';
  782. }
  783. $subcfg = array( 'NL' => $value );
  784. $res = TRUE;
  785. break;
  786. case 'TZID':
  787. $this->dtzid = $value;
  788. $subcfg = array( 'TZID' => $value );
  789. $res = TRUE;
  790. break;
  791. case 'UNIQUE_ID':
  792. $value = trim( $value );
  793. $this->unique_id = $value;
  794. $this->_makeProdid();
  795. $subcfg = array( 'UNIQUE_ID' => $value );
  796. $res = TRUE;
  797. break;
  798. case 'URL':
  799. /* remote file - URL */
  800. $value = str_replace( array( 'HTTP://', 'WEBCAL://', 'webcal://' ), 'http://', trim( $value ));
  801. if( 'http://' != substr( $value, 0, 7 ))
  802. return FALSE;
  803. $s1 = $this->url;
  804. $this->url = $value;
  805. $s2 = $this->directory;
  806. $this->directory = null;
  807. $parts = pathinfo( $value );
  808. if( FALSE === $this->setConfig( 'filename', $parts['basename'] )) {
  809. $this->url = $s1;
  810. $this->directory = $s2;
  811. return FALSE;
  812. }
  813. else
  814. return TRUE;
  815. break;
  816. default: // any unvalid config key.. .
  817. return TRUE;
  818. }
  819. if( !$res ) return FALSE;
  820. if( isset( $subcfg ) && !empty( $this->components )) {
  821. foreach( $subcfg as $cfgkey => $cfgvalue ) {
  822. foreach( $this->components as $cix => $component ) {
  823. $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE );
  824. if( !$res )
  825. break 2;
  826. $this->components[$cix] = $component->copy(); // PHP4 compliant
  827. }
  828. }
  829. }
  830. return $res;
  831. }
  832. /*********************************************************************************/
  833. /**
  834. * add calendar component to container
  835. *
  836. * alias to setComponent
  837. *
  838. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  839. * @since 1.x.x - 2007-04-24
  840. * @param object $component calendar component
  841. * @return void
  842. */
  843. function addComponent( $component ) {
  844. $this->setComponent( $component );
  845. }
  846. /**
  847. * delete calendar component from container
  848. *
  849. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  850. * @since 2.8.8 - 2011-03-15
  851. * @param mixed $arg1 ordno / component type / component uid
  852. * @param mixed $arg2 optional, ordno if arg1 = component type
  853. * @return void
  854. */
  855. function deleteComponent( $arg1, $arg2=FALSE ) {
  856. $argType = $index = null;
  857. if ( ctype_digit( (string) $arg1 )) {
  858. $argType = 'INDEX';
  859. $index = (int) $arg1 - 1;
  860. }
  861. elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
  862. $argType = strtolower( $arg1 );
  863. $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
  864. }
  865. $cix1dC = 0;
  866. foreach ( $this->components as $cix => $component) {
  867. if( empty( $component )) continue;
  868. if(( 'INDEX' == $argType ) && ( $index == $cix )) {
  869. unset( $this->components[$cix] );
  870. return TRUE;
  871. }
  872. elseif( $argType == $component->objName ) {
  873. if( $index == $cix1dC ) {
  874. unset( $this->components[$cix] );
  875. return TRUE;
  876. }
  877. $cix1dC++;
  878. }
  879. elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
  880. unset( $this->components[$cix] );
  881. return TRUE;
  882. }
  883. }
  884. return FALSE;
  885. }
  886. /**
  887. * get calendar component from container
  888. *
  889. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  890. * @since 2.13.5 - 2012-08-08
  891. * @param mixed $arg1 optional, ordno/component type/ component uid
  892. * @param mixed $arg2 optional, ordno if arg1 = component type
  893. * @return object
  894. */
  895. function getComponent( $arg1=FALSE, $arg2=FALSE ) {
  896. $index = $argType = null;
  897. if ( !$arg1 ) { // first or next in component chain
  898. $argType = 'INDEX';
  899. $index = $this->compix['INDEX'] = ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
  900. }
  901. elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain
  902. $argType = 'INDEX';
  903. $index = (int) $arg1;
  904. unset( $this->compix );
  905. }
  906. elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
  907. $arg2 = implode( '-', array_keys( $arg1 ));
  908. $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1;
  909. $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' );
  910. $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' );
  911. $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' );
  912. }
  913. elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name
  914. unset( $this->compix['INDEX'] );
  915. $argType = strtolower( $arg1 );
  916. if( !$arg2 )
  917. $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
  918. elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
  919. $index = (int) $arg2;
  920. }
  921. elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument
  922. if( !$arg2 )
  923. $index = $this->compix[$arg1] = ( isset( $this->compix[$arg1] )) ? $this->compix[$arg1] + 1 : 1;
  924. elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
  925. $index = (int) $arg2;
  926. }
  927. if( isset( $index ))
  928. $index -= 1;
  929. $ckeys = array_keys( $this->components );
  930. if( !empty( $index) && ( $index > end( $ckeys )))
  931. return FALSE;
  932. $cix1gC = 0;
  933. foreach ( $this->components as $cix => $component) {
  934. if( empty( $component )) continue;
  935. if(( 'INDEX' == $argType ) && ( $index == $cix ))
  936. return $component->copy();
  937. elseif( $argType == $component->objName ) {
  938. if( $index == $cix1gC )
  939. return $component->copy();
  940. $cix1gC++;
  941. }
  942. elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
  943. $hit = array();
  944. foreach( $arg1 as $pName => $pValue ) {
  945. $pName = strtoupper( $pName );
  946. if( !in_array( $pName, $dateProps ) && !in_array( $pName, $otherProps ))
  947. continue;
  948. if( in_array( $pName, $mProps )) { // multiple occurrence
  949. $propValues = array();
  950. $component->_getProperties( $pName, $propValues );
  951. $propValues = array_keys( $propValues );
  952. $hit[] = ( in_array( $pValue, $propValues )) ? TRUE : FALSE;
  953. continue;
  954. } // end if(.. .// multiple occurrence
  955. if( FALSE === ( $value = $component->getProperty( $pName ))) { // single occurrence
  956. $hit[] = FALSE; // missing property
  957. continue;
  958. }
  959. if( 'SUMMARY' == $pName ) { // exists within (any case)
  960. $hit[] = ( FALSE !== stripos( $value, $pValue )) ? TRUE : FALSE;
  961. continue;
  962. }
  963. if( in_array( strtoupper( $pName ), $dateProps )) {
  964. $valuedate = sprintf( '%04d%02d%02d', $value['year'], $value['month'], $value['day'] );
  965. if( 8 < strlen( $pValue )) {
  966. if( isset( $value['hour'] )) {
  967. if( 'T' == substr( $pValue, 8, 1 ))
  968. $pValue = str_replace( 'T', '', $pValue );
  969. $valuedate .= sprintf( '%02d%02d%02d', $value['hour'], $value['min'], $value['sec'] );
  970. }
  971. else
  972. $pValue = substr( $pValue, 0, 8 );
  973. }
  974. $hit[] = ( $pValue == $valuedate ) ? TRUE : FALSE;
  975. continue;
  976. }
  977. elseif( !is_array( $value ))
  978. $value = array( $value );
  979. foreach( $value as $part ) {
  980. $part = ( FALSE !== strpos( $part, ',' )) ? explode( ',', $part ) : array( $part );
  981. foreach( $part as $subPart ) {
  982. if( $pValue == $subPart ) {
  983. $hit[] = TRUE;
  984. continue 3;
  985. }
  986. }
  987. } // end foreach( $value as $part )
  988. $hit[] = FALSE; // no hit in property
  989. } // end foreach( $arg1 as $pName => $pValue )
  990. if( in_array( TRUE, $hit )) {
  991. if( $index == $cix1gC )
  992. return $component->copy();
  993. $cix1gC++;
  994. }
  995. } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
  996. elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { // UID
  997. if( $index == $cix1gC )
  998. return $component->copy();
  999. $cix1gC++;
  1000. }
  1001. } // end foreach ( $this->components.. .
  1002. /* not found.. . */
  1003. unset( $this->compix );
  1004. return FALSE;
  1005. }
  1006. /**
  1007. * create new calendar component, already included within calendar
  1008. *
  1009. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1010. * @since 2.6.33 - 2011-01-03
  1011. * @param string $compType component type
  1012. * @return object (reference)
  1013. */
  1014. function & newComponent( $compType ) {
  1015. $config = $this->getConfig();
  1016. $keys = array_keys( $this->components );
  1017. $ix = end( $keys) + 1;
  1018. switch( strtoupper( $compType )) {
  1019. case 'EVENT':
  1020. case 'VEVENT':
  1021. $this->components[$ix] = new vevent( $config );
  1022. break;
  1023. case 'TODO':
  1024. case 'VTODO':
  1025. $this->components[$ix] = new vtodo( $config );
  1026. break;
  1027. case 'JOURNAL':
  1028. case 'VJOURNAL':
  1029. $this->components[$ix] = new vjournal( $config );
  1030. break;
  1031. case 'FREEBUSY':
  1032. case 'VFREEBUSY':
  1033. $this->components[$ix] = new vfreebusy( $config );
  1034. break;
  1035. case 'TIMEZONE':
  1036. case 'VTIMEZONE':
  1037. array_unshift( $this->components, new vtimezone( $config ));
  1038. $ix = 0;
  1039. break;
  1040. default:
  1041. return FALSE;
  1042. }
  1043. return $this->components[$ix];
  1044. }
  1045. /**
  1046. * select components from calendar on date or selectOption basis
  1047. *
  1048. * Ensure DTSTART is set for every component.
  1049. * No date controls occurs.
  1050. *
  1051. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1052. * @since 2.16.12 - 2013-02-10
  1053. * @param mixed $startY optional, start Year, default current Year ALT. array selecOptions ( *[ <propName> => <uniqueValue> ] )
  1054. * @param int $startM optional, start Month, default current Month
  1055. * @param int $startD optional, start Day, default current Day
  1056. * @param int $endY optional, end Year, default $startY
  1057. * @param int $endY optional, end Month, default $startM
  1058. * @param int $endY optional, end Day, default $startD
  1059. * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)
  1060. * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][]
  1061. * TRUE => output : array[] (ignores split)
  1062. * @param bool $any optional, TRUE (default) - select component(-s) that occurs within period
  1063. * FALSE - only component(-s) that starts within period
  1064. * @param bool $split optional, TRUE (default) - one component copy every DAY it occurs during the
  1065. * period (implies flat=FALSE)
  1066. * FALSE - one occurance of component only in output array
  1067. * @return array or FALSE
  1068. */
  1069. function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) {
  1070. /* check if empty calendar */
  1071. if( 0 >= count( $this->components )) return FALSE;
  1072. if( is_array( $startY ))
  1073. return $this->selectComponents2( $startY );
  1074. /* check default dates */
  1075. if( !$startY ) $startY = date( 'Y' );
  1076. if( !$startM ) $startM = date( 'm' );
  1077. if( !$startD ) $startD = date( 'd' );
  1078. $startDate = mktime( 0, 0, 0, $startM, $startD, $startY );
  1079. if( !$endY ) $endY = $startY;
  1080. if( !$endM ) $endM = $startM;
  1081. if( !$endD ) $endD = $startD;
  1082. $endDate = mktime( 23, 59, 59, $endM, $endD, $endY );
  1083. // echo 'selectComp arg='.date( 'Y-m-d H:i:s', $startDate).' -- '.date( 'Y-m-d H:i:s', $endDate)."<br />\n"; $tcnt = 0;// test ###
  1084. /* check component types */
  1085. $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' );
  1086. if( is_array( $cType )) {
  1087. foreach( $cType as $cix => $theType ) {
  1088. $cType[$cix] = $theType = strtolower( $theType );
  1089. if( !in_array( $theType, $validTypes ))
  1090. $cType[$cix] = 'vevent';
  1091. }
  1092. $cType = array_unique( $cType );
  1093. }
  1094. elseif( !empty( $cType )) {
  1095. $cType = strtolower( $cType );
  1096. if( !in_array( $cType, $validTypes ))
  1097. $cType = array( 'vevent' );
  1098. else
  1099. $cType = array( $cType );
  1100. }
  1101. else
  1102. $cType = $validTypes;
  1103. if( 0 >= count( $cType ))
  1104. $cType = $validTypes;
  1105. if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination
  1106. $split = FALSE;
  1107. if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination
  1108. $split = FALSE;
  1109. /* iterate components */
  1110. $result = array();
  1111. $this->sort( 'UID' );
  1112. $compUIDcmp = null;
  1113. $recurridList = array();
  1114. foreach ( $this->components as $cix => $component ) {
  1115. if( empty( $component )) continue;
  1116. unset( $start );
  1117. /* deselect unvalid type components */
  1118. if( !in_array( $component->objName, $cType ))
  1119. continue;
  1120. $start = $component->getProperty( 'dtstart' );
  1121. /* select due when dtstart is missing */
  1122. if( empty( $start ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due' ))))
  1123. continue;
  1124. if( empty( $start ))
  1125. continue;
  1126. $compUID = $component->getProperty( 'UID' );
  1127. if( $compUIDcmp != $compUID ) {
  1128. $compUIDcmp = $compUID;
  1129. unset( $exdatelist, $recurridList );
  1130. }
  1131. $dtendExist = $dueExist = $durationExist = $endAllDayEvent = $recurrid = FALSE;
  1132. unset( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $workstart, $workend, $endDateFormat ); // clean up
  1133. $startWdate = iCalUtilityFunctions::_date2timestamp( $start );
  1134. $startDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
  1135. /* get end date from dtend/due/duration properties */
  1136. $end = $component->getProperty( 'dtend' );
  1137. if( !empty( $end )) {
  1138. $dtendExist = TRUE;
  1139. $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
  1140. }
  1141. if( empty( $end ) && ( $component->objName == 'vtodo' )) {
  1142. $end = $component->getProperty( 'due' );
  1143. if( !empty( $end )) {
  1144. $dueExist = TRUE;
  1145. $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
  1146. }
  1147. }
  1148. if( !empty( $end ) && !isset( $end['hour'] )) {
  1149. /* a DTEND without time part regards an event that ends the day before,
  1150. for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
  1151. $endAllDayEvent = TRUE;
  1152. $endWdate = mktime( 23, 59, 59, $end['month'], ($end['day'] - 1), $end['year'] );
  1153. $end['year'] = date( 'Y', $endWdate );
  1154. $end['month'] = date( 'm', $endWdate );
  1155. $end['day'] = date( 'd', $endWdate );
  1156. $end['hour'] = 23;
  1157. $end['min'] = $end['sec'] = 59;
  1158. }
  1159. if( empty( $end )) {
  1160. $end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format
  1161. if( !empty( $end ))
  1162. $durationExist = TRUE;
  1163. $endDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
  1164. // if( !empty($end)) echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
  1165. }
  1166. if( empty( $end )) { // assume one day duration if missing end date
  1167. $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
  1168. }
  1169. // if( isset($end)) echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
  1170. $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
  1171. if( $endWdate < $startWdate ) { // MUST be after start date!!
  1172. $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
  1173. $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
  1174. }
  1175. $rdurWsecs = $endWdate - $startWdate; // compute event (component) duration in seconds
  1176. /* make a list of optional exclude dates for component occurence from exrule and exdate */
  1177. $exdatelist = array();
  1178. $workstart = iCalUtilityFunctions::_timestamp2date(( $startDate - $rdurWsecs ), 6);
  1179. $workend = iCalUtilityFunctions::_timestamp2date(( $endDate + $rdurWsecs ), 6);
  1180. while( FALSE !== ( $exrule = $component->getProperty( 'exrule' ))) // check exrule
  1181. iCalUtilityFunctions::_recur2date( $exdatelist, $exrule, $start, $workstart, $workend );
  1182. while( FALSE !== ( $exdate = $component->getProperty( 'exdate' ))) { // check exdate
  1183. foreach( $exdate as $theExdate ) {
  1184. $exWdate = iCalUtilityFunctions::_date2timestamp( $theExdate );
  1185. $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate )); // on a day-basis !!!
  1186. if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate ))
  1187. $exdatelist[$exWdate] = TRUE;
  1188. } // end - foreach( $exdate as $theExdate )
  1189. } // end - check exdate
  1190. /* check recurrence-id (note, a missing sequence=0, don't test foer sequence), remove hit with reccurr-id date */
  1191. if( FALSE !== ( $recurrid = $component->getProperty( 'recurrence-id' ))) {
  1192. // echo "adding ".$recurrid['year'].'-'.$recurrid['month'].'-'.$recurrid['day']." to recurridList<br>\n"; // test ###
  1193. $recurrid = iCalUtilityFunctions::_date2timestamp( $recurrid );
  1194. $recurrid = mktime( 0, 0, 0, date( 'm', $recurrid ), date( 'd', $recurrid ), date( 'Y', $recurrid )); // on a day-basis !!!
  1195. $recurridList[$recurrid] = TRUE; // no recurring to start this day
  1196. } // end recurrence-id/sequence test
  1197. /* select only components with.. . */
  1198. if(( !$any && ( $startWdate >= $startDate ) && ( $startWdate <= $endDate )) || // (dt)start within the period
  1199. ( $any && ( $startWdate < $endDate ) && ( $endWdate >= $startDate ))) { // occurs within the period
  1200. /* add the selected component (WITHIN valid dates) to output array */
  1201. if( $flat ) { // any=true/false, ignores split
  1202. if( !$recurrid )
  1203. $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id)
  1204. }
  1205. elseif( $split ) { // split the original component
  1206. if( $endWdate > $endDate )
  1207. $endWdate = $endDate; // use period end date
  1208. $rstart = $startWdate;
  1209. if( $rstart < $startDate )
  1210. $rstart = $startDate; // use period start date
  1211. $startYMD = $rstartYMD = date( 'Ymd', $rstart );
  1212. $endYMD = date( 'Ymd', $endWdate );
  1213. $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
  1214. // echo "start org comp = $rstartYMD, endYMD=$endYMD<br />\n"; // test ###
  1215. if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
  1216. while( $rstartYMD <= $endYMD ) { // iterate
  1217. if( isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist
  1218. $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
  1219. $rstartYMD = date( 'Ymd', $rstart );
  1220. continue;
  1221. }
  1222. if( $rstartYMD > $startYMD ) // date after dtstart
  1223. $datestring = date( $startDateFormat, $checkDate ); // mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart )));
  1224. else
  1225. $datestring = date( $startDateFormat, $rstart );
  1226. if( isset( $start['tz'] ))
  1227. $datestring .= ' '.$start['tz'];
  1228. // echo "split org comp rstartYMD=$rstartYMD (datestring=$datestring)<br />\n"; // test ###
  1229. $component->setProperty( 'X-CURRENT-DTSTART', $datestring );
  1230. if( $dtendExist || $dueExist || $durationExist ) {
  1231. if( $rstartYMD < $endYMD ) // not the last day
  1232. $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ));
  1233. else
  1234. $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
  1235. if( $endAllDayEvent && $dtendExist )
  1236. $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day
  1237. $datestring = date( $endDateFormat, $tend );
  1238. if( isset( $end['tz'] ))
  1239. $datestring .= ' '.$end['tz'];
  1240. $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
  1241. $component->setProperty( $propName, $datestring );
  1242. } // end if( $dtendExist || $dueExist || $durationExist )
  1243. $wd = getdate( $rstart );
  1244. $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output
  1245. $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
  1246. $rstartYMD = date( 'Ymd', $rstart );
  1247. $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
  1248. } // end while( $rstart <= $endWdate )
  1249. }
  1250. } // end elseif( $split ) - else use component date
  1251. elseif( $recurrid && !$flat && !$any && !$split )
  1252. $continue = TRUE;
  1253. else { // !$flat && !$split, i.e. no flat array and DTSTART within period
  1254. $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!!
  1255. if( !$any || !isset( $exdatelist[$checkDate] )) { // exclude any recurrence date, found in exdatelist
  1256. $wd = getdate( $startWdate );
  1257. $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output
  1258. }
  1259. }
  1260. } // end if(( $startWdate >= $startDate ) && ( $startWdate <= $endDate ))
  1261. /* if 'any' components, check components with reccurrence rules, removing all excluding dates */
  1262. if( TRUE === $any ) {
  1263. /* make a list of optional repeating dates for component occurence, rrule, rdate */
  1264. $recurlist = array();
  1265. while( FALSE !== ( $rrule = $component->getProperty( 'rrule' ))) // check rrule
  1266. iCalUtilityFunctions::_recur2date( $recurlist, $rrule, $start, $workstart, $workend );
  1267. foreach( $recurlist as $recurkey => $recurvalue ) // key=match date as timestamp
  1268. $recurlist[$recurkey] = $rdurWsecs; // add duration in seconds
  1269. while( FALSE !== ( $rdate = $component->getProperty( 'rdate' ))) { // check rdate
  1270. foreach( $rdate as $theRdate ) {
  1271. if( is_array( $theRdate ) && ( 2 == count( $theRdate )) && // all days within PERIOD
  1272. array_key_exists( '0', $theRdate ) && array_key_exists( '1', $theRdate )) {
  1273. $rstart = iCalUtilityFunctions::_date2timestamp( $theRdate[0] );
  1274. if(( $rstart < ( $startDate - $rdurWsecs )) || ( $rstart > $endDate ))
  1275. continue;
  1276. if( isset( $theRdate[1]['year'] )) // date-date period
  1277. $rend = iCalUtilityFunctions::_date2timestamp( $theRdate[1] );
  1278. else { // date-duration period
  1279. $rend = iCalUtilityFunctions::_duration2date( $theRdate[0], $theRdate[1] );
  1280. $rend = iCalUtilityFunctions::_date2timestamp( $rend );
  1281. }
  1282. while( $rstart < $rend ) {
  1283. $recurlist[$rstart] = $rdurWsecs; // set start date for recurrence instance + rdate duration in seconds
  1284. $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
  1285. }
  1286. } // PERIOD end
  1287. else { // single date
  1288. $theRdate = iCalUtilityFunctions::_date2timestamp( $theRdate );
  1289. if((( $startDate - $rdurWsecs ) <= $theRdate ) && ( $endDate >= $theRdate ))
  1290. $recurlist[$theRdate] = $rdurWsecs; // set start date for recurrence instance + event duration in seconds
  1291. }
  1292. }
  1293. } // end - check rdate
  1294. foreach( $recurlist as $recurkey => $durvalue ) { // remove all recurrence START dates found in the exdatelist
  1295. $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!!
  1296. if( isset( $exdatelist[$checkDate] )) // no recurring to start this day
  1297. unset( $recurlist[$recurkey] );
  1298. }
  1299. if( 0 < count( $recurlist )) {
  1300. ksort( $recurlist );
  1301. $xRecurrence = 1;
  1302. $component2 = $component->copy();
  1303. $compUID = $component2->getProperty( 'UID' );
  1304. foreach( $recurlist as $recurkey => $durvalue ) {
  1305. // echo "recurKey=".date( 'Y-m-d H:i:s', $recurkey ).' dur='.iCalUtilityFunctions::offsetSec2His( $durvalue )."<br />\n"; // test ###;
  1306. if((( $startDate - $rdurWsecs ) > $recurkey ) || ( $endDate < $recurkey )) // not within period
  1307. continue;
  1308. $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!!
  1309. if( isset( $recurridList[$checkDate] )) // no recurring to start this day
  1310. continue;
  1311. if( isset( $exdatelist[$checkDate] )) // check excluded dates
  1312. continue;
  1313. if( $startWdate >= $recurkey ) // exclude component start date
  1314. continue;
  1315. $rstart = $recurkey;
  1316. $rend = $recurkey + $durvalue;
  1317. /* add repeating components within valid dates to output array, only start date set */
  1318. if( $flat ) {
  1319. if( !isset( $result[$compUID] )) // only one comp
  1320. $result[$compUID] = $component2->copy(); // copy to output
  1321. }
  1322. /* add repeating components within valid dates to output array, one each day */
  1323. elseif( $split ) {
  1324. $xRecurrence += 1;
  1325. if( $rend > $endDate )
  1326. $rend = $endDate;
  1327. $startYMD = $rstartYMD = date( 'Ymd', $rstart );
  1328. $endYMD = date( 'Ymd', $rend );
  1329. // echo "splitStart=".date( 'Y-m-d H:i:s', $rstart ).' end='.date( 'Y-m-d H:i:s', $rend )."<br />\n"; // test ###;
  1330. while( $rstart <= $rend ) { // iterate.. .
  1331. $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
  1332. if( isset( $recurridList[$checkDate] )) // no recurring to start this day
  1333. break;
  1334. if( isset( $exdatelist[$checkDate] )) // exclude any recurrence START date, found in exdatelist
  1335. break;
  1336. // echo "checking date after startdate=".date( 'Y-m-d H:i:s', $rstart ).' mot '.date( 'Y-m-d H:i:s', $startDate )."<br />"; // test ###;
  1337. if( $rstart >= $startDate ) { // date after dtstart
  1338. if( $rstartYMD > $startYMD ) // date after dtstart
  1339. $datestring = date( $startDateFormat, $checkDate );
  1340. else
  1341. $datestring = date( $startDateFormat, $rstart );
  1342. if( isset( $start['tz'] ))
  1343. $datestring .= ' '.$start['tz'];
  1344. // echo "spliting = $datestring<BR>\n"; // test ###
  1345. $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
  1346. if( $dtendExist || $dueExist || $durationExist ) {
  1347. if( $rstartYMD < $endYMD ) // not the last day
  1348. $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ));
  1349. else
  1350. $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
  1351. if( $endAllDayEvent && $dtendExist )
  1352. $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day
  1353. $datestring = date( $endDateFormat, $tend );
  1354. if( isset( $end['tz'] ))
  1355. $datestring .= ' '.$end['tz'];
  1356. $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
  1357. $component2->setProperty( $propName, $datestring );
  1358. } // end if( $dtendExist || $dueExist || $durationExist )
  1359. $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
  1360. $wd = getdate( $rstart );
  1361. $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output
  1362. } // end if( $checkDate > $startYMD ) { // date after dtstart
  1363. $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
  1364. $rstartYMD = date( 'Ymd', $rstart );
  1365. } // end while( $rstart <= $rend )
  1366. } // end elseif( $split )
  1367. elseif( $rstart >= $startDate ) { // date within period //* flat=FALSE && split=FALSE => one comp every recur startdate *//
  1368. $xRecurrence += 1;
  1369. $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
  1370. if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
  1371. $datestring = date( $startDateFormat, $rstart );
  1372. if( isset( $start['tz'] ))
  1373. $datestring .= ' '.$start['tz'];
  1374. //echo "X-CURRENT-DTSTART 2 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component2->setProperty( 'X-CNT', $tcnt ); // test ###
  1375. $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
  1376. if( $dtendExist || $dueExist || $durationExist ) {
  1377. $tend = $rstart + $rdurWsecs;
  1378. if( date( 'Ymd', $tend ) < date( 'Ymd', $endWdate ))
  1379. $tend = mktime( 23, 59, 59, date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend ));
  1380. else
  1381. $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $tend ), date( 'd', $tend ), date( 'Y', $tend ) ); // on a day-basis !!!
  1382. if( $endAllDayEvent && $dtendExist )
  1383. $tend += ( 24 * 3600 ); // alldaysevents has an end date 'day after' meaning this day
  1384. $datestring = date( $endDateFormat, $tend );
  1385. if( isset( $end['tz'] ))
  1386. $datestring .= ' '.$end['tz'];
  1387. $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
  1388. $component2->setProperty( $propName, $datestring );
  1389. } // end if( $dtendExist || $dueExist || $durationExist )
  1390. $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
  1391. $wd = getdate( $rstart );
  1392. $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component2->copy(); // copy to output
  1393. } // end if( !isset( $exdatelist[$checkDate] ))
  1394. } // end elseif( $rstart >= $startDate )
  1395. } // end foreach( $recurlist as $recurkey => $durvalue )
  1396. unset( $component2 );
  1397. } // end if( 0 < count( $recurlist ))
  1398. /* deselect components with startdate/enddate not within period */
  1399. if(( $endWdate < $startDate ) || ( $startWdate > $endDate ))
  1400. continue;
  1401. } // end if( TRUE === $any )
  1402. } // end foreach ( $this->components as $cix => $component )
  1403. unset( $dtendExist, $dueExist, $durationExist, $endAllDayEvent, $recurrid, $recurridList,
  1404. $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $recurlist, $workstart, $workend, $endDateFormat ); // clean up
  1405. if( 0 >= count( $result )) return FALSE;
  1406. elseif( !$flat ) {
  1407. foreach( $result as $y => $yeararr ) {
  1408. foreach( $yeararr as $m => $montharr ) {
  1409. foreach( $montharr as $d => $dayarr ) {
  1410. if( empty( $result[$y][$m][$d] ))
  1411. unset( $result[$y][$m][$d] );
  1412. else
  1413. $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index, hoping they are in hour order.. .
  1414. }
  1415. if( empty( $result[$y][$m] ))
  1416. unset( $result[$y][$m] );
  1417. else
  1418. ksort( $result[$y][$m] );
  1419. }
  1420. if( empty( $result[$y] ))
  1421. unset( $result[$y] );
  1422. else
  1423. ksort( $result[$y] );
  1424. }
  1425. if( empty( $result ))
  1426. unset( $result );
  1427. else
  1428. ksort( $result );
  1429. } // end elseif( !$flat )
  1430. if( 0 >= count( $result ))
  1431. return FALSE;
  1432. return $result;
  1433. }
  1434. /**
  1435. * select components from calendar on based on specific property value(-s)
  1436. *
  1437. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1438. * @since 2.16.6 - 2012-12-26
  1439. * @param array $selectOptions, (string) key => (mixed) value, (key=propertyName)
  1440. * @return array
  1441. */
  1442. function selectComponents2( $selectOptions ) {
  1443. $output = array();
  1444. $allowedComps = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' );
  1445. $allowedProperties = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' );
  1446. foreach( $this->components as $cix => $component3 ) {
  1447. if( !in_array( $component3->objName, $allowedComps ))
  1448. continue;
  1449. $uid = $component3->getProperty( 'UID' );
  1450. foreach( $selectOptions as $propName => $pvalue ) {
  1451. $propName = strtoupper( $propName );
  1452. if( !in_array( $propName, $allowedProperties ))
  1453. continue;
  1454. if( !is_array( $pvalue ))
  1455. $pvalue = array( $pvalue );
  1456. if(( 'UID' == $propName ) && in_array( $uid, $pvalue )) {
  1457. $output[$uid][] = $component3->copy();
  1458. continue;
  1459. }
  1460. elseif(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'CONTACT' == $propName ) || ( 'RELATED-TO' == $propName ) || ( 'RESOURCES' == $propName )) { // multiple occurrence?
  1461. $propValues = array();
  1462. $component3->_getProperties( $propName, $propValues );
  1463. $propValues = array_keys( $propValues );
  1464. foreach( $pvalue as $theValue ) {
  1465. if( in_array( $theValue, $propValues )) { // && !isset( $output[$uid] )) {
  1466. $output[$uid][] = $component3->copy();
  1467. break;
  1468. }
  1469. }
  1470. continue;
  1471. } // end elseif( // multiple occurrence?
  1472. elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single occurrence
  1473. continue;
  1474. if( is_array( $d )) {
  1475. foreach( $d as $part ) {
  1476. if( in_array( $part, $pvalue ) && !isset( $output[$uid] ))
  1477. $output[$uid][] = $component3->copy();
  1478. }
  1479. }
  1480. elseif(( 'SUMMARY' == $propName ) && !isset( $output[$uid] )) {
  1481. foreach( $pvalue as $pval ) {
  1482. if( FALSE !== stripos( $d, $pval )) {
  1483. $output[$uid][] = $component3->copy();
  1484. break;
  1485. }
  1486. }
  1487. }
  1488. elseif( in_array( $d, $pvalue ) && !isset( $output[$uid] ))
  1489. $output[$uid][] = $component3->copy();
  1490. } // end foreach( $selectOptions as $propName => $pvalue ) {
  1491. } // end foreach( $this->components as $cix => $component3 ) {
  1492. if( !empty( $output )) {
  1493. ksort( $output ); // uid order
  1494. $output2 = array();
  1495. foreach( $output as $uid => $components ) {
  1496. foreach( $components as $component )
  1497. $output2[] = $component;
  1498. }
  1499. $output = $output2;
  1500. }
  1501. return $output;
  1502. }
  1503. /**
  1504. * add calendar component to container
  1505. *
  1506. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1507. * @since 2.8.8 - 2011-03-15
  1508. * @param object $component calendar component
  1509. * @param mixed $arg1 optional, ordno/component type/ component uid
  1510. * @param mixed $arg2 optional, ordno if arg1 = component type
  1511. * @return void
  1512. */
  1513. function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) {
  1514. $component->setConfig( $this->getConfig(), FALSE, TRUE );
  1515. if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) {
  1516. /* make sure dtstamp and uid is set */
  1517. $dummy1 = $component->getProperty( 'dtstamp' );
  1518. $dummy2 = $component->getProperty( 'uid' );
  1519. }
  1520. if( !$arg1 ) { // plain insert, last in chain
  1521. $this->components[] = $component->copy();
  1522. return TRUE;
  1523. }
  1524. $argType = $index = null;
  1525. if ( ctype_digit( (string) $arg1 )) { // index insert/replace
  1526. $argType = 'INDEX';
  1527. $index = (int) $arg1 - 1;
  1528. }
  1529. elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
  1530. $argType = strtolower( $arg1 );
  1531. $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
  1532. }
  1533. // else if arg1 is set, arg1 must be an UID
  1534. $cix1sC = 0;
  1535. foreach ( $this->components as $cix => $component2) {
  1536. if( empty( $component2 )) continue;
  1537. if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
  1538. $this->components[$cix] = $component->copy();
  1539. return TRUE;
  1540. }
  1541. elseif( $argType == $component2->objName ) { // component Type index insert/replace
  1542. if( $index == $cix1sC ) {
  1543. $this->components[$cix] = $component->copy();
  1544. return TRUE;
  1545. }
  1546. $cix1sC++;
  1547. }
  1548. elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
  1549. $this->components[$cix] = $component->copy();
  1550. return TRUE;
  1551. }
  1552. }
  1553. /* arg1=index and not found.. . insert at index .. .*/
  1554. if( 'INDEX' == $argType ) {
  1555. $this->components[$index] = $component->copy();
  1556. ksort( $this->components, SORT_NUMERIC );
  1557. }
  1558. else /* not found.. . insert last in chain anyway .. .*/
  1559. $this->components[] = $component->copy();
  1560. return TRUE;
  1561. }
  1562. /**
  1563. * sort iCal compoments
  1564. *
  1565. * ascending sort on properties (if exist) x-current-dtstart, dtstart,
  1566. * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid if called without arguments,
  1567. * otherwise sorting on specific (argument) property values
  1568. *
  1569. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1570. * @since 2.16.4 - 2012-12-17
  1571. * @param string $sortArg, optional
  1572. * @return void
  1573. *
  1574. */
  1575. function sort( $sortArg=FALSE ) {
  1576. if( is_array( $this->components )) {
  1577. if( $sortArg ) {
  1578. $sortArg = strtoupper( $sortArg );
  1579. if( !in_array( $sortArg, array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'DTSTAMP', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' )))
  1580. $sortArg = FALSE;
  1581. }
  1582. /* set sort parameters for each component */
  1583. foreach( $this->components as $cix => & $c ) {
  1584. $c->srtk = array( '0', '0', '0', '0' );
  1585. if( 'vtimezone' == $c->objName ) {
  1586. if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' )))
  1587. $c->srtk[0] = 0;
  1588. continue;
  1589. }
  1590. elseif( $sortArg ) {
  1591. if(( 'ATTENDEE' == $sortArg ) || ( 'CATEGORIES' == $sortArg ) || ( 'CONTACT' == $sortArg ) || ( 'RELATED-TO' == $sortArg ) || ( 'RESOURCES' == $sortArg )) {
  1592. $propValues = array();
  1593. $c->_getProperties( $sortArg, $propValues );
  1594. if( !empty( $propValues )) {
  1595. $sk = array_keys( $propValues );
  1596. $c->srtk[0] = $sk[0];
  1597. if( 'RELATED-TO' == $sortArg )
  1598. $c->srtk[0] .= $c->getProperty( 'uid' );
  1599. }
  1600. elseif( 'RELATED-TO' == $sortArg )
  1601. $c->srtk[0] = $c->getProperty( 'uid' );
  1602. }
  1603. elseif( FALSE !== ( $d = $c->getProperty( $sortArg ))) {
  1604. $c->srtk[0] = $d;
  1605. if( 'UID' == $sortArg ) {
  1606. if( FALSE !== ( $d = $c->getProperty( 'recurrence-id' ))) {
  1607. $c->srtk[1] = iCalUtilityFunctions::_date2strdate( $d );
  1608. if( FALSE === ( $c->srtk[2] = $c->getProperty( 'sequence' )))
  1609. $c->srtk[2] = PHP_INT_MAX;
  1610. }
  1611. else
  1612. $c->srtk[1] = $c->srtk[2] = PHP_INT_MAX;
  1613. }
  1614. }
  1615. continue;
  1616. }
  1617. if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) {
  1618. $c->srtk[0] = iCalUtilityFunctions::_strdate2date( $d[1] );
  1619. unset( $c->srtk[0]['unparsedtext'] );
  1620. }
  1621. elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' )))
  1622. $c->srtk[1] = 0; // sortkey 0 : dtstart
  1623. if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' ))) {
  1624. $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); // sortkey 1 : dtend/due(/dtstart+duration)
  1625. unset( $c->srtk[1]['unparsedtext'] );
  1626. }
  1627. elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) {
  1628. if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' ))) {
  1629. $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] );
  1630. unset( $c->srtk[1]['unparsedtext'] );
  1631. }
  1632. elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' )))
  1633. if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE )))
  1634. $c->srtk[1] = 0;
  1635. }
  1636. if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' ))) // sortkey 2 : created/dtstamp
  1637. if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' )))
  1638. $c->srtk[2] = 0;
  1639. if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' ))) // sortkey 3 : uid
  1640. $c->srtk[3] = 0;
  1641. } // end foreach( $this->components as & $c
  1642. /* sort */
  1643. usort( $this->components, array( 'iCalUtilityFunctions', '_cmpfcn' ));
  1644. }
  1645. }
  1646. /**
  1647. * parse iCal text/file into vcalendar, components, properties and parameters
  1648. *
  1649. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1650. * @since 2.16.2 - 2012-12-18
  1651. * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings
  1652. * @return bool FALSE if error occurs during parsing
  1653. *
  1654. */
  1655. function parse( $unparsedtext=FALSE ) {
  1656. $nl = $this->getConfig( 'nl' );
  1657. if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) {
  1658. /* directory+filename is set previously via setConfig directory+filename or url */
  1659. if( FALSE === ( $filename = $this->getConfig( 'url' )))
  1660. $filename = $this->getConfig( 'dirfile' );
  1661. /* READ FILE */
  1662. if( FALSE === ( $rows = file_get_contents( $filename )))
  1663. return FALSE; /* err 1 */
  1664. }
  1665. elseif( is_array( $unparsedtext ))
  1666. $rows = implode( '\n'.$nl, $unparsedtext );
  1667. else
  1668. $rows = & $unparsedtext;
  1669. /* fix line folding */
  1670. $rows = explode( $nl, iCalUtilityFunctions::convEolChar( $rows, $nl ));
  1671. /* skip leading (empty/invalid) lines */
  1672. foreach( $rows as $lix => $line ) {
  1673. if( FALSE !== stripos( $line, 'BEGIN:VCALENDAR' ))
  1674. break;
  1675. unset( $rows[$lix] );
  1676. }
  1677. $rcnt = count( $rows );
  1678. if( 3 > $rcnt ) /* err 10 */
  1679. return FALSE;
  1680. /* skip trailing empty lines and ensure an end row */
  1681. $lix = array_keys( $rows );
  1682. $lix = end( $lix );
  1683. while( 3 < $lix ) {
  1684. $tst = trim( $rows[$lix] );
  1685. if(( '\n' == $tst ) || empty( $tst )) {
  1686. unset( $rows[$lix] );
  1687. $lix--;
  1688. continue;
  1689. }
  1690. if( FALSE === stripos( $rows[$lix], 'END:VCALENDAR' ))
  1691. $rows[] = 'END:VCALENDAR';
  1692. break;
  1693. }
  1694. $comp = & $this;
  1695. $calsync = $compsync = 0;
  1696. /* identify components and update unparsed data within component */
  1697. $config = $this->getConfig();
  1698. $endtxt = array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' );
  1699. foreach( $rows as $lix => $line ) {
  1700. if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) {
  1701. $calsync++;
  1702. continue;
  1703. }
  1704. elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) {
  1705. if( 0 < $compsync )
  1706. $this->components[] = $comp->copy();
  1707. $compsync--;
  1708. $calsync--;
  1709. break;
  1710. }
  1711. elseif( 1 != $calsync )
  1712. return FALSE; /* err 20 */
  1713. elseif( in_array( strtoupper( substr( $line, 0, 6 )), $endtxt )) {
  1714. $this->components[] = $comp->copy();
  1715. $compsync--;
  1716. continue;
  1717. }
  1718. if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) {
  1719. $comp = new vevent( $config );
  1720. $compsync++;
  1721. }
  1722. elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) {
  1723. $comp = new vfreebusy( $config );
  1724. $compsync++;
  1725. }
  1726. elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) {
  1727. $comp = new vjournal( $config );
  1728. $compsync++;
  1729. }
  1730. elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) {
  1731. $comp = new vtodo( $config );
  1732. $compsync++;
  1733. }
  1734. elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) {
  1735. $comp = new vtimezone( $config );
  1736. $compsync++;
  1737. }
  1738. else { /* update component with unparsed data */
  1739. $comp->unparsed[] = $line;
  1740. }
  1741. } // end foreach( $rows as $line )
  1742. unset( $config, $endtxt );
  1743. /* parse data for calendar (this) object */
  1744. if( isset( $this->unparsed ) && is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) {
  1745. /* concatenate property values spread over several lines */
  1746. $propnames = array( 'calscale','method','prodid','version','x-' );
  1747. $proprows = array();
  1748. for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines
  1749. $line = rtrim( $this->unparsed[$i], $nl );
  1750. while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} ))
  1751. $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl );
  1752. $proprows[] = $line;
  1753. }
  1754. $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' );
  1755. $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' );
  1756. $paramProto4 = array( 'crid:', 'news:', 'pres:' );
  1757. foreach( $proprows as $line ) {
  1758. if( '\n' == substr( $line, -2 ))
  1759. $line = substr( $line, 0, -2 );
  1760. /* get property name */
  1761. $propname = '';
  1762. $cix = 0;
  1763. while( FALSE !== ( $char = substr( $line, $cix, 1 ))) {
  1764. if( in_array( $char, array( ':', ';' )))
  1765. break;
  1766. else
  1767. $propname .= $char;
  1768. $cix++;
  1769. }
  1770. /* skip non standard property names */
  1771. if(( 'x-' != strtolower( substr( $propname, 0, 2 ))) && !in_array( strtolower( $propname ), $propnames ))
  1772. continue;
  1773. /* ignore version/prodid properties */
  1774. if( in_array( strtolower( $propname ), array( 'version', 'prodid' )))
  1775. continue;
  1776. /* rest of the line is opt.params and value */
  1777. $line = substr( $line, $cix);
  1778. /* separate attributes from value */
  1779. $attr = array();
  1780. $attrix = -1;
  1781. $strlen = strlen( $line );
  1782. $WithinQuotes = FALSE;
  1783. $cix = 0;
  1784. while( FALSE !== substr( $line, $cix, 1 )) {
  1785. if( ( ':' == $line[$cix] ) &&
  1786. ( substr( $line,$cix, 3 ) != '://' ) &&
  1787. ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) &&
  1788. ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) &&
  1789. ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) &&
  1790. ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) &&
  1791. !$WithinQuotes ) {
  1792. $attrEnd = TRUE;
  1793. if(( $cix < ( $strlen - 4 )) &&
  1794. ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
  1795. for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
  1796. if( '://' == substr( $line, $c2ix - 2, 3 )) {
  1797. $attrEnd = FALSE;
  1798. break; // an URI with a portnr!!
  1799. }
  1800. }
  1801. }
  1802. if( $attrEnd) {
  1803. $line = substr( $line, ( $cix + 1 ));
  1804. break;
  1805. }
  1806. }
  1807. if( '"' == $line[$cix] )
  1808. $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE;
  1809. if( ';' == $line[$cix] )
  1810. $attr[++$attrix] = null;
  1811. else
  1812. $attr[$attrix] .= $line[$cix];
  1813. $cix++;
  1814. }
  1815. /* make attributes in array format */
  1816. $propattr = array();
  1817. foreach( $attr as $attribute ) {
  1818. $attrsplit = explode( '=', $attribute, 2 );
  1819. if( 1 < count( $attrsplit ))
  1820. $propattr[$attrsplit[0]] = $attrsplit[1];
  1821. else
  1822. $propattr[] = $attribute;
  1823. }
  1824. /* update Property */
  1825. if( FALSE !== strpos( $line, ',' )) {
  1826. $content = array( 0 => '' );
  1827. $cix = $lix = 0;
  1828. while( FALSE !== substr( $line, $lix, 1 )) {
  1829. if(( 0 < $lix ) && ( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) {
  1830. $cix++;
  1831. $content[$cix] = '';
  1832. }
  1833. else
  1834. $content[$cix] .= $line[$lix];
  1835. $lix++;
  1836. }
  1837. if( 1 < count( $content )) {
  1838. foreach( $content as $cix => $contentPart )
  1839. $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart );
  1840. $this->setProperty( $propname, $content, $propattr );
  1841. continue;
  1842. }
  1843. else
  1844. $line = reset( $content );
  1845. $line = iCalUtilityFunctions::_strunrep( $line );
  1846. }
  1847. $this->setProperty( $propname, rtrim( $line, "\x00..\x1F" ), $propattr );
  1848. } // end - foreach( $this->unparsed.. .
  1849. } // end - if( is_array( $this->unparsed.. .
  1850. unset( $unparsedtext, $rows, $this->unparsed, $proprows );
  1851. /* parse Components */
  1852. if( is_array( $this->components ) && ( 0 < count( $this->components ))) {
  1853. $ckeys = array_keys( $this->components );
  1854. foreach( $ckeys as $ckey ) {
  1855. if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
  1856. $this->components[$ckey]->parse();
  1857. }
  1858. }
  1859. }
  1860. else
  1861. return FALSE; /* err 91 or something.. . */
  1862. return TRUE;
  1863. }
  1864. /*********************************************************************************/
  1865. /**
  1866. * creates formatted output for calendar object instance
  1867. *
  1868. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1869. * @since 2.10.16 - 2011-10-28
  1870. * @return string
  1871. */
  1872. function createCalendar() {
  1873. $calendarInit = $calendarxCaldecl = $calendarStart = $calendar = '';
  1874. switch( $this->format ) {
  1875. case 'xcal':
  1876. $calendarInit = '<?xml version="1.0" encoding="UTF-8"?>'.$this->nl.
  1877. '<!DOCTYPE vcalendar PUBLIC "-//IETF//DTD XCAL/iCalendar XML//EN"'.$this->nl.
  1878. '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"';
  1879. $calendarStart = '>'.$this->nl.'<vcalendar';
  1880. break;
  1881. default:
  1882. $calendarStart = 'BEGIN:VCALENDAR'.$this->nl;
  1883. break;
  1884. }
  1885. $calendarStart .= $this->createVersion();
  1886. $calendarStart .= $this->createProdid();
  1887. $calendarStart .= $this->createCalscale();
  1888. $calendarStart .= $this->createMethod();
  1889. if( 'xcal' == $this->format )
  1890. $calendarStart .= '>'.$this->nl;
  1891. $calendar .= $this->createXprop();
  1892. foreach( $this->components as $component ) {
  1893. if( empty( $component )) continue;
  1894. $component->setConfig( $this->getConfig(), FALSE, TRUE );
  1895. $calendar .= $component->createComponent( $this->xcaldecl );
  1896. }
  1897. if(( 'xcal' == $this->format ) && ( 0 < count( $this->xcaldecl ))) { // xCal only
  1898. $calendarInit .= ' [';
  1899. $old_xcaldecl = array();
  1900. foreach( $this->xcaldecl as $declix => $declPart ) {
  1901. if(( 0 < count( $old_xcaldecl)) &&
  1902. isset( $declPart['uri'] ) && isset( $declPart['external'] ) &&
  1903. isset( $old_xcaldecl['uri'] ) && isset( $old_xcaldecl['external'] ) &&
  1904. ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) &&
  1905. ( in_array( $declPart['external'], $old_xcaldecl['external'] )))
  1906. continue; // no duplicate uri and ext. references
  1907. if(( 0 < count( $old_xcaldecl)) &&
  1908. !isset( $declPart['uri'] ) && !isset( $declPart['uri'] ) &&
  1909. isset( $declPart['ref'] ) && isset( $old_xcaldecl['ref'] ) &&
  1910. ( in_array( $declPart['ref'], $old_xcaldecl['ref'] )))
  1911. continue; // no duplicate element declarations
  1912. $calendarxCaldecl .= $this->nl.'<!';
  1913. foreach( $declPart as $declKey => $declValue ) {
  1914. switch( $declKey ) { // index
  1915. case 'xmldecl': // no 1
  1916. $calendarxCaldecl .= $declValue.' ';
  1917. break;
  1918. case 'uri': // no 2
  1919. $calendarxCaldecl .= $declValue.' ';
  1920. $old_xcaldecl['uri'][] = $declValue;
  1921. break;
  1922. case 'ref': // no 3
  1923. $calendarxCaldecl .= $declValue.' ';
  1924. $old_xcaldecl['ref'][] = $declValue;
  1925. break;
  1926. case 'external': // no 4
  1927. $calendarxCaldecl .= '"'.$declValue.'" ';
  1928. $old_xcaldecl['external'][] = $declValue;
  1929. break;
  1930. case 'type': // no 5
  1931. $calendarxCaldecl .= $declValue.' ';
  1932. break;
  1933. case 'type2': // no 6
  1934. $calendarxCaldecl .= $declValue;
  1935. break;
  1936. }
  1937. }
  1938. $calendarxCaldecl .= '>';
  1939. }
  1940. $calendarxCaldecl .= $this->nl.']';
  1941. }
  1942. switch( $this->format ) {
  1943. case 'xcal':
  1944. $calendar .= '</vcalendar>'.$this->nl;
  1945. break;
  1946. default:
  1947. $calendar .= 'END:VCALENDAR'.$this->nl;
  1948. break;
  1949. }
  1950. return $calendarInit.$calendarxCaldecl.$calendarStart.$calendar;
  1951. }
  1952. /**
  1953. * a HTTP redirect header is sent with created, updated and/or parsed calendar
  1954. *
  1955. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1956. * @since 2.10.24 - 2011-12-23
  1957. * @param bool $utf8Encode
  1958. * @param bool $gzip
  1959. * @return redirect
  1960. */
  1961. function returnCalendar( $utf8Encode=FALSE, $gzip=FALSE ) {
  1962. $filename = $this->getConfig( 'filename' );
  1963. $output = $this->createCalendar();
  1964. if( $utf8Encode )
  1965. $output = utf8_encode( $output );
  1966. if( $gzip ) {
  1967. $output = gzencode( $output, 9 );
  1968. header( 'Content-Encoding: gzip' );
  1969. header( 'Vary: *' );
  1970. header( 'Content-Length: '.strlen( $output ));
  1971. }
  1972. if( 'xcal' == $this->format )
  1973. header( 'Content-Type: application/calendar+xml; charset=utf-8' );
  1974. else
  1975. header( 'Content-Type: text/calendar; charset=utf-8' );
  1976. header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
  1977. header( 'Cache-Control: max-age=10' );
  1978. die( $output );
  1979. }
  1980. /**
  1981. * save content in a file
  1982. *
  1983. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1984. * @since 2.2.12 - 2007-12-30
  1985. * @param string $directory optional
  1986. * @param string $filename optional
  1987. * @param string $delimiter optional
  1988. * @return bool
  1989. */
  1990. function saveCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE ) {
  1991. if( $directory )
  1992. $this->setConfig( 'directory', $directory );
  1993. if( $filename )
  1994. $this->setConfig( 'filename', $filename );
  1995. if( $delimiter && ($delimiter != DIRECTORY_SEPARATOR ))
  1996. $this->setConfig( 'delimiter', $delimiter );
  1997. if( FALSE === ( $dirfile = $this->getConfig( 'url' )))
  1998. $dirfile = $this->getConfig( 'dirfile' );
  1999. $iCalFile = @fopen( $dirfile, 'w' );
  2000. if( $iCalFile ) {
  2001. if( FALSE === fwrite( $iCalFile, $this->createCalendar() ))
  2002. return FALSE;
  2003. fclose( $iCalFile );
  2004. return TRUE;
  2005. }
  2006. else
  2007. return FALSE;
  2008. }
  2009. /**
  2010. * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent
  2011. * else FALSE is returned
  2012. *
  2013. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2014. * @since 2.2.12 - 2007-10-28
  2015. * @param string $directory optional alt. int timeout
  2016. * @param string $filename optional
  2017. * @param string $delimiter optional
  2018. * @param int timeout optional, default 3600 sec
  2019. * @return redirect/FALSE
  2020. */
  2021. function useCachedCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE, $timeout=3600) {
  2022. if ( $directory && ctype_digit( (string) $directory ) && !$filename ) {
  2023. $timeout = (int) $directory;
  2024. $directory = FALSE;
  2025. }
  2026. if( $directory )
  2027. $this->setConfig( 'directory', $directory );
  2028. if( $filename )
  2029. $this->setConfig( 'filename', $filename );
  2030. if( $delimiter && ( $delimiter != DIRECTORY_SEPARATOR ))
  2031. $this->setConfig( 'delimiter', $delimiter );
  2032. $filesize = $this->getConfig( 'filesize' );
  2033. if( 0 >= $filesize )
  2034. return FALSE;
  2035. $dirfile = $this->getConfig( 'dirfile' );
  2036. if( time() - filemtime( $dirfile ) < $timeout) {
  2037. clearstatcache();
  2038. $dirfile = $this->getConfig( 'dirfile' );
  2039. $filename = $this->getConfig( 'filename' );
  2040. // if( headers_sent( $filename, $linenum ))
  2041. // die( "Headers already sent in $filename on line $linenum\n" );
  2042. if( 'xcal' == $this->format )
  2043. header( 'Content-Type: application/calendar+xml; charset=utf-8' );
  2044. else
  2045. header( 'Content-Type: text/calendar; charset=utf-8' );
  2046. header( 'Content-Length: '.$filesize );
  2047. header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
  2048. header( 'Cache-Control: max-age=10' );
  2049. $fp = @fopen( $dirfile, 'r' );
  2050. if( $fp ) {
  2051. fpassthru( $fp );
  2052. fclose( $fp );
  2053. }
  2054. die();
  2055. }
  2056. else
  2057. return FALSE;
  2058. }
  2059. }
  2060. /*********************************************************************************/
  2061. /*********************************************************************************/
  2062. /**
  2063. * abstract class for calendar components
  2064. *
  2065. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2066. * @since 2.9.6 - 2011-05-14
  2067. */
  2068. class calendarComponent {
  2069. // component property variables
  2070. var $uid;
  2071. var $dtstamp;
  2072. // component config variables
  2073. var $allowEmpty;
  2074. var $language;
  2075. var $nl;
  2076. var $unique_id;
  2077. var $format;
  2078. var $objName; // created automatically at instance creation
  2079. var $dtzid; // default (local) timezone
  2080. // component internal variables
  2081. var $componentStart1;
  2082. var $componentStart2;
  2083. var $componentEnd1;
  2084. var $componentEnd2;
  2085. var $elementStart1;
  2086. var $elementStart2;
  2087. var $elementEnd1;
  2088. var $elementEnd2;
  2089. var $intAttrDelimiter;
  2090. var $attributeDelimiter;
  2091. var $valueInit;
  2092. // component xCal declaration container
  2093. var $xcaldecl;
  2094. /**
  2095. * constructor for calendar component object
  2096. *
  2097. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2098. * @since 2.9.6 - 2011-05-17
  2099. */
  2100. function calendarComponent() {
  2101. $this->objName = ( isset( $this->timezonetype )) ?
  2102. strtolower( $this->timezonetype ) : get_class ( $this );
  2103. $this->uid = array();
  2104. $this->dtstamp = array();
  2105. $this->language = null;
  2106. $this->nl = null;
  2107. $this->unique_id = null;
  2108. $this->format = null;
  2109. $this->dtzid = null;
  2110. $this->allowEmpty = TRUE;
  2111. $this->xcaldecl = array();
  2112. $this->_createFormat();
  2113. $this->_makeDtstamp();
  2114. }
  2115. /*********************************************************************************/
  2116. /**
  2117. * Property Name: ACTION
  2118. */
  2119. /**
  2120. * creates formatted output for calendar component property action
  2121. *
  2122. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2123. * @since 2.4.8 - 2008-10-22
  2124. * @return string
  2125. */
  2126. function createAction() {
  2127. if( empty( $this->action )) return FALSE;
  2128. if( empty( $this->action['value'] ))
  2129. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE;
  2130. $attributes = $this->_createParams( $this->action['params'] );
  2131. return $this->_createElement( 'ACTION', $attributes, $this->action['value'] );
  2132. }
  2133. /**
  2134. * set calendar component property action
  2135. *
  2136. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2137. * @since 2.4.8 - 2008-11-04
  2138. * @param string $value "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
  2139. * @param mixed $params
  2140. * @return bool
  2141. */
  2142. function setAction( $value, $params=FALSE ) {
  2143. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  2144. $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  2145. return TRUE;
  2146. }
  2147. /*********************************************************************************/
  2148. /**
  2149. * Property Name: ATTACH
  2150. */
  2151. /**
  2152. * creates formatted output for calendar component property attach
  2153. *
  2154. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2155. * @since 2.11.16 - 2012-02-04
  2156. * @return string
  2157. */
  2158. function createAttach() {
  2159. if( empty( $this->attach )) return FALSE;
  2160. $output = null;
  2161. foreach( $this->attach as $attachPart ) {
  2162. if( !empty( $attachPart['value'] )) {
  2163. $attributes = $this->_createParams( $attachPart['params'] );
  2164. if(( 'xcal' != $this->format ) && isset( $attachPart['params']['VALUE'] ) && ( 'BINARY' == $attachPart['params']['VALUE'] )) {
  2165. $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes );
  2166. $str = 'ATTACH'.$attributes.$this->valueInit.$attachPart['value'];
  2167. $output = substr( $str, 0, 75 ).$this->nl;
  2168. $str = substr( $str, 75 );
  2169. $output .= ' '.chunk_split( $str, 74, $this->nl.' ' );
  2170. if( ' ' == substr( $output, -1 ))
  2171. $output = rtrim( $output );
  2172. if( $this->nl != substr( $output, ( 0 - strlen( $this->nl ))))
  2173. $output .= $this->nl;
  2174. return $output;
  2175. }
  2176. $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] );
  2177. }
  2178. elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' );
  2179. }
  2180. return $output;
  2181. }
  2182. /**
  2183. * set calendar component property attach
  2184. *
  2185. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2186. * @since 2.5.1 - 2008-11-06
  2187. * @param string $value
  2188. * @param array $params, optional
  2189. * @param integer $index, optional
  2190. * @return bool
  2191. */
  2192. function setAttach( $value, $params=FALSE, $index=FALSE ) {
  2193. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  2194. iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index );
  2195. return TRUE;
  2196. }
  2197. /*********************************************************************************/
  2198. /**
  2199. * Property Name: ATTENDEE
  2200. */
  2201. /**
  2202. * creates formatted output for calendar component property attendee
  2203. *
  2204. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2205. * @since 2.11.12 - 2012-01-31
  2206. * @return string
  2207. */
  2208. function createAttendee() {
  2209. if( empty( $this->attendee )) return FALSE;
  2210. $output = null;
  2211. foreach( $this->attendee as $attendeePart ) { // start foreach 1
  2212. if( empty( $attendeePart['value'] )) {
  2213. if( $this->getConfig( 'allowEmpty' ))
  2214. $output .= $this->_createElement( 'ATTENDEE' );
  2215. continue;
  2216. }
  2217. $attendee1 = $attendee2 = null;
  2218. foreach( $attendeePart as $paramlabel => $paramvalue ) { // start foreach 2
  2219. if( 'value' == $paramlabel )
  2220. $attendee2 .= $paramvalue;
  2221. elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif
  2222. $mParams = array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' );
  2223. foreach( $paramvalue as $pKey => $pValue ) { // fix (opt) quotes
  2224. if( is_array( $pValue ) || in_array( $pKey, $mParams ))
  2225. continue;
  2226. if(( FALSE !== strpos( $pValue, ':' )) ||
  2227. ( FALSE !== strpos( $pValue, ';' )) ||
  2228. ( FALSE !== strpos( $pValue, ',' )))
  2229. $paramvalue[$pKey] = '"'.$pValue.'"';
  2230. }
  2231. // set attenddee parameters in rfc2445 order
  2232. if( isset( $paramvalue['CUTYPE'] ))
  2233. $attendee1 .= $this->intAttrDelimiter.'CUTYPE='.$paramvalue['CUTYPE'];
  2234. if( isset( $paramvalue['MEMBER'] )) {
  2235. $attendee1 .= $this->intAttrDelimiter.'MEMBER=';
  2236. foreach( $paramvalue['MEMBER'] as $cix => $opv )
  2237. $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ;
  2238. }
  2239. if( isset( $paramvalue['ROLE'] ))
  2240. $attendee1 .= $this->intAttrDelimiter.'ROLE='.$paramvalue['ROLE'];
  2241. if( isset( $paramvalue['PARTSTAT'] ))
  2242. $attendee1 .= $this->intAttrDelimiter.'PARTSTAT='.$paramvalue['PARTSTAT'];
  2243. if( isset( $paramvalue['RSVP'] ))
  2244. $attendee1 .= $this->intAttrDelimiter.'RSVP='.$paramvalue['RSVP'];
  2245. if( isset( $paramvalue['DELEGATED-TO'] )) {
  2246. $attendee1 .= $this->intAttrDelimiter.'DELEGATED-TO=';
  2247. foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv )
  2248. $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ;
  2249. }
  2250. if( isset( $paramvalue['DELEGATED-FROM'] )) {
  2251. $attendee1 .= $this->intAttrDelimiter.'DELEGATED-FROM=';
  2252. foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv )
  2253. $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ;
  2254. }
  2255. if( isset( $paramvalue['SENT-BY'] ))
  2256. $attendee1 .= $this->intAttrDelimiter.'SENT-BY='.$paramvalue['SENT-BY'];
  2257. if( isset( $paramvalue['CN'] ))
  2258. $attendee1 .= $this->intAttrDelimiter.'CN='.$paramvalue['CN'];
  2259. if( isset( $paramvalue['DIR'] )) {
  2260. $delim = ( FALSE === strpos( $paramvalue['DIR'], '"' )) ? '"' : '';
  2261. $attendee1 .= $this->intAttrDelimiter.'DIR='.$delim.$paramvalue['DIR'].$delim;
  2262. }
  2263. if( isset( $paramvalue['LANGUAGE'] ))
  2264. $attendee1 .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE'];
  2265. $xparams = array();
  2266. foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3
  2267. if( ctype_digit( (string) $optparamlabel )) {
  2268. $xparams[] = $optparamvalue;
  2269. continue;
  2270. }
  2271. if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' )))
  2272. $xparams[$optparamlabel] = $optparamvalue;
  2273. } // end foreach 3
  2274. ksort( $xparams, SORT_STRING );
  2275. foreach( $xparams as $paramKey => $paramValue ) {
  2276. if( ctype_digit( (string) $paramKey ))
  2277. $attendee1 .= $this->intAttrDelimiter.$paramValue;
  2278. else
  2279. $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue";
  2280. } // end foreach 3
  2281. } // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue )))
  2282. } // end foreach 2
  2283. $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 );
  2284. } // end foreach 1
  2285. return $output;
  2286. }
  2287. /**
  2288. * set calendar component property attach
  2289. *
  2290. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2291. * @since 2.12.18 - 2012-07-13
  2292. * @param string $value
  2293. * @param array $params, optional
  2294. * @param integer $index, optional
  2295. * @return bool
  2296. */
  2297. function setAttendee( $value, $params=FALSE, $index=FALSE ) {
  2298. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  2299. // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// may exist.. . also in params
  2300. if( !empty( $value )) {
  2301. if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
  2302. $value = 'MAILTO:'.$value;
  2303. elseif( !empty( $value ))
  2304. $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos );
  2305. $value = str_replace( 'mailto:', 'MAILTO:', $value );
  2306. }
  2307. $params2 = array();
  2308. if( is_array($params )) {
  2309. $optarrays = array();
  2310. foreach( $params as $optparamlabel => $optparamvalue ) {
  2311. $optparamlabel = strtoupper( $optparamlabel );
  2312. switch( $optparamlabel ) {
  2313. case 'MEMBER':
  2314. case 'DELEGATED-TO':
  2315. case 'DELEGATED-FROM':
  2316. if( !is_array( $optparamvalue ))
  2317. $optparamvalue = array( $optparamvalue );
  2318. foreach( $optparamvalue as $part ) {
  2319. $part = trim( $part );
  2320. if(( '"' == substr( $part, 0, 1 )) &&
  2321. ( '"' == substr( $part, -1 )))
  2322. $part = substr( $part, 1, ( strlen( $part ) - 2 ));
  2323. if( 'mailto:' != strtolower( substr( $part, 0, 7 )))
  2324. $part = "MAILTO:$part";
  2325. else
  2326. $part = 'MAILTO:'.substr( $part, 7 );
  2327. $optarrays[$optparamlabel][] = $part;
  2328. }
  2329. break;
  2330. default:
  2331. if(( '"' == substr( $optparamvalue, 0, 1 )) &&
  2332. ( '"' == substr( $optparamvalue, -1 )))
  2333. $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 ));
  2334. if( 'SENT-BY' == $optparamlabel ) {
  2335. if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 )))
  2336. $optparamvalue = "MAILTO:$optparamvalue";
  2337. else
  2338. $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 );
  2339. }
  2340. $params2[$optparamlabel] = $optparamvalue;
  2341. break;
  2342. } // end switch( $optparamlabel.. .
  2343. } // end foreach( $optparam.. .
  2344. foreach( $optarrays as $optparamlabel => $optparams )
  2345. $params2[$optparamlabel] = $optparams;
  2346. }
  2347. // remove defaults
  2348. iCalUtilityFunctions::_existRem( $params2, 'CUTYPE', 'INDIVIDUAL' );
  2349. iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' );
  2350. iCalUtilityFunctions::_existRem( $params2, 'ROLE', 'REQ-PARTICIPANT' );
  2351. iCalUtilityFunctions::_existRem( $params2, 'RSVP', 'FALSE' );
  2352. // check language setting
  2353. if( isset( $params2['CN' ] )) {
  2354. $lang = $this->getConfig( 'language' );
  2355. if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang ))
  2356. $params2['LANGUAGE' ] = $lang;
  2357. }
  2358. iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index );
  2359. return TRUE;
  2360. }
  2361. /*********************************************************************************/
  2362. /**
  2363. * Property Name: CATEGORIES
  2364. */
  2365. /**
  2366. * creates formatted output for calendar component property categories
  2367. *
  2368. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2369. * @since 2.16.2 - 2012-12-18
  2370. * @return string
  2371. */
  2372. function createCategories() {
  2373. if( empty( $this->categories )) return FALSE;
  2374. $output = null;
  2375. foreach( $this->categories as $category ) {
  2376. if( empty( $category['value'] )) {
  2377. if ( $this->getConfig( 'allowEmpty' ))
  2378. $output .= $this->_createElement( 'CATEGORIES' );
  2379. continue;
  2380. }
  2381. $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' ));
  2382. if( is_array( $category['value'] )) {
  2383. foreach( $category['value'] as $cix => $categoryPart )
  2384. $category['value'][$cix] = iCalUtilityFunctions::_strrep( $categoryPart, $this->format, $this->nl );
  2385. $content = implode( ',', $category['value'] );
  2386. }
  2387. else
  2388. $content = iCalUtilityFunctions::_strrep( $category['value'], $this->format, $this->nl );
  2389. $output .= $this->_createElement( 'CATEGORIES', $attributes, $content );
  2390. }
  2391. return $output;
  2392. }
  2393. /**
  2394. * set calendar component property categories
  2395. *
  2396. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2397. * @since 2.5.1 - 2008-11-06
  2398. * @param mixed $value
  2399. * @param array $params, optional
  2400. * @param integer $index, optional
  2401. * @return bool
  2402. */
  2403. function setCategories( $value, $params=FALSE, $index=FALSE ) {
  2404. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  2405. iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index );
  2406. return TRUE;
  2407. }
  2408. /*********************************************************************************/
  2409. /**
  2410. * Property Name: CLASS
  2411. */
  2412. /**
  2413. * creates formatted output for calendar component property class
  2414. *
  2415. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2416. * @since 0.9.7 - 2006-11-20
  2417. * @return string
  2418. */
  2419. function createClass() {
  2420. if( empty( $this->class )) return FALSE;
  2421. if( empty( $this->class['value'] ))
  2422. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE;
  2423. $attributes = $this->_createParams( $this->class['params'] );
  2424. return $this->_createElement( 'CLASS', $attributes, $this->class['value'] );
  2425. }
  2426. /**
  2427. * set calendar component property class
  2428. *
  2429. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2430. * @since 2.4.8 - 2008-11-04
  2431. * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name
  2432. * @param array $params optional
  2433. * @return bool
  2434. */
  2435. function setClass( $value, $params=FALSE ) {
  2436. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  2437. $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  2438. return TRUE;
  2439. }
  2440. /*********************************************************************************/
  2441. /**
  2442. * Property Name: COMMENT
  2443. */
  2444. /**
  2445. * creates formatted output for calendar component property comment
  2446. *
  2447. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2448. * @since 2.16.2 - 2012-12-18
  2449. * @return string
  2450. */
  2451. function createComment() {
  2452. if( empty( $this->comment )) return FALSE;
  2453. $output = null;
  2454. foreach( $this->comment as $commentPart ) {
  2455. if( empty( $commentPart['value'] )) {
  2456. if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' );
  2457. continue;
  2458. }
  2459. $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' ));
  2460. $content = iCalUtilityFunctions::_strrep( $commentPart['value'], $this->format, $this->nl );
  2461. $output .= $this->_createElement( 'COMMENT', $attributes, $content );
  2462. }
  2463. return $output;
  2464. }
  2465. /**
  2466. * set calendar component property comment
  2467. *
  2468. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2469. * @since 2.5.1 - 2008-11-06
  2470. * @param string $value
  2471. * @param array $params, optional
  2472. * @param integer $index, optional
  2473. * @return bool
  2474. */
  2475. function setComment( $value, $params=FALSE, $index=FALSE ) {
  2476. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  2477. iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index );
  2478. return TRUE;
  2479. }
  2480. /*********************************************************************************/
  2481. /**
  2482. * Property Name: COMPLETED
  2483. */
  2484. /**
  2485. * creates formatted output for calendar component property completed
  2486. *
  2487. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2488. * @since 2.4.8 - 2008-10-22
  2489. * @return string
  2490. */
  2491. function createCompleted( ) {
  2492. if( empty( $this->completed )) return FALSE;
  2493. if( !isset( $this->completed['value']['year'] ) &&
  2494. !isset( $this->completed['value']['month'] ) &&
  2495. !isset( $this->completed['value']['day'] ) &&
  2496. !isset( $this->completed['value']['hour'] ) &&
  2497. !isset( $this->completed['value']['min'] ) &&
  2498. !isset( $this->completed['value']['sec'] ))
  2499. if( $this->getConfig( 'allowEmpty' ))
  2500. return $this->_createElement( 'COMPLETED' );
  2501. else return FALSE;
  2502. $formatted = iCalUtilityFunctions::_date2strdate( $this->completed['value'], 7 );
  2503. $attributes = $this->_createParams( $this->completed['params'] );
  2504. return $this->_createElement( 'COMPLETED', $attributes, $formatted );
  2505. }
  2506. /**
  2507. * set calendar component property completed
  2508. *
  2509. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2510. * @since 2.4.8 - 2008-10-23
  2511. * @param mixed $year
  2512. * @param mixed $month optional
  2513. * @param int $day optional
  2514. * @param int $hour optional
  2515. * @param int $min optional
  2516. * @param int $sec optional
  2517. * @param array $params optional
  2518. * @return bool
  2519. */
  2520. function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
  2521. if( empty( $year )) {
  2522. if( $this->getConfig( 'allowEmpty' )) {
  2523. $this->completed = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
  2524. return TRUE;
  2525. }
  2526. else
  2527. return FALSE;
  2528. }
  2529. $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
  2530. return TRUE;
  2531. }
  2532. /*********************************************************************************/
  2533. /**
  2534. * Property Name: CONTACT
  2535. */
  2536. /**
  2537. * creates formatted output for calendar component property contact
  2538. *
  2539. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2540. * @since 2.16.2 - 2012-12-18
  2541. * @return string
  2542. */
  2543. function createContact() {
  2544. if( empty( $this->contact )) return FALSE;
  2545. $output = null;
  2546. foreach( $this->contact as $contact ) {
  2547. if( !empty( $contact['value'] )) {
  2548. $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' ));
  2549. $content = iCalUtilityFunctions::_strrep( $contact['value'], $this->format, $this->nl );
  2550. $output .= $this->_createElement( 'CONTACT', $attributes, $content );
  2551. }
  2552. elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' );
  2553. }
  2554. return $output;
  2555. }
  2556. /**
  2557. * set calendar component property contact
  2558. *
  2559. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2560. * @since 2.5.1 - 2008-11-05
  2561. * @param string $value
  2562. * @param array $params, optional
  2563. * @param integer $index, optional
  2564. * @return bool
  2565. */
  2566. function setContact( $value, $params=FALSE, $index=FALSE ) {
  2567. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  2568. iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index );
  2569. return TRUE;
  2570. }
  2571. /*********************************************************************************/
  2572. /**
  2573. * Property Name: CREATED
  2574. */
  2575. /**
  2576. * creates formatted output for calendar component property created
  2577. *
  2578. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2579. * @since 2.4.8 - 2008-10-21
  2580. * @return string
  2581. */
  2582. function createCreated() {
  2583. if( empty( $this->created )) return FALSE;
  2584. $formatted = iCalUtilityFunctions::_date2strdate( $this->created['value'], 7 );
  2585. $attributes = $this->_createParams( $this->created['params'] );
  2586. return $this->_createElement( 'CREATED', $attributes, $formatted );
  2587. }
  2588. /**
  2589. * set calendar component property created
  2590. *
  2591. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2592. * @since 2.4.8 - 2008-10-23
  2593. * @param mixed $year optional
  2594. * @param mixed $month optional
  2595. * @param int $day optional
  2596. * @param int $hour optional
  2597. * @param int $min optional
  2598. * @param int $sec optional
  2599. * @param mixed $params optional
  2600. * @return bool
  2601. */
  2602. function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
  2603. if( !isset( $year )) {
  2604. $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
  2605. }
  2606. $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
  2607. return TRUE;
  2608. }
  2609. /*********************************************************************************/
  2610. /**
  2611. * Property Name: DESCRIPTION
  2612. */
  2613. /**
  2614. * creates formatted output for calendar component property description
  2615. *
  2616. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2617. * @since 2.16.2 - 2012-12-18
  2618. * @return string
  2619. */
  2620. function createDescription() {
  2621. if( empty( $this->description )) return FALSE;
  2622. $output = null;
  2623. foreach( $this->description as $description ) {
  2624. if( !empty( $description['value'] )) {
  2625. $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' ));
  2626. $content = iCalUtilityFunctions::_strrep( $description['value'], $this->format, $this->nl );
  2627. $output .= $this->_createElement( 'DESCRIPTION', $attributes, $content );
  2628. }
  2629. elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' );
  2630. }
  2631. return $output;
  2632. }
  2633. /**
  2634. * set calendar component property description
  2635. *
  2636. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2637. * @since 2.6.24 - 2010-11-06
  2638. * @param string $value
  2639. * @param array $params, optional
  2640. * @param integer $index, optional
  2641. * @return bool
  2642. */
  2643. function setDescription( $value, $params=FALSE, $index=FALSE ) {
  2644. if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; }
  2645. if( 'vjournal' != $this->objName )
  2646. $index = 1;
  2647. iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index );
  2648. return TRUE;
  2649. }
  2650. /*********************************************************************************/
  2651. /**
  2652. * Property Name: DTEND
  2653. */
  2654. /**
  2655. * creates formatted output for calendar component property dtend
  2656. *
  2657. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2658. * @since 2.14.4 - 2012-09-26
  2659. * @return string
  2660. */
  2661. function createDtend() {
  2662. if( empty( $this->dtend )) return FALSE;
  2663. if( !isset( $this->dtend['value']['year'] ) &&
  2664. !isset( $this->dtend['value']['month'] ) &&
  2665. !isset( $this->dtend['value']['day'] ) &&
  2666. !isset( $this->dtend['value']['hour'] ) &&
  2667. !isset( $this->dtend['value']['min'] ) &&
  2668. !isset( $this->dtend['value']['sec'] ))
  2669. if( $this->getConfig( 'allowEmpty' ))
  2670. return $this->_createElement( 'DTEND' );
  2671. else return FALSE;
  2672. $parno = ( isset( $this->dtend['params']['VALUE'] ) && ( 'DATE' == $this->dtend['params']['VALUE'] )) ? 3 : null;
  2673. $formatted = iCalUtilityFunctions::_date2strdate( $this->dtend['value'], $parno );
  2674. $attributes = $this->_createParams( $this->dtend['params'] );
  2675. return $this->_createElement( 'DTEND', $attributes, $formatted );
  2676. }
  2677. /**
  2678. * set calendar component property dtend
  2679. *
  2680. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2681. * @since 2.9.6 - 2011-05-14
  2682. * @param mixed $year
  2683. * @param mixed $month optional
  2684. * @param int $day optional
  2685. * @param int $hour optional
  2686. * @param int $min optional
  2687. * @param int $sec optional
  2688. * @param string $tz optional
  2689. * @param array params optional
  2690. * @return bool
  2691. */
  2692. function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
  2693. if( empty( $year )) {
  2694. if( $this->getConfig( 'allowEmpty' )) {
  2695. $this->dtend = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
  2696. return TRUE;
  2697. }
  2698. else
  2699. return FALSE;
  2700. }
  2701. $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
  2702. return TRUE;
  2703. }
  2704. /*********************************************************************************/
  2705. /**
  2706. * Property Name: DTSTAMP
  2707. */
  2708. /**
  2709. * creates formatted output for calendar component property dtstamp
  2710. *
  2711. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2712. * @since 2.4.4 - 2008-03-07
  2713. * @return string
  2714. */
  2715. function createDtstamp() {
  2716. if( !isset( $this->dtstamp['value']['year'] ) &&
  2717. !isset( $this->dtstamp['value']['month'] ) &&
  2718. !isset( $this->dtstamp['value']['day'] ) &&
  2719. !isset( $this->dtstamp['value']['hour'] ) &&
  2720. !isset( $this->dtstamp['value']['min'] ) &&
  2721. !isset( $this->dtstamp['value']['sec'] ))
  2722. $this->_makeDtstamp();
  2723. $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstamp['value'], 7 );
  2724. $attributes = $this->_createParams( $this->dtstamp['params'] );
  2725. return $this->_createElement( 'DTSTAMP', $attributes, $formatted );
  2726. }
  2727. /**
  2728. * computes datestamp for calendar component object instance dtstamp
  2729. *
  2730. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2731. * @since 2.14.1 - 2012-09-29
  2732. * @return void
  2733. */
  2734. function _makeDtstamp() {
  2735. $d = date( 'Y-m-d-H-i-s', mktime( date('H'), date('i'), (date('s') - date( 'Z' )), date('m'), date('d'), date('Y')));
  2736. $date = explode( '-', $d );
  2737. $this->dtstamp['value'] = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2], 'hour' => $date[3], 'min' => $date[4], 'sec' => $date[5], 'tz' => 'Z' );
  2738. $this->dtstamp['params'] = null;
  2739. }
  2740. /**
  2741. * set calendar component property dtstamp
  2742. *
  2743. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2744. * @since 2.4.8 - 2008-10-23
  2745. * @param mixed $year
  2746. * @param mixed $month optional
  2747. * @param int $day optional
  2748. * @param int $hour optional
  2749. * @param int $min optional
  2750. * @param int $sec optional
  2751. * @param array $params optional
  2752. * @return TRUE
  2753. */
  2754. function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
  2755. if( empty( $year ))
  2756. $this->_makeDtstamp();
  2757. else
  2758. $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
  2759. return TRUE;
  2760. }
  2761. /*********************************************************************************/
  2762. /**
  2763. * Property Name: DTSTART
  2764. */
  2765. /**
  2766. * creates formatted output for calendar component property dtstart
  2767. *
  2768. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2769. * @since 2.14.4 - 2012-09-26
  2770. * @return string
  2771. */
  2772. function createDtstart() {
  2773. if( empty( $this->dtstart )) return FALSE;
  2774. if( !isset( $this->dtstart['value']['year'] ) &&
  2775. !isset( $this->dtstart['value']['month'] ) &&
  2776. !isset( $this->dtstart['value']['day'] ) &&
  2777. !isset( $this->dtstart['value']['hour'] ) &&
  2778. !isset( $this->dtstart['value']['min'] ) &&
  2779. !isset( $this->dtstart['value']['sec'] )) {
  2780. if( $this->getConfig( 'allowEmpty' ))
  2781. return $this->_createElement( 'DTSTART' );
  2782. else return FALSE;
  2783. }
  2784. if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))
  2785. unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] );
  2786. $parno = ( isset( $this->dtstart['params']['VALUE'] ) && ( 'DATE' == $this->dtstart['params']['VALUE'] )) ? 3 : null;
  2787. $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstart['value'], $parno );
  2788. $attributes = $this->_createParams( $this->dtstart['params'] );
  2789. return $this->_createElement( 'DTSTART', $attributes, $formatted );
  2790. }
  2791. /**
  2792. * set calendar component property dtstart
  2793. *
  2794. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2795. * @since 2.6.22 - 2010-09-22
  2796. * @param mixed $year
  2797. * @param mixed $month optional
  2798. * @param int $day optional
  2799. * @param int $hour optional
  2800. * @param int $min optional
  2801. * @param int $sec optional
  2802. * @param string $tz optional
  2803. * @param array $params optional
  2804. * @return bool
  2805. */
  2806. function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
  2807. if( empty( $year )) {
  2808. if( $this->getConfig( 'allowEmpty' )) {
  2809. $this->dtstart = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
  2810. return TRUE;
  2811. }
  2812. else
  2813. return FALSE;
  2814. }
  2815. $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName, $this->getConfig( 'TZID' ));
  2816. return TRUE;
  2817. }
  2818. /*********************************************************************************/
  2819. /**
  2820. * Property Name: DUE
  2821. */
  2822. /**
  2823. * creates formatted output for calendar component property due
  2824. *
  2825. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2826. * @since 2.14.4 - 2012-09-26
  2827. * @return string
  2828. */
  2829. function createDue() {
  2830. if( empty( $this->due )) return FALSE;
  2831. if( !isset( $this->due['value']['year'] ) &&
  2832. !isset( $this->due['value']['month'] ) &&
  2833. !isset( $this->due['value']['day'] ) &&
  2834. !isset( $this->due['value']['hour'] ) &&
  2835. !isset( $this->due['value']['min'] ) &&
  2836. !isset( $this->due['value']['sec'] )) {
  2837. if( $this->getConfig( 'allowEmpty' ))
  2838. return $this->_createElement( 'DUE' );
  2839. else
  2840. return FALSE;
  2841. }
  2842. $parno = ( isset( $this->due['params']['VALUE'] ) && ( 'DATE' == $this->due['params']['VALUE'] )) ? 3 : null;
  2843. $formatted = iCalUtilityFunctions::_date2strdate( $this->due['value'], $parno );
  2844. $attributes = $this->_createParams( $this->due['params'] );
  2845. return $this->_createElement( 'DUE', $attributes, $formatted );
  2846. }
  2847. /**
  2848. * set calendar component property due
  2849. *
  2850. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2851. * @since 2.4.8 - 2008-11-04
  2852. * @param mixed $year
  2853. * @param mixed $month optional
  2854. * @param int $day optional
  2855. * @param int $hour optional
  2856. * @param int $min optional
  2857. * @param int $sec optional
  2858. * @param array $params optional
  2859. * @return bool
  2860. */
  2861. function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
  2862. if( empty( $year )) {
  2863. if( $this->getConfig( 'allowEmpty' )) {
  2864. $this->due = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
  2865. return TRUE;
  2866. }
  2867. else
  2868. return FALSE;
  2869. }
  2870. $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
  2871. return TRUE;
  2872. }
  2873. /*********************************************************************************/
  2874. /**
  2875. * Property Name: DURATION
  2876. */
  2877. /**
  2878. * creates formatted output for calendar component property duration
  2879. *
  2880. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2881. * @since 2.4.8 - 2008-10-21
  2882. * @return string
  2883. */
  2884. function createDuration() {
  2885. if( empty( $this->duration )) return FALSE;
  2886. if( !isset( $this->duration['value']['week'] ) &&
  2887. !isset( $this->duration['value']['day'] ) &&
  2888. !isset( $this->duration['value']['hour'] ) &&
  2889. !isset( $this->duration['value']['min'] ) &&
  2890. !isset( $this->duration['value']['sec'] ))
  2891. if( $this->getConfig( 'allowEmpty' ))
  2892. return $this->_createElement( 'DURATION', array(), null );
  2893. else return FALSE;
  2894. $attributes = $this->_createParams( $this->duration['params'] );
  2895. return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_duration2str( $this->duration['value'] ));
  2896. }
  2897. /**
  2898. * set calendar component property duration
  2899. *
  2900. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2901. * @since 2.4.8 - 2008-11-04
  2902. * @param mixed $week
  2903. * @param mixed $day optional
  2904. * @param int $hour optional
  2905. * @param int $min optional
  2906. * @param int $sec optional
  2907. * @param array $params optional
  2908. * @return bool
  2909. */
  2910. function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
  2911. if( empty( $week )) if( $this->getConfig( 'allowEmpty' )) $week = null; else return FALSE;
  2912. if( is_array( $week ) && ( 1 <= count( $week )))
  2913. $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
  2914. elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) {
  2915. $week = trim( $week );
  2916. if( in_array( substr( $week, 0, 1 ), array( '+', '-' )))
  2917. $week = substr( $week, 1 );
  2918. $this->duration = array( 'value' => iCalUtilityFunctions::_durationStr2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
  2919. }
  2920. elseif( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec ))
  2921. return FALSE;
  2922. else
  2923. $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions::_setParams( $params ));
  2924. return TRUE;
  2925. }
  2926. /*********************************************************************************/
  2927. /**
  2928. * Property Name: EXDATE
  2929. */
  2930. /**
  2931. * creates formatted output for calendar component property exdate
  2932. *
  2933. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2934. * @since 2.16.5 - 2012-12-28
  2935. * @return string
  2936. */
  2937. function createExdate() {
  2938. if( empty( $this->exdate )) return FALSE;
  2939. $output = null;
  2940. $exdates = array();
  2941. foreach( $this->exdate as $theExdate ) {
  2942. if( empty( $theExdate['value'] )) {
  2943. if( $this->getConfig( 'allowEmpty' ))
  2944. $output .= $this->_createElement( 'EXDATE' );
  2945. continue;
  2946. }
  2947. if( 1 < count( $theExdate['value'] ))
  2948. usort( $theExdate['value'], array( 'iCalUtilityFunctions', '_sortExdate1' ));
  2949. $exdates[] = $theExdate;
  2950. }
  2951. if( 1 < count( $exdates ))
  2952. usort( $exdates, array( 'iCalUtilityFunctions', '_sortExdate2' ));
  2953. foreach( $exdates as $theExdate ) {
  2954. $content = $attributes = null;
  2955. foreach( $theExdate['value'] as $eix => $exdatePart ) {
  2956. $parno = count( $exdatePart );
  2957. $formatted = iCalUtilityFunctions::_date2strdate( $exdatePart, $parno );
  2958. if( isset( $theExdate['params']['TZID'] ))
  2959. $formatted = str_replace( 'Z', '', $formatted);
  2960. if( 0 < $eix ) {
  2961. if( isset( $theExdate['value'][0]['tz'] )) {
  2962. if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) ||
  2963. ( 'Z' == $theExdate['value'][0]['tz'] )) {
  2964. if( 'Z' != substr( $formatted, -1 ))
  2965. $formatted .= 'Z';
  2966. }
  2967. else
  2968. $formatted = str_replace( 'Z', '', $formatted );
  2969. }
  2970. else
  2971. $formatted = str_replace( 'Z', '', $formatted );
  2972. } // end if( 0 < $eix )
  2973. $content .= ( 0 < $eix ) ? ','.$formatted : $formatted;
  2974. } // end foreach( $theExdate['value'] as $eix => $exdatePart )
  2975. $attributes .= $this->_createParams( $theExdate['params'] );
  2976. $output .= $this->_createElement( 'EXDATE', $attributes, $content );
  2977. } // end foreach( $exdates as $theExdate )
  2978. return $output;
  2979. }
  2980. /**
  2981. * set calendar component property exdate
  2982. *
  2983. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  2984. * @since 2.14.1 - 2012-10-02
  2985. * @param array exdates
  2986. * @param array $params, optional
  2987. * @param integer $index, optional
  2988. * @return bool
  2989. */
  2990. function setExdate( $exdates, $params=FALSE, $index=FALSE ) {
  2991. if( empty( $exdates )) {
  2992. if( $this->getConfig( 'allowEmpty' )) {
  2993. iCalUtilityFunctions::_setMval( $this->exdate, null, $params, FALSE, $index );
  2994. return TRUE;
  2995. }
  2996. else
  2997. return FALSE;
  2998. }
  2999. $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
  3000. $toZ = ( isset( $input['params']['TZID'] ) && in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) ? TRUE : FALSE;
  3001. /* ev. check 1:st date and save ev. timezone **/
  3002. iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] );
  3003. iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter
  3004. foreach( $exdates as $eix => $theExdate ) {
  3005. iCalUtilityFunctions::_strDate2arr( $theExdate );
  3006. if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate )) {
  3007. if( isset( $theExdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theExdate['tz'] )) {
  3008. if( isset( $input['params']['TZID'] ))
  3009. $theExdate['tz'] = $input['params']['TZID'];
  3010. else
  3011. $input['params']['TZID'] = $theExdate['tz'];
  3012. }
  3013. $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno );
  3014. }
  3015. elseif( is_array( $theExdate )) {
  3016. $d = iCalUtilityFunctions::_chkDateArr( $theExdate, $parno );
  3017. if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) {
  3018. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
  3019. $exdatea = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  3020. unset( $exdatea['unparsedtext'] );
  3021. }
  3022. else
  3023. $exdatea = $d;
  3024. }
  3025. elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18
  3026. $exdatea = iCalUtilityFunctions::_strdate2date( $theExdate, $parno );
  3027. unset( $exdatea['unparsedtext'] );
  3028. }
  3029. if( 3 == $parno )
  3030. unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] );
  3031. elseif( isset( $exdatea['tz'] ))
  3032. $exdatea['tz'] = (string) $exdatea['tz'];
  3033. if( isset( $input['params']['TZID'] ) ||
  3034. ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) ||
  3035. ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) ||
  3036. ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] )))
  3037. unset( $exdatea['tz'] );
  3038. if( $toZ ) // time zone Z
  3039. $exdatea['tz'] = 'Z';
  3040. $input['value'][] = $exdatea;
  3041. }
  3042. if( 0 >= count( $input['value'] ))
  3043. return FALSE;
  3044. if( 3 == $parno ) {
  3045. $input['params']['VALUE'] = 'DATE';
  3046. unset( $input['params']['TZID'] );
  3047. }
  3048. if( $toZ ) // time zone Z
  3049. unset( $input['params']['TZID'] );
  3050. iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index );
  3051. return TRUE;
  3052. }
  3053. /*********************************************************************************/
  3054. /**
  3055. * Property Name: EXRULE
  3056. */
  3057. /**
  3058. * creates formatted output for calendar component property exrule
  3059. *
  3060. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3061. * @since 2.4.8 - 2008-10-22
  3062. * @return string
  3063. */
  3064. function createExrule() {
  3065. if( empty( $this->exrule )) return FALSE;
  3066. return $this->_format_recur( 'EXRULE', $this->exrule );
  3067. }
  3068. /**
  3069. * set calendar component property exdate
  3070. *
  3071. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3072. * @since 2.5.1 - 2008-11-05
  3073. * @param array $exruleset
  3074. * @param array $params, optional
  3075. * @param integer $index, optional
  3076. * @return bool
  3077. */
  3078. function setExrule( $exruleset, $params=FALSE, $index=FALSE ) {
  3079. if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = null; else return FALSE;
  3080. iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index );
  3081. return TRUE;
  3082. }
  3083. /*********************************************************************************/
  3084. /**
  3085. * Property Name: FREEBUSY
  3086. */
  3087. /**
  3088. * creates formatted output for calendar component property freebusy
  3089. *
  3090. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3091. * @since 2.1.23 - 2012-02-16
  3092. * @return string
  3093. */
  3094. function createFreebusy() {
  3095. if( empty( $this->freebusy )) return FALSE;
  3096. $output = null;
  3097. foreach( $this->freebusy as $freebusyPart ) {
  3098. if( empty( $freebusyPart['value'] ) || (( 1 == count( $freebusyPart['value'] )) && isset( $freebusyPart['value']['fbtype'] ))) {
  3099. if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' );
  3100. continue;
  3101. }
  3102. $attributes = $content = null;
  3103. if( isset( $freebusyPart['value']['fbtype'] )) {
  3104. $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype'];
  3105. unset( $freebusyPart['value']['fbtype'] );
  3106. $freebusyPart['value'] = array_values( $freebusyPart['value'] );
  3107. }
  3108. else
  3109. $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY';
  3110. $attributes .= $this->_createParams( $freebusyPart['params'] );
  3111. $fno = 1;
  3112. $cnt = count( $freebusyPart['value']);
  3113. foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) {
  3114. $formatted = iCalUtilityFunctions::_date2strdate( $freebusyPeriod[0] );
  3115. $content .= $formatted;
  3116. $content .= '/';
  3117. $cnt2 = count( $freebusyPeriod[1]);
  3118. if( array_key_exists( 'year', $freebusyPeriod[1] )) // date-time
  3119. $cnt2 = 7;
  3120. elseif( array_key_exists( 'week', $freebusyPeriod[1] )) // duration
  3121. $cnt2 = 5;
  3122. if(( 7 == $cnt2 ) && // period= -> date-time
  3123. isset( $freebusyPeriod[1]['year'] ) &&
  3124. isset( $freebusyPeriod[1]['month'] ) &&
  3125. isset( $freebusyPeriod[1]['day'] )) {
  3126. $content .= iCalUtilityFunctions::_date2strdate( $freebusyPeriod[1] );
  3127. }
  3128. else { // period= -> dur-time
  3129. $content .= iCalUtilityFunctions::_duration2str( $freebusyPeriod[1] );
  3130. }
  3131. if( $fno < $cnt )
  3132. $content .= ',';
  3133. $fno++;
  3134. }
  3135. $output .= $this->_createElement( 'FREEBUSY', $attributes, $content );
  3136. }
  3137. return $output;
  3138. }
  3139. /**
  3140. * set calendar component property freebusy
  3141. *
  3142. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3143. * @since 2.10.30 - 2012-01-16
  3144. * @param string $fbType
  3145. * @param array $fbValues
  3146. * @param array $params, optional
  3147. * @param integer $index, optional
  3148. * @return bool
  3149. */
  3150. function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) {
  3151. if( empty( $fbValues )) {
  3152. if( $this->getConfig( 'allowEmpty' )) {
  3153. iCalUtilityFunctions::_setMval( $this->freebusy, null, $params, FALSE, $index );
  3154. return TRUE;
  3155. }
  3156. else
  3157. return FALSE;
  3158. }
  3159. $fbType = strtoupper( $fbType );
  3160. if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) &&
  3161. ( 'X-' != substr( $fbType, 0, 2 )))
  3162. $fbType = 'BUSY';
  3163. $input = array( 'fbtype' => $fbType );
  3164. foreach( $fbValues as $fbPeriod ) { // periods => period
  3165. if( empty( $fbPeriod ))
  3166. continue;
  3167. $freebusyPeriod = array();
  3168. foreach( $fbPeriod as $fbMember ) { // pairs => singlepart
  3169. $freebusyPairMember = array();
  3170. if( is_array( $fbMember )) {
  3171. if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value
  3172. $freebusyPairMember = iCalUtilityFunctions::_chkDateArr( $fbMember, 7 );
  3173. $freebusyPairMember['tz'] = 'Z';
  3174. }
  3175. elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value
  3176. $freebusyPairMember = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 );
  3177. $freebusyPairMember['tz'] = 'Z';
  3178. }
  3179. else { // array format duration
  3180. $freebusyPairMember = iCalUtilityFunctions::_duration2arr( $fbMember );
  3181. }
  3182. }
  3183. elseif(( 3 <= strlen( trim( $fbMember ))) && // string format duration
  3184. ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) {
  3185. if( 'P' != $fbMember{0} )
  3186. $fbmember = substr( $fbMember, 1 );
  3187. $freebusyPairMember = iCalUtilityFunctions::_durationStr2arr( $fbMember );
  3188. }
  3189. elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18
  3190. $freebusyPairMember = iCalUtilityFunctions::_strdate2date( $fbMember, 7 );
  3191. unset( $freebusyPairMember['unparsedtext'] );
  3192. $freebusyPairMember['tz'] = 'Z';
  3193. }
  3194. $freebusyPeriod[] = $freebusyPairMember;
  3195. }
  3196. $input[] = $freebusyPeriod;
  3197. }
  3198. iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index );
  3199. return TRUE;
  3200. }
  3201. /*********************************************************************************/
  3202. /**
  3203. * Property Name: GEO
  3204. */
  3205. /**
  3206. * creates formatted output for calendar component property geo
  3207. *
  3208. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3209. * @since 2.12.6 - 2012-04-21
  3210. * @return string
  3211. */
  3212. function createGeo() {
  3213. if( empty( $this->geo )) return FALSE;
  3214. if( empty( $this->geo['value'] ))
  3215. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE;
  3216. $attributes = $this->_createParams( $this->geo['params'] );
  3217. if( 0.0 < $this->geo['value']['latitude'] )
  3218. $sign = '+';
  3219. else
  3220. $sign = ( 0.0 > $this->geo['value']['latitude'] ) ? '-' : '';
  3221. $content = $sign.sprintf( "%09.6f", abs( $this->geo['value']['latitude'] )); // sprintf && lpad && float && sign !"#¤%&/(
  3222. $content = rtrim( rtrim( $content, '0' ), '.' );
  3223. if( 0.0 < $this->geo['value']['longitude'] )
  3224. $sign = '+';
  3225. else
  3226. $sign = ( 0.0 > $this->geo['value']['longitude'] ) ? '-' : '';
  3227. $content .= ';'.$sign.sprintf( '%8.6f', abs( $this->geo['value']['longitude'] )); // sprintf && lpad && float && sign !"#¤%&/(
  3228. $content = rtrim( rtrim( $content, '0' ), '.' );
  3229. return $this->_createElement( 'GEO', $attributes, $content );
  3230. }
  3231. /**
  3232. * set calendar component property geo
  3233. *
  3234. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3235. * @since 2.12.5 - 2012-04-21
  3236. * @param float $latitude
  3237. * @param float $longitude
  3238. * @param array $params optional
  3239. * @return bool
  3240. */
  3241. function setGeo( $latitude, $longitude, $params=FALSE ) {
  3242. if(( !empty( $latitude ) || ( 0 == $latitude )) &&
  3243. ( !empty( $longitude ) || ( 0 == $longitude ))) {
  3244. if( !is_array( $this->geo )) $this->geo = array();
  3245. $this->geo['value']['latitude'] = (float) $latitude;
  3246. $this->geo['value']['longitude'] = (float) $longitude;
  3247. $this->geo['params'] = iCalUtilityFunctions::_setParams( $params );
  3248. }
  3249. elseif( $this->getConfig( 'allowEmpty' ))
  3250. $this->geo = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
  3251. else
  3252. return FALSE;
  3253. return TRUE;
  3254. }
  3255. /*********************************************************************************/
  3256. /**
  3257. * Property Name: LAST-MODIFIED
  3258. */
  3259. /**
  3260. * creates formatted output for calendar component property last-modified
  3261. *
  3262. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3263. * @since 2.4.8 - 2008-10-21
  3264. * @return string
  3265. */
  3266. function createLastModified() {
  3267. if( empty( $this->lastmodified )) return FALSE;
  3268. $attributes = $this->_createParams( $this->lastmodified['params'] );
  3269. $formatted = iCalUtilityFunctions::_date2strdate( $this->lastmodified['value'], 7 );
  3270. return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted );
  3271. }
  3272. /**
  3273. * set calendar component property completed
  3274. *
  3275. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3276. * @since 2.4.8 - 2008-10-23
  3277. * @param mixed $year optional
  3278. * @param mixed $month optional
  3279. * @param int $day optional
  3280. * @param int $hour optional
  3281. * @param int $min optional
  3282. * @param int $sec optional
  3283. * @param array $params optional
  3284. * @return boll
  3285. */
  3286. function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
  3287. if( empty( $year ))
  3288. $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
  3289. $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
  3290. return TRUE;
  3291. }
  3292. /*********************************************************************************/
  3293. /**
  3294. * Property Name: LOCATION
  3295. */
  3296. /**
  3297. * creates formatted output for calendar component property location
  3298. *
  3299. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3300. * @since 2.16.2 - 2012-12-18
  3301. * @return string
  3302. */
  3303. function createLocation() {
  3304. if( empty( $this->location )) return FALSE;
  3305. if( empty( $this->location['value'] ))
  3306. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE;
  3307. $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' ));
  3308. $content = iCalUtilityFunctions::_strrep( $this->location['value'], $this->format, $this->nl );
  3309. return $this->_createElement( 'LOCATION', $attributes, $content );
  3310. }
  3311. /**
  3312. * set calendar component property location
  3313. '
  3314. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3315. * @since 2.4.8 - 2008-11-04
  3316. * @param string $value
  3317. * @param array params optional
  3318. * @return bool
  3319. */
  3320. function setLocation( $value, $params=FALSE ) {
  3321. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  3322. $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  3323. return TRUE;
  3324. }
  3325. /*********************************************************************************/
  3326. /**
  3327. * Property Name: ORGANIZER
  3328. */
  3329. /**
  3330. * creates formatted output for calendar component property organizer
  3331. *
  3332. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3333. * @since 2.6.33 - 2010-12-17
  3334. * @return string
  3335. */
  3336. function createOrganizer() {
  3337. if( empty( $this->organizer )) return FALSE;
  3338. if( empty( $this->organizer['value'] ))
  3339. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE;
  3340. $attributes = $this->_createParams( $this->organizer['params']
  3341. , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' ));
  3342. return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] );
  3343. }
  3344. /**
  3345. * set calendar component property organizer
  3346. *
  3347. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3348. * @since 2.12.18 - 2012-07-13
  3349. * @param string $value
  3350. * @param array params optional
  3351. * @return bool
  3352. */
  3353. function setOrganizer( $value, $params=FALSE ) {
  3354. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  3355. if( !empty( $value )) {
  3356. if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
  3357. $value = 'MAILTO:'.$value;
  3358. elseif( !empty( $value ))
  3359. $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos );
  3360. $value = str_replace( 'mailto:', 'MAILTO:', $value );
  3361. }
  3362. $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  3363. if( isset( $this->organizer['params']['SENT-BY'] )){
  3364. if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 )))
  3365. $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY'];
  3366. else
  3367. $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 );
  3368. }
  3369. return TRUE;
  3370. }
  3371. /*********************************************************************************/
  3372. /**
  3373. * Property Name: PERCENT-COMPLETE
  3374. */
  3375. /**
  3376. * creates formatted output for calendar component property percent-complete
  3377. *
  3378. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3379. * @since 2.9.3 - 2011-05-14
  3380. * @return string
  3381. */
  3382. function createPercentComplete() {
  3383. if( !isset($this->percentcomplete) || ( empty( $this->percentcomplete ) && !is_numeric( $this->percentcomplete ))) return FALSE;
  3384. if( !isset( $this->percentcomplete['value'] ) || ( empty( $this->percentcomplete['value'] ) && !is_numeric( $this->percentcomplete['value'] )))
  3385. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE;
  3386. $attributes = $this->_createParams( $this->percentcomplete['params'] );
  3387. return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] );
  3388. }
  3389. /**
  3390. * set calendar component property percent-complete
  3391. *
  3392. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3393. * @since 2.9.3 - 2011-05-14
  3394. * @param int $value
  3395. * @param array $params optional
  3396. * @return bool
  3397. */
  3398. function setPercentComplete( $value, $params=FALSE ) {
  3399. if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  3400. $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  3401. return TRUE;
  3402. }
  3403. /*********************************************************************************/
  3404. /**
  3405. * Property Name: PRIORITY
  3406. */
  3407. /**
  3408. * creates formatted output for calendar component property priority
  3409. *
  3410. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3411. * @since 2.9.3 - 2011-05-14
  3412. * @return string
  3413. */
  3414. function createPriority() {
  3415. if( !isset($this->priority) || ( empty( $this->priority ) && !is_numeric( $this->priority ))) return FALSE;
  3416. if( !isset( $this->priority['value'] ) || ( empty( $this->priority['value'] ) && !is_numeric( $this->priority['value'] )))
  3417. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE;
  3418. $attributes = $this->_createParams( $this->priority['params'] );
  3419. return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] );
  3420. }
  3421. /**
  3422. * set calendar component property priority
  3423. *
  3424. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3425. * @since 2.9.3 - 2011-05-14
  3426. * @param int $value
  3427. * @param array $params optional
  3428. * @return bool
  3429. */
  3430. function setPriority( $value, $params=FALSE ) {
  3431. if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  3432. $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  3433. return TRUE;
  3434. }
  3435. /*********************************************************************************/
  3436. /**
  3437. * Property Name: RDATE
  3438. */
  3439. /**
  3440. * creates formatted output for calendar component property rdate
  3441. *
  3442. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3443. * @since 2.16.9 - 2013-01-09
  3444. * @return string
  3445. */
  3446. function createRdate() {
  3447. if( empty( $this->rdate )) return FALSE;
  3448. $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
  3449. $output = null;
  3450. $rdates = array();
  3451. foreach( $this->rdate as $rpix => $theRdate ) {
  3452. if( empty( $theRdate['value'] )) {
  3453. if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' );
  3454. continue;
  3455. }
  3456. if( $utctime )
  3457. unset( $theRdate['params']['TZID'] );
  3458. if( 1 < count( $theRdate['value'] ))
  3459. usort( $theRdate['value'], array( 'iCalUtilityFunctions', '_sortRdate1' ));
  3460. $rdates[] = $theRdate;
  3461. }
  3462. if( 1 < count( $rdates ))
  3463. usort( $rdates, array( 'iCalUtilityFunctions', '_sortRdate2' ));
  3464. foreach( $rdates as $rpix => $theRdate ) {
  3465. $attributes = $this->_createParams( $theRdate['params'] );
  3466. $cnt = count( $theRdate['value'] );
  3467. $content = null;
  3468. $rno = 1;
  3469. foreach( $theRdate['value'] as $rix => $rdatePart ) {
  3470. $contentPart = null;
  3471. if( is_array( $rdatePart ) &&
  3472. isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD
  3473. if( $utctime )
  3474. unset( $rdatePart[0]['tz'] );
  3475. $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[0] ); // PERIOD part 1
  3476. if( $utctime || !empty( $theRdate['params']['TZID'] ))
  3477. $formatted = str_replace( 'Z', '', $formatted);
  3478. $contentPart .= $formatted;
  3479. $contentPart .= '/';
  3480. $cnt2 = count( $rdatePart[1]);
  3481. if( array_key_exists( 'year', $rdatePart[1] )) {
  3482. if( array_key_exists( 'hour', $rdatePart[1] ))
  3483. $cnt2 = 7; // date-time
  3484. else
  3485. $cnt2 = 3; // date
  3486. }
  3487. elseif( array_key_exists( 'week', $rdatePart[1] )) // duration
  3488. $cnt2 = 5;
  3489. if(( 7 == $cnt2 ) && // period= -> date-time
  3490. isset( $rdatePart[1]['year'] ) &&
  3491. isset( $rdatePart[1]['month'] ) &&
  3492. isset( $rdatePart[1]['day'] )) {
  3493. if( $utctime )
  3494. unset( $rdatePart[1]['tz'] );
  3495. $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[1] ); // PERIOD part 2
  3496. if( $utctime || !empty( $theRdate['params']['TZID'] ))
  3497. $formatted = str_replace( 'Z', '', $formatted );
  3498. $contentPart .= $formatted;
  3499. }
  3500. else { // period= -> dur-time
  3501. $contentPart .= iCalUtilityFunctions::_duration2str( $rdatePart[1] );
  3502. }
  3503. } // PERIOD end
  3504. else { // SINGLE date start
  3505. if( $utctime )
  3506. unset( $rdatePart['tz'] );
  3507. $parno = ( isset( $theRdate['params']['VALUE'] ) && ( 'DATE' == isset( $theRdate['params']['VALUE'] ))) ? 3 : null;
  3508. $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart, $parno );
  3509. if( $utctime || !empty( $theRdate['params']['TZID'] ))
  3510. $formatted = str_replace( 'Z', '', $formatted);
  3511. $contentPart .= $formatted;
  3512. }
  3513. $content .= $contentPart;
  3514. if( $rno < $cnt )
  3515. $content .= ',';
  3516. $rno++;
  3517. }
  3518. $output .= $this->_createElement( 'RDATE', $attributes, $content );
  3519. }
  3520. return $output;
  3521. }
  3522. /**
  3523. * set calendar component property rdate
  3524. *
  3525. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3526. * @since 2.14.1 - 2012-10-04
  3527. * @param array $rdates
  3528. * @param array $params, optional
  3529. * @param integer $index, optional
  3530. * @return bool
  3531. */
  3532. function setRdate( $rdates, $params=FALSE, $index=FALSE ) {
  3533. if( empty( $rdates )) {
  3534. if( $this->getConfig( 'allowEmpty' )) {
  3535. iCalUtilityFunctions::_setMval( $this->rdate, null, $params, FALSE, $index );
  3536. return TRUE;
  3537. }
  3538. else
  3539. return FALSE;
  3540. }
  3541. $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
  3542. if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) {
  3543. unset( $input['params']['TZID'] );
  3544. $input['params']['VALUE'] = 'DATE-TIME';
  3545. }
  3546. $zArr = array( 'GMT', 'UTC', 'Z' );
  3547. $toZ = ( isset( $params['TZID'] ) && in_array( strtoupper( $params['TZID'] ), $zArr )) ? TRUE : FALSE;
  3548. /* check if PERIOD, if not set */
  3549. if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) &&
  3550. isset( $rdates[0] ) && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) &&
  3551. isset( $rdates[0][0] ) && isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) &&
  3552. (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) ||
  3553. iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) ||
  3554. ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] ))))) &&
  3555. ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] ))))))
  3556. $input['params']['VALUE'] = 'PERIOD';
  3557. /* check 1:st date, upd. $parno (opt) and save ev. timezone **/
  3558. $date = reset( $rdates );
  3559. if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD
  3560. $date = reset( $date );
  3561. iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] );
  3562. iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default
  3563. foreach( $rdates as $rpix => $theRdate ) {
  3564. $inputa = null;
  3565. iCalUtilityFunctions::_strDate2arr( $theRdate );
  3566. if( is_array( $theRdate )) {
  3567. if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD
  3568. foreach( $theRdate as $rix => $rPeriod ) {
  3569. iCalUtilityFunctions::_strDate2arr( $theRdate );
  3570. if( is_array( $rPeriod )) {
  3571. if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) { // timestamp
  3572. if( isset( $rPeriod['tz'] ) && !iCalUtilityFunctions::_isOffset( $rPeriod['tz'] )) {
  3573. if( isset( $input['params']['TZID'] ))
  3574. $rPeriod['tz'] = $input['params']['TZID'];
  3575. else
  3576. $input['params']['TZID'] = $rPeriod['tz'];
  3577. }
  3578. $inputab = iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno );
  3579. }
  3580. elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod )) {
  3581. $d = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_chkDateArr( $rPeriod, $parno ) : iCalUtilityFunctions::_chkDateArr( $rPeriod, 6 );
  3582. if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) {
  3583. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
  3584. $inputab = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  3585. unset( $inputab['unparsedtext'] );
  3586. }
  3587. else
  3588. $inputab = $d;
  3589. }
  3590. elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) { // text-date
  3591. $inputab = iCalUtilityFunctions::_strdate2date( reset( $rPeriod ), $parno );
  3592. unset( $inputab['unparsedtext'] );
  3593. }
  3594. else // array format duration
  3595. $inputab = iCalUtilityFunctions::_duration2arr( $rPeriod );
  3596. }
  3597. elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration
  3598. ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) {
  3599. if( 'P' != $rPeriod[0] )
  3600. $rPeriod = substr( $rPeriod, 1 );
  3601. $inputab = iCalUtilityFunctions::_durationStr2arr( $rPeriod );
  3602. }
  3603. elseif( 8 <= strlen( trim( $rPeriod ))) { // text date ex. 2006-08-03 10:12:18
  3604. $inputab = iCalUtilityFunctions::_strdate2date( $rPeriod, $parno );
  3605. unset( $inputab['unparsedtext'] );
  3606. }
  3607. if(( 0 == $rpix ) && ( 0 == $rix )) {
  3608. if( isset( $inputab['tz'] ) && in_array( strtoupper( $inputab['tz'] ), $zArr )) {
  3609. $inputab['tz'] = 'Z';
  3610. $toZ = TRUE;
  3611. }
  3612. }
  3613. else {
  3614. if( isset( $inputa[0]['tz'] ) && ( 'Z' == $inputa[0]['tz'] ) && isset( $inputab['year'] ))
  3615. $inputab['tz'] = 'Z';
  3616. else
  3617. unset( $inputab['tz'] );
  3618. }
  3619. if( $toZ && isset( $inputab['year'] ) )
  3620. $inputab['tz'] = 'Z';
  3621. $inputa[] = $inputab;
  3622. }
  3623. } // PERIOD end
  3624. elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) { // timestamp
  3625. if( isset( $theRdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theRdate['tz'] )) {
  3626. if( isset( $input['params']['TZID'] ))
  3627. $theRdate['tz'] = $input['params']['TZID'];
  3628. else
  3629. $input['params']['TZID'] = $theRdate['tz'];
  3630. }
  3631. $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno );
  3632. }
  3633. else { // date[-time]
  3634. $inputa = iCalUtilityFunctions::_chkDateArr( $theRdate, $parno );
  3635. if( isset( $inputa['tz'] ) && ( 'Z' != $inputa['tz'] ) && iCalUtilityFunctions::_isOffset( $inputa['tz'] )) {
  3636. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $inputa['year'], $inputa['month'], $inputa['day'], $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] );
  3637. $inputa = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  3638. unset( $inputa['unparsedtext'] );
  3639. }
  3640. }
  3641. }
  3642. elseif( 8 <= strlen( trim( $theRdate ))) { // text date ex. 2006-08-03 10:12:18
  3643. $inputa = iCalUtilityFunctions::_strdate2date( $theRdate, $parno );
  3644. unset( $inputa['unparsedtext'] );
  3645. if( $toZ )
  3646. $inputa['tz'] = 'Z';
  3647. }
  3648. if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD
  3649. if(( 0 == $rpix ) && !$toZ )
  3650. $toZ = ( isset( $inputa['tz'] ) && in_array( strtoupper( $inputa['tz'] ), $zArr )) ? TRUE : FALSE;
  3651. if( $toZ )
  3652. $inputa['tz'] = 'Z';
  3653. if( 3 == $parno )
  3654. unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] );
  3655. elseif( isset( $inputa['tz'] ))
  3656. $inputa['tz'] = (string) $inputa['tz'];
  3657. if( isset( $input['params']['TZID'] ) || ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))))
  3658. if( !$toZ )
  3659. unset( $inputa['tz'] );
  3660. }
  3661. $input['value'][] = $inputa;
  3662. }
  3663. if( 3 == $parno ) {
  3664. $input['params']['VALUE'] = 'DATE';
  3665. unset( $input['params']['TZID'] );
  3666. }
  3667. if( $toZ )
  3668. unset( $input['params']['TZID'] );
  3669. iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index );
  3670. return TRUE;
  3671. }
  3672. /*********************************************************************************/
  3673. /**
  3674. * Property Name: RECURRENCE-ID
  3675. */
  3676. /**
  3677. * creates formatted output for calendar component property recurrence-id
  3678. *
  3679. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3680. * @since 2.14.4 - 2012-09-26
  3681. * @return string
  3682. */
  3683. function createRecurrenceid() {
  3684. if( empty( $this->recurrenceid )) return FALSE;
  3685. if( empty( $this->recurrenceid['value'] ))
  3686. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE;
  3687. $parno = ( isset( $this->recurrenceid['params']['VALUE'] ) && ( 'DATE' == $this->recurrenceid['params']['VALUE'] )) ? 3 : null;
  3688. $formatted = iCalUtilityFunctions::_date2strdate( $this->recurrenceid['value'], $parno );
  3689. $attributes = $this->_createParams( $this->recurrenceid['params'] );
  3690. return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted );
  3691. }
  3692. /**
  3693. * set calendar component property recurrence-id
  3694. *
  3695. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3696. * @since 2.9.6 - 2011-05-15
  3697. * @param mixed $year
  3698. * @param mixed $month optional
  3699. * @param int $day optional
  3700. * @param int $hour optional
  3701. * @param int $min optional
  3702. * @param int $sec optional
  3703. * @param array $params optional
  3704. * @return bool
  3705. */
  3706. function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
  3707. if( empty( $year )) {
  3708. if( $this->getConfig( 'allowEmpty' )) {
  3709. $this->recurrenceid = array( 'value' => null, 'params' => null );
  3710. return TRUE;
  3711. }
  3712. else
  3713. return FALSE;
  3714. }
  3715. $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
  3716. return TRUE;
  3717. }
  3718. /*********************************************************************************/
  3719. /**
  3720. * Property Name: RELATED-TO
  3721. */
  3722. /**
  3723. * creates formatted output for calendar component property related-to
  3724. *
  3725. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3726. * @since 2.16.2 - 2012-12-18
  3727. * @return string
  3728. */
  3729. function createRelatedTo() {
  3730. if( empty( $this->relatedto )) return FALSE;
  3731. $output = null;
  3732. foreach( $this->relatedto as $relation ) {
  3733. if( !empty( $relation['value'] ))
  3734. $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ), iCalUtilityFunctions::_strrep( $relation['value'], $this->format, $this->nl ));
  3735. elseif( $this->getConfig( 'allowEmpty' ))
  3736. $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ));
  3737. }
  3738. return $output;
  3739. }
  3740. /**
  3741. * set calendar component property related-to
  3742. *
  3743. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3744. * @since 2.11.24 - 2012-02-23
  3745. * @param float $relid
  3746. * @param array $params, optional
  3747. * @param index $index, optional
  3748. * @return bool
  3749. */
  3750. function setRelatedTo( $value, $params=FALSE, $index=FALSE ) {
  3751. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  3752. iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default
  3753. iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index );
  3754. return TRUE;
  3755. }
  3756. /*********************************************************************************/
  3757. /**
  3758. * Property Name: REPEAT
  3759. */
  3760. /**
  3761. * creates formatted output for calendar component property repeat
  3762. *
  3763. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3764. * @since 2.9.3 - 2011-05-14
  3765. * @return string
  3766. */
  3767. function createRepeat() {
  3768. if( !isset( $this->repeat ) || ( empty( $this->repeat ) && !is_numeric( $this->repeat ))) return FALSE;
  3769. if( !isset( $this->repeat['value']) || ( empty( $this->repeat['value'] ) && !is_numeric( $this->repeat['value'] )))
  3770. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE;
  3771. $attributes = $this->_createParams( $this->repeat['params'] );
  3772. return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] );
  3773. }
  3774. /**
  3775. * set calendar component property repeat
  3776. *
  3777. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3778. * @since 2.9.3 - 2011-05-14
  3779. * @param string $value
  3780. * @param array $params optional
  3781. * @return void
  3782. */
  3783. function setRepeat( $value, $params=FALSE ) {
  3784. if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  3785. $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  3786. return TRUE;
  3787. }
  3788. /*********************************************************************************/
  3789. /**
  3790. * Property Name: REQUEST-STATUS
  3791. */
  3792. /**
  3793. * creates formatted output for calendar component property request-status
  3794. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3795. * @since 2.16.2 - 2012-12-18
  3796. * @return string
  3797. */
  3798. function createRequestStatus() {
  3799. if( empty( $this->requeststatus )) return FALSE;
  3800. $output = null;
  3801. foreach( $this->requeststatus as $rstat ) {
  3802. if( empty( $rstat['value']['statcode'] )) {
  3803. if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' );
  3804. continue;
  3805. }
  3806. $attributes = $this->_createParams( $rstat['params'], array( 'LANGUAGE' ));
  3807. $content = number_format( (float) $rstat['value']['statcode'], 2, '.', '');
  3808. $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['text'], $this->format, $this->nl );
  3809. if( isset( $rstat['value']['extdata'] ))
  3810. $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['extdata'], $this->format, $this->nl );
  3811. $output .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content );
  3812. }
  3813. return $output;
  3814. }
  3815. /**
  3816. * set calendar component property request-status
  3817. *
  3818. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3819. * @since 2.5.1 - 2008-11-05
  3820. * @param float $statcode
  3821. * @param string $text
  3822. * @param string $extdata, optional
  3823. * @param array $params, optional
  3824. * @param integer $index, optional
  3825. * @return bool
  3826. */
  3827. function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) {
  3828. if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = null; else return FALSE;
  3829. $input = array( 'statcode' => $statcode, 'text' => $text );
  3830. if( $extdata )
  3831. $input['extdata'] = $extdata;
  3832. iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index );
  3833. return TRUE;
  3834. }
  3835. /*********************************************************************************/
  3836. /**
  3837. * Property Name: RESOURCES
  3838. */
  3839. /**
  3840. * creates formatted output for calendar component property resources
  3841. *
  3842. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3843. * @since 2.16.2 - 2012-12-18
  3844. * @return string
  3845. */
  3846. function createResources() {
  3847. if( empty( $this->resources )) return FALSE;
  3848. $output = null;
  3849. foreach( $this->resources as $resource ) {
  3850. if( empty( $resource['value'] )) {
  3851. if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' );
  3852. continue;
  3853. }
  3854. $attributes = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' ));
  3855. if( is_array( $resource['value'] )) {
  3856. foreach( $resource['value'] as $rix => $resourcePart )
  3857. $resource['value'][$rix] = iCalUtilityFunctions::_strrep( $resourcePart, $this->format, $this->nl );
  3858. $content = implode( ',', $resource['value'] );
  3859. }
  3860. else
  3861. $content = iCalUtilityFunctions::_strrep( $resource['value'], $this->format, $this->nl );
  3862. $output .= $this->_createElement( 'RESOURCES', $attributes, $content );
  3863. }
  3864. return $output;
  3865. }
  3866. /**
  3867. * set calendar component property recources
  3868. *
  3869. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3870. * @since 2.5.1 - 2008-11-05
  3871. * @param mixed $value
  3872. * @param array $params, optional
  3873. * @param integer $index, optional
  3874. * @return bool
  3875. */
  3876. function setResources( $value, $params=FALSE, $index=FALSE ) {
  3877. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  3878. iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index );
  3879. return TRUE;
  3880. }
  3881. /*********************************************************************************/
  3882. /**
  3883. * Property Name: RRULE
  3884. */
  3885. /**
  3886. * creates formatted output for calendar component property rrule
  3887. *
  3888. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3889. * @since 2.4.8 - 2008-10-21
  3890. * @return string
  3891. */
  3892. function createRrule() {
  3893. if( empty( $this->rrule )) return FALSE;
  3894. return $this->_format_recur( 'RRULE', $this->rrule );
  3895. }
  3896. /**
  3897. * set calendar component property rrule
  3898. *
  3899. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3900. * @since 2.5.1 - 2008-11-05
  3901. * @param array $rruleset
  3902. * @param array $params, optional
  3903. * @param integer $index, optional
  3904. * @return void
  3905. */
  3906. function setRrule( $rruleset, $params=FALSE, $index=FALSE ) {
  3907. if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = null; else return FALSE;
  3908. iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index );
  3909. return TRUE;
  3910. }
  3911. /*********************************************************************************/
  3912. /**
  3913. * Property Name: SEQUENCE
  3914. */
  3915. /**
  3916. * creates formatted output for calendar component property sequence
  3917. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3918. * @since 2.9.3 - 2011-05-14
  3919. * @return string
  3920. */
  3921. function createSequence() {
  3922. if( !isset( $this->sequence ) || ( empty( $this->sequence ) && !is_numeric( $this->sequence ))) return FALSE;
  3923. if(( !isset($this->sequence['value'] ) || ( empty( $this->sequence['value'] ) && !is_numeric( $this->sequence['value'] ))) &&
  3924. ( '0' != $this->sequence['value'] ))
  3925. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE;
  3926. $attributes = $this->_createParams( $this->sequence['params'] );
  3927. return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] );
  3928. }
  3929. /**
  3930. * set calendar component property sequence
  3931. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3932. * @since 2.10.8 - 2011-09-19
  3933. * @param int $value optional
  3934. * @param array $params optional
  3935. * @return bool
  3936. */
  3937. function setSequence( $value=FALSE, $params=FALSE ) {
  3938. if(( empty( $value ) && !is_numeric( $value )) && ( '0' != $value ))
  3939. $value = ( isset( $this->sequence['value'] ) && ( -1 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : '0';
  3940. $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  3941. return TRUE;
  3942. }
  3943. /*********************************************************************************/
  3944. /**
  3945. * Property Name: STATUS
  3946. */
  3947. /**
  3948. * creates formatted output for calendar component property status
  3949. *
  3950. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3951. * @since 2.4.8 - 2008-10-21
  3952. * @return string
  3953. */
  3954. function createStatus() {
  3955. if( empty( $this->status )) return FALSE;
  3956. if( empty( $this->status['value'] ))
  3957. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE;
  3958. $attributes = $this->_createParams( $this->status['params'] );
  3959. return $this->_createElement( 'STATUS', $attributes, $this->status['value'] );
  3960. }
  3961. /**
  3962. * set calendar component property status
  3963. *
  3964. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3965. * @since 2.4.8 - 2008-11-04
  3966. * @param string $value
  3967. * @param array $params optional
  3968. * @return bool
  3969. */
  3970. function setStatus( $value, $params=FALSE ) {
  3971. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  3972. $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  3973. return TRUE;
  3974. }
  3975. /*********************************************************************************/
  3976. /**
  3977. * Property Name: SUMMARY
  3978. */
  3979. /**
  3980. * creates formatted output for calendar component property summary
  3981. *
  3982. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3983. * @since 2.16.2 - 2012-12-18
  3984. * @return string
  3985. */
  3986. function createSummary() {
  3987. if( empty( $this->summary )) return FALSE;
  3988. if( empty( $this->summary['value'] ))
  3989. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE;
  3990. $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' ));
  3991. $content = iCalUtilityFunctions::_strrep( $this->summary['value'], $this->format, $this->nl );
  3992. return $this->_createElement( 'SUMMARY', $attributes, $content );
  3993. }
  3994. /**
  3995. * set calendar component property summary
  3996. *
  3997. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  3998. * @since 2.4.8 - 2008-11-04
  3999. * @param string $value
  4000. * @param string $params optional
  4001. * @return bool
  4002. */
  4003. function setSummary( $value, $params=FALSE ) {
  4004. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  4005. $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  4006. return TRUE;
  4007. }
  4008. /*********************************************************************************/
  4009. /**
  4010. * Property Name: TRANSP
  4011. */
  4012. /**
  4013. * creates formatted output for calendar component property transp
  4014. *
  4015. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4016. * @since 2.4.8 - 2008-10-21
  4017. * @return string
  4018. */
  4019. function createTransp() {
  4020. if( empty( $this->transp )) return FALSE;
  4021. if( empty( $this->transp['value'] ))
  4022. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE;
  4023. $attributes = $this->_createParams( $this->transp['params'] );
  4024. return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] );
  4025. }
  4026. /**
  4027. * set calendar component property transp
  4028. *
  4029. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4030. * @since 2.4.8 - 2008-11-04
  4031. * @param string $value
  4032. * @param string $params optional
  4033. * @return bool
  4034. */
  4035. function setTransp( $value, $params=FALSE ) {
  4036. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  4037. $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  4038. return TRUE;
  4039. }
  4040. /*********************************************************************************/
  4041. /**
  4042. * Property Name: TRIGGER
  4043. */
  4044. /**
  4045. * creates formatted output for calendar component property trigger
  4046. *
  4047. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4048. * @since 2.4.16 - 2008-10-21
  4049. * @return string
  4050. */
  4051. function createTrigger() {
  4052. if( empty( $this->trigger )) return FALSE;
  4053. if( empty( $this->trigger['value'] ))
  4054. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE;
  4055. $content = $attributes = null;
  4056. if( isset( $this->trigger['value']['year'] ) &&
  4057. isset( $this->trigger['value']['month'] ) &&
  4058. isset( $this->trigger['value']['day'] ))
  4059. $content .= iCalUtilityFunctions::_date2strdate( $this->trigger['value'] );
  4060. else {
  4061. if( TRUE !== $this->trigger['value']['relatedStart'] )
  4062. $attributes .= $this->intAttrDelimiter.'RELATED=END';
  4063. if( $this->trigger['value']['before'] )
  4064. $content .= '-';
  4065. $content .= iCalUtilityFunctions::_duration2str( $this->trigger['value'] );
  4066. }
  4067. $attributes .= $this->_createParams( $this->trigger['params'] );
  4068. return $this->_createElement( 'TRIGGER', $attributes, $content );
  4069. }
  4070. /**
  4071. * set calendar component property trigger
  4072. *
  4073. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4074. * @since 2.14.1 - 2012-09-20
  4075. * @param mixed $year
  4076. * @param mixed $month optional
  4077. * @param int $day optional
  4078. * @param int $week optional
  4079. * @param int $hour optional
  4080. * @param int $min optional
  4081. * @param int $sec optional
  4082. * @param bool $relatedStart optional
  4083. * @param bool $before optional
  4084. * @param array $params optional
  4085. * @return bool
  4086. */
  4087. function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) {
  4088. if( empty( $year ) && empty( $month ) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec ))
  4089. if( $this->getConfig( 'allowEmpty' )) {
  4090. $this->trigger = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
  4091. return TRUE;
  4092. }
  4093. else
  4094. return FALSE;
  4095. if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp UTC
  4096. $params = iCalUtilityFunctions::_setParams( $month );
  4097. $date = iCalUtilityFunctions::_timestamp2date( $year, 7 );
  4098. foreach( $date as $k => $v )
  4099. $$k = $v;
  4100. }
  4101. elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) {
  4102. $params = iCalUtilityFunctions::_setParams( $month );
  4103. if(!(array_key_exists( 'year', $year ) && // exclude date-time
  4104. array_key_exists( 'month', $year ) &&
  4105. array_key_exists( 'day', $year ))) { // when this must be a duration
  4106. if( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] )))
  4107. $relatedStart = FALSE;
  4108. else
  4109. $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE;
  4110. $before = ( array_key_exists( 'before', $year ) && ( TRUE !== $year['before'] )) ? FALSE : TRUE;
  4111. }
  4112. $SSYY = ( array_key_exists( 'year', $year )) ? $year['year'] : null;
  4113. $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null;
  4114. $day = ( array_key_exists( 'day', $year )) ? $year['day'] : null;
  4115. $week = ( array_key_exists( 'week', $year )) ? $year['week'] : null;
  4116. $hour = ( array_key_exists( 'hour', $year )) ? $year['hour'] : 0; //null;
  4117. $min = ( array_key_exists( 'min', $year )) ? $year['min'] : 0; //null;
  4118. $sec = ( array_key_exists( 'sec', $year )) ? $year['sec'] : 0; //null;
  4119. $year = $SSYY;
  4120. }
  4121. elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) { // duration or date in a string
  4122. $params = iCalUtilityFunctions::_setParams( $month );
  4123. if( in_array( $year[0], array( 'P', '+', '-' ))) { // duration
  4124. $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) ? FALSE : TRUE;
  4125. $before = ( '-' == $year[0] ) ? TRUE : FALSE;
  4126. if( 'P' != $year[0] )
  4127. $year = substr( $year, 1 );
  4128. $date = iCalUtilityFunctions::_durationStr2arr( $year);
  4129. }
  4130. else // date
  4131. $date = iCalUtilityFunctions::_strdate2date( $year, 7 );
  4132. unset( $year, $month, $day, $date['unparsedtext'] );
  4133. if( empty( $date ))
  4134. $sec = 0;
  4135. else
  4136. foreach( $date as $k => $v )
  4137. $$k = $v;
  4138. }
  4139. else // single values in function input parameters
  4140. $params = iCalUtilityFunctions::_setParams( $params );
  4141. if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date
  4142. $params['VALUE'] = 'DATE-TIME';
  4143. $hour = ( $hour ) ? $hour : 0;
  4144. $min = ( $min ) ? $min : 0;
  4145. $sec = ( $sec ) ? $sec : 0;
  4146. $this->trigger = array( 'params' => $params );
  4147. $this->trigger['value'] = array( 'year' => $year
  4148. , 'month' => $month
  4149. , 'day' => $day
  4150. , 'hour' => $hour
  4151. , 'min' => $min
  4152. , 'sec' => $sec
  4153. , 'tz' => 'Z' );
  4154. return TRUE;
  4155. }
  4156. elseif(( empty( $year ) && empty( $month )) && // duration
  4157. (( !empty( $week ) || ( 0 == $week )) ||
  4158. ( !empty( $day ) || ( 0 == $day )) ||
  4159. ( !empty( $hour ) || ( 0 == $hour )) ||
  4160. ( !empty( $min ) || ( 0 == $min )) ||
  4161. ( !empty( $sec ) || ( 0 == $sec )))) {
  4162. unset( $params['RELATED'] ); // set at output creation (END only)
  4163. unset( $params['VALUE'] ); // 'DURATION' default
  4164. $this->trigger = array( 'params' => $params );
  4165. $this->trigger['value'] = array();
  4166. if( !empty( $week )) $this->trigger['value']['week'] = $week;
  4167. if( !empty( $day )) $this->trigger['value']['day'] = $day;
  4168. if( !empty( $hour )) $this->trigger['value']['hour'] = $hour;
  4169. if( !empty( $min )) $this->trigger['value']['min'] = $min;
  4170. if( !empty( $sec )) $this->trigger['value']['sec'] = $sec;
  4171. if( empty( $this->trigger['value'] )) {
  4172. $this->trigger['value']['sec'] = 0;
  4173. $before = FALSE;
  4174. }
  4175. $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE;
  4176. $before = ( FALSE !== $before ) ? TRUE : FALSE;
  4177. $this->trigger['value']['relatedStart'] = $relatedStart;
  4178. $this->trigger['value']['before'] = $before;
  4179. return TRUE;
  4180. }
  4181. return FALSE;
  4182. }
  4183. /*********************************************************************************/
  4184. /**
  4185. * Property Name: TZID
  4186. */
  4187. /**
  4188. * creates formatted output for calendar component property tzid
  4189. *
  4190. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4191. * @since 2.16.2 - 2012-12-18
  4192. * @return string
  4193. */
  4194. function createTzid() {
  4195. if( empty( $this->tzid )) return FALSE;
  4196. if( empty( $this->tzid['value'] ))
  4197. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE;
  4198. $attributes = $this->_createParams( $this->tzid['params'] );
  4199. return $this->_createElement( 'TZID', $attributes, iCalUtilityFunctions::_strrep( $this->tzid['value'], $this->format, $this->nl ));
  4200. }
  4201. /**
  4202. * set calendar component property tzid
  4203. *
  4204. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4205. * @since 2.4.8 - 2008-11-04
  4206. * @param string $value
  4207. * @param array $params optional
  4208. * @return bool
  4209. */
  4210. function setTzid( $value, $params=FALSE ) {
  4211. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  4212. $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  4213. return TRUE;
  4214. }
  4215. /*********************************************************************************/
  4216. /**
  4217. * .. .
  4218. * Property Name: TZNAME
  4219. */
  4220. /**
  4221. * creates formatted output for calendar component property tzname
  4222. *
  4223. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4224. * @since 2.16.2 - 2012-12-18
  4225. * @return string
  4226. */
  4227. function createTzname() {
  4228. if( empty( $this->tzname )) return FALSE;
  4229. $output = null;
  4230. foreach( $this->tzname as $theName ) {
  4231. if( !empty( $theName['value'] )) {
  4232. $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' ));
  4233. $output .= $this->_createElement( 'TZNAME', $attributes, iCalUtilityFunctions::_strrep( $theName['value'], $this->format, $this->nl ));
  4234. }
  4235. elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' );
  4236. }
  4237. return $output;
  4238. }
  4239. /**
  4240. * set calendar component property tzname
  4241. *
  4242. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4243. * @since 2.5.1 - 2008-11-05
  4244. * @param string $value
  4245. * @param string $params, optional
  4246. * @param integer $index, optional
  4247. * @return bool
  4248. */
  4249. function setTzname( $value, $params=FALSE, $index=FALSE ) {
  4250. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  4251. iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index );
  4252. return TRUE;
  4253. }
  4254. /*********************************************************************************/
  4255. /**
  4256. * Property Name: TZOFFSETFROM
  4257. */
  4258. /**
  4259. * creates formatted output for calendar component property tzoffsetfrom
  4260. *
  4261. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4262. * @since 2.4.8 - 2008-10-21
  4263. * @return string
  4264. */
  4265. function createTzoffsetfrom() {
  4266. if( empty( $this->tzoffsetfrom )) return FALSE;
  4267. if( empty( $this->tzoffsetfrom['value'] ))
  4268. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE;
  4269. $attributes = $this->_createParams( $this->tzoffsetfrom['params'] );
  4270. return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] );
  4271. }
  4272. /**
  4273. * set calendar component property tzoffsetfrom
  4274. *
  4275. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4276. * @since 2.4.8 - 2008-11-04
  4277. * @param string $value
  4278. * @param string $params optional
  4279. * @return bool
  4280. */
  4281. function setTzoffsetfrom( $value, $params=FALSE ) {
  4282. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  4283. $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  4284. return TRUE;
  4285. }
  4286. /*********************************************************************************/
  4287. /**
  4288. * Property Name: TZOFFSETTO
  4289. */
  4290. /**
  4291. * creates formatted output for calendar component property tzoffsetto
  4292. *
  4293. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4294. * @since 2.4.8 - 2008-10-21
  4295. * @return string
  4296. */
  4297. function createTzoffsetto() {
  4298. if( empty( $this->tzoffsetto )) return FALSE;
  4299. if( empty( $this->tzoffsetto['value'] ))
  4300. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE;
  4301. $attributes = $this->_createParams( $this->tzoffsetto['params'] );
  4302. return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] );
  4303. }
  4304. /**
  4305. * set calendar component property tzoffsetto
  4306. *
  4307. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4308. * @since 2.4.8 - 2008-11-04
  4309. * @param string $value
  4310. * @param string $params optional
  4311. * @return bool
  4312. */
  4313. function setTzoffsetto( $value, $params=FALSE ) {
  4314. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  4315. $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  4316. return TRUE;
  4317. }
  4318. /*********************************************************************************/
  4319. /**
  4320. * Property Name: TZURL
  4321. */
  4322. /**
  4323. * creates formatted output for calendar component property tzurl
  4324. *
  4325. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4326. * @since 2.4.8 - 2008-10-21
  4327. * @return string
  4328. */
  4329. function createTzurl() {
  4330. if( empty( $this->tzurl )) return FALSE;
  4331. if( empty( $this->tzurl['value'] ))
  4332. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE;
  4333. $attributes = $this->_createParams( $this->tzurl['params'] );
  4334. return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] );
  4335. }
  4336. /**
  4337. * set calendar component property tzurl
  4338. *
  4339. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4340. * @since 2.4.8 - 2008-11-04
  4341. * @param string $value
  4342. * @param string $params optional
  4343. * @return boll
  4344. */
  4345. function setTzurl( $value, $params=FALSE ) {
  4346. if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  4347. $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  4348. return TRUE;
  4349. }
  4350. /*********************************************************************************/
  4351. /**
  4352. * Property Name: UID
  4353. */
  4354. /**
  4355. * creates formatted output for calendar component property uid
  4356. *
  4357. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4358. * @since 0.9.7 - 2006-11-20
  4359. * @return string
  4360. */
  4361. function createUid() {
  4362. if( 0 >= count( $this->uid ))
  4363. $this->_makeuid();
  4364. $attributes = $this->_createParams( $this->uid['params'] );
  4365. return $this->_createElement( 'UID', $attributes, $this->uid['value'] );
  4366. }
  4367. /**
  4368. * create an unique id for this calendar component object instance
  4369. *
  4370. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4371. * @since 2.2.7 - 2007-09-04
  4372. * @return void
  4373. */
  4374. function _makeUid() {
  4375. $date = date('Ymd\THisT');
  4376. $unique = substr(microtime(), 2, 4);
  4377. $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890';
  4378. $start = 0;
  4379. $end = strlen( $base ) - 1;
  4380. $length = 6;
  4381. $str = null;
  4382. for( $p = 0; $p < $length; $p++ )
  4383. $unique .= $base{mt_rand( $start, $end )};
  4384. $this->uid = array( 'params' => null );
  4385. $this->uid['value'] = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' );
  4386. }
  4387. /**
  4388. * set calendar component property uid
  4389. *
  4390. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4391. * @since 2.4.8 - 2008-11-04
  4392. * @param string $value
  4393. * @param string $params optional
  4394. * @return bool
  4395. */
  4396. function setUid( $value, $params=FALSE ) {
  4397. if( empty( $value )) return FALSE; // no allowEmpty check here !!!!
  4398. $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  4399. return TRUE;
  4400. }
  4401. /*********************************************************************************/
  4402. /**
  4403. * Property Name: URL
  4404. */
  4405. /**
  4406. * creates formatted output for calendar component property url
  4407. *
  4408. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4409. * @since 2.4.8 - 2008-10-21
  4410. * @return string
  4411. */
  4412. function createUrl() {
  4413. if( empty( $this->url )) return FALSE;
  4414. if( empty( $this->url['value'] ))
  4415. return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE;
  4416. $attributes = $this->_createParams( $this->url['params'] );
  4417. return $this->_createElement( 'URL', $attributes, $this->url['value'] );
  4418. }
  4419. /**
  4420. * set calendar component property url
  4421. *
  4422. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4423. * @since 2.16.7 - 2013-01-11
  4424. * @param string $value
  4425. * @param string $params optional
  4426. * @return bool
  4427. */
  4428. function setUrl( $value, $params=FALSE ) {
  4429. if( !empty( $value )) {
  4430. if( !filter_var( $value, FILTER_VALIDATE_URL ) && ( 'urn' != strtolower( substr( $value, 0, 3 ))))
  4431. return FALSE;
  4432. }
  4433. elseif( $this->getConfig( 'allowEmpty' ))
  4434. $value = null;
  4435. else
  4436. return FALSE;
  4437. $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
  4438. return TRUE;
  4439. }
  4440. /*********************************************************************************/
  4441. /**
  4442. * Property Name: x-prop
  4443. */
  4444. /**
  4445. * creates formatted output for calendar component property x-prop
  4446. *
  4447. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4448. * @since 2.16.2 - 2012-12-18
  4449. * @return string
  4450. */
  4451. function createXprop() {
  4452. if( empty( $this->xprop )) return FALSE;
  4453. $output = null;
  4454. foreach( $this->xprop as $label => $xpropPart ) {
  4455. if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) {
  4456. if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $label );
  4457. continue;
  4458. }
  4459. $attributes = $this->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
  4460. if( is_array( $xpropPart['value'] )) {
  4461. foreach( $xpropPart['value'] as $pix => $theXpart )
  4462. $xpropPart['value'][$pix] = iCalUtilityFunctions::_strrep( $theXpart, $this->format, $this->format );
  4463. $xpropPart['value'] = implode( ',', $xpropPart['value'] );
  4464. }
  4465. else
  4466. $xpropPart['value'] = iCalUtilityFunctions::_strrep( $xpropPart['value'], $this->format, $this->nl );
  4467. $output .= $this->_createElement( $label, $attributes, $xpropPart['value'] );
  4468. }
  4469. return $output;
  4470. }
  4471. /**
  4472. * set calendar component property x-prop
  4473. *
  4474. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4475. * @since 2.11.9 - 2012-01-16
  4476. * @param string $label
  4477. * @param mixed $value
  4478. * @param array $params optional
  4479. * @return bool
  4480. */
  4481. function setXprop( $label, $value, $params=FALSE ) {
  4482. if( empty( $label ))
  4483. return FALSE;
  4484. if( 'X-' != strtoupper( substr( $label, 0, 2 )))
  4485. return FALSE;
  4486. if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
  4487. $xprop = array( 'value' => $value );
  4488. $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
  4489. if( !is_array( $this->xprop )) $this->xprop = array();
  4490. $this->xprop[strtoupper( $label )] = $xprop;
  4491. return TRUE;
  4492. }
  4493. /*********************************************************************************/
  4494. /*********************************************************************************/
  4495. /**
  4496. * create element format parts
  4497. *
  4498. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4499. * @since 2.0.6 - 2006-06-20
  4500. * @return string
  4501. */
  4502. function _createFormat() {
  4503. $objectname = null;
  4504. switch( $this->format ) {
  4505. case 'xcal':
  4506. $objectname = ( isset( $this->timezonetype )) ?
  4507. strtolower( $this->timezonetype ) : strtolower( $this->objName );
  4508. $this->componentStart1 = $this->elementStart1 = '<';
  4509. $this->componentStart2 = $this->elementStart2 = '>';
  4510. $this->componentEnd1 = $this->elementEnd1 = '</';
  4511. $this->componentEnd2 = $this->elementEnd2 = '>'.$this->nl;
  4512. $this->intAttrDelimiter = '<!-- -->';
  4513. $this->attributeDelimiter = $this->nl;
  4514. $this->valueInit = null;
  4515. break;
  4516. default:
  4517. $objectname = ( isset( $this->timezonetype )) ?
  4518. strtoupper( $this->timezonetype ) : strtoupper( $this->objName );
  4519. $this->componentStart1 = 'BEGIN:';
  4520. $this->componentStart2 = null;
  4521. $this->componentEnd1 = 'END:';
  4522. $this->componentEnd2 = $this->nl;
  4523. $this->elementStart1 = null;
  4524. $this->elementStart2 = null;
  4525. $this->elementEnd1 = null;
  4526. $this->elementEnd2 = $this->nl;
  4527. $this->intAttrDelimiter = '<!-- -->';
  4528. $this->attributeDelimiter = ';';
  4529. $this->valueInit = ':';
  4530. break;
  4531. }
  4532. return $objectname;
  4533. }
  4534. /**
  4535. * creates formatted output for calendar component property
  4536. *
  4537. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4538. * @since 2.16.2 - 2012-12-18
  4539. * @param string $label property name
  4540. * @param string $attributes property attributes
  4541. * @param string $content property content (optional)
  4542. * @return string
  4543. */
  4544. function _createElement( $label, $attributes=null, $content=FALSE ) {
  4545. switch( $this->format ) {
  4546. case 'xcal':
  4547. $label = strtolower( $label );
  4548. break;
  4549. default:
  4550. $label = strtoupper( $label );
  4551. break;
  4552. }
  4553. $output = $this->elementStart1.$label;
  4554. $categoriesAttrLang = null;
  4555. $attachInlineBinary = FALSE;
  4556. $attachfmttype = null;
  4557. if (( 'xcal' == $this->format) && ( 'x-' == substr( $label, 0, 2 ))) {
  4558. $this->xcaldecl[] = array( 'xmldecl' => 'ELEMENT'
  4559. , 'ref' => $label
  4560. , 'type2' => '(#PCDATA)' );
  4561. }
  4562. if( !empty( $attributes )) {
  4563. $attributes = trim( $attributes );
  4564. if ( 'xcal' == $this->format ) {
  4565. $attributes2 = explode( $this->intAttrDelimiter, $attributes );
  4566. $attributes = null;
  4567. foreach( $attributes2 as $aix => $attribute ) {
  4568. $attrKVarr = explode( '=', $attribute );
  4569. if( empty( $attrKVarr[0] ))
  4570. continue;
  4571. if( !isset( $attrKVarr[1] )) {
  4572. $attrValue = $attrKVarr[0];
  4573. $attrKey = $aix;
  4574. }
  4575. elseif( 2 == count( $attrKVarr)) {
  4576. $attrKey = strtolower( $attrKVarr[0] );
  4577. $attrValue = $attrKVarr[1];
  4578. }
  4579. else {
  4580. $attrKey = strtolower( $attrKVarr[0] );
  4581. unset( $attrKVarr[0] );
  4582. $attrValue = implode( '=', $attrKVarr );
  4583. }
  4584. if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) {
  4585. $attachInlineBinary = TRUE;
  4586. if( 'fmttype' == $attrKey )
  4587. $attachfmttype = $attrKey.'='.$attrValue;
  4588. continue;
  4589. }
  4590. elseif(( 'categories' == $label ) && ( 'language' == $attrKey ))
  4591. $categoriesAttrLang = $attrKey.'='.$attrValue;
  4592. else {
  4593. $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
  4594. $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null;
  4595. if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) {
  4596. $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 ));
  4597. $attrValue = str_replace( '"', '', $attrValue );
  4598. }
  4599. $attributes .= '"'.htmlspecialchars( $attrValue ).'"';
  4600. }
  4601. }
  4602. }
  4603. else {
  4604. $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes );
  4605. }
  4606. }
  4607. if(( 'xcal' == $this->format) &&
  4608. ((( 'attach' == $label ) && !$attachInlineBinary ) || ( in_array( $label, array( 'tzurl', 'url' ))))) {
  4609. $pos = strrpos($content, "/");
  4610. $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content;
  4611. $this->xcaldecl[] = array( 'xmldecl' => 'ENTITY'
  4612. , 'uri' => $docname
  4613. , 'ref' => 'SYSTEM'
  4614. , 'external' => $content
  4615. , 'type' => 'NDATA'
  4616. , 'type2' => 'BINERY' );
  4617. $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
  4618. $attributes .= 'uri="'.$docname.'"';
  4619. $content = null;
  4620. if( 'attach' == $label ) {
  4621. $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes );
  4622. $content = $this->nl.$this->_createElement( 'extref', $attributes, null );
  4623. $attributes = null;
  4624. }
  4625. }
  4626. elseif(( 'xcal' == $this->format) && ( 'attach' == $label ) && $attachInlineBinary ) {
  4627. $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute
  4628. }
  4629. $output .= $attributes;
  4630. if( !$content && ( '0' != $content )) {
  4631. switch( $this->format ) {
  4632. case 'xcal':
  4633. $output .= ' /';
  4634. $output .= $this->elementStart2.$this->nl;
  4635. return $output;
  4636. break;
  4637. default:
  4638. $output .= $this->elementStart2.$this->valueInit;
  4639. return iCalUtilityFunctions::_size75( $output, $this->nl );
  4640. break;
  4641. }
  4642. }
  4643. $output .= $this->elementStart2;
  4644. $output .= $this->valueInit.$content;
  4645. switch( $this->format ) {
  4646. case 'xcal':
  4647. return $output.$this->elementEnd1.$label.$this->elementEnd2;
  4648. break;
  4649. default:
  4650. return iCalUtilityFunctions::_size75( $output, $this->nl );
  4651. break;
  4652. }
  4653. }
  4654. /**
  4655. * creates formatted output for calendar component property parameters
  4656. *
  4657. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4658. * @since 2.10.27 - 2012-01-16
  4659. * @param array $params optional
  4660. * @param array $ctrKeys optional
  4661. * @return string
  4662. */
  4663. function _createParams( $params=array(), $ctrKeys=array() ) {
  4664. if( !is_array( $params ) || empty( $params ))
  4665. $params = array();
  4666. $attrLANG = $attr1 = $attr2 = $lang = null;
  4667. $CNattrKey = ( in_array( 'CN', $ctrKeys )) ? TRUE : FALSE ;
  4668. $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ;
  4669. $CNattrExist = $LANGattrExist = FALSE;
  4670. $xparams = array();
  4671. foreach( $params as $paramKey => $paramValue ) {
  4672. if(( FALSE !== strpos( $paramValue, ':' )) ||
  4673. ( FALSE !== strpos( $paramValue, ';' )) ||
  4674. ( FALSE !== strpos( $paramValue, ',' )))
  4675. $paramValue = '"'.$paramValue.'"';
  4676. if( ctype_digit( (string) $paramKey )) {
  4677. $xparams[] = $paramValue;
  4678. continue;
  4679. }
  4680. $paramKey = strtoupper( $paramKey );
  4681. if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' )))
  4682. $xparams[$paramKey] = $paramValue;
  4683. else
  4684. $params[$paramKey] = $paramValue;
  4685. }
  4686. ksort( $xparams, SORT_STRING );
  4687. foreach( $xparams as $paramKey => $paramValue ) {
  4688. if( ctype_digit( (string) $paramKey ))
  4689. $attr2 .= $this->intAttrDelimiter.$paramValue;
  4690. else
  4691. $attr2 .= $this->intAttrDelimiter."$paramKey=$paramValue";
  4692. }
  4693. if( isset( $params['FMTTYPE'] ) && !in_array( 'FMTTYPE', $ctrKeys )) {
  4694. $attr1 .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2;
  4695. $attr2 = null;
  4696. }
  4697. if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING', $ctrKeys )) {
  4698. if( !empty( $attr2 )) {
  4699. $attr1 .= $attr2;
  4700. $attr2 = null;
  4701. }
  4702. $attr1 .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING'];
  4703. }
  4704. if( isset( $params['VALUE'] ) && !in_array( 'VALUE', $ctrKeys ))
  4705. $attr1 .= $this->intAttrDelimiter.'VALUE='.$params['VALUE'];
  4706. if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys )) {
  4707. $attr1 .= $this->intAttrDelimiter.'TZID='.$params['TZID'];
  4708. }
  4709. if( isset( $params['RANGE'] ) && !in_array( 'RANGE', $ctrKeys ))
  4710. $attr1 .= $this->intAttrDelimiter.'RANGE='.$params['RANGE'];
  4711. if( isset( $params['RELTYPE'] ) && !in_array( 'RELTYPE', $ctrKeys ))
  4712. $attr1 .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE'];
  4713. if( isset( $params['CN'] ) && $CNattrKey ) {
  4714. $attr1 = $this->intAttrDelimiter.'CN='.$params['CN'];
  4715. $CNattrExist = TRUE;
  4716. }
  4717. if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) {
  4718. $delim = ( FALSE !== strpos( $params['DIR'], '"' )) ? '' : '"';
  4719. $attr1 .= $this->intAttrDelimiter.'DIR='.$delim.$params['DIR'].$delim;
  4720. }
  4721. if( isset( $params['SENT-BY'] ) && in_array( 'SENT-BY', $ctrKeys ))
  4722. $attr1 .= $this->intAttrDelimiter.'SENT-BY='.$params['SENT-BY'];
  4723. if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys )) {
  4724. $delim = ( FALSE !== strpos( $params['ALTREP'], '"' )) ? '' : '"';
  4725. $attr1 .= $this->intAttrDelimiter.'ALTREP='.$delim.$params['ALTREP'].$delim;
  4726. }
  4727. if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) {
  4728. $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE'];
  4729. $LANGattrExist = TRUE;
  4730. }
  4731. if( !$LANGattrExist ) {
  4732. $lang = $this->getConfig( 'language' );
  4733. if(( $CNattrExist || $LANGattrKey ) && $lang )
  4734. $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang;
  4735. }
  4736. return $attr1.$attrLANG.$attr2;
  4737. }
  4738. /**
  4739. * creates formatted output for calendar component property data value type recur
  4740. *
  4741. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4742. * @since 2.14.1 - 2012-10-06
  4743. * @param array $recurlabel
  4744. * @param array $recurdata
  4745. * @return string
  4746. */
  4747. function _format_recur( $recurlabel, $recurdata ) {
  4748. $output = null;
  4749. foreach( $recurdata as $therule ) {
  4750. if( empty( $therule['value'] )) {
  4751. if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel );
  4752. continue;
  4753. }
  4754. $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null;
  4755. $content1 = $content2 = null;
  4756. foreach( $therule['value'] as $rulelabel => $rulevalue ) {
  4757. switch( $rulelabel ) {
  4758. case 'FREQ': {
  4759. $content1 .= "FREQ=$rulevalue";
  4760. break;
  4761. }
  4762. case 'UNTIL': {
  4763. $parno = ( isset( $rulevalue['hour'] )) ? 7 : 3;
  4764. $content2 .= ';UNTIL='.iCalUtilityFunctions::_date2strdate( $rulevalue, $parno );
  4765. break;
  4766. }
  4767. case 'COUNT':
  4768. case 'INTERVAL':
  4769. case 'WKST': {
  4770. $content2 .= ";$rulelabel=$rulevalue";
  4771. break;
  4772. }
  4773. case 'BYSECOND':
  4774. case 'BYMINUTE':
  4775. case 'BYHOUR':
  4776. case 'BYMONTHDAY':
  4777. case 'BYYEARDAY':
  4778. case 'BYWEEKNO':
  4779. case 'BYMONTH':
  4780. case 'BYSETPOS': {
  4781. $content2 .= ";$rulelabel=";
  4782. if( is_array( $rulevalue )) {
  4783. foreach( $rulevalue as $vix => $valuePart ) {
  4784. $content2 .= ( $vix ) ? ',' : null;
  4785. $content2 .= $valuePart;
  4786. }
  4787. }
  4788. else
  4789. $content2 .= $rulevalue;
  4790. break;
  4791. }
  4792. case 'BYDAY': {
  4793. $content2 .= ";$rulelabel=";
  4794. $bydaycnt = 0;
  4795. foreach( $rulevalue as $vix => $valuePart ) {
  4796. $content21 = $content22 = null;
  4797. if( is_array( $valuePart )) {
  4798. $content2 .= ( $bydaycnt ) ? ',' : null;
  4799. foreach( $valuePart as $vix2 => $valuePart2 ) {
  4800. if( 'DAY' != strtoupper( $vix2 ))
  4801. $content21 .= $valuePart2;
  4802. else
  4803. $content22 .= $valuePart2;
  4804. }
  4805. $content2 .= $content21.$content22;
  4806. $bydaycnt++;
  4807. }
  4808. else {
  4809. $content2 .= ( $bydaycnt ) ? ',' : null;
  4810. if( 'DAY' != strtoupper( $vix ))
  4811. $content21 .= $valuePart;
  4812. else {
  4813. $content22 .= $valuePart;
  4814. $bydaycnt++;
  4815. }
  4816. $content2 .= $content21.$content22;
  4817. }
  4818. }
  4819. break;
  4820. }
  4821. default: {
  4822. $content2 .= ";$rulelabel=$rulevalue";
  4823. break;
  4824. }
  4825. }
  4826. }
  4827. $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 );
  4828. }
  4829. return $output;
  4830. }
  4831. /**
  4832. * check if property not exists within component
  4833. *
  4834. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4835. * @since 2.5.1 - 2008-10-15
  4836. * @param string $propName
  4837. * @return bool
  4838. */
  4839. function _notExistProp( $propName ) {
  4840. if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed
  4841. $propName = strtolower( $propName );
  4842. if( 'last-modified' == $propName ) { if( !isset( $this->lastmodified )) return TRUE; }
  4843. elseif( 'percent-complete' == $propName ) { if( !isset( $this->percentcomplete )) return TRUE; }
  4844. elseif( 'recurrence-id' == $propName ) { if( !isset( $this->recurrenceid )) return TRUE; }
  4845. elseif( 'related-to' == $propName ) { if( !isset( $this->relatedto )) return TRUE; }
  4846. elseif( 'request-status' == $propName ) { if( !isset( $this->requeststatus )) return TRUE; }
  4847. elseif(( 'x-' != substr($propName,0,2)) && !isset( $this->$propName )) return TRUE;
  4848. return FALSE;
  4849. }
  4850. /*********************************************************************************/
  4851. /*********************************************************************************/
  4852. /**
  4853. * get general component config variables or info about subcomponents
  4854. *
  4855. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4856. * @since 2.9.6 - 2011-05-14
  4857. * @param mixed $config
  4858. * @return value
  4859. */
  4860. function getConfig( $config = FALSE) {
  4861. if( !$config ) {
  4862. $return = array();
  4863. $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' );
  4864. $return['FORMAT'] = $this->getConfig( 'FORMAT' );
  4865. if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' )))
  4866. $return['LANGUAGE'] = $lang;
  4867. $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
  4868. $return['TZTD'] = $this->getConfig( 'TZID' );
  4869. $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' );
  4870. return $return;
  4871. }
  4872. switch( strtoupper( $config )) {
  4873. case 'ALLOWEMPTY':
  4874. return $this->allowEmpty;
  4875. break;
  4876. case 'COMPSINFO':
  4877. unset( $this->compix );
  4878. $info = array();
  4879. if( isset( $this->components )) {
  4880. foreach( $this->components as $cix => $component ) {
  4881. if( empty( $component )) continue;
  4882. $info[$cix]['ordno'] = $cix + 1;
  4883. $info[$cix]['type'] = $component->objName;
  4884. $info[$cix]['uid'] = $component->getProperty( 'uid' );
  4885. $info[$cix]['props'] = $component->getConfig( 'propinfo' );
  4886. $info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
  4887. }
  4888. }
  4889. return $info;
  4890. break;
  4891. case 'FORMAT':
  4892. return $this->format;
  4893. break;
  4894. case 'LANGUAGE':
  4895. // get language for calendar component as defined in [RFC 1766]
  4896. return $this->language;
  4897. break;
  4898. case 'NL':
  4899. case 'NEWLINECHAR':
  4900. return $this->nl;
  4901. break;
  4902. case 'PROPINFO':
  4903. $output = array();
  4904. if( !in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
  4905. if( empty( $this->uid['value'] )) $this->_makeuid();
  4906. $output['UID'] = 1;
  4907. if( empty( $this->dtstamp )) $this->_makeDtstamp();
  4908. $output['DTSTAMP'] = 1;
  4909. }
  4910. if( !empty( $this->summary )) $output['SUMMARY'] = 1;
  4911. if( !empty( $this->description )) $output['DESCRIPTION'] = count( $this->description );
  4912. if( !empty( $this->dtstart )) $output['DTSTART'] = 1;
  4913. if( !empty( $this->dtend )) $output['DTEND'] = 1;
  4914. if( !empty( $this->due )) $output['DUE'] = 1;
  4915. if( !empty( $this->duration )) $output['DURATION'] = 1;
  4916. if( !empty( $this->rrule )) $output['RRULE'] = count( $this->rrule );
  4917. if( !empty( $this->rdate )) $output['RDATE'] = count( $this->rdate );
  4918. if( !empty( $this->exdate )) $output['EXDATE'] = count( $this->exdate );
  4919. if( !empty( $this->exrule )) $output['EXRULE'] = count( $this->exrule );
  4920. if( !empty( $this->action )) $output['ACTION'] = 1;
  4921. if( !empty( $this->attach )) $output['ATTACH'] = count( $this->attach );
  4922. if( !empty( $this->attendee )) $output['ATTENDEE'] = count( $this->attendee );
  4923. if( !empty( $this->categories )) $output['CATEGORIES'] = count( $this->categories );
  4924. if( !empty( $this->class )) $output['CLASS'] = 1;
  4925. if( !empty( $this->comment )) $output['COMMENT'] = count( $this->comment );
  4926. if( !empty( $this->completed )) $output['COMPLETED'] = 1;
  4927. if( !empty( $this->contact )) $output['CONTACT'] = count( $this->contact );
  4928. if( !empty( $this->created )) $output['CREATED'] = 1;
  4929. if( !empty( $this->freebusy )) $output['FREEBUSY'] = count( $this->freebusy );
  4930. if( !empty( $this->geo )) $output['GEO'] = 1;
  4931. if( !empty( $this->lastmodified )) $output['LAST-MODIFIED'] = 1;
  4932. if( !empty( $this->location )) $output['LOCATION'] = 1;
  4933. if( !empty( $this->organizer )) $output['ORGANIZER'] = 1;
  4934. if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1;
  4935. if( !empty( $this->priority )) $output['PRIORITY'] = 1;
  4936. if( !empty( $this->recurrenceid )) $output['RECURRENCE-ID'] = 1;
  4937. if( !empty( $this->relatedto )) $output['RELATED-TO'] = count( $this->relatedto );
  4938. if( !empty( $this->repeat )) $output['REPEAT'] = 1;
  4939. if( !empty( $this->requeststatus )) $output['REQUEST-STATUS'] = count( $this->requeststatus );
  4940. if( !empty( $this->resources )) $output['RESOURCES'] = count( $this->resources );
  4941. if( !empty( $this->sequence )) $output['SEQUENCE'] = 1;
  4942. if( !empty( $this->sequence )) $output['SEQUENCE'] = 1;
  4943. if( !empty( $this->status )) $output['STATUS'] = 1;
  4944. if( !empty( $this->transp )) $output['TRANSP'] = 1;
  4945. if( !empty( $this->trigger )) $output['TRIGGER'] = 1;
  4946. if( !empty( $this->tzid )) $output['TZID'] = 1;
  4947. if( !empty( $this->tzname )) $output['TZNAME'] = count( $this->tzname );
  4948. if( !empty( $this->tzoffsetfrom )) $output['TZOFFSETFROM'] = 1;
  4949. if( !empty( $this->tzoffsetto )) $output['TZOFFSETTO'] = 1;
  4950. if( !empty( $this->tzurl )) $output['TZURL'] = 1;
  4951. if( !empty( $this->url )) $output['URL'] = 1;
  4952. if( !empty( $this->xprop )) $output['X-PROP'] = count( $this->xprop );
  4953. return $output;
  4954. break;
  4955. case 'SETPROPERTYNAMES':
  4956. return array_keys( $this->getConfig( 'propinfo' ));
  4957. break;
  4958. case 'TZID':
  4959. return $this->dtzid;
  4960. break;
  4961. case 'UNIQUE_ID':
  4962. if( empty( $this->unique_id ))
  4963. $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
  4964. return $this->unique_id;
  4965. break;
  4966. }
  4967. }
  4968. /**
  4969. * general component config setting
  4970. *
  4971. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  4972. * @since 2.10.18 - 2011-10-28
  4973. * @param mixed $config
  4974. * @param string $value
  4975. * @param bool $softUpdate
  4976. * @return void
  4977. */
  4978. function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) {
  4979. if( is_array( $config )) {
  4980. $ak = array_keys( $config );
  4981. foreach( $ak as $k ) {
  4982. if( 'NEWLINECHAR' == strtoupper( $k )) {
  4983. if( FALSE === $this->setConfig( 'NEWLINECHAR', $config[$k] ))
  4984. return FALSE;
  4985. unset( $config[$k] );
  4986. break;
  4987. }
  4988. }
  4989. foreach( $config as $cKey => $cValue ) {
  4990. if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate ))
  4991. return FALSE;
  4992. }
  4993. return TRUE;
  4994. }
  4995. $res = FALSE;
  4996. switch( strtoupper( $config )) {
  4997. case 'ALLOWEMPTY':
  4998. $this->allowEmpty = $value;
  4999. $subcfg = array( 'ALLOWEMPTY' => $value );
  5000. $res = TRUE;
  5001. break;
  5002. case 'FORMAT':
  5003. $value = trim( strtolower( $value ));
  5004. $this->format = $value;
  5005. $this->_createFormat();
  5006. $subcfg = array( 'FORMAT' => $value );
  5007. $res = TRUE;
  5008. break;
  5009. case 'LANGUAGE':
  5010. // set language for calendar component as defined in [RFC 1766]
  5011. $value = trim( $value );
  5012. if( empty( $this->language ) || !$softUpdate )
  5013. $this->language = $value;
  5014. $subcfg = array( 'LANGUAGE' => $value );
  5015. $res = TRUE;
  5016. break;
  5017. case 'NL':
  5018. case 'NEWLINECHAR':
  5019. $this->nl = $value;
  5020. $this->_createFormat();
  5021. $subcfg = array( 'NL' => $value );
  5022. $res = TRUE;
  5023. break;
  5024. case 'TZID':
  5025. $this->dtzid = $value;
  5026. $subcfg = array( 'TZID' => $value );
  5027. $res = TRUE;
  5028. break;
  5029. case 'UNIQUE_ID':
  5030. $value = trim( $value );
  5031. $this->unique_id = $value;
  5032. $subcfg = array( 'UNIQUE_ID' => $value );
  5033. $res = TRUE;
  5034. break;
  5035. default: // any unvalid config key.. .
  5036. return TRUE;
  5037. }
  5038. if( !$res ) return FALSE;
  5039. if( isset( $subcfg ) && !empty( $this->components )) {
  5040. foreach( $subcfg as $cfgkey => $cfgvalue ) {
  5041. foreach( $this->components as $cix => $component ) {
  5042. $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate );
  5043. if( !$res )
  5044. break 2;
  5045. $this->components[$cix] = $component->copy(); // PHP4 compliant
  5046. }
  5047. }
  5048. }
  5049. return $res;
  5050. }
  5051. /*********************************************************************************/
  5052. /**
  5053. * delete component property value
  5054. *
  5055. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  5056. * @since 2.8.8 - 2011-03-15
  5057. * @param mixed $propName, bool FALSE => X-property
  5058. * @param int $propix, optional, if specific property is wanted in case of multiply occurences
  5059. * @return bool, if successfull delete TRUE
  5060. */
  5061. function deleteProperty( $propName=FALSE, $propix=FALSE ) {
  5062. if( $this->_notExistProp( $propName )) return FALSE;
  5063. $propName = strtoupper( $propName );
  5064. if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE',
  5065. 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) {
  5066. if( !$propix )
  5067. $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
  5068. $this->propdelix[$propName] = --$propix;
  5069. }
  5070. $return = FALSE;
  5071. switch( $propName ) {
  5072. case 'ACTION':
  5073. if( !empty( $this->action )) {
  5074. $this->action = '';
  5075. $return = TRUE;
  5076. }
  5077. break;
  5078. case 'ATTACH':
  5079. return $this->deletePropertyM( $this->attach, $this->propdelix[$propName] );
  5080. break;
  5081. case 'ATTENDEE':
  5082. return $this->deletePropertyM( $this->attendee, $this->propdelix[$propName] );
  5083. break;
  5084. case 'CATEGORIES':
  5085. return $this->deletePropertyM( $this->categories, $this->propdelix[$propName] );
  5086. break;
  5087. case 'CLASS':
  5088. if( !empty( $this->class )) {
  5089. $this->class = '';
  5090. $return = TRUE;
  5091. }
  5092. break;
  5093. case 'COMMENT':
  5094. return $this->deletePropertyM( $this->comment, $this->propdelix[$propName] );
  5095. break;
  5096. case 'COMPLETED':
  5097. if( !empty( $this->completed )) {
  5098. $this->completed = '';
  5099. $return = TRUE;
  5100. }
  5101. break;
  5102. case 'CONTACT':
  5103. return $this->deletePropertyM( $this->contact, $this->propdelix[$propName] );
  5104. break;
  5105. case 'CREATED':
  5106. if( !empty( $this->created )) {
  5107. $this->created = '';
  5108. $return = TRUE;
  5109. }
  5110. break;
  5111. case 'DESCRIPTION':
  5112. return $this->deletePropertyM( $this->description, $this->propdelix[$propName] );
  5113. break;
  5114. case 'DTEND':
  5115. if( !empty( $this->dtend )) {
  5116. $this->dtend = '';
  5117. $return = TRUE;
  5118. }
  5119. break;
  5120. case 'DTSTAMP':
  5121. if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
  5122. return FALSE;
  5123. if( !empty( $this->dtstamp )) {
  5124. $this->dtstamp = '';
  5125. $return = TRUE;
  5126. }
  5127. break;
  5128. case 'DTSTART':
  5129. if( !empty( $this->dtstart )) {
  5130. $this->dtstart = '';
  5131. $return = TRUE;
  5132. }
  5133. break;
  5134. case 'DUE':
  5135. if( !empty( $this->due )) {
  5136. $this->due = '';
  5137. $return = TRUE;
  5138. }
  5139. break;
  5140. case 'DURATION':
  5141. if( !empty( $this->duration )) {
  5142. $this->duration = '';
  5143. $return = TRUE;
  5144. }
  5145. break;
  5146. case 'EXDATE':
  5147. return $this->deletePropertyM( $this->exdate, $this->propdelix[$propName] );
  5148. break;
  5149. case 'EXRULE':
  5150. return $this->deletePropertyM( $this->exrule, $this->propdelix[$propName] );
  5151. break;
  5152. case 'FREEBUSY':
  5153. return $this->deletePropertyM( $this->freebusy, $this->propdelix[$propName] );
  5154. break;
  5155. case 'GEO':
  5156. if( !empty( $this->geo )) {
  5157. $this->geo = '';
  5158. $return = TRUE;
  5159. }
  5160. break;
  5161. case 'LAST-MODIFIED':
  5162. if( !empty( $this->lastmodified )) {
  5163. $this->lastmodified = '';
  5164. $return = TRUE;
  5165. }
  5166. break;
  5167. case 'LOCATION':
  5168. if( !empty( $this->location )) {
  5169. $this->location = '';
  5170. $return = TRUE;
  5171. }
  5172. break;
  5173. case 'ORGANIZER':
  5174. if( !empty( $this->organizer )) {
  5175. $this->organizer = '';
  5176. $return = TRUE;
  5177. }
  5178. break;
  5179. case 'PERCENT-COMPLETE':
  5180. if( !empty( $this->percentcomplete )) {
  5181. $this->percentcomplete = '';
  5182. $return = TRUE;
  5183. }
  5184. break;
  5185. case 'PRIORITY':
  5186. if( !empty( $this->priority )) {
  5187. $this->priority = '';
  5188. $return = TRUE;
  5189. }
  5190. break;
  5191. case 'RDATE':
  5192. return $this->deletePropertyM( $this->rdate, $this->propdelix[$propName] );
  5193. break;
  5194. case 'RECURRENCE-ID':
  5195. if( !empty( $this->recurrenceid )) {
  5196. $this->recurrenceid = '';
  5197. $return = TRUE;
  5198. }
  5199. break;
  5200. case 'RELATED-TO':
  5201. return $this->deletePropertyM( $this->relatedto, $this->propdelix[$propName] );
  5202. break;
  5203. case 'REPEAT':
  5204. if( !empty( $this->repeat )) {
  5205. $this->repeat = '';
  5206. $return = TRUE;
  5207. }
  5208. break;
  5209. case 'REQUEST-STATUS':
  5210. return $this->deletePropertyM( $this->requeststatus, $this->propdelix[$propName] );
  5211. break;
  5212. case 'RESOURCES':
  5213. return $this->deletePropertyM( $this->resources, $this->propdelix[$propName] );
  5214. break;
  5215. case 'RRULE':
  5216. return $this->deletePropertyM( $this->rrule, $this->propdelix[$propName] );
  5217. break;
  5218. case 'SEQUENCE':
  5219. if( !empty( $this->sequence )) {
  5220. $this->sequence = '';
  5221. $return = TRUE;
  5222. }
  5223. break;
  5224. case 'STATUS':
  5225. if( !empty( $this->status )) {
  5226. $this->status = '';
  5227. $return = TRUE;
  5228. }
  5229. break;
  5230. case 'SUMMARY':
  5231. if( !empty( $this->summary )) {
  5232. $this->summary = '';
  5233. $return = TRUE;
  5234. }
  5235. break;
  5236. case 'TRANSP':
  5237. if( !empty( $this->transp )) {
  5238. $this->transp = '';
  5239. $return = TRUE;
  5240. }
  5241. break;
  5242. case 'TRIGGER':
  5243. if( !empty( $this->trigger )) {
  5244. $this->trigger = '';
  5245. $return = TRUE;
  5246. }
  5247. break;
  5248. case 'TZID':
  5249. if( !empty( $this->tzid )) {
  5250. $this->tzid = '';
  5251. $return = TRUE;
  5252. }
  5253. break;
  5254. case 'TZNAME':
  5255. return $this->deletePropertyM( $this->tzname, $this->propdelix[$propName] );
  5256. break;
  5257. case 'TZOFFSETFROM':
  5258. if( !empty( $this->tzoffsetfrom )) {
  5259. $this->tzoffsetfrom = '';
  5260. $return = TRUE;
  5261. }
  5262. break;
  5263. case 'TZOFFSETTO':
  5264. if( !empty( $this->tzoffsetto )) {
  5265. $this->tzoffsetto = '';
  5266. $return = TRUE;
  5267. }
  5268. break;
  5269. case 'TZURL':
  5270. if( !empty( $this->tzurl )) {
  5271. $this->tzurl = '';
  5272. $return = TRUE;
  5273. }
  5274. break;
  5275. case 'UID':
  5276. if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
  5277. return FALSE;
  5278. if( !empty( $this->uid )) {
  5279. $this->uid = '';
  5280. $return = TRUE;
  5281. }
  5282. break;
  5283. case 'URL':
  5284. if( !empty( $this->url )) {
  5285. $this->url = '';
  5286. $return = TRUE;
  5287. }
  5288. break;
  5289. default:
  5290. $reduced = '';
  5291. if( $propName != 'X-PROP' ) {
  5292. if( !isset( $this->xprop[$propName] )) return FALSE;
  5293. foreach( $this->xprop as $k => $a ) {
  5294. if(( $k != $propName ) && !empty( $a ))
  5295. $reduced[$k] = $a;
  5296. }
  5297. }
  5298. else {
  5299. if( count( $this->xprop ) <= $propix ) { unset( $this->propdelix[$propName] ); return FALSE; }
  5300. $xpropno = 0;
  5301. foreach( $this->xprop as $xpropkey => $xpropvalue ) {
  5302. if( $propix != $xpropno )
  5303. $reduced[$xpropkey] = $xpropvalue;
  5304. $xpropno++;
  5305. }
  5306. }
  5307. $this->xprop = $reduced;
  5308. if( empty( $this->xprop )) {
  5309. unset( $this->propdelix[$propName] );
  5310. return FALSE;
  5311. }
  5312. return TRUE;
  5313. }
  5314. return $return;
  5315. }
  5316. /*********************************************************************************/
  5317. /**
  5318. * delete component property value, fixing components with multiple occurencies
  5319. *
  5320. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  5321. * @since 2.8.8 - 2011-03-15
  5322. * @param array $multiprop, reference to a component property
  5323. * @param int $propix, reference to removal counter
  5324. * @return bool TRUE
  5325. */
  5326. function deletePropertyM( & $multiprop, & $propix ) {
  5327. if( isset( $multiprop[$propix] ))
  5328. unset( $multiprop[$propix] );
  5329. if( empty( $multiprop )) {
  5330. $multiprop = '';
  5331. unset( $propix );
  5332. return FALSE;
  5333. }
  5334. else
  5335. return TRUE;
  5336. }
  5337. /**
  5338. * get component property value/params
  5339. *
  5340. * if property has multiply values, consequtive function calls are needed
  5341. *
  5342. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  5343. * @since 2.12.4 - 2012-04-22
  5344. * @param string $propName, optional
  5345. * @param int @propix, optional, if specific property is wanted in case of multiply occurences
  5346. * @param bool $inclParam=FALSE
  5347. * @param bool $specform=FALSE
  5348. * @return mixed
  5349. */
  5350. function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) {
  5351. if( 'GEOLOCATION' == strtoupper( $propName )) {
  5352. $content = $this->getProperty( 'LOCATION' );
  5353. $content = ( !empty( $content )) ? $content.' ' : '';
  5354. if(( FALSE === ( $geo = $this->getProperty( 'GEO' ))) || empty( $geo ))
  5355. return FALSE;
  5356. if( 0.0 < $geo['latitude'] )
  5357. $sign = '+';
  5358. else
  5359. $sign = ( 0.0 > $geo['latitude'] ) ? '-' : '';
  5360. $content .= $sign.sprintf( "%09.6f", abs( $geo['latitude'] )); // sprintf && lpad && float && sign !"#¤%&/(
  5361. $content = rtrim( rtrim( $content, '0' ), '.' );
  5362. if( 0.0 < $geo['longitude'] )
  5363. $sign = '+';
  5364. else
  5365. $sign = ( 0.0 > $geo['longitude'] ) ? '-' : '';
  5366. return $content.$sign.sprintf( '%8.6f', abs( $geo['longitude'] )).'/'; // sprintf && lpad && float && sign !"#¤%&/(
  5367. }
  5368. if( $this->_notExistProp( $propName )) return FALSE;
  5369. $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
  5370. if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE',
  5371. 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) {
  5372. if( !$propix )
  5373. $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
  5374. $this->propix[$propName] = --$propix;
  5375. }
  5376. switch( $propName ) {
  5377. case 'ACTION':
  5378. if( !empty( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value'];
  5379. break;
  5380. case 'ATTACH':
  5381. $ak = ( is_array( $this->attach )) ? array_keys( $this->attach ) : array();
  5382. while( is_array( $this->attach ) && !isset( $this->attach[$propix] ) && ( 0 < count( $this->attach )) && ( $propix < end( $ak )))
  5383. $propix++;
  5384. $this->propix[$propName] = $propix;
  5385. if( !isset( $this->attach[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5386. return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value'];
  5387. break;
  5388. case 'ATTENDEE':
  5389. $ak = ( is_array( $this->attendee )) ? array_keys( $this->attendee ) : array();
  5390. while( is_array( $this->attendee ) && !isset( $this->attendee[$propix] ) && ( 0 < count( $this->attendee )) && ( $propix < end( $ak )))
  5391. $propix++;
  5392. $this->propix[$propName] = $propix;
  5393. if( !isset( $this->attendee[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5394. return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value'];
  5395. break;
  5396. case 'CATEGORIES':
  5397. $ak = ( is_array( $this->categories )) ? array_keys( $this->categories ) : array();
  5398. while( is_array( $this->categories ) && !isset( $this->categories[$propix] ) && ( 0 < count( $this->categories )) && ( $propix < end( $ak )))
  5399. $propix++;
  5400. $this->propix[$propName] = $propix;
  5401. if( !isset( $this->categories[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5402. return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value'];
  5403. break;
  5404. case 'CLASS':
  5405. if( !empty( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value'];
  5406. break;
  5407. case 'COMMENT':
  5408. $ak = ( is_array( $this->comment )) ? array_keys( $this->comment ) : array();
  5409. while( is_array( $this->comment ) && !isset( $this->comment[$propix] ) && ( 0 < count( $this->comment )) && ( $propix < end( $ak )))
  5410. $propix++;
  5411. $this->propix[$propName] = $propix;
  5412. if( !isset( $this->comment[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5413. return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value'];
  5414. break;
  5415. case 'COMPLETED':
  5416. if( !empty( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value'];
  5417. break;
  5418. case 'CONTACT':
  5419. $ak = ( is_array( $this->contact )) ? array_keys( $this->contact ) : array();
  5420. while( is_array( $this->contact ) && !isset( $this->contact[$propix] ) && ( 0 < count( $this->contact )) && ( $propix < end( $ak )))
  5421. $propix++;
  5422. $this->propix[$propName] = $propix;
  5423. if( !isset( $this->contact[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5424. return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value'];
  5425. break;
  5426. case 'CREATED':
  5427. if( !empty( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value'];
  5428. break;
  5429. case 'DESCRIPTION':
  5430. $ak = ( is_array( $this->description )) ? array_keys( $this->description ) : array();
  5431. while( is_array( $this->description ) && !isset( $this->description[$propix] ) && ( 0 < count( $this->description )) && ( $propix < end( $ak )))
  5432. $propix++;
  5433. $this->propix[$propName] = $propix;
  5434. if( !isset( $this->description[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5435. return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value'];
  5436. break;
  5437. case 'DTEND':
  5438. if( !empty( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value'];
  5439. break;
  5440. case 'DTSTAMP':
  5441. if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
  5442. return;
  5443. if( !isset( $this->dtstamp['value'] ))
  5444. $this->_makeDtstamp();
  5445. return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value'];
  5446. break;
  5447. case 'DTSTART':
  5448. if( !empty( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value'];
  5449. break;
  5450. case 'DUE':
  5451. if( !empty( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value'];
  5452. break;
  5453. case 'DURATION':
  5454. if( !isset( $this->duration['value'] )) return FALSE;
  5455. $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value'];
  5456. return ( $inclParam ) ? array( 'value' => $value, 'params' => $this->duration['params'] ) : $value;
  5457. break;
  5458. case 'EXDATE':
  5459. $ak = ( is_array( $this->exdate )) ? array_keys( $this->exdate ) : array();
  5460. while( is_array( $this->exdate ) && !isset( $this->exdate[$propix] ) && ( 0 < count( $this->exdate )) && ( $propix < end( $ak )))
  5461. $propix++;
  5462. $this->propix[$propName] = $propix;
  5463. if( !isset( $this->exdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5464. return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value'];
  5465. break;
  5466. case 'EXRULE':
  5467. $ak = ( is_array( $this->exrule )) ? array_keys( $this->exrule ) : array();
  5468. while( is_array( $this->exrule ) && !isset( $this->exrule[$propix] ) && ( 0 < count( $this->exrule )) && ( $propix < end( $ak )))
  5469. $propix++;
  5470. $this->propix[$propName] = $propix;
  5471. if( !isset( $this->exrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5472. return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value'];
  5473. break;
  5474. case 'FREEBUSY':
  5475. $ak = ( is_array( $this->freebusy )) ? array_keys( $this->freebusy ) : array();
  5476. while( is_array( $this->freebusy ) && !isset( $this->freebusy[$propix] ) && ( 0 < count( $this->freebusy )) && ( $propix < end( $ak )))
  5477. $propix++;
  5478. $this->propix[$propName] = $propix;
  5479. if( !isset( $this->freebusy[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5480. return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value'];
  5481. break;
  5482. case 'GEO':
  5483. if( !empty( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value'];
  5484. break;
  5485. case 'LAST-MODIFIED':
  5486. if( !empty( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value'];
  5487. break;
  5488. case 'LOCATION':
  5489. if( !empty( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value'];
  5490. break;
  5491. case 'ORGANIZER':
  5492. if( !empty( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value'];
  5493. break;
  5494. case 'PERCENT-COMPLETE':
  5495. if( !empty( $this->percentcomplete['value'] ) || ( isset( $this->percentcomplete['value'] ) && ( '0' == $this->percentcomplete['value'] ))) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value'];
  5496. break;
  5497. case 'PRIORITY':
  5498. if( !empty( $this->priority['value'] ) || ( isset( $this->priority['value'] ) && ('0' == $this->priority['value'] ))) return ( $inclParam ) ? $this->priority : $this->priority['value'];
  5499. break;
  5500. case 'RDATE':
  5501. $ak = ( is_array( $this->rdate )) ? array_keys( $this->rdate ) : array();
  5502. while( is_array( $this->rdate ) && !isset( $this->rdate[$propix] ) && ( 0 < count( $this->rdate )) && ( $propix < end( $ak )))
  5503. $propix++;
  5504. $this->propix[$propName] = $propix;
  5505. if( !isset( $this->rdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5506. return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value'];
  5507. break;
  5508. case 'RECURRENCE-ID':
  5509. if( !empty( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value'];
  5510. break;
  5511. case 'RELATED-TO':
  5512. $ak = ( is_array( $this->relatedto )) ? array_keys( $this->relatedto ) : array();
  5513. while( is_array( $this->relatedto ) && !isset( $this->relatedto[$propix] ) && ( 0 < count( $this->relatedto )) && ( $propix < end( $ak )))
  5514. $propix++;
  5515. $this->propix[$propName] = $propix;
  5516. if( !isset( $this->relatedto[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5517. return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value'];
  5518. break;
  5519. case 'REPEAT':
  5520. if( !empty( $this->repeat['value'] ) || ( isset( $this->repeat['value'] ) && ( '0' == $this->repeat['value'] ))) return ( $inclParam ) ? $this->repeat : $this->repeat['value'];
  5521. break;
  5522. case 'REQUEST-STATUS':
  5523. $ak = ( is_array( $this->requeststatus )) ? array_keys( $this->requeststatus ) : array();
  5524. while( is_array( $this->requeststatus ) && !isset( $this->requeststatus[$propix] ) && ( 0 < count( $this->requeststatus )) && ( $propix < end( $ak )))
  5525. $propix++;
  5526. $this->propix[$propName] = $propix;
  5527. if( !isset( $this->requeststatus[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5528. return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value'];
  5529. break;
  5530. case 'RESOURCES':
  5531. $ak = ( is_array( $this->resources )) ? array_keys( $this->resources ) : array();
  5532. while( is_array( $this->resources ) && !isset( $this->resources[$propix] ) && ( 0 < count( $this->resources )) && ( $propix < end( $ak )))
  5533. $propix++;
  5534. $this->propix[$propName] = $propix;
  5535. if( !isset( $this->resources[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5536. return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value'];
  5537. break;
  5538. case 'RRULE':
  5539. $ak = ( is_array( $this->rrule )) ? array_keys( $this->rrule ) : array();
  5540. while( is_array( $this->rrule ) && !isset( $this->rrule[$propix] ) && ( 0 < count( $this->rrule )) && ( $propix < end( $ak )))
  5541. $propix++;
  5542. $this->propix[$propName] = $propix;
  5543. if( !isset( $this->rrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5544. return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value'];
  5545. break;
  5546. case 'SEQUENCE':
  5547. if( isset( $this->sequence['value'] ) && ( isset( $this->sequence['value'] ) && ( '0' <= $this->sequence['value'] ))) return ( $inclParam ) ? $this->sequence : $this->sequence['value'];
  5548. break;
  5549. case 'STATUS':
  5550. if( !empty( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value'];
  5551. break;
  5552. case 'SUMMARY':
  5553. if( !empty( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value'];
  5554. break;
  5555. case 'TRANSP':
  5556. if( !empty( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value'];
  5557. break;
  5558. case 'TRIGGER':
  5559. if( !empty( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value'];
  5560. break;
  5561. case 'TZID':
  5562. if( !empty( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value'];
  5563. break;
  5564. case 'TZNAME':
  5565. $ak = ( is_array( $this->tzname )) ? array_keys( $this->tzname ) : array();
  5566. while( is_array( $this->tzname ) && !isset( $this->tzname[$propix] ) && ( 0 < count( $this->tzname )) && ( $propix < end( $ak )))
  5567. $propix++;
  5568. $this->propix[$propName] = $propix;
  5569. if( !isset( $this->tzname[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
  5570. return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value'];
  5571. break;
  5572. case 'TZOFFSETFROM':
  5573. if( !empty( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value'];
  5574. break;
  5575. case 'TZOFFSETTO':
  5576. if( !empty( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value'];
  5577. break;
  5578. case 'TZURL':
  5579. if( !empty( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value'];
  5580. break;
  5581. case 'UID':
  5582. if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
  5583. return FALSE;
  5584. if( empty( $this->uid['value'] ))
  5585. $this->_makeuid();
  5586. return ( $inclParam ) ? $this->uid : $this->uid['value'];
  5587. break;
  5588. case 'URL':
  5589. if( !empty( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value'];
  5590. break;
  5591. default:
  5592. if( $propName != 'X-PROP' ) {
  5593. if( !isset( $this->xprop[$propName] )) return FALSE;
  5594. return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
  5595. : array( $propName, $this->xprop[$propName]['value'] );
  5596. }
  5597. else {
  5598. if( empty( $this->xprop )) return FALSE;
  5599. $xpropno = 0;
  5600. foreach( $this->xprop as $xpropkey => $xpropvalue ) {
  5601. if( $propix == $xpropno )
  5602. return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
  5603. : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
  5604. else
  5605. $xpropno++;
  5606. }
  5607. return FALSE; // not found ??
  5608. }
  5609. }
  5610. return FALSE;
  5611. }
  5612. /**
  5613. * returns calendar property unique values for 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO' or 'RESOURCES' and for each, number of occurrence
  5614. *
  5615. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  5616. * @since 2.13.4 - 2012-08-07
  5617. * @param string $propName
  5618. * @param array $output, incremented result array
  5619. */
  5620. function _getProperties( $propName, & $output ) {
  5621. if( empty( $output ))
  5622. $output = array();
  5623. if( !in_array( strtoupper( $propName ), array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' )))
  5624. return $output;
  5625. while( FALSE !== ( $content = $this->getProperty( $propName ))) {
  5626. if( empty( $content ))
  5627. continue;
  5628. if( is_array( $content )) {
  5629. foreach( $content as $part ) {
  5630. if( FALSE !== strpos( $part, ',' )) {
  5631. $part = explode( ',', $part );
  5632. foreach( $part as $thePart ) {
  5633. $thePart = trim( $thePart );
  5634. if( !empty( $thePart )) {
  5635. if( !isset( $output[$thePart] ))
  5636. $output[$thePart] = 1;
  5637. else
  5638. $output[$thePart] += 1;
  5639. }
  5640. }
  5641. }
  5642. else {
  5643. $part = trim( $part );
  5644. if( !isset( $output[$part] ))
  5645. $output[$part] = 1;
  5646. else
  5647. $output[$part] += 1;
  5648. }
  5649. }
  5650. } // end if( is_array( $content ))
  5651. elseif( FALSE !== strpos( $content, ',' )) {
  5652. $content = explode( ',', $content );
  5653. foreach( $content as $thePart ) {
  5654. $thePart = trim( $thePart );
  5655. if( !empty( $thePart )) {
  5656. if( !isset( $output[$thePart] ))
  5657. $output[$thePart] = 1;
  5658. else
  5659. $output[$thePart] += 1;
  5660. }
  5661. }
  5662. } // end elseif( FALSE !== strpos( $content, ',' ))
  5663. else {
  5664. $content = trim( $content );
  5665. if( !empty( $content )) {
  5666. if( !isset( $output[$content] ))
  5667. $output[$content] = 1;
  5668. else
  5669. $output[$content] += 1;
  5670. }
  5671. }
  5672. }
  5673. ksort( $output );
  5674. }
  5675. /**
  5676. * general component property setting
  5677. *
  5678. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  5679. * @since 2.5.1 - 2008-11-05
  5680. * @param mixed $args variable number of function arguments,
  5681. * first argument is ALWAYS component name,
  5682. * second ALWAYS component value!
  5683. * @return void
  5684. */
  5685. function setProperty() {
  5686. $numargs = func_num_args();
  5687. if( 1 > $numargs ) return FALSE;
  5688. $arglist = func_get_args();
  5689. if( $this->_notExistProp( $arglist[0] )) return FALSE;
  5690. if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] )))
  5691. return FALSE;
  5692. $arglist[0] = strtoupper( $arglist[0] );
  5693. for( $argix=$numargs; $argix < 12; $argix++ ) {
  5694. if( !isset( $arglist[$argix] ))
  5695. $arglist[$argix] = null;
  5696. }
  5697. switch( $arglist[0] ) {
  5698. case 'ACTION':
  5699. return $this->setAction( $arglist[1], $arglist[2] );
  5700. case 'ATTACH':
  5701. return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] );
  5702. case 'ATTENDEE':
  5703. return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] );
  5704. case 'CATEGORIES':
  5705. return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] );
  5706. case 'CLASS':
  5707. return $this->setClass( $arglist[1], $arglist[2] );
  5708. case 'COMMENT':
  5709. return $this->setComment( $arglist[1], $arglist[2], $arglist[3] );
  5710. case 'COMPLETED':
  5711. return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
  5712. case 'CONTACT':
  5713. return $this->setContact( $arglist[1], $arglist[2], $arglist[3] );
  5714. case 'CREATED':
  5715. return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
  5716. case 'DESCRIPTION':
  5717. return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] );
  5718. case 'DTEND':
  5719. return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
  5720. case 'DTSTAMP':
  5721. return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
  5722. case 'DTSTART':
  5723. return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
  5724. case 'DUE':
  5725. return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
  5726. case 'DURATION':
  5727. return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] );
  5728. case 'EXDATE':
  5729. return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] );
  5730. case 'EXRULE':
  5731. return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] );
  5732. case 'FREEBUSY':
  5733. return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] );
  5734. case 'GEO':
  5735. return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] );
  5736. case 'LAST-MODIFIED':
  5737. return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
  5738. case 'LOCATION':
  5739. return $this->setLocation( $arglist[1], $arglist[2] );
  5740. case 'ORGANIZER':
  5741. return $this->setOrganizer( $arglist[1], $arglist[2] );
  5742. case 'PERCENT-COMPLETE':
  5743. return $this->setPercentComplete( $arglist[1], $arglist[2] );
  5744. case 'PRIORITY':
  5745. return $this->setPriority( $arglist[1], $arglist[2] );
  5746. case 'RDATE':
  5747. return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] );
  5748. case 'RECURRENCE-ID':
  5749. return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
  5750. case 'RELATED-TO':
  5751. return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] );
  5752. case 'REPEAT':
  5753. return $this->setRepeat( $arglist[1], $arglist[2] );
  5754. case 'REQUEST-STATUS':
  5755. return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] );
  5756. case 'RESOURCES':
  5757. return $this->setResources( $arglist[1], $arglist[2], $arglist[3] );
  5758. case 'RRULE':
  5759. return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] );
  5760. case 'SEQUENCE':
  5761. return $this->setSequence( $arglist[1], $arglist[2] );
  5762. case 'STATUS':
  5763. return $this->setStatus( $arglist[1], $arglist[2] );
  5764. case 'SUMMARY':
  5765. return $this->setSummary( $arglist[1], $arglist[2] );
  5766. case 'TRANSP':
  5767. return $this->setTransp( $arglist[1], $arglist[2] );
  5768. case 'TRIGGER':
  5769. 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] );
  5770. case 'TZID':
  5771. return $this->setTzid( $arglist[1], $arglist[2] );
  5772. case 'TZNAME':
  5773. return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] );
  5774. case 'TZOFFSETFROM':
  5775. return $this->setTzoffsetfrom( $arglist[1], $arglist[2] );
  5776. case 'TZOFFSETTO':
  5777. return $this->setTzoffsetto( $arglist[1], $arglist[2] );
  5778. case 'TZURL':
  5779. return $this->setTzurl( $arglist[1], $arglist[2] );
  5780. case 'UID':
  5781. return $this->setUid( $arglist[1], $arglist[2] );
  5782. case 'URL':
  5783. return $this->setUrl( $arglist[1], $arglist[2] );
  5784. default:
  5785. return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
  5786. }
  5787. return FALSE;
  5788. }
  5789. /*********************************************************************************/
  5790. /**
  5791. * parse component unparsed data into properties
  5792. *
  5793. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  5794. * @since 2.16.2 - 2012-12-18
  5795. * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of strings
  5796. * @return bool FALSE if error occurs during parsing
  5797. *
  5798. */
  5799. function parse( $unparsedtext=null ) {
  5800. $nl = $this->getConfig( 'nl' );
  5801. if( !empty( $unparsedtext )) {
  5802. if( is_array( $unparsedtext ))
  5803. $unparsedtext = implode( '\n'.$nl, $unparsedtext );
  5804. $unparsedtext = explode( $nl, iCalUtilityFunctions::convEolChar( $unparsedtext, $nl ));
  5805. }
  5806. elseif( !isset( $this->unparsed ))
  5807. $unparsedtext = array();
  5808. else
  5809. $unparsedtext = $this->unparsed;
  5810. /* skip leading (empty/invalid) lines */
  5811. foreach( $unparsedtext as $lix => $line ) {
  5812. $tst = trim( $line );
  5813. if(( '\n' == $tst ) || empty( $tst ))
  5814. unset( $unparsedtext[$lix] );
  5815. else
  5816. break;
  5817. }
  5818. $this->unparsed = array();
  5819. $comp = & $this;
  5820. $config = $this->getConfig();
  5821. $compsync = $subsync = 0;
  5822. foreach ( $unparsedtext as $lix => $line ) {
  5823. if( 'END:VALARM' == strtoupper( substr( $line, 0, 10 ))) {
  5824. if( 1 != $subsync ) return FALSE;
  5825. $this->components[] = $comp->copy();
  5826. $subsync--;
  5827. }
  5828. elseif( 'END:DAYLIGHT' == strtoupper( substr( $line, 0, 12 ))) {
  5829. if( 1 != $subsync ) return FALSE;
  5830. $this->components[] = $comp->copy();
  5831. $subsync--;
  5832. }
  5833. elseif( 'END:STANDARD' == strtoupper( substr( $line, 0, 12 ))) {
  5834. if( 1 != $subsync ) return FALSE;
  5835. array_unshift( $this->components, $comp->copy());
  5836. $subsync--;
  5837. }
  5838. elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) { // end:<component>
  5839. if( 1 != $compsync ) return FALSE;
  5840. if( 0 < $subsync )
  5841. $this->components[] = $comp->copy();
  5842. $compsync--;
  5843. break; /* skip trailing empty lines */
  5844. }
  5845. elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) {
  5846. $comp = new valarm( $config);
  5847. $subsync++;
  5848. }
  5849. elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) {
  5850. $comp = new vtimezone( 'standard', $config );
  5851. $subsync++;
  5852. }
  5853. elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) {
  5854. $comp = new vtimezone( 'daylight', $config );
  5855. $subsync++;
  5856. }
  5857. elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) // begin:<component>
  5858. $compsync++;
  5859. else
  5860. $comp->unparsed[] = $line;
  5861. }
  5862. if( 0 < $subsync )
  5863. $this->components[] = $comp->copy();
  5864. unset( $config );
  5865. /* concatenate property values spread over several lines */
  5866. $lastix = -1;
  5867. $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed'
  5868. , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart'
  5869. , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo'
  5870. , 'last-modified', 'location', 'organizer', 'percent-complete'
  5871. , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat'
  5872. , 'request-status', 'resources', 'rrule', 'sequence', 'status'
  5873. , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom'
  5874. , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' );
  5875. $proprows = array();
  5876. for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines
  5877. $line = rtrim( $this->unparsed[$i], $nl );
  5878. while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} ))
  5879. $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl );
  5880. $proprows[] = $line;
  5881. }
  5882. /* parse each property 'line' */
  5883. $paramMStz = array( 'utc-', 'utc+', 'gmt-', 'gmt+' );
  5884. $paramProto3 = array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' );
  5885. $paramProto4 = array( 'crid:', 'news:', 'pres:' );
  5886. foreach( $proprows as $line ) {
  5887. if( '\n' == substr( $line, -2 ))
  5888. $line = substr( $line, 0, -2 );
  5889. /* get propname */
  5890. $propname = null;
  5891. $cix = 0;
  5892. while( isset( $line[$cix] )) {
  5893. if( in_array( $line[$cix], array( ':', ';' )))
  5894. break;
  5895. else
  5896. $propname .= $line[$cix];
  5897. $cix++;
  5898. }
  5899. if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) {
  5900. $propname2 = $propname;
  5901. $propname = 'X-';
  5902. }
  5903. if( !in_array( strtolower( $propname ), $propnames )) // skip non standard property names
  5904. continue;
  5905. /* rest of the line is opt.params and value */
  5906. $line = substr( $line, $cix );
  5907. /* separate attributes from value */
  5908. $attr = array();
  5909. $attrix = -1;
  5910. $clen = strlen( $line );
  5911. $WithinQuotes = FALSE;
  5912. $cix = 0;
  5913. while( FALSE !== substr( $line, $cix, 1 )) {
  5914. if( ( ':' == $line[$cix] ) &&
  5915. ( substr( $line,$cix, 3 ) != '://' ) &&
  5916. ( !in_array( strtolower( substr( $line,$cix - 6, 4 )), $paramMStz )) &&
  5917. ( !in_array( strtolower( substr( $line,$cix - 3, 4 )), $paramProto3 )) &&
  5918. ( !in_array( strtolower( substr( $line,$cix - 4, 5 )), $paramProto4 )) &&
  5919. ( strtolower( substr( $line,$cix - 6, 7 )) != 'mailto:' ) &&
  5920. !$WithinQuotes ) {
  5921. $attrEnd = TRUE;
  5922. if(( $cix < ( $clen - 4 )) &&
  5923. ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
  5924. for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
  5925. if( '://' == substr( $line, $c2ix - 2, 3 )) {
  5926. $attrEnd = FALSE;
  5927. break; // an URI with a portnr!!
  5928. }
  5929. }
  5930. }
  5931. if( $attrEnd) {
  5932. $line = substr( $line, ( $cix + 1 ));
  5933. break;
  5934. }
  5935. $cix++;
  5936. }
  5937. if( '"' == $line[$cix] )
  5938. $WithinQuotes = ( FALSE === $WithinQuotes ) ? TRUE : FALSE;
  5939. if( ';' == $line[$cix] )
  5940. $attr[++$attrix] = null;
  5941. else
  5942. $attr[$attrix] .= $line[$cix];
  5943. $cix++;
  5944. }
  5945. /* make attributes in array format */
  5946. $propattr = array();
  5947. foreach( $attr as $attribute ) {
  5948. $attrsplit = explode( '=', $attribute, 2 );
  5949. if( 1 < count( $attrsplit ))
  5950. $propattr[$attrsplit[0]] = $attrsplit[1];
  5951. else
  5952. $propattr[] = $attribute;
  5953. }
  5954. /* call setProperty( $propname.. . */
  5955. switch( strtoupper( $propname )) {
  5956. case 'ATTENDEE':
  5957. foreach( $propattr as $pix => $attr ) {
  5958. if( !in_array( strtoupper( $pix ), array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' )))
  5959. continue;
  5960. $attr2 = explode( ',', $attr );
  5961. if( 1 < count( $attr2 ))
  5962. $propattr[$pix] = $attr2;
  5963. }
  5964. $this->setProperty( $propname, $line, $propattr );
  5965. break;
  5966. case 'X-':
  5967. $propname = ( isset( $propname2 )) ? $propname2 : $propname;
  5968. unset( $propname2 );
  5969. case 'CATEGORIES':
  5970. case 'RESOURCES':
  5971. if( FALSE !== strpos( $line, ',' )) {
  5972. $content = array( 0 => '' );
  5973. $cix = $lix = 0;
  5974. while( FALSE !== substr( $line, $lix, 1 )) {
  5975. if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) {
  5976. $cix++;
  5977. $content[$cix] = '';
  5978. }
  5979. else
  5980. $content[$cix] .= $line[$lix];
  5981. $lix++;
  5982. }
  5983. if( 1 < count( $content )) {
  5984. $content = array_values( $content );
  5985. foreach( $content as $cix => $contentPart )
  5986. $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart );
  5987. $this->setProperty( $propname, $content, $propattr );
  5988. break;
  5989. }
  5990. else
  5991. $line = reset( $content );
  5992. }
  5993. case 'COMMENT':
  5994. case 'CONTACT':
  5995. case 'DESCRIPTION':
  5996. case 'LOCATION':
  5997. case 'SUMMARY':
  5998. if( empty( $line ))
  5999. $propattr = null;
  6000. $this->setProperty( $propname, iCalUtilityFunctions::_strunrep( $line ), $propattr );
  6001. break;
  6002. case 'REQUEST-STATUS':
  6003. $values = explode( ';', $line, 3 );
  6004. $values[1] = ( !isset( $values[1] )) ? null : iCalUtilityFunctions::_strunrep( $values[1] );
  6005. $values[2] = ( !isset( $values[2] )) ? null : iCalUtilityFunctions::_strunrep( $values[2] );
  6006. $this->setProperty( $propname
  6007. , $values[0] // statcode
  6008. , $values[1] // statdesc
  6009. , $values[2] // extdata
  6010. , $propattr );
  6011. break;
  6012. case 'FREEBUSY':
  6013. $fbtype = ( isset( $propattr['FBTYPE'] )) ? $propattr['FBTYPE'] : ''; // force setting default, if missing
  6014. unset( $propattr['FBTYPE'] );
  6015. $values = explode( ',', $line );
  6016. foreach( $values as $vix => $value ) {
  6017. $value2 = explode( '/', $value );
  6018. if( 1 < count( $value2 ))
  6019. $values[$vix] = $value2;
  6020. }
  6021. $this->setProperty( $propname, $fbtype, $values, $propattr );
  6022. break;
  6023. case 'GEO':
  6024. $value = explode( ';', $line, 2 );
  6025. if( 2 > count( $value ))
  6026. $value[1] = null;
  6027. $this->setProperty( $propname, $value[0], $value[1], $propattr );
  6028. break;
  6029. case 'EXDATE':
  6030. $values = ( !empty( $line )) ? explode( ',', $line ) : null;
  6031. $this->setProperty( $propname, $values, $propattr );
  6032. break;
  6033. case 'RDATE':
  6034. if( empty( $line )) {
  6035. $this->setProperty( $propname, $line, $propattr );
  6036. break;
  6037. }
  6038. $values = explode( ',', $line );
  6039. foreach( $values as $vix => $value ) {
  6040. $value2 = explode( '/', $value );
  6041. if( 1 < count( $value2 ))
  6042. $values[$vix] = $value2;
  6043. }
  6044. $this->setProperty( $propname, $values, $propattr );
  6045. break;
  6046. case 'EXRULE':
  6047. case 'RRULE':
  6048. $values = explode( ';', $line );
  6049. $recur = array();
  6050. foreach( $values as $value2 ) {
  6051. if( empty( $value2 ))
  6052. continue; // ;-char in ending position ???
  6053. $value3 = explode( '=', $value2, 2 );
  6054. $rulelabel = strtoupper( $value3[0] );
  6055. switch( $rulelabel ) {
  6056. case 'BYDAY': {
  6057. $value4 = explode( ',', $value3[1] );
  6058. if( 1 < count( $value4 )) {
  6059. foreach( $value4 as $v5ix => $value5 ) {
  6060. $value6 = array();
  6061. $dayno = $dayname = null;
  6062. $value5 = trim( (string) $value5 );
  6063. if(( ctype_alpha( substr( $value5, -1 ))) &&
  6064. ( ctype_alpha( substr( $value5, -2, 1 )))) {
  6065. $dayname = substr( $value5, -2, 2 );
  6066. if( 2 < strlen( $value5 ))
  6067. $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
  6068. }
  6069. if( $dayno )
  6070. $value6[] = $dayno;
  6071. if( $dayname )
  6072. $value6['DAY'] = $dayname;
  6073. $value4[$v5ix] = $value6;
  6074. }
  6075. }
  6076. else {
  6077. $value4 = array();
  6078. $dayno = $dayname = null;
  6079. $value5 = trim( (string) $value3[1] );
  6080. if(( ctype_alpha( substr( $value5, -1 ))) &&
  6081. ( ctype_alpha( substr( $value5, -2, 1 )))) {
  6082. $dayname = substr( $value5, -2, 2 );
  6083. if( 2 < strlen( $value5 ))
  6084. $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
  6085. }
  6086. if( $dayno )
  6087. $value4[] = $dayno;
  6088. if( $dayname )
  6089. $value4['DAY'] = $dayname;
  6090. }
  6091. $recur[$rulelabel] = $value4;
  6092. break;
  6093. }
  6094. default: {
  6095. $value4 = explode( ',', $value3[1] );
  6096. if( 1 < count( $value4 ))
  6097. $value3[1] = $value4;
  6098. $recur[$rulelabel] = $value3[1];
  6099. break;
  6100. }
  6101. } // end - switch $rulelabel
  6102. } // end - foreach( $values.. .
  6103. $this->setProperty( $propname, $recur, $propattr );
  6104. break;
  6105. case 'ACTION':
  6106. case 'CLASSIFICATION':
  6107. case 'STATUS':
  6108. case 'TRANSP':
  6109. case 'UID':
  6110. case 'TZID':
  6111. case 'RELATED-TO':
  6112. case 'TZNAME':
  6113. $line = iCalUtilityFunctions::_strunrep( $line );
  6114. default:
  6115. $this->setProperty( $propname, $line, $propattr );
  6116. break;
  6117. } // end switch( $propname.. .
  6118. } // end - foreach( $proprows.. .
  6119. unset( $unparsedtext, $this->unparsed, $proprows );
  6120. if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) {
  6121. $ckeys = array_keys( $this->components );
  6122. foreach( $ckeys as $ckey ) {
  6123. if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
  6124. $this->components[$ckey]->parse();
  6125. }
  6126. }
  6127. }
  6128. return TRUE;
  6129. }
  6130. /*********************************************************************************/
  6131. /*********************************************************************************/
  6132. /**
  6133. * return a copy of this component
  6134. *
  6135. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6136. * @since 2.15.4 - 2012-10-18
  6137. * @return object
  6138. */
  6139. function copy() {
  6140. return unserialize( serialize( $this ));
  6141. }
  6142. /*********************************************************************************/
  6143. /*********************************************************************************/
  6144. /**
  6145. * delete calendar subcomponent from component container
  6146. *
  6147. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6148. * @since 2.8.8 - 2011-03-15
  6149. * @param mixed $arg1 ordno / component type / component uid
  6150. * @param mixed $arg2 optional, ordno if arg1 = component type
  6151. * @return void
  6152. */
  6153. function deleteComponent( $arg1, $arg2=FALSE ) {
  6154. if( !isset( $this->components )) return FALSE;
  6155. $argType = $index = null;
  6156. if ( ctype_digit( (string) $arg1 )) {
  6157. $argType = 'INDEX';
  6158. $index = (int) $arg1 - 1;
  6159. }
  6160. elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
  6161. $argType = strtolower( $arg1 );
  6162. $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
  6163. }
  6164. $cix2dC = 0;
  6165. foreach ( $this->components as $cix => $component) {
  6166. if( empty( $component )) continue;
  6167. if(( 'INDEX' == $argType ) && ( $index == $cix )) {
  6168. unset( $this->components[$cix] );
  6169. return TRUE;
  6170. }
  6171. elseif( $argType == $component->objName ) {
  6172. if( $index == $cix2dC ) {
  6173. unset( $this->components[$cix] );
  6174. return TRUE;
  6175. }
  6176. $cix2dC++;
  6177. }
  6178. elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
  6179. unset( $this->components[$cix] );
  6180. return TRUE;
  6181. }
  6182. }
  6183. return FALSE;
  6184. }
  6185. /**
  6186. * get calendar component subcomponent from component container
  6187. *
  6188. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6189. * @since 2.8.8 - 2011-03-15
  6190. * @param mixed $arg1 optional, ordno/component type/ component uid
  6191. * @param mixed $arg2 optional, ordno if arg1 = component type
  6192. * @return object
  6193. */
  6194. function getComponent ( $arg1=FALSE, $arg2=FALSE ) {
  6195. if( !isset( $this->components )) return FALSE;
  6196. $index = $argType = null;
  6197. if ( !$arg1 ) {
  6198. $argType = 'INDEX';
  6199. $index = $this->compix['INDEX'] =
  6200. ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
  6201. }
  6202. elseif ( ctype_digit( (string) $arg1 )) {
  6203. $argType = 'INDEX';
  6204. $index = (int) $arg1;
  6205. unset( $this->compix );
  6206. }
  6207. elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
  6208. unset( $this->compix['INDEX'] );
  6209. $argType = strtolower( $arg1 );
  6210. if( !$arg2 )
  6211. $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
  6212. else
  6213. $index = (int) $arg2;
  6214. }
  6215. $index -= 1;
  6216. $ckeys = array_keys( $this->components );
  6217. if( !empty( $index) && ( $index > end( $ckeys )))
  6218. return FALSE;
  6219. $cix2gC = 0;
  6220. foreach( $this->components as $cix => $component ) {
  6221. if( empty( $component )) continue;
  6222. if(( 'INDEX' == $argType ) && ( $index == $cix ))
  6223. return $component->copy();
  6224. elseif( $argType == $component->objName ) {
  6225. if( $index == $cix2gC )
  6226. return $component->copy();
  6227. $cix2gC++;
  6228. }
  6229. elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' )))
  6230. return $component->copy();
  6231. }
  6232. /* not found.. . */
  6233. unset( $this->compix );
  6234. return false;
  6235. }
  6236. /**
  6237. * add calendar component as subcomponent to container for subcomponents
  6238. *
  6239. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6240. * @since 1.x.x - 2007-04-24
  6241. * @param object $component calendar component
  6242. * @return void
  6243. */
  6244. function addSubComponent ( $component ) {
  6245. $this->setComponent( $component );
  6246. }
  6247. /**
  6248. * create new calendar component subcomponent, already included within component
  6249. *
  6250. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6251. * @since 2.6.33 - 2011-01-03
  6252. * @param string $compType subcomponent type
  6253. * @return object (reference)
  6254. */
  6255. function & newComponent( $compType ) {
  6256. $config = $this->getConfig();
  6257. $keys = array_keys( $this->components );
  6258. $ix = end( $keys) + 1;
  6259. switch( strtoupper( $compType )) {
  6260. case 'ALARM':
  6261. case 'VALARM':
  6262. $this->components[$ix] = new valarm( $config );
  6263. break;
  6264. case 'STANDARD':
  6265. array_unshift( $this->components, new vtimezone( 'STANDARD', $config ));
  6266. $ix = 0;
  6267. break;
  6268. case 'DAYLIGHT':
  6269. $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config );
  6270. break;
  6271. default:
  6272. return FALSE;
  6273. }
  6274. return $this->components[$ix];
  6275. }
  6276. /**
  6277. * add calendar component as subcomponent to container for subcomponents
  6278. *
  6279. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6280. * @since 2.8.8 - 2011-03-15
  6281. * @param object $component calendar component
  6282. * @param mixed $arg1 optional, ordno/component type/ component uid
  6283. * @param mixed $arg2 optional, ordno if arg1 = component type
  6284. * @return bool
  6285. */
  6286. function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) {
  6287. if( !isset( $this->components )) return FALSE;
  6288. $component->setConfig( $this->getConfig(), FALSE, TRUE );
  6289. if( !in_array( $component->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
  6290. /* make sure dtstamp and uid is set */
  6291. $dummy = $component->getProperty( 'dtstamp' );
  6292. $dummy = $component->getProperty( 'uid' );
  6293. }
  6294. if( !$arg1 ) { // plain insert, last in chain
  6295. $this->components[] = $component->copy();
  6296. return TRUE;
  6297. }
  6298. $argType = $index = null;
  6299. if ( ctype_digit( (string) $arg1 )) { // index insert/replace
  6300. $argType = 'INDEX';
  6301. $index = (int) $arg1 - 1;
  6302. }
  6303. elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
  6304. $argType = strtolower( $arg1 );
  6305. $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
  6306. }
  6307. // else if arg1 is set, arg1 must be an UID
  6308. $cix2sC = 0;
  6309. foreach ( $this->components as $cix => $component2 ) {
  6310. if( empty( $component2 )) continue;
  6311. if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
  6312. $this->components[$cix] = $component->copy();
  6313. return TRUE;
  6314. }
  6315. elseif( $argType == $component2->objName ) { // component Type index insert/replace
  6316. if( $index == $cix2sC ) {
  6317. $this->components[$cix] = $component->copy();
  6318. return TRUE;
  6319. }
  6320. $cix2sC++;
  6321. }
  6322. elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
  6323. $this->components[$cix] = $component->copy();
  6324. return TRUE;
  6325. }
  6326. }
  6327. /* arg1=index and not found.. . insert at index .. .*/
  6328. if( 'INDEX' == $argType ) {
  6329. $this->components[$index] = $component->copy();
  6330. ksort( $this->components, SORT_NUMERIC );
  6331. }
  6332. else /* not found.. . insert last in chain anyway .. .*/
  6333. $this->components[] = $component->copy();
  6334. return TRUE;
  6335. }
  6336. /**
  6337. * creates formatted output for subcomponents
  6338. *
  6339. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6340. * @since 2.11.20 - 2012-02-06
  6341. * @param array $xcaldecl
  6342. * @return string
  6343. */
  6344. function createSubComponent() {
  6345. $output = null;
  6346. if( 'vtimezone' == $this->objName ) { // sort subComponents, first standard, then daylight, in dtstart order
  6347. $stdarr = $dlarr = array();
  6348. foreach( $this->components as $component ) {
  6349. if( empty( $component ))
  6350. continue;
  6351. $dt = $component->getProperty( 'dtstart' );
  6352. $key = sprintf( '%04d%02d%02d%02d%02d%02d000', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] );
  6353. if( 'standard' == $component->objName ) {
  6354. while( isset( $stdarr[$key] ))
  6355. $key += 1;
  6356. $stdarr[$key] = $component->copy();
  6357. }
  6358. elseif( 'daylight' == $component->objName ) {
  6359. while( isset( $dlarr[$key] ))
  6360. $key += 1;
  6361. $dlarr[$key] = $component->copy();
  6362. }
  6363. } // end foreach( $this->components as $component )
  6364. $this->components = array();
  6365. ksort( $stdarr, SORT_NUMERIC );
  6366. foreach( $stdarr as $std )
  6367. $this->components[] = $std->copy();
  6368. unset( $stdarr );
  6369. ksort( $dlarr, SORT_NUMERIC );
  6370. foreach( $dlarr as $dl )
  6371. $this->components[] = $dl->copy();
  6372. unset( $dlarr );
  6373. } // end if( 'vtimezone' == $this->objName )
  6374. foreach( $this->components as $component ) {
  6375. $component->setConfig( $this->getConfig(), FALSE, TRUE );
  6376. $output .= $component->createComponent( $this->xcaldecl );
  6377. }
  6378. return $output;
  6379. }
  6380. }
  6381. /*********************************************************************************/
  6382. /*********************************************************************************/
  6383. /**
  6384. * class for calendar component VEVENT
  6385. *
  6386. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6387. * @since 2.5.1 - 2008-10-12
  6388. */
  6389. class vevent extends calendarComponent {
  6390. var $attach;
  6391. var $attendee;
  6392. var $categories;
  6393. var $comment;
  6394. var $contact;
  6395. var $class;
  6396. var $created;
  6397. var $description;
  6398. var $dtend;
  6399. var $dtstart;
  6400. var $duration;
  6401. var $exdate;
  6402. var $exrule;
  6403. var $geo;
  6404. var $lastmodified;
  6405. var $location;
  6406. var $organizer;
  6407. var $priority;
  6408. var $rdate;
  6409. var $recurrenceid;
  6410. var $relatedto;
  6411. var $requeststatus;
  6412. var $resources;
  6413. var $rrule;
  6414. var $sequence;
  6415. var $status;
  6416. var $summary;
  6417. var $transp;
  6418. var $url;
  6419. var $xprop;
  6420. // component subcomponents container
  6421. var $components;
  6422. /**
  6423. * constructor for calendar component VEVENT object
  6424. *
  6425. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6426. * @since 2.8.2 - 2011-05-01
  6427. * @param array $config
  6428. * @return void
  6429. */
  6430. function vevent( $config = array()) {
  6431. $this->calendarComponent();
  6432. $this->attach = '';
  6433. $this->attendee = '';
  6434. $this->categories = '';
  6435. $this->class = '';
  6436. $this->comment = '';
  6437. $this->contact = '';
  6438. $this->created = '';
  6439. $this->description = '';
  6440. $this->dtstart = '';
  6441. $this->dtend = '';
  6442. $this->duration = '';
  6443. $this->exdate = '';
  6444. $this->exrule = '';
  6445. $this->geo = '';
  6446. $this->lastmodified = '';
  6447. $this->location = '';
  6448. $this->organizer = '';
  6449. $this->priority = '';
  6450. $this->rdate = '';
  6451. $this->recurrenceid = '';
  6452. $this->relatedto = '';
  6453. $this->requeststatus = '';
  6454. $this->resources = '';
  6455. $this->rrule = '';
  6456. $this->sequence = '';
  6457. $this->status = '';
  6458. $this->summary = '';
  6459. $this->transp = '';
  6460. $this->url = '';
  6461. $this->xprop = '';
  6462. $this->components = array();
  6463. if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
  6464. $config['language'] = ICAL_LANG;
  6465. if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
  6466. if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
  6467. if( !isset( $config['format'] )) $config['format'] = 'iCal';
  6468. if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
  6469. $this->setConfig( $config );
  6470. }
  6471. /**
  6472. * create formatted output for calendar component VEVENT object instance
  6473. *
  6474. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6475. * @since 2.10.16 - 2011-10-28
  6476. * @param array $xcaldecl
  6477. * @return string
  6478. */
  6479. function createComponent( &$xcaldecl ) {
  6480. $objectname = $this->_createFormat();
  6481. $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
  6482. $component .= $this->createUid();
  6483. $component .= $this->createDtstamp();
  6484. $component .= $this->createAttach();
  6485. $component .= $this->createAttendee();
  6486. $component .= $this->createCategories();
  6487. $component .= $this->createComment();
  6488. $component .= $this->createContact();
  6489. $component .= $this->createClass();
  6490. $component .= $this->createCreated();
  6491. $component .= $this->createDescription();
  6492. $component .= $this->createDtstart();
  6493. $component .= $this->createDtend();
  6494. $component .= $this->createDuration();
  6495. $component .= $this->createExdate();
  6496. $component .= $this->createExrule();
  6497. $component .= $this->createGeo();
  6498. $component .= $this->createLastModified();
  6499. $component .= $this->createLocation();
  6500. $component .= $this->createOrganizer();
  6501. $component .= $this->createPriority();
  6502. $component .= $this->createRdate();
  6503. $component .= $this->createRrule();
  6504. $component .= $this->createRelatedTo();
  6505. $component .= $this->createRequestStatus();
  6506. $component .= $this->createRecurrenceid();
  6507. $component .= $this->createResources();
  6508. $component .= $this->createSequence();
  6509. $component .= $this->createStatus();
  6510. $component .= $this->createSummary();
  6511. $component .= $this->createTransp();
  6512. $component .= $this->createUrl();
  6513. $component .= $this->createXprop();
  6514. $component .= $this->createSubComponent();
  6515. $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
  6516. if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
  6517. foreach( $this->xcaldecl as $localxcaldecl )
  6518. $xcaldecl[] = $localxcaldecl;
  6519. }
  6520. return $component;
  6521. }
  6522. }
  6523. /*********************************************************************************/
  6524. /*********************************************************************************/
  6525. /**
  6526. * class for calendar component VTODO
  6527. *
  6528. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6529. * @since 2.5.1 - 2008-10-12
  6530. */
  6531. class vtodo extends calendarComponent {
  6532. var $attach;
  6533. var $attendee;
  6534. var $categories;
  6535. var $comment;
  6536. var $completed;
  6537. var $contact;
  6538. var $class;
  6539. var $created;
  6540. var $description;
  6541. var $dtstart;
  6542. var $due;
  6543. var $duration;
  6544. var $exdate;
  6545. var $exrule;
  6546. var $geo;
  6547. var $lastmodified;
  6548. var $location;
  6549. var $organizer;
  6550. var $percentcomplete;
  6551. var $priority;
  6552. var $rdate;
  6553. var $recurrenceid;
  6554. var $relatedto;
  6555. var $requeststatus;
  6556. var $resources;
  6557. var $rrule;
  6558. var $sequence;
  6559. var $status;
  6560. var $summary;
  6561. var $url;
  6562. var $xprop;
  6563. // component subcomponents container
  6564. var $components;
  6565. /**
  6566. * constructor for calendar component VTODO object
  6567. *
  6568. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6569. * @since 2.8.2 - 2011-05-01
  6570. * @param array $config
  6571. * @return void
  6572. */
  6573. function vtodo( $config = array()) {
  6574. $this->calendarComponent();
  6575. $this->attach = '';
  6576. $this->attendee = '';
  6577. $this->categories = '';
  6578. $this->class = '';
  6579. $this->comment = '';
  6580. $this->completed = '';
  6581. $this->contact = '';
  6582. $this->created = '';
  6583. $this->description = '';
  6584. $this->dtstart = '';
  6585. $this->due = '';
  6586. $this->duration = '';
  6587. $this->exdate = '';
  6588. $this->exrule = '';
  6589. $this->geo = '';
  6590. $this->lastmodified = '';
  6591. $this->location = '';
  6592. $this->organizer = '';
  6593. $this->percentcomplete = '';
  6594. $this->priority = '';
  6595. $this->rdate = '';
  6596. $this->recurrenceid = '';
  6597. $this->relatedto = '';
  6598. $this->requeststatus = '';
  6599. $this->resources = '';
  6600. $this->rrule = '';
  6601. $this->sequence = '';
  6602. $this->status = '';
  6603. $this->summary = '';
  6604. $this->url = '';
  6605. $this->xprop = '';
  6606. $this->components = array();
  6607. if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
  6608. $config['language'] = ICAL_LANG;
  6609. if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
  6610. if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
  6611. if( !isset( $config['format'] )) $config['format'] = 'iCal';
  6612. if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
  6613. $this->setConfig( $config );
  6614. }
  6615. /**
  6616. * create formatted output for calendar component VTODO object instance
  6617. *
  6618. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6619. * @since 2.5.1 - 2008-11-07
  6620. * @param array $xcaldecl
  6621. * @return string
  6622. */
  6623. function createComponent( &$xcaldecl ) {
  6624. $objectname = $this->_createFormat();
  6625. $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
  6626. $component .= $this->createUid();
  6627. $component .= $this->createDtstamp();
  6628. $component .= $this->createAttach();
  6629. $component .= $this->createAttendee();
  6630. $component .= $this->createCategories();
  6631. $component .= $this->createClass();
  6632. $component .= $this->createComment();
  6633. $component .= $this->createCompleted();
  6634. $component .= $this->createContact();
  6635. $component .= $this->createCreated();
  6636. $component .= $this->createDescription();
  6637. $component .= $this->createDtstart();
  6638. $component .= $this->createDue();
  6639. $component .= $this->createDuration();
  6640. $component .= $this->createExdate();
  6641. $component .= $this->createExrule();
  6642. $component .= $this->createGeo();
  6643. $component .= $this->createLastModified();
  6644. $component .= $this->createLocation();
  6645. $component .= $this->createOrganizer();
  6646. $component .= $this->createPercentComplete();
  6647. $component .= $this->createPriority();
  6648. $component .= $this->createRdate();
  6649. $component .= $this->createRelatedTo();
  6650. $component .= $this->createRequestStatus();
  6651. $component .= $this->createRecurrenceid();
  6652. $component .= $this->createResources();
  6653. $component .= $this->createRrule();
  6654. $component .= $this->createSequence();
  6655. $component .= $this->createStatus();
  6656. $component .= $this->createSummary();
  6657. $component .= $this->createUrl();
  6658. $component .= $this->createXprop();
  6659. $component .= $this->createSubComponent();
  6660. $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
  6661. if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
  6662. foreach( $this->xcaldecl as $localxcaldecl )
  6663. $xcaldecl[] = $localxcaldecl;
  6664. }
  6665. return $component;
  6666. }
  6667. }
  6668. /*********************************************************************************/
  6669. /*********************************************************************************/
  6670. /**
  6671. * class for calendar component VJOURNAL
  6672. *
  6673. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6674. * @since 2.5.1 - 2008-10-12
  6675. */
  6676. class vjournal extends calendarComponent {
  6677. var $attach;
  6678. var $attendee;
  6679. var $categories;
  6680. var $comment;
  6681. var $contact;
  6682. var $class;
  6683. var $created;
  6684. var $description;
  6685. var $dtstart;
  6686. var $exdate;
  6687. var $exrule;
  6688. var $lastmodified;
  6689. var $organizer;
  6690. var $rdate;
  6691. var $recurrenceid;
  6692. var $relatedto;
  6693. var $requeststatus;
  6694. var $rrule;
  6695. var $sequence;
  6696. var $status;
  6697. var $summary;
  6698. var $url;
  6699. var $xprop;
  6700. /**
  6701. * constructor for calendar component VJOURNAL object
  6702. *
  6703. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6704. * @since 2.8.2 - 2011-05-01
  6705. * @param array $config
  6706. * @return void
  6707. */
  6708. function vjournal( $config = array()) {
  6709. $this->calendarComponent();
  6710. $this->attach = '';
  6711. $this->attendee = '';
  6712. $this->categories = '';
  6713. $this->class = '';
  6714. $this->comment = '';
  6715. $this->contact = '';
  6716. $this->created = '';
  6717. $this->description = '';
  6718. $this->dtstart = '';
  6719. $this->exdate = '';
  6720. $this->exrule = '';
  6721. $this->lastmodified = '';
  6722. $this->organizer = '';
  6723. $this->rdate = '';
  6724. $this->recurrenceid = '';
  6725. $this->relatedto = '';
  6726. $this->requeststatus = '';
  6727. $this->rrule = '';
  6728. $this->sequence = '';
  6729. $this->status = '';
  6730. $this->summary = '';
  6731. $this->url = '';
  6732. $this->xprop = '';
  6733. if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
  6734. $config['language'] = ICAL_LANG;
  6735. if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
  6736. if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
  6737. if( !isset( $config['format'] )) $config['format'] = 'iCal';
  6738. if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
  6739. $this->setConfig( $config );
  6740. }
  6741. /**
  6742. * create formatted output for calendar component VJOURNAL object instance
  6743. *
  6744. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6745. * @since 2.5.1 - 2008-10-12
  6746. * @param array $xcaldecl
  6747. * @return string
  6748. */
  6749. function createComponent( &$xcaldecl ) {
  6750. $objectname = $this->_createFormat();
  6751. $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
  6752. $component .= $this->createUid();
  6753. $component .= $this->createDtstamp();
  6754. $component .= $this->createAttach();
  6755. $component .= $this->createAttendee();
  6756. $component .= $this->createCategories();
  6757. $component .= $this->createClass();
  6758. $component .= $this->createComment();
  6759. $component .= $this->createContact();
  6760. $component .= $this->createCreated();
  6761. $component .= $this->createDescription();
  6762. $component .= $this->createDtstart();
  6763. $component .= $this->createExdate();
  6764. $component .= $this->createExrule();
  6765. $component .= $this->createLastModified();
  6766. $component .= $this->createOrganizer();
  6767. $component .= $this->createRdate();
  6768. $component .= $this->createRequestStatus();
  6769. $component .= $this->createRecurrenceid();
  6770. $component .= $this->createRelatedTo();
  6771. $component .= $this->createRrule();
  6772. $component .= $this->createSequence();
  6773. $component .= $this->createStatus();
  6774. $component .= $this->createSummary();
  6775. $component .= $this->createUrl();
  6776. $component .= $this->createXprop();
  6777. $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
  6778. if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
  6779. foreach( $this->xcaldecl as $localxcaldecl )
  6780. $xcaldecl[] = $localxcaldecl;
  6781. }
  6782. return $component;
  6783. }
  6784. }
  6785. /*********************************************************************************/
  6786. /*********************************************************************************/
  6787. /**
  6788. * class for calendar component VFREEBUSY
  6789. *
  6790. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6791. * @since 2.5.1 - 2008-10-12
  6792. */
  6793. class vfreebusy extends calendarComponent {
  6794. var $attendee;
  6795. var $comment;
  6796. var $contact;
  6797. var $dtend;
  6798. var $dtstart;
  6799. var $duration;
  6800. var $freebusy;
  6801. var $organizer;
  6802. var $requeststatus;
  6803. var $url;
  6804. var $xprop;
  6805. // component subcomponents container
  6806. var $components;
  6807. /**
  6808. * constructor for calendar component VFREEBUSY object
  6809. *
  6810. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6811. * @since 2.8.2 - 2011-05-01
  6812. * @param array $config
  6813. * @return void
  6814. */
  6815. function vfreebusy( $config = array()) {
  6816. $this->calendarComponent();
  6817. $this->attendee = '';
  6818. $this->comment = '';
  6819. $this->contact = '';
  6820. $this->dtend = '';
  6821. $this->dtstart = '';
  6822. $this->duration = '';
  6823. $this->freebusy = '';
  6824. $this->organizer = '';
  6825. $this->requeststatus = '';
  6826. $this->url = '';
  6827. $this->xprop = '';
  6828. if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
  6829. $config['language'] = ICAL_LANG;
  6830. if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
  6831. if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
  6832. if( !isset( $config['format'] )) $config['format'] = 'iCal';
  6833. if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
  6834. $this->setConfig( $config );
  6835. }
  6836. /**
  6837. * create formatted output for calendar component VFREEBUSY object instance
  6838. *
  6839. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6840. * @since 2.3.1 - 2007-11-19
  6841. * @param array $xcaldecl
  6842. * @return string
  6843. */
  6844. function createComponent( &$xcaldecl ) {
  6845. $objectname = $this->_createFormat();
  6846. $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
  6847. $component .= $this->createUid();
  6848. $component .= $this->createDtstamp();
  6849. $component .= $this->createAttendee();
  6850. $component .= $this->createComment();
  6851. $component .= $this->createContact();
  6852. $component .= $this->createDtstart();
  6853. $component .= $this->createDtend();
  6854. $component .= $this->createDuration();
  6855. $component .= $this->createFreebusy();
  6856. $component .= $this->createOrganizer();
  6857. $component .= $this->createRequestStatus();
  6858. $component .= $this->createUrl();
  6859. $component .= $this->createXprop();
  6860. $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
  6861. if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
  6862. foreach( $this->xcaldecl as $localxcaldecl )
  6863. $xcaldecl[] = $localxcaldecl;
  6864. }
  6865. return $component;
  6866. }
  6867. }
  6868. /*********************************************************************************/
  6869. /*********************************************************************************/
  6870. /**
  6871. * class for calendar component VALARM
  6872. *
  6873. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6874. * @since 2.5.1 - 2008-10-12
  6875. */
  6876. class valarm extends calendarComponent {
  6877. var $action;
  6878. var $attach;
  6879. var $attendee;
  6880. var $description;
  6881. var $duration;
  6882. var $repeat;
  6883. var $summary;
  6884. var $trigger;
  6885. var $xprop;
  6886. /**
  6887. * constructor for calendar component VALARM object
  6888. *
  6889. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6890. * @since 2.8.2 - 2011-05-01
  6891. * @param array $config
  6892. * @return void
  6893. */
  6894. function valarm( $config = array()) {
  6895. $this->calendarComponent();
  6896. $this->action = '';
  6897. $this->attach = '';
  6898. $this->attendee = '';
  6899. $this->description = '';
  6900. $this->duration = '';
  6901. $this->repeat = '';
  6902. $this->summary = '';
  6903. $this->trigger = '';
  6904. $this->xprop = '';
  6905. if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
  6906. $config['language'] = ICAL_LANG;
  6907. if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
  6908. if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
  6909. if( !isset( $config['format'] )) $config['format'] = 'iCal';
  6910. if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
  6911. $this->setConfig( $config );
  6912. }
  6913. /**
  6914. * create formatted output for calendar component VALARM object instance
  6915. *
  6916. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6917. * @since 2.5.1 - 2008-10-22
  6918. * @param array $xcaldecl
  6919. * @return string
  6920. */
  6921. function createComponent( &$xcaldecl ) {
  6922. $objectname = $this->_createFormat();
  6923. $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
  6924. $component .= $this->createAction();
  6925. $component .= $this->createAttach();
  6926. $component .= $this->createAttendee();
  6927. $component .= $this->createDescription();
  6928. $component .= $this->createDuration();
  6929. $component .= $this->createRepeat();
  6930. $component .= $this->createSummary();
  6931. $component .= $this->createTrigger();
  6932. $component .= $this->createXprop();
  6933. $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
  6934. if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
  6935. foreach( $this->xcaldecl as $localxcaldecl )
  6936. $xcaldecl[] = $localxcaldecl;
  6937. }
  6938. return $component;
  6939. }
  6940. }
  6941. /**********************************************************************************
  6942. /*********************************************************************************/
  6943. /**
  6944. * class for calendar component VTIMEZONE
  6945. *
  6946. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6947. * @since 2.5.1 - 2008-10-12
  6948. */
  6949. class vtimezone extends calendarComponent {
  6950. var $timezonetype;
  6951. var $comment;
  6952. var $dtstart;
  6953. var $lastmodified;
  6954. var $rdate;
  6955. var $rrule;
  6956. var $tzid;
  6957. var $tzname;
  6958. var $tzoffsetfrom;
  6959. var $tzoffsetto;
  6960. var $tzurl;
  6961. var $xprop;
  6962. // component subcomponents container
  6963. var $components;
  6964. /**
  6965. * constructor for calendar component VTIMEZONE object
  6966. *
  6967. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  6968. * @since 2.8.2 - 2011-05-01
  6969. * @param mixed $timezonetype optional, default FALSE ( STANDARD / DAYLIGHT )
  6970. * @param array $config
  6971. * @return void
  6972. */
  6973. function vtimezone( $timezonetype=FALSE, $config = array()) {
  6974. if( is_array( $timezonetype )) {
  6975. $config = $timezonetype;
  6976. $timezonetype = FALSE;
  6977. }
  6978. if( !$timezonetype )
  6979. $this->timezonetype = 'VTIMEZONE';
  6980. else
  6981. $this->timezonetype = strtoupper( $timezonetype );
  6982. $this->calendarComponent();
  6983. $this->comment = '';
  6984. $this->dtstart = '';
  6985. $this->lastmodified = '';
  6986. $this->rdate = '';
  6987. $this->rrule = '';
  6988. $this->tzid = '';
  6989. $this->tzname = '';
  6990. $this->tzoffsetfrom = '';
  6991. $this->tzoffsetto = '';
  6992. $this->tzurl = '';
  6993. $this->xprop = '';
  6994. $this->components = array();
  6995. if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
  6996. $config['language'] = ICAL_LANG;
  6997. if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
  6998. if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
  6999. if( !isset( $config['format'] )) $config['format'] = 'iCal';
  7000. if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
  7001. $this->setConfig( $config );
  7002. }
  7003. /**
  7004. * create formatted output for calendar component VTIMEZONE object instance
  7005. *
  7006. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7007. * @since 2.5.1 - 2008-10-25
  7008. * @param array $xcaldecl
  7009. * @return string
  7010. */
  7011. function createComponent( &$xcaldecl ) {
  7012. $objectname = $this->_createFormat();
  7013. $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
  7014. $component .= $this->createTzid();
  7015. $component .= $this->createLastModified();
  7016. $component .= $this->createTzurl();
  7017. $component .= $this->createDtstart();
  7018. $component .= $this->createTzoffsetfrom();
  7019. $component .= $this->createTzoffsetto();
  7020. $component .= $this->createComment();
  7021. $component .= $this->createRdate();
  7022. $component .= $this->createRrule();
  7023. $component .= $this->createTzname();
  7024. $component .= $this->createXprop();
  7025. $component .= $this->createSubComponent();
  7026. $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
  7027. if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
  7028. foreach( $this->xcaldecl as $localxcaldecl )
  7029. $xcaldecl[] = $localxcaldecl;
  7030. }
  7031. return $component;
  7032. }
  7033. }
  7034. /*********************************************************************************/
  7035. /*********************************************************************************/
  7036. /**
  7037. * moving all utility (static) functions to a utility class
  7038. * 20111223 - move iCalUtilityFunctions class to the end of the iCalcreator class file
  7039. *
  7040. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7041. * @since 2.10.1 - 2011-07-16
  7042. *
  7043. */
  7044. class iCalUtilityFunctions {
  7045. // Store the single instance of iCalUtilityFunctions
  7046. private static $m_pInstance;
  7047. // Private constructor to limit object instantiation to within the class
  7048. private function __construct() {
  7049. $m_pInstance = FALSE;
  7050. }
  7051. // Getter method for creating/returning the single instance of this class
  7052. public static function getInstance() {
  7053. if (!self::$m_pInstance)
  7054. self::$m_pInstance = new iCalUtilityFunctions();
  7055. return self::$m_pInstance;
  7056. }
  7057. /**
  7058. * ensures internal date-time/date format (keyed array) for an input date-time/date array (keyed or unkeyed)
  7059. *
  7060. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7061. * @since 2.14.1 - 2012-09-27
  7062. * @param array $datetime
  7063. * @param int $parno optional, default FALSE
  7064. * @return array
  7065. */
  7066. public static function _date_time_array( $datetime, $parno=FALSE ) {
  7067. return iCalUtilityFunctions::_chkDateArr( $datetime, $parno );
  7068. }
  7069. public static function _chkDateArr( $datetime, $parno=FALSE ) {
  7070. $output = array();
  7071. foreach( $datetime as $dateKey => $datePart ) {
  7072. switch ( $dateKey ) {
  7073. case '0': case 'year': $output['year'] = $datePart; break;
  7074. case '1': case 'month': $output['month'] = $datePart; break;
  7075. case '2': case 'day': $output['day'] = $datePart; break;
  7076. }
  7077. if( 3 != $parno ) {
  7078. switch ( $dateKey ) {
  7079. case '0':
  7080. case '1':
  7081. case '2': break;
  7082. case '3': case 'hour': $output['hour'] = $datePart; break;
  7083. case '4': case 'min' : $output['min'] = $datePart; break;
  7084. case '5': case 'sec' : $output['sec'] = $datePart; break;
  7085. case '6': case 'tz' : $output['tz'] = $datePart; break;
  7086. }
  7087. }
  7088. }
  7089. if( 3 != $parno ) {
  7090. if( !isset( $output['hour'] )) $output['hour'] = 0;
  7091. if( !isset( $output['min'] )) $output['min'] = 0;
  7092. if( !isset( $output['sec'] )) $output['sec'] = 0;
  7093. if( isset( $output['tz'] ) &&
  7094. (( '+0000' == $output['tz'] ) || ( '-0000' == $output['tz'] ) || ( '+000000' == $output['tz'] ) || ( '-000000' == $output['tz'] )))
  7095. $output['tz'] = 'Z';
  7096. }
  7097. return $output;
  7098. }
  7099. /**
  7100. * check date(-time) and params arrays for an opt. timezone and if it is a DATE-TIME or DATE (updates $parno and params)
  7101. *
  7102. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7103. * @since 2.10.30 - 2012-01-16
  7104. * @param array $date, date to check
  7105. * @param int $parno, no of date parts (i.e. year, month.. .)
  7106. * @param array $params, property parameters
  7107. * @return void
  7108. */
  7109. public static function _chkdatecfg( $theDate, & $parno, & $params ) {
  7110. if( isset( $params['TZID'] ))
  7111. $parno = 6;
  7112. elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))
  7113. $parno = 3;
  7114. else {
  7115. if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] ))
  7116. $parno = 7;
  7117. if( is_array( $theDate )) {
  7118. if( isset( $theDate['timestamp'] ))
  7119. $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null;
  7120. else
  7121. $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null;
  7122. if( !empty( $tzid )) {
  7123. $parno = 7;
  7124. if( !iCalUtilityFunctions::_isOffset( $tzid ))
  7125. $params['TZID'] = $tzid; // save only timezone
  7126. }
  7127. elseif( !$parno && ( 3 == count( $theDate )) &&
  7128. ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )))
  7129. $parno = 3;
  7130. else
  7131. $parno = 6;
  7132. }
  7133. else { // string
  7134. $date = trim( $theDate );
  7135. if( 'Z' == substr( $date, -1 ))
  7136. $parno = 7; // UTC DATE-TIME
  7137. elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) &&
  7138. ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' ))))
  7139. $parno = 3; // DATE
  7140. $date = iCalUtilityFunctions::_strdate2date( $date, $parno );
  7141. unset( $date['unparsedtext'] );
  7142. if( !empty( $date['tz'] )) {
  7143. $parno = 7;
  7144. if( !iCalUtilityFunctions::_isOffset( $date['tz'] ))
  7145. $params['TZID'] = $date['tz']; // save only timezone
  7146. }
  7147. elseif( empty( $parno ))
  7148. $parno = 6;
  7149. }
  7150. if( isset( $params['TZID'] ))
  7151. $parno = 6;
  7152. }
  7153. }
  7154. /**
  7155. * vcalendar sort callback function
  7156. *
  7157. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7158. * @since 2.16.2 - 2012-12-17
  7159. * @param array $a
  7160. * @param array $b
  7161. * @return int
  7162. */
  7163. public static function _cmpfcn( $a, $b ) {
  7164. if( empty( $a )) return -1;
  7165. if( empty( $b )) return 1;
  7166. if( 'vtimezone' == $a->objName ) {
  7167. if( 'vtimezone' != $b->objName ) return -1;
  7168. elseif( $a->srtk[0] <= $b->srtk[0] ) return -1;
  7169. else return 1;
  7170. }
  7171. elseif( 'vtimezone' == $b->objName ) return 1;
  7172. $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' );
  7173. for( $k = 0; $k < 4 ; $k++ ) {
  7174. if( empty( $a->srtk[$k] )) return -1;
  7175. elseif( empty( $b->srtk[$k] )) return 1;
  7176. if( is_array( $a->srtk[$k] )) {
  7177. if( is_array( $b->srtk[$k] )) {
  7178. foreach( $sortkeys as $key ) {
  7179. if ( !isset( $a->srtk[$k][$key] )) return -1;
  7180. elseif( !isset( $b->srtk[$k][$key] )) return 1;
  7181. if ( empty( $a->srtk[$k][$key] )) return -1;
  7182. elseif( empty( $b->srtk[$k][$key] )) return 1;
  7183. if ( $a->srtk[$k][$key] == $b->srtk[$k][$key])
  7184. continue;
  7185. if (( (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] ))
  7186. return -1;
  7187. elseif(( (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] ))
  7188. return 1;
  7189. }
  7190. }
  7191. else return -1;
  7192. }
  7193. elseif( is_array( $b->srtk[$k] )) return 1;
  7194. elseif( $a->srtk[$k] < $b->srtk[$k] ) return -1;
  7195. elseif( $a->srtk[$k] > $b->srtk[$k] ) return 1;
  7196. }
  7197. return 0;
  7198. }
  7199. /**
  7200. * byte oriented line folding fix
  7201. *
  7202. * remove any line-endings that may include spaces or tabs
  7203. * and convert all line endings (iCal default '\r\n'),
  7204. * takes care of '\r\n', '\r' and '\n' and mixed '\r\n'+'\r', '\r\n'+'\n'
  7205. *
  7206. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7207. * @since 2.12.17 - 2012-07-12
  7208. * @param string $text
  7209. * @param string $nl
  7210. * @return string
  7211. */
  7212. public static function convEolChar( & $text, $nl ) {
  7213. $outp = '';
  7214. $cix = 0;
  7215. while( isset( $text[$cix] )) {
  7216. if( isset( $text[$cix + 2] ) && ( "\r" == $text[$cix] ) && ( "\n" == $text[$cix + 1] ) &&
  7217. (( " " == $text[$cix + 2] ) || ( "\t" == $text[$cix + 2] ))) // 2 pos eolchar + ' ' or '\t'
  7218. $cix += 2; // skip 3
  7219. elseif( isset( $text[$cix + 1] ) && ( "\r" == $text[$cix] ) && ( "\n" == $text[$cix + 1] )) {
  7220. $outp .= $nl; // 2 pos eolchar
  7221. $cix += 1; // replace with $nl
  7222. }
  7223. elseif( isset( $text[$cix + 1] ) && (( "\r" == $text[$cix] ) || ( "\n" == $text[$cix] )) &&
  7224. (( " " == $text[$cix + 1] ) || ( "\t" == $text[$cix + 1] ))) // 1 pos eolchar + ' ' or '\t'
  7225. $cix += 1; // skip 2
  7226. elseif(( "\r" == $text[$cix] ) || ( "\n" == $text[$cix] )) // 1 pos eolchar
  7227. $outp .= $nl; // replace with $nl
  7228. else
  7229. $outp .= $text[$cix]; // add any other byte
  7230. $cix += 1;
  7231. }
  7232. return $outp;
  7233. }
  7234. /**
  7235. * create a calendar timezone and standard/daylight components
  7236. *
  7237. * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone:
  7238. *
  7239. * BEGIN:VTIMEZONE
  7240. * TZID:Europe/Stockholm
  7241. * BEGIN:STANDARD
  7242. * DTSTART:20101031T020000
  7243. * TZOFFSETFROM:+0200
  7244. * TZOFFSETTO:+0100
  7245. * TZNAME:CET
  7246. * END:STANDARD
  7247. * BEGIN:DAYLIGHT
  7248. * DTSTART:20100328T030000
  7249. * TZOFFSETFROM:+0100
  7250. * TZOFFSETTO:+0200
  7251. * TZNAME:CEST
  7252. * END:DAYLIGHT
  7253. * END:VTIMEZONE
  7254. *
  7255. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7256. * @since 2.16.1 - 2012-11-26
  7257. * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi <icalcreator@onebigsystem.com>
  7258. * Additional changes jpirkey
  7259. * @param object $calendar, reference to an iCalcreator calendar instance
  7260. * @param string $timezone, a PHP5 (DateTimeZone) valid timezone
  7261. * @param array $xProp, *[x-propName => x-propValue], optional
  7262. * @param int $from a unix timestamp
  7263. * @param int $to a unix timestamp
  7264. * @return bool
  7265. */
  7266. public static function createTimezone( & $calendar, $timezone, $xProp=array(), $from=null, $to=null ) {
  7267. if( empty( $timezone ))
  7268. return FALSE;
  7269. if( !empty( $from ) && !is_int( $from ))
  7270. return FALSE;
  7271. if( !empty( $to ) && !is_int( $to ))
  7272. return FALSE;
  7273. try {
  7274. $dtz = new DateTimeZone( $timezone );
  7275. $transitions = $dtz->getTransitions();
  7276. $utcTz = new DateTimeZone( 'UTC' );
  7277. }
  7278. catch( Exception $e ) { return FALSE; }
  7279. if( empty( $to )) {
  7280. $dates = array_keys( $calendar->getProperty( 'dtstart' ));
  7281. if( empty( $dates ))
  7282. $dates = array( date( 'Ymd' ));
  7283. }
  7284. if( !empty( $from ))
  7285. $dateFrom = new DateTime( "@$from" ); // set lowest date (UTC)
  7286. else {
  7287. $from = reset( $dates ); // set lowest date to the lowest dtstart date
  7288. $dateFrom = new DateTime( $from.'T000000', $dtz );
  7289. $dateFrom->modify( '-1 month' ); // set $dateFrom to one month before the lowest date
  7290. $dateFrom->setTimezone( $utcTz ); // convert local date to UTC
  7291. }
  7292. $dateFromYmd = $dateFrom->format('Y-m-d' );
  7293. if( !empty( $to ))
  7294. $dateTo = new DateTime( "@$to" ); // set end date (UTC)
  7295. else {
  7296. $to = end( $dates ); // set highest date to the highest dtstart date
  7297. $dateTo = new DateTime( $to.'T235959', $dtz );
  7298. $dateTo->modify( '+1 year' ); // set $dateTo to one year after the highest date
  7299. $dateTo->setTimezone( $utcTz ); // convert local date to UTC
  7300. }
  7301. $dateToYmd = $dateTo->format('Y-m-d' );
  7302. unset( $dtz );
  7303. $transTemp = array();
  7304. $prevOffsetfrom = 0;
  7305. $stdIx = $dlghtIx = null;
  7306. $prevTrans = FALSE;
  7307. foreach( $transitions as $tix => $trans ) { // all transitions in date-time order!!
  7308. $date = new DateTime( "@{$trans['ts']}" ); // set transition date (UTC)
  7309. $transDateYmd = $date->format('Y-m-d' );
  7310. if ( $transDateYmd < $dateFromYmd ) {
  7311. $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom
  7312. $prevTrans = $trans; // save it in case we don't find any that match
  7313. $prevTrans['offsetfrom'] = ( 0 < $tix ) ? $transitions[$tix-1]['offset'] : 0;
  7314. continue;
  7315. }
  7316. if( $transDateYmd > $dateToYmd )
  7317. break; // loop always (?) breaks here
  7318. if( !empty( $prevOffsetfrom ) || ( 0 == $prevOffsetfrom )) {
  7319. $trans['offsetfrom'] = $prevOffsetfrom; // i.e. set previous offsetto as offsetFrom
  7320. $date->modify( $trans['offsetfrom'].'seconds' ); // convert utc date to local date
  7321. $d = $date->format( 'Y-n-j-G-i-s' ); // set date to array to ease up dtstart and (opt) rdate setting
  7322. $d = explode( '-', $d );
  7323. $trans['time'] = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] );
  7324. }
  7325. $prevOffsetfrom = $trans['offset'];
  7326. if( TRUE !== $trans['isdst'] ) { // standard timezone
  7327. if( !empty( $stdIx ) && isset( $transTemp[$stdIx]['offsetfrom'] ) && // check for any repeating rdate's (in order)
  7328. ( $transTemp[$stdIx]['abbr'] == $trans['abbr'] ) &&
  7329. ( $transTemp[$stdIx]['offsetfrom'] == $trans['offsetfrom'] ) &&
  7330. ( $transTemp[$stdIx]['offset'] == $trans['offset'] )) {
  7331. $transTemp[$stdIx]['rdate'][] = $trans['time'];
  7332. continue;
  7333. }
  7334. $stdIx = $tix;
  7335. } // end standard timezone
  7336. else { // daylight timezone
  7337. if( !empty( $dlghtIx ) && isset( $transTemp[$dlghtIx]['offsetfrom'] ) && // check for any repeating rdate's (in order)
  7338. ( $transTemp[$dlghtIx]['abbr'] == $trans['abbr'] ) &&
  7339. ( $transTemp[$dlghtIx]['offsetfrom'] == $trans['offsetfrom'] ) &&
  7340. ( $transTemp[$dlghtIx]['offset'] == $trans['offset'] )) {
  7341. $transTemp[$dlghtIx]['rdate'][] = $trans['time'];
  7342. continue;
  7343. }
  7344. $dlghtIx = $tix;
  7345. } // end daylight timezone
  7346. $transTemp[$tix] = $trans;
  7347. } // end foreach( $transitions as $tix => $trans )
  7348. $tz = & $calendar->newComponent( 'vtimezone' );
  7349. $tz->setproperty( 'tzid', $timezone );
  7350. if( !empty( $xProp )) {
  7351. foreach( $xProp as $xPropName => $xPropValue )
  7352. if( 'x-' == strtolower( substr( $xPropName, 0, 2 )))
  7353. $tz->setproperty( $xPropName, $xPropValue );
  7354. }
  7355. if( empty( $transTemp )) { // if no match found
  7356. if( $prevTrans ) { // then we use the last transition (before startdate) for the tz info
  7357. $date = new DateTime( "@{$prevTrans['ts']}" ); // set transition date (UTC)
  7358. $date->modify( $prevTrans['offsetfrom'].'seconds' ); // convert utc date to local date
  7359. $d = $date->format( 'Y-n-j-G-i-s' ); // set date to array to ease up dtstart setting
  7360. $d = explode( '-', $d );
  7361. $prevTrans['time'] = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] );
  7362. $transTemp[0] = $prevTrans;
  7363. }
  7364. else { // or we use the timezone identifier to BUILD the standard tz info (?)
  7365. $date = new DateTime( 'now', new DateTimeZone( $timezone ));
  7366. $transTemp[0] = array( 'time' => $date->format( 'Y-m-d\TH:i:s O' )
  7367. , 'offset' => $date->format( 'Z' )
  7368. , 'offsetfrom' => $date->format( 'Z' )
  7369. , 'isdst' => FALSE );
  7370. }
  7371. }
  7372. unset( $transitions, $date, $prevTrans );
  7373. foreach( $transTemp as $tix => $trans ) {
  7374. $type = ( TRUE !== $trans['isdst'] ) ? 'standard' : 'daylight';
  7375. $scomp = & $tz->newComponent( $type );
  7376. $scomp->setProperty( 'dtstart', $trans['time'] );
  7377. // $scomp->setProperty( 'x-utc-timestamp', $tix.' : '.$trans['ts'] ); // test ###
  7378. if( !empty( $trans['abbr'] ))
  7379. $scomp->setProperty( 'tzname', $trans['abbr'] );
  7380. if( isset( $trans['offsetfrom'] ))
  7381. $scomp->setProperty( 'tzoffsetfrom', iCalUtilityFunctions::offsetSec2His( $trans['offsetfrom'] ));
  7382. $scomp->setProperty( 'tzoffsetto', iCalUtilityFunctions::offsetSec2His( $trans['offset'] ));
  7383. if( isset( $trans['rdate'] ))
  7384. $scomp->setProperty( 'RDATE', $trans['rdate'] );
  7385. }
  7386. return TRUE;
  7387. }
  7388. /**
  7389. * creates formatted output for calendar component property data value type date/date-time
  7390. *
  7391. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7392. * @since 2.14.1 - 2012-09-17
  7393. * @param array $datetime
  7394. * @param int $parno, optional, default 6
  7395. * @return string
  7396. */
  7397. public static function _format_date_time( $datetime, $parno=6 ) {
  7398. return iCalUtilityFunctions::_date2strdate( $datetime, $parno );
  7399. }
  7400. public static function _date2strdate( $datetime, $parno=6 ) {
  7401. if( !isset( $datetime['year'] ) &&
  7402. !isset( $datetime['month'] ) &&
  7403. !isset( $datetime['day'] ) &&
  7404. !isset( $datetime['hour'] ) &&
  7405. !isset( $datetime['min'] ) &&
  7406. !isset( $datetime['sec'] ))
  7407. return;
  7408. $output = null;
  7409. foreach( $datetime as $dkey => & $dvalue )
  7410. if( 'tz' != $dkey ) $dvalue = (integer) $dvalue;
  7411. $output = sprintf( '%04d%02d%02d', $datetime['year'], $datetime['month'], $datetime['day'] );
  7412. if( 3 == $parno )
  7413. return $output;
  7414. if( !isset( $datetime['hour'] )) $datetime['hour'] = 0;
  7415. if( !isset( $datetime['min'] )) $datetime['min'] = 0;
  7416. if( !isset( $datetime['sec'] )) $datetime['sec'] = 0;
  7417. $output .= sprintf( 'T%02d%02d%02d', $datetime['hour'], $datetime['min'], $datetime['sec'] );
  7418. if( isset( $datetime['tz'] ) && ( '' < trim( $datetime['tz'] ))) {
  7419. $datetime['tz'] = trim( $datetime['tz'] );
  7420. if( 'Z' == $datetime['tz'] )
  7421. $parno = 7;
  7422. elseif( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) {
  7423. $parno = 7;
  7424. $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] );
  7425. try {
  7426. $d = new DateTime( $output, new DateTimeZone( 'UTC' ));
  7427. if( 0 != $offset ) // adjust för offset
  7428. $d->modify( "$offset seconds" );
  7429. $output = $d->format( 'Ymd\THis' );
  7430. }
  7431. catch( Exception $e ) {
  7432. $output = date( 'Ymd\THis', mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] - $offset), $datetime['month'], $datetime['day'], $datetime['year'] ));
  7433. }
  7434. }
  7435. if( 7 == $parno )
  7436. $output .= 'Z';
  7437. }
  7438. return $output;
  7439. }
  7440. /**
  7441. * convert a date/datetime (array) to timestamp
  7442. *
  7443. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7444. * @since 2.14.1 - 2012-09-29
  7445. * @param array $datetime datetime(/date)
  7446. * @param string $wtz timezone
  7447. * @return int
  7448. */
  7449. public static function _date2timestamp( $datetime, $wtz=null ) {
  7450. if( !isset( $datetime['hour'] )) $datetime['hour'] = 0;
  7451. if( !isset( $datetime['min'] )) $datetime['min'] = 0;
  7452. if( !isset( $datetime['sec'] )) $datetime['sec'] = 0;
  7453. if( empty( $wtz ) && ( !isset( $datetime['tz'] ) || empty( $datetime['tz'] )))
  7454. return mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year'] );
  7455. $output = $offset = 0;
  7456. if( empty( $wtz )) {
  7457. if( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) {
  7458. $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] ) * -1;
  7459. $wtz = 'UTC';
  7460. }
  7461. else
  7462. $wtz = $datetime['tz'];
  7463. }
  7464. if(( 'Z' == $wtz ) || ( 'GMT' == strtoupper( $wtz )))
  7465. $wtz = 'UTC';
  7466. try {
  7467. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $datetime['year'], $datetime['month'], $datetime['day'], $datetime['hour'], $datetime['min'], $datetime['sec'] );
  7468. $d = new DateTime( $strdate, new DateTimeZone( $wtz ));
  7469. if( 0 != $offset ) // adjust for offset
  7470. $d->modify( $offset.' seconds' );
  7471. $output = $d->format( 'U' );
  7472. unset( $d );
  7473. }
  7474. catch( Exception $e ) {
  7475. $output = mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year'] );
  7476. }
  7477. return $output;
  7478. }
  7479. /**
  7480. * ensures internal duration format for input in array format
  7481. *
  7482. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7483. * @since 2.14.1 - 2012-09-25
  7484. * @param array $duration
  7485. * @return array
  7486. */
  7487. public static function _duration_array( $duration ) {
  7488. return iCalUtilityFunctions::_duration2arr( $duration );
  7489. }
  7490. public static function _duration2arr( $duration ) {
  7491. $output = array();
  7492. if( is_array( $duration ) &&
  7493. ( 1 == count( $duration )) &&
  7494. isset( $duration['sec'] ) &&
  7495. ( 60 < $duration['sec'] )) {
  7496. $durseconds = $duration['sec'];
  7497. $output['week'] = (int) floor( $durseconds / ( 60 * 60 * 24 * 7 ));
  7498. $durseconds = $durseconds % ( 60 * 60 * 24 * 7 );
  7499. $output['day'] = (int) floor( $durseconds / ( 60 * 60 * 24 ));
  7500. $durseconds = $durseconds % ( 60 * 60 * 24 );
  7501. $output['hour'] = (int) floor( $durseconds / ( 60 * 60 ));
  7502. $durseconds = $durseconds % ( 60 * 60 );
  7503. $output['min'] = (int) floor( $durseconds / ( 60 ));
  7504. $output['sec'] = ( $durseconds % ( 60 ));
  7505. }
  7506. else {
  7507. foreach( $duration as $durKey => $durValue ) {
  7508. if( empty( $durValue )) continue;
  7509. switch ( $durKey ) {
  7510. case '0': case 'week': $output['week'] = $durValue; break;
  7511. case '1': case 'day': $output['day'] = $durValue; break;
  7512. case '2': case 'hour': $output['hour'] = $durValue; break;
  7513. case '3': case 'min': $output['min'] = $durValue; break;
  7514. case '4': case 'sec': $output['sec'] = $durValue; break;
  7515. }
  7516. }
  7517. }
  7518. if( isset( $output['week'] ) && ( 0 < $output['week'] )) {
  7519. unset( $output['day'], $output['hour'], $output['min'], $output['sec'] );
  7520. return $output;
  7521. }
  7522. unset( $output['week'] );
  7523. if( empty( $output['day'] ))
  7524. unset( $output['day'] );
  7525. if ( isset( $output['hour'] ) || isset( $output['min'] ) || isset( $output['sec'] )) {
  7526. if( !isset( $output['hour'] )) $output['hour'] = 0;
  7527. if( !isset( $output['min'] )) $output['min'] = 0;
  7528. if( !isset( $output['sec'] )) $output['sec'] = 0;
  7529. if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] ))
  7530. unset( $output['hour'], $output['min'], $output['sec'] );
  7531. }
  7532. return $output;
  7533. }
  7534. /**
  7535. * convert startdate+duration to a array format datetime
  7536. *
  7537. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7538. * @since 2.15.12 - 2012-10-31
  7539. * @param array $startdate
  7540. * @param array $duration
  7541. * @return array, date format
  7542. */
  7543. public static function _duration2date( $startdate, $duration ) {
  7544. $dateOnly = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE;
  7545. $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0;
  7546. $startdate['min'] = ( isset( $startdate['min'] )) ? $startdate['min'] : 0;
  7547. $startdate['sec'] = ( isset( $startdate['sec'] )) ? $startdate['sec'] : 0;
  7548. $dtend = 0;
  7549. if( isset( $duration['week'] )) $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 );
  7550. if( isset( $duration['day'] )) $dtend += ( $duration['day'] * 24 * 60 * 60 );
  7551. if( isset( $duration['hour'] )) $dtend += ( $duration['hour'] * 60 *60 );
  7552. if( isset( $duration['min'] )) $dtend += ( $duration['min'] * 60 );
  7553. if( isset( $duration['sec'] )) $dtend += $duration['sec'];
  7554. $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'] ));
  7555. $d = explode( '-', $date );
  7556. $dtend2 = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] );
  7557. if( isset( $startdate['tz'] ))
  7558. $dtend2['tz'] = $startdate['tz'];
  7559. if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] )))
  7560. unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] );
  7561. return $dtend2;
  7562. }
  7563. /**
  7564. * ensures internal duration format for an input string (iCal) formatted duration
  7565. *
  7566. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7567. * @since 2.14.1 - 2012-09-25
  7568. * @param string $duration
  7569. * @return array
  7570. */
  7571. public static function _duration_string( $duration ) {
  7572. return iCalUtilityFunctions::_durationStr2arr( $duration );
  7573. }
  7574. public static function _durationStr2arr( $duration ) {
  7575. $duration = (string) trim( $duration );
  7576. while( 'P' != strtoupper( substr( $duration, 0, 1 ))) {
  7577. if( 0 < strlen( $duration ))
  7578. $duration = substr( $duration, 1 );
  7579. else
  7580. return false; // no leading P !?!?
  7581. }
  7582. $duration = substr( $duration, 1 ); // skip P
  7583. $duration = str_replace ( 't', 'T', $duration );
  7584. $duration = str_replace ( 'T', '', $duration );
  7585. $output = array();
  7586. $val = null;
  7587. for( $ix=0; $ix < strlen( $duration ); $ix++ ) {
  7588. switch( strtoupper( substr( $duration, $ix, 1 ))) {
  7589. case 'W':
  7590. $output['week'] = $val;
  7591. $val = null;
  7592. break;
  7593. case 'D':
  7594. $output['day'] = $val;
  7595. $val = null;
  7596. break;
  7597. case 'H':
  7598. $output['hour'] = $val;
  7599. $val = null;
  7600. break;
  7601. case 'M':
  7602. $output['min'] = $val;
  7603. $val = null;
  7604. break;
  7605. case 'S':
  7606. $output['sec'] = $val;
  7607. $val = null;
  7608. break;
  7609. default:
  7610. if( !ctype_digit( substr( $duration, $ix, 1 )))
  7611. return false; // unknown duration control character !?!?
  7612. else
  7613. $val .= substr( $duration, $ix, 1 );
  7614. }
  7615. }
  7616. return iCalUtilityFunctions::_duration2arr( $output );
  7617. }
  7618. /**
  7619. * creates formatted output for calendar component property data value type duration
  7620. *
  7621. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7622. * @since 2.15.8 - 2012-10-30
  7623. * @param array $duration, array( week, day, hour, min, sec )
  7624. * @return string
  7625. */
  7626. public static function _format_duration( $duration ) {
  7627. return iCalUtilityFunctions::_duration2str( $duration );
  7628. }
  7629. public static function _duration2str( $duration ) {
  7630. if( isset( $duration['week'] ) ||
  7631. isset( $duration['day'] ) ||
  7632. isset( $duration['hour'] ) ||
  7633. isset( $duration['min'] ) ||
  7634. isset( $duration['sec'] ))
  7635. $ok = TRUE;
  7636. else
  7637. return;
  7638. if( isset( $duration['week'] ) && ( 0 < $duration['week'] ))
  7639. return 'P'.$duration['week'].'W';
  7640. $output = 'P';
  7641. if( isset($duration['day'] ) && ( 0 < $duration['day'] ))
  7642. $output .= $duration['day'].'D';
  7643. if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ||
  7644. ( isset( $duration['min']) && ( 0 < $duration['min'] )) ||
  7645. ( isset( $duration['sec']) && ( 0 < $duration['sec'] ))) {
  7646. $output .= 'T';
  7647. $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '0H';
  7648. $output .= ( isset( $duration['min']) && ( 0 < $duration['min'] )) ? $duration['min']. 'M' : '0M';
  7649. $output .= ( isset( $duration['sec']) && ( 0 < $duration['sec'] )) ? $duration['sec']. 'S' : '0S';
  7650. }
  7651. if( 'P' == $output )
  7652. $output = 'PT0H0M0S';
  7653. return $output;
  7654. }
  7655. /**
  7656. * removes expkey+expvalue from array and returns hitval (if found) else returns elseval
  7657. *
  7658. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7659. * @since 2.4.16 - 2008-11-08
  7660. * @param array $array
  7661. * @param string $expkey, expected key
  7662. * @param string $expval, expected value
  7663. * @param int $hitVal optional, return value if found
  7664. * @param int $elseVal optional, return value if not found
  7665. * @param int $preSet optional, return value if already preset
  7666. * @return int
  7667. */
  7668. public static function _existRem( &$array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) {
  7669. if( $preSet )
  7670. return $preSet;
  7671. if( !is_array( $array ) || ( 0 == count( $array )))
  7672. return $elseVal;
  7673. foreach( $array as $key => $value ) {
  7674. if( strtoupper( $expkey ) == strtoupper( $key )) {
  7675. if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) {
  7676. unset( $array[$key] );
  7677. return $hitVal;
  7678. }
  7679. }
  7680. }
  7681. return $elseVal;
  7682. }
  7683. /**
  7684. * checks if input contains a (array formatted) date/time
  7685. *
  7686. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7687. * @since 2.11.8 - 2012-01-20
  7688. * @param array $input
  7689. * @return bool
  7690. */
  7691. public static function _isArrayDate( $input ) {
  7692. if( !is_array( $input ))
  7693. return FALSE;
  7694. if( isset( $input['week'] ) || ( !in_array( count( $input ), array( 3, 6, 7 ))))
  7695. return FALSE;
  7696. if( 7 == count( $input ))
  7697. return TRUE;
  7698. if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
  7699. return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
  7700. if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] ))
  7701. return FALSE;
  7702. if( in_array( 0, $input ))
  7703. return FALSE;
  7704. if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] ))
  7705. return FALSE;
  7706. if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) &&
  7707. checkdate( (int) $input[1], (int) $input[2], (int) $input[0] ))
  7708. return TRUE;
  7709. $input = iCalUtilityFunctions::_strdate2date( $input[1].'/'.$input[2].'/'.$input[0], 3 ); // m - d - Y
  7710. if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
  7711. return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
  7712. return FALSE;
  7713. }
  7714. /**
  7715. * checks if input array contains a timestamp date
  7716. *
  7717. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7718. * @since 2.4.16 - 2008-10-18
  7719. * @param array $input
  7720. * @return bool
  7721. */
  7722. public static function _isArrayTimestampDate( $input ) {
  7723. return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ;
  7724. }
  7725. /**
  7726. * controls if input string contains (trailing) UTC/iCal offset
  7727. *
  7728. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7729. * @since 2.14.1 - 2012-09-21
  7730. * @param string $input
  7731. * @return bool
  7732. */
  7733. public static function _isOffset( $input ) {
  7734. $input = trim( (string) $input );
  7735. if( 'Z' == substr( $input, -1 ))
  7736. return TRUE;
  7737. elseif(( 5 <= strlen( $input )) &&
  7738. ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) &&
  7739. ( '0000' <= substr( $input, -4 )) && ( '9999' >= substr( $input, -4 )))
  7740. return TRUE;
  7741. elseif(( 7 <= strlen( $input )) &&
  7742. ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) &&
  7743. ( '000000' <= substr( $input, -6 )) && ( '999999' >= substr( $input, -6 )))
  7744. return TRUE;
  7745. return FALSE;
  7746. }
  7747. /**
  7748. * (very simple) conversion of a MS timezone to a PHP5 valid (Date-)timezone
  7749. * matching (MS) UCT offset and time zone descriptors
  7750. *
  7751. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7752. * @since 2.14.1 - 2012-09-16
  7753. * @param string $timezone, input/output variable reference
  7754. * @return bool
  7755. */
  7756. public static function ms2phpTZ( & $timezone ) {
  7757. if( empty( $timezone ))
  7758. return FALSE;
  7759. $search = str_replace( '"', '', $timezone );
  7760. $search = str_replace( array('GMT', 'gmt', 'utc' ), 'UTC', $search );
  7761. if( '(UTC' != substr( $search, 0, 4 ))
  7762. return FALSE;
  7763. if( FALSE === ( $pos = strpos( $search, ')' )))
  7764. return FALSE;
  7765. $pos = strpos( $search, ')' );
  7766. $searchOffset = substr( $search, 4, ( $pos - 4 ));
  7767. $searchOffset = iCalUtilityFunctions::_tz2offset( str_replace( ':', '', $searchOffset ));
  7768. while( ' ' ==substr( $search, ( $pos + 1 )))
  7769. $pos += 1;
  7770. $searchText = trim( str_replace( array( '(', ')', '&', ',', ' ' ), ' ', substr( $search, ( $pos + 1 )) ));
  7771. $searchWords = explode( ' ', $searchText );
  7772. $timezone_abbreviations = DateTimeZone::listAbbreviations();
  7773. $hits = array();
  7774. foreach( $timezone_abbreviations as $name => $transitions ) {
  7775. foreach( $transitions as $cnt => $transition ) {
  7776. if( empty( $transition['offset'] ) ||
  7777. empty( $transition['timezone_id'] ) ||
  7778. ( $transition['offset'] != $searchOffset ))
  7779. continue;
  7780. $cWords = explode( '/', $transition['timezone_id'] );
  7781. $cPrio = $hitCnt = $rank = 0;
  7782. foreach( $cWords as $cWord ) {
  7783. if( empty( $cWord ))
  7784. continue;
  7785. $cPrio += 1;
  7786. $sPrio = 0;
  7787. foreach( $searchWords as $sWord ) {
  7788. if( empty( $sWord ) || ( 'time' == strtolower( $sWord )))
  7789. continue;
  7790. $sPrio += 1;
  7791. if( strtolower( $cWord ) == strtolower( $sWord )) {
  7792. $hitCnt += 1;
  7793. $rank += ( $cPrio + $sPrio );
  7794. }
  7795. else
  7796. $rank += 10;
  7797. }
  7798. }
  7799. if( 0 < $hitCnt ) {
  7800. $hits[$rank][] = $transition['timezone_id'];
  7801. }
  7802. }
  7803. }
  7804. unset( $timezone_abbreviations );
  7805. if( empty( $hits ))
  7806. return FALSE;
  7807. ksort( $hits );
  7808. foreach( $hits as $rank => $tzs ) {
  7809. if( !empty( $tzs )) {
  7810. $timezone = reset( $tzs );
  7811. return TRUE;
  7812. }
  7813. }
  7814. return FALSE;
  7815. }
  7816. /**
  7817. * transforms offset in seconds to [-/+]hhmm[ss]
  7818. *
  7819. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7820. * @since 2011-05-02
  7821. * @param string $seconds
  7822. * @return string
  7823. */
  7824. public static function offsetSec2His( $seconds ) {
  7825. if( '-' == substr( $seconds, 0, 1 )) {
  7826. $prefix = '-';
  7827. $seconds = substr( $seconds, 1 );
  7828. }
  7829. elseif( '+' == substr( $seconds, 0, 1 )) {
  7830. $prefix = '+';
  7831. $seconds = substr( $seconds, 1 );
  7832. }
  7833. else
  7834. $prefix = '+';
  7835. $output = '';
  7836. $hour = (int) floor( $seconds / 3600 );
  7837. if( 10 > $hour )
  7838. $hour = '0'.$hour;
  7839. $seconds = $seconds % 3600;
  7840. $min = (int) floor( $seconds / 60 );
  7841. if( 10 > $min )
  7842. $min = '0'.$min;
  7843. $output = $hour.$min;
  7844. $seconds = $seconds % 60;
  7845. if( 0 < $seconds) {
  7846. if( 9 < $seconds)
  7847. $output .= $seconds;
  7848. else
  7849. $output .= '0'.$seconds;
  7850. }
  7851. return $prefix.$output;
  7852. }
  7853. /**
  7854. * updates an array with dates based on a recur pattern
  7855. *
  7856. * if missing, UNTIL is set 1 year from startdate (emergency break)
  7857. *
  7858. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  7859. * @since 2.10.19 - 2011-10-31
  7860. * @param array $result, array to update, array([timestamp] => timestamp)
  7861. * @param array $recur, pattern for recurrency (only value part, params ignored)
  7862. * @param array $wdate, component start date
  7863. * @param array $startdate, start date
  7864. * @param array $enddate, optional
  7865. * @return void
  7866. * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start
  7867. */
  7868. public static function _recur2date( & $result, $recur, $wdate, $startdate, $enddate=FALSE ) {
  7869. foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v;
  7870. $wdateStart = $wdate;
  7871. $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate );
  7872. $startdatets = iCalUtilityFunctions::_date2timestamp( $startdate );
  7873. if( !$enddate ) {
  7874. $enddate = $startdate;
  7875. $enddate['year'] += 1;
  7876. }
  7877. // echo "recur __in_ comp start ".implode('-',$wdate)." period start ".implode('-',$startdate)." period end ".implode('-',$enddate)."<br />\n";print_r($recur);echo "<br />\n";//test###
  7878. $endDatets = iCalUtilityFunctions::_date2timestamp( $enddate ); // fix break
  7879. if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] ))
  7880. $recur['UNTIL'] = $enddate; // create break
  7881. if( isset( $recur['UNTIL'] )) {
  7882. $tdatets = iCalUtilityFunctions::_date2timestamp( $recur['UNTIL'] );
  7883. if( $endDatets > $tdatets ) {
  7884. $endDatets = $tdatets; // emergency break
  7885. $enddate = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
  7886. }
  7887. else
  7888. $recur['UNTIL'] = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
  7889. }
  7890. if( $wdatets > $endDatets ) {
  7891. // echo "recur out of date ".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
  7892. return array(); // nothing to do.. .
  7893. }
  7894. if( !isset( $recur['FREQ'] )) // "MUST be specified.. ."
  7895. $recur['FREQ'] = 'DAILY'; // ??
  7896. $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ??
  7897. $weekStart = (int) date( 'W', ( $wdatets + $wkst ));
  7898. if( !isset( $recur['INTERVAL'] ))
  7899. $recur['INTERVAL'] = 1;
  7900. $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence
  7901. /* find out how to step up dates and set index for interval count */
  7902. $step = array();
  7903. if( 'YEARLY' == $recur['FREQ'] )
  7904. $step['year'] = 1;
  7905. elseif( 'MONTHLY' == $recur['FREQ'] )
  7906. $step['month'] = 1;
  7907. elseif( 'WEEKLY' == $recur['FREQ'] )
  7908. $step['day'] = 7;
  7909. else
  7910. $step['day'] = 1;
  7911. if( isset( $step['year'] ) && isset( $recur['BYMONTH'] ))
  7912. $step = array( 'month' => 1 );
  7913. if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ??
  7914. $step = array( 'day' => 7 );
  7915. if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] ))
  7916. $step = array( 'day' => 1 );
  7917. $intervalarr = array();
  7918. if( 1 < $recur['INTERVAL'] ) {
  7919. $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
  7920. $intervalarr = array( $intervalix => 0 );
  7921. }
  7922. if( isset( $recur['BYSETPOS'] )) { // save start date + weekno
  7923. $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array();
  7924. // echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold<br />\n"; // test ###
  7925. if( is_array( $recur['BYSETPOS'] )) {
  7926. foreach( $recur['BYSETPOS'] as $bix => $bval )
  7927. $recur['BYSETPOS'][$bix] = (int) $bval;
  7928. }
  7929. else
  7930. $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] );
  7931. if( 'YEARLY' == $recur['FREQ'] ) {
  7932. $wdate['month'] = $wdate['day'] = 1; // start from beginning of year
  7933. $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate );
  7934. iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'year' => 1 )); // make sure to count whole last year
  7935. }
  7936. elseif( 'MONTHLY' == $recur['FREQ'] ) {
  7937. $wdate['day'] = 1; // start from beginning of month
  7938. $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate );
  7939. iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'month' => 1 )); // make sure to count whole last month
  7940. }
  7941. else
  7942. iCalUtilityFunctions::_stepdate( $enddate, $endDatets, $step); // make sure to count whole last period
  7943. // echo "BYSETPOS endDat++ =".implode('-',$enddate).' step='.var_export($step,TRUE)."<br />\n";//test###
  7944. $bysetposWold = (int) date( 'W', ( $wdatets + $wkst ));
  7945. $bysetposYold = $wdate['year'];
  7946. $bysetposMold = $wdate['month'];
  7947. $bysetposDold = $wdate['day'];
  7948. }
  7949. else
  7950. iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
  7951. $year_old = null;
  7952. $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
  7953. /* MAIN LOOP */
  7954. // echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."<br />\n";//test
  7955. while( TRUE ) {
  7956. if( isset( $endDatets ) && ( $wdatets > $endDatets ))
  7957. break;
  7958. if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
  7959. break;
  7960. if( $year_old != $wdate['year'] ) {
  7961. $year_old = $wdate['year'];
  7962. $daycnts = array();
  7963. $yeardays = $weekno = 0;
  7964. $yeardaycnt = array();
  7965. foreach( $daynames as $dn )
  7966. $yeardaycnt[$dn] = 0;
  7967. for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters
  7968. $daycnts[$m] = array();
  7969. $weekdaycnt = array();
  7970. foreach( $daynames as $dn )
  7971. $weekdaycnt[$dn] = 0;
  7972. $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
  7973. for( $d = 1; $d <= $mcnt; $d++ ) {
  7974. $daycnts[$m][$d] = array();
  7975. if( isset( $recur['BYYEARDAY'] )) {
  7976. $yeardays++;
  7977. $daycnts[$m][$d]['yearcnt_up'] = $yeardays;
  7978. }
  7979. if( isset( $recur['BYDAY'] )) {
  7980. $day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] ));
  7981. $day = $daynames[$day];
  7982. $daycnts[$m][$d]['DAY'] = $day;
  7983. $weekdaycnt[$day]++;
  7984. $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day];
  7985. $yeardaycnt[$day]++;
  7986. $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day];
  7987. }
  7988. if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
  7989. $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year']));
  7990. }
  7991. }
  7992. $daycnt = 0;
  7993. $yeardaycnt = array();
  7994. if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) {
  7995. $weekno = null;
  7996. for( $d=31; $d > 25; $d-- ) { // get last weekno for year
  7997. if( !$weekno )
  7998. $weekno = $daycnts[12][$d]['weekno_up'];
  7999. elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) {
  8000. $weekno = $daycnts[12][$d]['weekno_up'];
  8001. break;
  8002. }
  8003. }
  8004. }
  8005. for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters
  8006. $weekdaycnt = array();
  8007. foreach( $daynames as $dn )
  8008. $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
  8009. $monthcnt = 0;
  8010. $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
  8011. for( $d = $mcnt; $d > 0; $d-- ) {
  8012. if( isset( $recur['BYYEARDAY'] )) {
  8013. $daycnt -= 1;
  8014. $daycnts[$m][$d]['yearcnt_down'] = $daycnt;
  8015. }
  8016. if( isset( $recur['BYMONTHDAY'] )) {
  8017. $monthcnt -= 1;
  8018. $daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
  8019. }
  8020. if( isset( $recur['BYDAY'] )) {
  8021. $day = $daycnts[$m][$d]['DAY'];
  8022. $weekdaycnt[$day] -= 1;
  8023. $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day];
  8024. $yeardaycnt[$day] -= 1;
  8025. $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day];
  8026. }
  8027. if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
  8028. $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1);
  8029. }
  8030. }
  8031. }
  8032. /* check interval */
  8033. if( 1 < $recur['INTERVAL'] ) {
  8034. /* create interval index */
  8035. $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
  8036. /* check interval */
  8037. $currentKey = array_keys( $intervalarr );
  8038. $currentKey = end( $currentKey ); // get last index
  8039. if( $currentKey != $intervalix )
  8040. $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 ));
  8041. if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) &&
  8042. ( 0 != $intervalarr[$intervalix] )) {
  8043. /* step up date */
  8044. // echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
  8045. iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
  8046. continue;
  8047. }
  8048. else // continue within the selected interval
  8049. $intervalarr[$intervalix] = 0;
  8050. // echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
  8051. }
  8052. $updateOK = TRUE;
  8053. if( $updateOK && isset( $recur['BYMONTH'] ))
  8054. $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH']
  8055. , $wdate['month']
  8056. ,($wdate['month'] - 13));
  8057. if( $updateOK && isset( $recur['BYWEEKNO'] ))
  8058. $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO']
  8059. , $daycnts[$wdate['month']][$wdate['day']]['weekno_up']
  8060. , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] );
  8061. if( $updateOK && isset( $recur['BYYEARDAY'] ))
  8062. $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY']
  8063. , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up']
  8064. , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] );
  8065. if( $updateOK && isset( $recur['BYMONTHDAY'] ))
  8066. $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY']
  8067. , $wdate['day']
  8068. , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] );
  8069. // echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n";//test###
  8070. if( $updateOK && isset( $recur['BYDAY'] )) {
  8071. $updateOK = FALSE;
  8072. $m = $wdate['month'];
  8073. $d = $wdate['day'];
  8074. if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no
  8075. $daynoexists = $daynosw = $daynamesw = FALSE;
  8076. if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] )
  8077. $daynamesw = TRUE;
  8078. if( isset( $recur['BYDAY'][0] )) {
  8079. $daynoexists = TRUE;
  8080. if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] ))
  8081. $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
  8082. , $daycnts[$m][$d]['monthdayno_up']
  8083. , $daycnts[$m][$d]['monthdayno_down'] );
  8084. elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
  8085. $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
  8086. , $daycnts[$m][$d]['yeardayno_up']
  8087. , $daycnts[$m][$d]['yeardayno_down'] );
  8088. }
  8089. if(( $daynoexists && $daynosw && $daynamesw ) ||
  8090. ( !$daynoexists && !$daynosw && $daynamesw )) {
  8091. $updateOK = TRUE;
  8092. // 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<br />\n"; // test ###
  8093. }
  8094. // 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<br />\n"; // test ###
  8095. }
  8096. else {
  8097. foreach( $recur['BYDAY'] as $bydayvalue ) {
  8098. $daynoexists = $daynosw = $daynamesw = FALSE;
  8099. if( isset( $bydayvalue['DAY'] ) &&
  8100. ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] ))
  8101. $daynamesw = TRUE;
  8102. if( isset( $bydayvalue[0] )) {
  8103. $daynoexists = TRUE;
  8104. if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) ||
  8105. isset( $recur['BYMONTH'] ))
  8106. $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
  8107. , $daycnts[$m][$d]['monthdayno_up']
  8108. , $daycnts[$m][$d]['monthdayno_down'] );
  8109. elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
  8110. $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
  8111. , $daycnts[$m][$d]['yeardayno_up']
  8112. , $daycnts[$m][$d]['yeardayno_down'] );
  8113. }
  8114. // echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
  8115. if(( $daynoexists && $daynosw && $daynamesw ) ||
  8116. ( !$daynoexists && !$daynosw && $daynamesw )) {
  8117. $updateOK = TRUE;
  8118. break;
  8119. }
  8120. }
  8121. }
  8122. }
  8123. // echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n"; // test ###
  8124. /* check BYSETPOS */
  8125. if( $updateOK ) {
  8126. if( isset( $recur['BYSETPOS'] ) &&
  8127. ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) {
  8128. if( isset( $recur['WEEKLY'] )) {
  8129. if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] )
  8130. $bysetposw1[] = $wdatets;
  8131. else
  8132. $bysetposw2[] = $wdatets;
  8133. }
  8134. else {
  8135. if(( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
  8136. ( $bysetposYold == $wdate['year'] )) ||
  8137. ( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) &&
  8138. (( $bysetposYold == $wdate['year'] ) &&
  8139. ( $bysetposMold == $wdate['month'] ))) ||
  8140. ( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
  8141. (( $bysetposYold == $wdate['year'] ) &&
  8142. ( $bysetposMold == $wdate['month']) &&
  8143. ( $bysetposDold == $wdate['day'] )))) {
  8144. // echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
  8145. $bysetposymd1[] = $wdatets;
  8146. }
  8147. else {
  8148. // echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
  8149. $bysetposymd2[] = $wdatets;
  8150. }
  8151. }
  8152. }
  8153. else {
  8154. /* update result array if BYSETPOS is set */
  8155. $countcnt++;
  8156. if( $startdatets <= $wdatets ) { // only output within period
  8157. $result[$wdatets] = TRUE;
  8158. // echo "recur ".date('Y-m-d H:i:s',$wdatets)."<br />\n";//test
  8159. }
  8160. // echo "recur undate ".date('Y-m-d H:i:s',$wdatets)." okdatstart ".date('Y-m-d H:i:s',$startdatets)."<br />\n";//test
  8161. $updateOK = FALSE;
  8162. }
  8163. }
  8164. /* step up date */
  8165. iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
  8166. /* check if BYSETPOS is set for updating result array */
  8167. if( $updateOK && isset( $recur['BYSETPOS'] )) {
  8168. $bysetpos = FALSE;
  8169. if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
  8170. ( $bysetposYold != $wdate['year'] )) {
  8171. $bysetpos = TRUE;
  8172. $bysetposYold = $wdate['year'];
  8173. }
  8174. elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] &&
  8175. (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) {
  8176. $bysetpos = TRUE;
  8177. $bysetposYold = $wdate['year'];
  8178. $bysetposMold = $wdate['month'];
  8179. }
  8180. elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) {
  8181. $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
  8182. if( $bysetposWold != $weekno ) {
  8183. $bysetposWold = $weekno;
  8184. $bysetpos = TRUE;
  8185. }
  8186. }
  8187. elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
  8188. (( $bysetposYold != $wdate['year'] ) ||
  8189. ( $bysetposMold != $wdate['month'] ) ||
  8190. ( $bysetposDold != $wdate['day'] ))) {
  8191. $bysetpos = TRUE;
  8192. $bysetposYold = $wdate['year'];
  8193. $bysetposMold = $wdate['month'];
  8194. $bysetposDold = $wdate['day'];
  8195. }
  8196. if( $bysetpos ) {
  8197. if( isset( $recur['BYWEEKNO'] )) {
  8198. $bysetposarr1 = & $bysetposw1;
  8199. $bysetposarr2 = & $bysetposw2;
  8200. }
  8201. else {
  8202. $bysetposarr1 = & $bysetposymd1;
  8203. $bysetposarr2 = & $bysetposymd2;
  8204. }
  8205. // echo 'test före out startYMD (weekno)='.$wdateStart['year'].':'.$wdateStart['month'].':'.$wdateStart['day']." ($weekStart) "; // test ###
  8206. foreach( $recur['BYSETPOS'] as $ix ) {
  8207. if( 0 > $ix ) // both positive and negative BYSETPOS allowed
  8208. $ix = ( count( $bysetposarr1 ) + $ix + 1);
  8209. $ix--;
  8210. if( isset( $bysetposarr1[$ix] )) {
  8211. if( $startdatets <= $bysetposarr1[$ix] ) { // only output within period
  8212. // $testdate = iCalUtilityFunctions::_timestamp2date( $bysetposarr1[$ix], 6 ); // test ###
  8213. // $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, $testdate['month'], $testdate['day'], $testdate['year'] )); // test ###
  8214. // echo " testYMD (weekno)=".$testdate['year'].':'.$testdate['month'].':'.$testdate['day']." ($testweekno)"; // test ###
  8215. $result[$bysetposarr1[$ix]] = TRUE;
  8216. // echo " recur ".date('Y-m-d H:i:s',$bysetposarr1[$ix]); // test ###
  8217. }
  8218. $countcnt++;
  8219. }
  8220. if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
  8221. break;
  8222. }
  8223. // echo "<br />\n"; // test ###
  8224. $bysetposarr1 = $bysetposarr2;
  8225. $bysetposarr2 = array();
  8226. }
  8227. }
  8228. }
  8229. }
  8230. public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) {
  8231. if( is_array( $BYvalue ) &&
  8232. ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue )))
  8233. return TRUE;
  8234. elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue ))
  8235. return TRUE;
  8236. else
  8237. return FALSE;
  8238. }
  8239. public static function _recurIntervalIx( $freq, $date, $wkst ) {
  8240. /* create interval index */
  8241. switch( $freq ) {
  8242. case 'YEARLY':
  8243. $intervalix = $date['year'];
  8244. break;
  8245. case 'MONTHLY':
  8246. $intervalix = $date['year'].'-'.$date['month'];
  8247. break;
  8248. case 'WEEKLY':
  8249. $wdatets = iCalUtilityFunctions::_date2timestamp( $date );
  8250. $intervalix = (int) date( 'W', ( $wdatets + $wkst ));
  8251. break;
  8252. case 'DAILY':
  8253. default:
  8254. $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day'];
  8255. break;
  8256. }
  8257. return $intervalix;
  8258. }
  8259. /**
  8260. * convert input format for exrule and rrule to internal format
  8261. *
  8262. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8263. * @since 2.14.1 - 2012-09-24
  8264. * @param array $rexrule
  8265. * @return array
  8266. */
  8267. public static function _setRexrule( $rexrule ) {
  8268. $input = array();
  8269. if( empty( $rexrule ))
  8270. return $input;
  8271. foreach( $rexrule as $rexrulelabel => $rexrulevalue ) {
  8272. $rexrulelabel = strtoupper( $rexrulelabel );
  8273. if( 'UNTIL' != $rexrulelabel )
  8274. $input[$rexrulelabel] = $rexrulevalue;
  8275. else {
  8276. iCalUtilityFunctions::_strDate2arr( $rexrulevalue );
  8277. if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp, always date-time UTC
  8278. $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 7, 'UTC' );
  8279. elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) { // date or UTC date-time
  8280. $parno = ( isset( $rexrulevalue['hour'] ) || isset( $rexrulevalue[4] )) ? 7 : 3;
  8281. $d = iCalUtilityFunctions::_chkDateArr( $rexrulevalue, $parno );
  8282. if(( 3 < $parno ) && isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) {
  8283. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
  8284. $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  8285. unset( $input[$rexrulelabel]['unparsedtext'] );
  8286. }
  8287. else
  8288. $input[$rexrulelabel] = $d;
  8289. }
  8290. elseif( 8 <= strlen( trim( $rexrulevalue ))) { // ex. textual date-time 2006-08-03 10:12:18 => UTC
  8291. $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $rexrulevalue );
  8292. unset( $input['$rexrulelabel']['unparsedtext'] );
  8293. }
  8294. if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] ))
  8295. $input[$rexrulelabel]['tz'] = 'Z';
  8296. }
  8297. }
  8298. /* set recurrence rule specification in rfc2445 order */
  8299. $input2 = array();
  8300. if( isset( $input['FREQ'] ))
  8301. $input2['FREQ'] = $input['FREQ'];
  8302. if( isset( $input['UNTIL'] ))
  8303. $input2['UNTIL'] = $input['UNTIL'];
  8304. elseif( isset( $input['COUNT'] ))
  8305. $input2['COUNT'] = $input['COUNT'];
  8306. if( isset( $input['INTERVAL'] ))
  8307. $input2['INTERVAL'] = $input['INTERVAL'];
  8308. if( isset( $input['BYSECOND'] ))
  8309. $input2['BYSECOND'] = $input['BYSECOND'];
  8310. if( isset( $input['BYMINUTE'] ))
  8311. $input2['BYMINUTE'] = $input['BYMINUTE'];
  8312. if( isset( $input['BYHOUR'] ))
  8313. $input2['BYHOUR'] = $input['BYHOUR'];
  8314. if( isset( $input['BYDAY'] )) {
  8315. if( !is_array( $input['BYDAY'] )) // ensure upper case.. .
  8316. $input2['BYDAY'] = strtoupper( $input['BYDAY'] );
  8317. else {
  8318. foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) {
  8319. if( 'DAY' == strtoupper( $BYDAYx ))
  8320. $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv );
  8321. elseif( !is_array( $BYDAYv )) {
  8322. $input2['BYDAY'][$BYDAYx] = $BYDAYv;
  8323. }
  8324. else {
  8325. foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) {
  8326. if( 'DAY' == strtoupper( $BYDAYx2 ))
  8327. $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 );
  8328. else
  8329. $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2;
  8330. }
  8331. }
  8332. }
  8333. }
  8334. }
  8335. if( isset( $input['BYMONTHDAY'] ))
  8336. $input2['BYMONTHDAY'] = $input['BYMONTHDAY'];
  8337. if( isset( $input['BYYEARDAY'] ))
  8338. $input2['BYYEARDAY'] = $input['BYYEARDAY'];
  8339. if( isset( $input['BYWEEKNO'] ))
  8340. $input2['BYWEEKNO'] = $input['BYWEEKNO'];
  8341. if( isset( $input['BYMONTH'] ))
  8342. $input2['BYMONTH'] = $input['BYMONTH'];
  8343. if( isset( $input['BYSETPOS'] ))
  8344. $input2['BYSETPOS'] = $input['BYSETPOS'];
  8345. if( isset( $input['WKST'] ))
  8346. $input2['WKST'] = $input['WKST'];
  8347. return $input2;
  8348. }
  8349. /**
  8350. * convert format for input date to internal date with parameters
  8351. *
  8352. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8353. * @since 2.14.1 - 2012-10-15
  8354. * @param mixed $year
  8355. * @param mixed $month optional
  8356. * @param int $day optional
  8357. * @param int $hour optional
  8358. * @param int $min optional
  8359. * @param int $sec optional
  8360. * @param string $tz optional
  8361. * @param array $params optional
  8362. * @param string $caller optional
  8363. * @param string $objName optional
  8364. * @param string $tzid optional
  8365. * @return array
  8366. */
  8367. 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 ) {
  8368. $input = $parno = null;
  8369. $localtime = (( 'dtstart' == $caller ) && in_array( $objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
  8370. iCalUtilityFunctions::_strDate2arr( $year );
  8371. if( iCalUtilityFunctions::_isArrayDate( $year )) {
  8372. $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, $parno );
  8373. if( 100 > $input['value']['year'] )
  8374. $input['value']['year'] += 2000;
  8375. if( $localtime )
  8376. unset( $month['VALUE'], $month['TZID'] );
  8377. elseif( !isset( $month['TZID'] ) && isset( $tzid ))
  8378. $month['TZID'] = $tzid;
  8379. if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] ))
  8380. unset( $month['TZID'] );
  8381. elseif( isset( $month['TZID'] ) && iCalUtilityFunctions::_isOffset( $month['TZID'] )) {
  8382. $input['value']['tz'] = $month['TZID'];
  8383. unset( $month['TZID'] );
  8384. }
  8385. $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
  8386. $hitval = ( isset( $input['value']['tz'] )) ? 7 : 6;
  8387. $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval );
  8388. $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $input['value'] ), $parno );
  8389. if(( 3 != $parno ) && isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
  8390. $d = $input['value'];
  8391. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
  8392. $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno );
  8393. unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
  8394. }
  8395. if( isset( $input['value']['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
  8396. $input['params']['TZID'] = $input['value']['tz'];
  8397. unset( $input['value']['tz'] );
  8398. }
  8399. } // end if( iCalUtilityFunctions::_isArrayDate( $year ))
  8400. elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
  8401. if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
  8402. $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
  8403. $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
  8404. $hitval = 7;
  8405. $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno );
  8406. if( !isset( $input['params']['TZID'] ) && !empty( $tzid ))
  8407. $input['params']['TZID'] = $tzid;
  8408. if( isset( $year['tz'] )) {
  8409. $parno = 6;
  8410. if( !iCalUtilityFunctions::_isOffset( $year['tz'] ))
  8411. $input['params']['TZID'] = $year['tz'];
  8412. }
  8413. elseif( isset( $input['params']['TZID'] )) {
  8414. $year['tz'] = $input['params']['TZID'];
  8415. $parno = 6;
  8416. if( iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
  8417. unset( $input['params']['TZID'] );
  8418. $parno = 7;
  8419. }
  8420. }
  8421. $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, $parno );
  8422. } // end elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year ))
  8423. elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18 [[[+/-]1234[56]] / timezone]
  8424. if( $localtime )
  8425. unset( $month['VALUE'], $month['TZID'] );
  8426. elseif( !isset( $month['TZID'] ) && !empty( $tzid ))
  8427. $month['TZID'] = $tzid;
  8428. $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
  8429. $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno );
  8430. $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno );
  8431. $input['value'] = iCalUtilityFunctions::_strdate2date( $year, $parno );
  8432. unset( $input['value']['unparsedtext'] );
  8433. if( isset( $input['value']['tz'] )) {
  8434. if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
  8435. $d = $input['value'];
  8436. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
  8437. $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  8438. unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
  8439. }
  8440. else {
  8441. $input['params']['TZID'] = $input['value']['tz'];
  8442. unset( $input['value']['tz'] );
  8443. }
  8444. }
  8445. elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
  8446. $d = $input['value'];
  8447. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $input['params']['TZID'] );
  8448. $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  8449. unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
  8450. }
  8451. } // end elseif( 8 <= strlen( trim( $year )))
  8452. else {
  8453. if( is_array( $params ))
  8454. $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
  8455. elseif( is_array( $tz )) {
  8456. $input['params'] = iCalUtilityFunctions::_setParams( $tz, array( 'VALUE' => 'DATE-TIME' ));
  8457. $tz = FALSE;
  8458. }
  8459. elseif( is_array( $hour )) {
  8460. $input['params'] = iCalUtilityFunctions::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' ));
  8461. $hour = $min = $sec = $tz = FALSE;
  8462. }
  8463. if( $localtime )
  8464. unset ( $input['params']['VALUE'], $input['params']['TZID'] );
  8465. elseif( !isset( $tz ) && !isset( $input['params']['TZID'] ) && !empty( $tzid ))
  8466. $input['params']['TZID'] = $tzid;
  8467. elseif( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz ))
  8468. unset( $input['params']['TZID'] );
  8469. elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
  8470. $tz = $input['params']['TZID'];
  8471. unset( $input['params']['TZID'] );
  8472. }
  8473. $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
  8474. $hitval = ( iCalUtilityFunctions::_isOffset( $tz )) ? 7 : 6;
  8475. $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno );
  8476. $input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day );
  8477. if( 3 != $parno ) {
  8478. $input['value']['hour'] = ( $hour ) ? $hour : '0';
  8479. $input['value']['min'] = ( $min ) ? $min : '0';
  8480. $input['value']['sec'] = ( $sec ) ? $sec : '0';
  8481. if( !empty( $tz ))
  8482. $input['value']['tz'] = $tz;
  8483. $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], $parno );
  8484. if( !empty( $tz ) && !iCalUtilityFunctions::_isOffset( $tz ))
  8485. $strdate .= ( 'Z' == $tz ) ? $tz : ' '.$tz;
  8486. $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno );
  8487. unset( $input['value']['unparsedtext'] );
  8488. if( isset( $input['value']['tz'] )) {
  8489. if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
  8490. $d = $input['value'];
  8491. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
  8492. $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  8493. unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
  8494. }
  8495. else {
  8496. $input['params']['TZID'] = $input['value']['tz'];
  8497. unset( $input['value']['tz'] );
  8498. }
  8499. }
  8500. elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
  8501. $d = $input['value'];
  8502. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $input['params']['TZID'] );
  8503. $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  8504. unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
  8505. }
  8506. }
  8507. } // end else (i.e. using all arguments)
  8508. if(( 3 == $parno ) || ( isset( $input['params']['VALUE'] ) && ( 'DATE' == $input['params']['VALUE'] ))) {
  8509. $input['params']['VALUE'] = 'DATE';
  8510. unset( $input['value']['hour'], $input['value']['min'], $input['value']['sec'], $input['value']['tz'], $input['params']['TZID'] );
  8511. }
  8512. elseif( isset( $input['params']['TZID'] )) {
  8513. if(( 'UTC' == strtoupper( $input['params']['TZID'] )) || ( 'GMT' == strtoupper( $input['params']['TZID'] ))) {
  8514. $input['value']['tz'] = 'Z';
  8515. unset( $input['params']['TZID'] );
  8516. }
  8517. else
  8518. unset( $input['value']['tz'] );
  8519. }
  8520. elseif( isset( $input['value']['tz'] )) {
  8521. if(( 'UTC' == strtoupper( $input['value']['tz'] )) || ( 'GMT' == strtoupper( $input['value']['tz'] )))
  8522. $input['value']['tz'] = 'Z';
  8523. if( 'Z' != $input['value']['tz'] ) {
  8524. $input['params']['TZID'] = $input['value']['tz'];
  8525. unset( $input['value']['tz'] );
  8526. }
  8527. else
  8528. unset( $input['params']['TZID'] );
  8529. }
  8530. if( $localtime )
  8531. unset( $input['value']['tz'], $input['params']['TZID'] );
  8532. return $input;
  8533. }
  8534. /**
  8535. * convert format for input date (UTC) to internal date with parameters
  8536. *
  8537. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8538. * @since 2.14.4 - 2012-10-06
  8539. * @param mixed $year
  8540. * @param mixed $month optional
  8541. * @param int $day optional
  8542. * @param int $hour optional
  8543. * @param int $min optional
  8544. * @param int $sec optional
  8545. * @param array $params optional
  8546. * @return array
  8547. */
  8548. public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
  8549. $input = null;
  8550. iCalUtilityFunctions::_strDate2arr( $year );
  8551. if( iCalUtilityFunctions::_isArrayDate( $year )) {
  8552. $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, 7 );
  8553. if( isset( $input['value']['year'] ) && ( 100 > $input['value']['year'] ))
  8554. $input['value']['year'] += 2000;
  8555. $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
  8556. if( isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
  8557. $d = $input['value'];
  8558. $strdate = sprintf( '%04d-%02d-%02d %02d:%02d:%02d %s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['min'], $d['sec'], $d['tz'] );
  8559. $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  8560. unset( $input['value']['unparsedtext'] );
  8561. }
  8562. }
  8563. elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
  8564. $year['tz'] = 'UTC';
  8565. $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, 7 );
  8566. $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
  8567. }
  8568. elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
  8569. $input['value'] = iCalUtilityFunctions::_strdate2date( $year, 7 );
  8570. unset( $input['value']['unparsedtext'] );
  8571. $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
  8572. }
  8573. else {
  8574. $input['value'] = array( 'year' => $year
  8575. , 'month' => $month
  8576. , 'day' => $day
  8577. , 'hour' => $hour
  8578. , 'min' => $min
  8579. , 'sec' => $sec );
  8580. if( isset( $tz )) $input['value']['tz'] = $tz;
  8581. if(( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) ||
  8582. ( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))) {
  8583. if( !isset( $tz ) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))
  8584. $input['value']['tz'] = $input['params']['TZID'];
  8585. unset( $input['params']['TZID'] );
  8586. $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], 7 );
  8587. $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
  8588. unset( $input['value']['unparsedtext'] );
  8589. }
  8590. $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
  8591. }
  8592. $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default
  8593. if( !isset( $input['value']['hour'] )) $input['value']['hour'] = 0;
  8594. if( !isset( $input['value']['min'] )) $input['value']['min'] = 0;
  8595. if( !isset( $input['value']['sec'] )) $input['value']['sec'] = 0;
  8596. $input['value']['tz'] = 'Z';
  8597. return $input;
  8598. }
  8599. /**
  8600. * check index and set (an indexed) content in multiple value array
  8601. *
  8602. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8603. * @since 2.6.12 - 2011-01-03
  8604. * @param array $valArr
  8605. * @param mixed $value
  8606. * @param array $params
  8607. * @param array $defaults
  8608. * @param int $index
  8609. * @return void
  8610. */
  8611. public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) {
  8612. if( !is_array( $valArr )) $valArr = array();
  8613. if( $index )
  8614. $index = $index - 1;
  8615. elseif( 0 < count( $valArr )) {
  8616. $keys = array_keys( $valArr );
  8617. $index = end( $keys ) + 1;
  8618. }
  8619. else
  8620. $index = 0;
  8621. $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults ));
  8622. ksort( $valArr );
  8623. }
  8624. /**
  8625. * set input (formatted) parameters- component property attributes
  8626. *
  8627. * default parameters can be set, if missing
  8628. *
  8629. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8630. * @since 1.x.x - 2007-05-01
  8631. * @param array $params
  8632. * @param array $defaults
  8633. * @return array
  8634. */
  8635. public static function _setParams( $params, $defaults=FALSE ) {
  8636. if( !is_array( $params))
  8637. $params = array();
  8638. $input = array();
  8639. foreach( $params as $paramKey => $paramValue ) {
  8640. if( is_array( $paramValue )) {
  8641. foreach( $paramValue as $pkey => $pValue ) {
  8642. if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 )))
  8643. $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 ));
  8644. }
  8645. }
  8646. elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 )))
  8647. $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 ));
  8648. if( 'VALUE' == strtoupper( $paramKey ))
  8649. $input['VALUE'] = strtoupper( $paramValue );
  8650. else
  8651. $input[strtoupper( $paramKey )] = $paramValue;
  8652. }
  8653. if( is_array( $defaults )) {
  8654. foreach( $defaults as $paramKey => $paramValue ) {
  8655. if( !isset( $input[$paramKey] ))
  8656. $input[$paramKey] = $paramValue;
  8657. }
  8658. }
  8659. return (0 < count( $input )) ? $input : null;
  8660. }
  8661. /**
  8662. * break lines at pos 75
  8663. *
  8664. * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
  8665. * break. Long content lines SHOULD be split into a multiple line
  8666. * representations using a line "folding" technique. That is, a long
  8667. * line can be split between any two characters by inserting a CRLF
  8668. * immediately followed by a single linear white space character (i.e.,
  8669. * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
  8670. * of CRLF followed immediately by a single linear white space character
  8671. * is ignored (i.e., removed) when processing the content type.
  8672. *
  8673. * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where
  8674. * the reserved expression "\n" in the arg $string could be broken up by the
  8675. * folding of lines, causing ambiguity in the return string.
  8676. *
  8677. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8678. * @since 2.16.2 - 2012-12-18
  8679. * @param string $value
  8680. * @return string
  8681. */
  8682. public static function _size75( $string, $nl ) {
  8683. $tmp = $string;
  8684. $string = '';
  8685. $cCnt = $x = 0;
  8686. while( TRUE ) {
  8687. if( !isset( $tmp[$x] )) {
  8688. $string .= $nl; // loop breakes here
  8689. break;
  8690. }
  8691. elseif(( 74 <= $cCnt ) && ( '\\' == $tmp[$x] ) && ( 'n' == $tmp[$x+1] )) {
  8692. $string .= $nl.' \n'; // don't break lines inside '\n'
  8693. $x += 2;
  8694. if( !isset( $tmp[$x] )) {
  8695. $string .= $nl;
  8696. break;
  8697. }
  8698. $cCnt = 3;
  8699. }
  8700. elseif( 75 <= $cCnt ) {
  8701. $string .= $nl.' ';
  8702. $cCnt = 1;
  8703. }
  8704. $byte = ord( $tmp[$x] );
  8705. $string .= $tmp[$x];
  8706. switch( TRUE ) { // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  8707. case(( $byte >= 0x20 ) && ( $byte <= 0x7F )): // characters U-00000000 - U-0000007F (same as ASCII)
  8708. $cCnt += 1;
  8709. break; // add a one byte character
  8710. case(( $byte & 0xE0) == 0xC0 ): // characters U-00000080 - U-000007FF, mask 110XXXXX
  8711. if( isset( $tmp[$x+1] )) {
  8712. $cCnt += 1;
  8713. $string .= $tmp[$x+1];
  8714. $x += 1; // add a two bytes character
  8715. }
  8716. break;
  8717. case(( $byte & 0xF0 ) == 0xE0 ): // characters U-00000800 - U-0000FFFF, mask 1110XXXX
  8718. if( isset( $tmp[$x+2] )) {
  8719. $cCnt += 1;
  8720. $string .= $tmp[$x+1].$tmp[$x+2];
  8721. $x += 2; // add a three bytes character
  8722. }
  8723. break;
  8724. case(( $byte & 0xF8 ) == 0xF0 ): // characters U-00010000 - U-001FFFFF, mask 11110XXX
  8725. if( isset( $tmp[$x+3] )) {
  8726. $cCnt += 1;
  8727. $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3];
  8728. $x += 3; // add a four bytes character
  8729. }
  8730. break;
  8731. case(( $byte & 0xFC ) == 0xF8 ): // characters U-00200000 - U-03FFFFFF, mask 111110XX
  8732. if( isset( $tmp[$x+4] )) {
  8733. $cCnt += 1;
  8734. $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4];
  8735. $x += 4; // add a five bytes character
  8736. }
  8737. break;
  8738. case(( $byte & 0xFE ) == 0xFC ): // characters U-04000000 - U-7FFFFFFF, mask 1111110X
  8739. if( isset( $tmp[$x+5] )) {
  8740. $cCnt += 1;
  8741. $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4].$tmp[$x+5];
  8742. $x += 5; // add a six bytes character
  8743. }
  8744. default: // add any other byte without counting up $cCnt
  8745. break;
  8746. } // end switch( TRUE )
  8747. $x += 1; // next 'byte' to test
  8748. } // end while( TRUE ) {
  8749. return $string;
  8750. }
  8751. /**
  8752. * sort callback functions for exdate
  8753. *
  8754. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8755. * @since 2.16.11 - 2013-01-12
  8756. * @param array $a
  8757. * @param array $b
  8758. * @return int
  8759. */
  8760. public static function _sortExdate1( $a, $b ) {
  8761. $as = sprintf( '%04d%02d%02d', $a['year'], $a['month'], $a['day'] );
  8762. $as .= ( isset( $a['hour'] )) ? sprintf( '%02d%02d%02d', $a['hour'], $a['min'], $a['sec'] ) : '';
  8763. $bs = sprintf( '%04d%02d%02d', $b['year'], $b['month'], $b['day'] );
  8764. $bs .= ( isset( $b['hour'] )) ? sprintf( '%02d%02d%02d', $b['hour'], $b['min'], $b['sec'] ) : '';
  8765. return strcmp( $as, $bs );
  8766. }
  8767. public static function _sortExdate2( $a, $b ) {
  8768. $val = reset( $a['value'] );
  8769. $as = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
  8770. $as .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
  8771. $val = reset( $b['value'] );
  8772. $bs = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
  8773. $bs .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
  8774. return strcmp( $as, $bs );
  8775. }
  8776. /**
  8777. * sort callback functions for rdate
  8778. *
  8779. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8780. * @since 2.16.9 - 2013-01-12
  8781. * @param array $a
  8782. * @param array $b
  8783. * @return int
  8784. */
  8785. public static function _sortRdate1( $a, $b ) {
  8786. $val = isset( $a['year'] ) ? $a : $a[0];
  8787. $as = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
  8788. $as .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
  8789. $val = isset( $b['year'] ) ? $b : $b[0];
  8790. $bs = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
  8791. $bs .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
  8792. return strcmp( $as, $bs );
  8793. }
  8794. public static function _sortRdate2( $a, $b ) {
  8795. $val = isset( $a['value'][0]['year'] ) ? $a['value'][0] : $a['value'][0][0];
  8796. $as = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
  8797. $as .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
  8798. $val = isset( $b['value'][0]['year'] ) ? $b['value'][0] : $b['value'][0][0];
  8799. $bs = sprintf( '%04d%02d%02d', $val['year'], $val['month'], $val['day'] );
  8800. $bs .= ( isset( $val['hour'] )) ? sprintf( '%02d%02d%02d', $val['hour'], $val['min'], $val['sec'] ) : '';
  8801. return strcmp( $as, $bs );
  8802. }
  8803. /**
  8804. * step date, return updated date, array and timpstamp
  8805. *
  8806. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8807. * @since 2.14.1 - 2012-09-24
  8808. * @param array $date, date to step
  8809. * @param int $timestamp
  8810. * @param array $step, default array( 'day' => 1 )
  8811. * @return void
  8812. */
  8813. public static function _stepdate( &$date, &$timestamp, $step=array( 'day' => 1 )) {
  8814. if( !isset( $date['hour'] )) $date['hour'] = 0;
  8815. if( !isset( $date['min'] )) $date['min'] = 0;
  8816. if( !isset( $date['sec'] )) $date['sec'] = 0;
  8817. foreach( $step as $stepix => $stepvalue )
  8818. $date[$stepix] += $stepvalue;
  8819. $timestamp = mktime( $date['hour'], $date['min'], $date['sec'], $date['month'], $date['day'], $date['year'] );
  8820. $d = date( 'Y-m-d-H-i-s', $timestamp);
  8821. $d = explode( '-', $d );
  8822. $date = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] );
  8823. foreach( $date as $k => $v )
  8824. $date[$k] = (int) $v;
  8825. }
  8826. /**
  8827. * convert a date from specific string to array format
  8828. *
  8829. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8830. * @since 2.11.8 - 2012-01-27
  8831. * @param mixed $input
  8832. * @return bool, TRUE on success
  8833. */
  8834. public static function _strDate2arr( & $input ) {
  8835. if( is_array( $input ))
  8836. return FALSE;
  8837. if( 5 > strlen( (string) $input ))
  8838. return FALSE;
  8839. $work = $input;
  8840. if( 2 == substr_count( $work, '-' ))
  8841. $work = str_replace( '-', '', $work );
  8842. if( 2 == substr_count( $work, '/' ))
  8843. $work = str_replace( '/', '', $work );
  8844. if( !ctype_digit( substr( $work, 0, 8 )))
  8845. return FALSE;
  8846. $temp = array( 'year' => (int) substr( $work, 0, 4 )
  8847. , 'month' => (int) substr( $work, 4, 2 )
  8848. , 'day' => (int) substr( $work, 6, 2 ));
  8849. if( !checkdate( $temp['month'], $temp['day'], $temp['year'] ))
  8850. return FALSE;
  8851. if( 8 == strlen( $work )) {
  8852. $input = $temp;
  8853. return TRUE;
  8854. }
  8855. if(( ' ' == substr( $work, 8, 1 )) || ( 'T' == substr( $work, 8, 1 )) || ( 't' == substr( $work, 8, 1 )))
  8856. $work = substr( $work, 9 );
  8857. elseif( ctype_digit( substr( $work, 8, 1 )))
  8858. $work = substr( $work, 8 );
  8859. else
  8860. return FALSE;
  8861. if( 2 == substr_count( $work, ':' ))
  8862. $work = str_replace( ':', '', $work );
  8863. if( !ctype_digit( substr( $work, 0, 4 )))
  8864. return FALSE;
  8865. $temp['hour'] = substr( $work, 0, 2 );
  8866. $temp['min'] = substr( $work, 2, 2 );
  8867. if((( 0 > $temp['hour'] ) || ( $temp['hour'] > 23 )) ||
  8868. (( 0 > $temp['min'] ) || ( $temp['min'] > 59 )))
  8869. return FALSE;
  8870. if( ctype_digit( substr( $work, 4, 2 ))) {
  8871. $temp['sec'] = substr( $work, 4, 2 );
  8872. if(( 0 > $temp['sec'] ) || ( $temp['sec'] > 59 ))
  8873. return FALSE;
  8874. $len = 6;
  8875. }
  8876. else {
  8877. $temp['sec'] = 0;
  8878. $len = 4;
  8879. }
  8880. if( $len < strlen( $work))
  8881. $temp['tz'] = trim( substr( $work, 6 ));
  8882. $input = $temp;
  8883. return TRUE;
  8884. }
  8885. /**
  8886. * ensures internal date-time/date format for input date-time/date in string fromat
  8887. *
  8888. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  8889. * @since 2.14.1 - 2012-10-07
  8890. * Modified to also return original string value by Yitzchok Lavi <icalcreator@onebigsystem.com>
  8891. * @param array $datetime
  8892. * @param int $parno optional, default FALSE
  8893. * @param moxed $wtz optional, default null
  8894. * @return array
  8895. */
  8896. public static function _date_time_string( $datetime, $parno=FALSE ) {
  8897. return iCalUtilityFunctions::_strdate2date( $datetime, $parno, null );
  8898. }
  8899. public static function _strdate2date( $datetime, $parno=FALSE, $wtz=null ) {
  8900. // save original input string to return it later
  8901. $unparseddatetime = $datetime;
  8902. $datetime = (string) trim( $datetime );
  8903. $tz = null;
  8904. $offset = 0;
  8905. $tzSts = FALSE;
  8906. $len = strlen( $datetime );
  8907. if( 'Z' == substr( $datetime, -1 )) {
  8908. $tz = 'Z';
  8909. $datetime = trim( substr( $datetime, 0, ( $len - 1 )));
  8910. $tzSts = TRUE;
  8911. $len = 88;
  8912. }
  8913. if( iCalUtilityFunctions::_isOffset( substr( $datetime, -5, 5 ))) { // [+/-]NNNN offset
  8914. $tz = substr( $datetime, -5, 5 );
  8915. $datetime = trim( substr( $datetime, 0, ($len - 5)));
  8916. $len = strlen( $datetime );
  8917. }
  8918. elseif( iCalUtilityFunctions::_isOffset( substr( $datetime, -7, 7 ))) { // [+/-]NNNNNN offset
  8919. $tz = substr( $datetime, -7, 7 );
  8920. $datetime = trim( substr( $datetime, 0, ($len - 7)));
  8921. $len = strlen( $datetime );
  8922. }
  8923. elseif( empty( $wtz ) && ctype_digit( substr( $datetime, 0, 4 )) && ctype_digit( substr( $datetime, -2, 2 )) && iCalUtilityFunctions::_strDate2arr( $datetime )) {
  8924. $output = $datetime;
  8925. if( !empty( $tz ))
  8926. $output['tz'] = 'Z';
  8927. $output['unparsedtext'] = $unparseddatetime;
  8928. return $output;
  8929. }
  8930. else {
  8931. $cx = $tx = 0; // find any trailing timezone or offset
  8932. for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) {
  8933. $char = substr( $datetime, $cx, 1 );
  8934. if(( ' ' == $char) || ctype_digit( $char ))
  8935. break; // if exists, tz ends here.. . ?
  8936. else
  8937. $tx--; // tz length counter
  8938. }
  8939. if( 0 > $tx ) { // if any
  8940. $tz = substr( $datetime, $tx );
  8941. $datetime = trim( substr( $datetime, 0, $len + $tx ));
  8942. $len = strlen( $datetime );
  8943. }
  8944. if(( 17 <= $len ) || // long textual datetime
  8945. ( ctype_digit( substr( $datetime, 0, 8 )) && ( 'T' == substr( $datetime, 8, 1 )) && ctype_digit( substr( $datetime, -6, 6 ))) ||
  8946. ( ctype_digit( substr( $datetime, 0, 14 )))) {
  8947. $len = 88;
  8948. $tzSts = TRUE;
  8949. }
  8950. else
  8951. $tz = null; // no tz for Y-m-d dates
  8952. }
  8953. if( empty( $tz ) && !empty( $wtz ))
  8954. $tz = $wtz;
  8955. if( 17 >= $len ) // any Y-m-d textual date
  8956. $tz = null;
  8957. if( !empty( $tz ) && ( 17 < $len )) { // tz set AND long textual datetime
  8958. if(( 'Z' != $tz ) && ( iCalUtilityFunctions::_isOffset( $tz ))) {
  8959. $offset = (string) iCalUtilityFunctions::_tz2offset( $tz ) * -1;
  8960. $tz = 'UTC';
  8961. $tzSts = TRUE;
  8962. }
  8963. elseif( !empty( $wtz ))
  8964. $tzSts = TRUE;
  8965. $tz = trim( $tz );
  8966. if(( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz )))
  8967. $tz = 'UTC';
  8968. if( 0 < substr_count( $datetime, '-' ))
  8969. $datetime = str_replace( '-', '/', $datetime );
  8970. try {
  8971. $d = new DateTime( $datetime, new DateTimeZone( $tz ));
  8972. if( 0 != $offset ) // adjust for offset
  8973. $d->modify( $offset.' seconds' );
  8974. $datestring = $d->format( 'Y-m-d-H-i-s' );
  8975. unset( $d );
  8976. }
  8977. catch( Exception $e ) {
  8978. $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime ));
  8979. }
  8980. } // end if( !empty( $tz ) && ( 17 < $len ))
  8981. else
  8982. $datestring = date( 'Y-m-d-H-i-s', strtotime( $datetime ));
  8983. // echo "<tr><td>&nbsp;<td colspan='3'>_strdate2date input=$datetime, tz=$tz, offset=$offset, wtz=$wtz, len=$len, prepDate=$datestring\n";
  8984. if( 'UTC' == $tz )
  8985. $tz = 'Z';
  8986. $d = explode( '-', $datestring );
  8987. $output = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2] );
  8988. if((( FALSE !== $parno ) && ( 3 != $parno )) || // parno is set to 6 or 7
  8989. (( FALSE === $parno ) && ( 'Z' == $tz )) || // parno is not set and UTC
  8990. (( 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
  8991. $output['hour'] = $d[3];
  8992. $output['min'] = $d[4];
  8993. $output['sec'] = $d[5];
  8994. if(( $tzSts || ( 7 == $parno )) && !empty( $tz ))
  8995. $output['tz'] = $tz;
  8996. }
  8997. // return original string in the array in case strtotime failed to make sense of it
  8998. $output['unparsedtext'] = $unparseddatetime;
  8999. return $output;
  9000. }
  9001. /********************************************************************************/
  9002. /**
  9003. * special characters management output
  9004. *
  9005. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9006. * @since 2.16.2 - 2012-12-18
  9007. * @param string $string
  9008. * @param string $format
  9009. * @param string $nl
  9010. * @return string
  9011. */
  9012. public static function _strrep( $string, $format, $nl ) {
  9013. switch( $format ) {
  9014. case 'xcal':
  9015. $string = str_replace( '\n', $nl, $string);
  9016. $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string ))));
  9017. break;
  9018. default:
  9019. $pos = 0;
  9020. $specChars = array( 'n', 'N', 'r', ',', ';' );
  9021. while( isset( $string[$pos] )) {
  9022. if( FALSE === ( $pos = strpos( $string, "\\", $pos )))
  9023. break;
  9024. if( !in_array( substr( $string, $pos, 1 ), $specChars )) {
  9025. $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 ));
  9026. $pos += 1;
  9027. }
  9028. $pos += 1;
  9029. }
  9030. if( FALSE !== strpos( $string, '"' ))
  9031. $string = str_replace('"', "'", $string);
  9032. if( FALSE !== strpos( $string, ',' ))
  9033. $string = str_replace(',', '\,', $string);
  9034. if( FALSE !== strpos( $string, ';' ))
  9035. $string = str_replace(';', '\;', $string);
  9036. if( FALSE !== strpos( $string, "\r\n" ))
  9037. $string = str_replace( "\r\n", '\n', $string);
  9038. elseif( FALSE !== strpos( $string, "\r" ))
  9039. $string = str_replace( "\r", '\n', $string);
  9040. elseif( FALSE !== strpos( $string, "\n" ))
  9041. $string = str_replace( "\n", '\n', $string);
  9042. if( FALSE !== strpos( $string, '\N' ))
  9043. $string = str_replace( '\N', '\n', $string);
  9044. // if( FALSE !== strpos( $string, $nl ))
  9045. $string = str_replace( $nl, '\n', $string);
  9046. break;
  9047. }
  9048. return $string;
  9049. }
  9050. /**
  9051. * special characters management input (from iCal file)
  9052. *
  9053. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9054. * @since 2.16.2 - 2012-12-18
  9055. * @param string $string
  9056. * @return string
  9057. */
  9058. public static function _strunrep( $string ) {
  9059. $string = str_replace( '\\\\', '\\', $string);
  9060. $string = str_replace( '\,', ',', $string);
  9061. $string = str_replace( '\;', ';', $string);
  9062. // $string = str_replace( '\n', $nl, $string); // ??
  9063. return $string;
  9064. }
  9065. /**
  9066. * convert timestamp to date array, default UTC or adjusted for offset/timezone
  9067. *
  9068. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9069. * @since 2.15.1 - 2012-10-17
  9070. * @param mixed $timestamp
  9071. * @param int $parno
  9072. * @param string $wtz
  9073. * @return array
  9074. */
  9075. public static function _timestamp2date( $timestamp, $parno=6, $wtz=null ) {
  9076. if( is_array( $timestamp )) {
  9077. $tz = ( isset( $timestamp['tz'] )) ? $timestamp['tz'] : $wtz;
  9078. $timestamp = $timestamp['timestamp'];
  9079. }
  9080. $tz = ( isset( $tz )) ? $tz : $wtz;
  9081. if( empty( $tz ) || ( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz )))
  9082. $tz = 'UTC';
  9083. elseif( iCalUtilityFunctions::_isOffset( $tz )) {
  9084. $offset = iCalUtilityFunctions::_tz2offset( $tz );
  9085. $tz = 'UTC';
  9086. }
  9087. try {
  9088. $d = new DateTime( "@$timestamp" ); // set UTC date
  9089. if( isset( $offset ) && ( 0 != $offset )) // adjust for offset
  9090. $d->modify( $offset.' seconds' );
  9091. elseif( 'UTC' != $tz )
  9092. $d->setTimezone( new DateTimeZone( $tz )); // convert to local date
  9093. $date = $d->format( 'Y-m-d-H-i-s' );
  9094. unset( $d );
  9095. }
  9096. catch( Exception $e ) {
  9097. $date = date( 'Y-m-d-H-i-s', $timestamp );
  9098. }
  9099. $date = explode( '-', $date );
  9100. $output = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2] );
  9101. if( 3 != $parno ) {
  9102. $output['hour'] = $date[3];
  9103. $output['min'] = $date[4];
  9104. $output['sec'] = $date[5];
  9105. if( 'UTC' == $tz && ( !isset( $offset ) || ( 0 == $offset )))
  9106. $output['tz'] = 'Z';
  9107. }
  9108. return $output;
  9109. }
  9110. /**
  9111. * convert timestamp (seconds) to duration in array format
  9112. *
  9113. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9114. * @since 2.6.23 - 2010-10-23
  9115. * @param int $timestamp
  9116. * @return array, duration format
  9117. */
  9118. public static function _timestamp2duration( $timestamp ) {
  9119. $dur = array();
  9120. $dur['week'] = (int) floor( $timestamp / ( 7 * 24 * 60 * 60 ));
  9121. $timestamp = $timestamp % ( 7 * 24 * 60 * 60 );
  9122. $dur['day'] = (int) floor( $timestamp / ( 24 * 60 * 60 ));
  9123. $timestamp = $timestamp % ( 24 * 60 * 60 );
  9124. $dur['hour'] = (int) floor( $timestamp / ( 60 * 60 ));
  9125. $timestamp = $timestamp % ( 60 * 60 );
  9126. $dur['min'] = (int) floor( $timestamp / ( 60 ));
  9127. $dur['sec'] = (int) $timestamp % ( 60 );
  9128. return $dur;
  9129. }
  9130. /**
  9131. * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0)
  9132. *
  9133. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9134. * @since 2.15.1 - 2012-10-17
  9135. * @param mixed $date, date to alter
  9136. * @param string $tzFrom, PHP valid 'from' timezone
  9137. * @param string $tzTo, PHP valid 'to' timezone, default 'UTC'
  9138. * @param string $format, date output format, default 'Ymd\THis'
  9139. * @return bool
  9140. */
  9141. public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) {
  9142. if( is_array( $date ) && isset( $date['timestamp'] )) {
  9143. try {
  9144. $d = new DateTime( "@{$date['timestamp']}" ); // set UTC date
  9145. $d->setTimezone(new DateTimeZone( $tzFrom )); // convert to 'from' date
  9146. }
  9147. catch( Exception $e ) { return FALSE; }
  9148. }
  9149. else {
  9150. if( iCalUtilityFunctions::_isArrayDate( $date )) {
  9151. if( isset( $date['tz'] ))
  9152. unset( $date['tz'] );
  9153. $date = iCalUtilityFunctions::_date2strdate( iCalUtilityFunctions::_chkDateArr( $date ));
  9154. }
  9155. if( 'Z' == substr( $date, -1 ))
  9156. $date = substr( $date, 0, ( strlen( $date ) - 2 ));
  9157. try { $d = new DateTime( $date, new DateTimeZone( $tzFrom )); }
  9158. catch( Exception $e ) { return FALSE; }
  9159. }
  9160. try { $d->setTimezone( new DateTimeZone( $tzTo )); }
  9161. catch( Exception $e ) { return FALSE; }
  9162. $date = $d->format( $format );
  9163. return TRUE;
  9164. }
  9165. /**
  9166. * convert offset, [+/-]HHmm[ss], to seconds, used when correcting UTC to localtime or v.v.
  9167. *
  9168. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9169. * @since 2.11.4 - 2012-01-11
  9170. * @param string $offset
  9171. * @return integer
  9172. */
  9173. public static function _tz2offset( $tz ) {
  9174. $tz = trim( (string) $tz );
  9175. $offset = 0;
  9176. if((( 5 != strlen( $tz )) && ( 7 != strlen( $tz ))) ||
  9177. (( '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) ||
  9178. (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) ||
  9179. (( 7 == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 ))))
  9180. return $offset;
  9181. $hours2sec = (int) substr( $tz, 1, 2 ) * 3600;
  9182. $min2sec = (int) substr( $tz, 3, 2 ) * 60;
  9183. $sec = ( 7 == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00';
  9184. $offset = $hours2sec + $min2sec + $sec;
  9185. $offset = ('-' == substr( $tz, 0, 1 )) ? $offset * -1 : $offset;
  9186. return $offset;
  9187. }
  9188. }
  9189. /*********************************************************************************/
  9190. /* iCalcreator vCard helper functions */
  9191. /*********************************************************************************/
  9192. /**
  9193. * convert single ATTENDEE, CONTACT or ORGANIZER (in email format) to vCard
  9194. * returns vCard/TRUE or if directory (if set) or file write is unvalid, FALSE
  9195. *
  9196. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9197. * @since 2.12.2 - 2012-07-11
  9198. * @param object $email
  9199. * $param string $version, vCard version (default 2.1)
  9200. * $param string $directory, where to save vCards (default FALSE)
  9201. * $param string $ext, vCard file extension (default 'vcf')
  9202. * @return mixed
  9203. */
  9204. function iCal2vCard( $email, $version='2.1', $directory=FALSE, $ext='vcf' ) {
  9205. if( FALSE === ( $pos = strpos( $email, '@' )))
  9206. return FALSE;
  9207. if( $directory ) {
  9208. if( DIRECTORY_SEPARATOR != substr( $directory, ( 0 - strlen( DIRECTORY_SEPARATOR ))))
  9209. $directory .= DIRECTORY_SEPARATOR;
  9210. if( !is_dir( $directory ) || !is_writable( $directory ))
  9211. return FALSE;
  9212. }
  9213. /* prepare vCard */
  9214. $email = str_replace( 'MAILTO:', '', $email );
  9215. $name = $person = substr( $email, 0, $pos );
  9216. if( ctype_upper( $name ) || ctype_lower( $name ))
  9217. $name = array( $name );
  9218. else {
  9219. if( FALSE !== ( $pos = strpos( $name, '.' ))) {
  9220. $name = explode( '.', $name );
  9221. foreach( $name as $k => $part )
  9222. $name[$k] = ucfirst( $part );
  9223. }
  9224. else { // split camelCase
  9225. $chars = $name;
  9226. $name = array( $chars[0] );
  9227. $k = 0;
  9228. $x = 1;
  9229. while( FALSE !== ( $char = substr( $chars, $x, 1 ))) {
  9230. if( ctype_upper( $char )) {
  9231. $k += 1;
  9232. $name[$k] = '';
  9233. }
  9234. $name[$k] .= $char;
  9235. $x++;
  9236. }
  9237. }
  9238. }
  9239. $nl = "\r\n";
  9240. $FN = 'FN:'.implode( ' ', $name ).$nl;
  9241. $name = array_reverse( $name );
  9242. $N = 'N:'.array_shift( $name );
  9243. $scCnt = 0;
  9244. while( NULL != ( $part = array_shift( $name ))) {
  9245. if(( '4.0' != $version ) || ( 4 > $scCnt ))
  9246. $scCnt += 1;
  9247. $N .= ';'.$part;
  9248. }
  9249. while(( '4.0' == $version ) && ( 4 > $scCnt )) {
  9250. $N .= ';';
  9251. $scCnt += 1;
  9252. }
  9253. $N .= $nl;
  9254. $EMAIL = 'EMAIL:'.$email.$nl;
  9255. /* create vCard */
  9256. $vCard = 'BEGIN:VCARD'.$nl;
  9257. $vCard .= "VERSION:$version$nl";
  9258. $vCard .= 'PRODID:-//kigkonsult.se '.ICALCREATOR_VERSION."//$nl";
  9259. $vCard .= $N;
  9260. $vCard .= $FN;
  9261. $vCard .= $EMAIL;
  9262. $vCard .= 'REV:'.gmdate( 'Ymd\THis\Z' ).$nl;
  9263. $vCard .= 'END:VCARD'.$nl;
  9264. /* save each vCard as (unique) single file */
  9265. if( $directory ) {
  9266. $fname = $directory.preg_replace( '/[^a-z0-9.]/i', '', $email );
  9267. $cnt = 1;
  9268. $dbl = '';
  9269. while( is_file ( $fname.$dbl.'.'.$ext )) {
  9270. $cnt += 1;
  9271. $dbl = "_$cnt";
  9272. }
  9273. if( FALSE === file_put_contents( $fname, $fname.$dbl.'.'.$ext ))
  9274. return FALSE;
  9275. return TRUE;
  9276. }
  9277. /* return vCard */
  9278. else
  9279. return $vCard;
  9280. }
  9281. /**
  9282. * convert ATTENDEEs, CONTACTs and ORGANIZERs (in email format) to vCards
  9283. *
  9284. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9285. * @since 2.12.2 - 2012-05-07
  9286. * @param object $calendar, iCalcreator vcalendar instance reference
  9287. * $param string $version, vCard version (default 2.1)
  9288. * $param string $directory, where to save vCards (default FALSE)
  9289. * $param string $ext, vCard file extension (default 'vcf')
  9290. * @return mixed
  9291. */
  9292. function iCal2vCards( & $calendar, $version='2.1', $directory=FALSE, $ext='vcf' ) {
  9293. $hits = array();
  9294. $vCardP = array( 'ATTENDEE', 'CONTACT', 'ORGANIZER' );
  9295. foreach( $vCardP as $prop ) {
  9296. $hits2 = $calendar->getProperty( $prop );
  9297. foreach( $hits2 as $propValue => $occCnt ) {
  9298. if( FALSE === ( $pos = strpos( $propValue, '@' )))
  9299. continue;
  9300. $propValue = str_replace( 'MAILTO:', '', $propValue );
  9301. if( isset( $hits[$propValue] ))
  9302. $hits[$propValue] += $occCnt;
  9303. else
  9304. $hits[$propValue] = $occCnt;
  9305. }
  9306. }
  9307. if( empty( $hits ))
  9308. return FALSE;
  9309. ksort( $hits );
  9310. $output = '';
  9311. foreach( $hits as $email => $skip ) {
  9312. $res = iCal2vCard( $email, $version, $directory, $ext );
  9313. if( $directory && !$res )
  9314. return FALSE;
  9315. elseif( !$res )
  9316. return $res;
  9317. else
  9318. $output .= $res;
  9319. }
  9320. if( $directory )
  9321. return TRUE;
  9322. if( !empty( $output ))
  9323. return $output;
  9324. return FALSE;
  9325. }
  9326. /*********************************************************************************/
  9327. /* iCalcreator XML (rfc6321) helper functions */
  9328. /*********************************************************************************/
  9329. /**
  9330. * format iCal XML output, rfc6321, using PHP SimpleXMLElement
  9331. *
  9332. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9333. * @since 2.15.6 - 2012-10-19
  9334. * @param object $calendar, iCalcreator vcalendar instance reference
  9335. * @return string
  9336. */
  9337. function iCal2XML( & $calendar ) {
  9338. /** fix an SimpleXMLElement instance and create root element */
  9339. $xmlstr = '<?xml version="1.0" encoding="utf-8"?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0">';
  9340. $xmlstr .= '<!-- created utilizing kigkonsult.se '.ICALCREATOR_VERSION.' iCal2XMl (rfc6321) -->';
  9341. $xmlstr .= '</icalendar>';
  9342. $xml = new SimpleXMLElement( $xmlstr );
  9343. $vcalendar = $xml->addChild( 'vcalendar' );
  9344. /** fix calendar properties */
  9345. $properties = $vcalendar->addChild( 'properties' );
  9346. $calProps = array( 'prodid', 'version', 'calscale', 'method' );
  9347. foreach( $calProps as $calProp ) {
  9348. if( FALSE !== ( $content = $calendar->getProperty( $calProp )))
  9349. _addXMLchild( $properties, $calProp, 'text', $content );
  9350. }
  9351. while( FALSE !== ( $content = $calendar->getProperty( FALSE, FALSE, TRUE )))
  9352. _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
  9353. $langCal = $calendar->getConfig( 'language' );
  9354. /** prepare to fix components with properties */
  9355. $components = $vcalendar->addChild( 'components' );
  9356. $comps = array( 'vtimezone', 'vevent', 'vtodo', 'vjournal', 'vfreebusy' );
  9357. foreach( $comps as $compName ) {
  9358. switch( $compName ) {
  9359. case 'vevent':
  9360. case 'vtodo':
  9361. $subComps = array( 'valarm' );
  9362. break;
  9363. case 'vjournal':
  9364. case 'vfreebusy':
  9365. $subComps = array();
  9366. break;
  9367. case 'vtimezone':
  9368. $subComps = array( 'standard', 'daylight' );
  9369. break;
  9370. } // end switch( $compName )
  9371. /** fix component properties */
  9372. while( FALSE !== ( $component = $calendar->getComponent( $compName ))) {
  9373. $child = $components->addChild( $compName );
  9374. $properties = $child->addChild( 'properties' );
  9375. $langComp = $component->getConfig( 'language' );
  9376. $props = $component->getConfig( 'setPropertyNames' );
  9377. foreach( $props as $prop ) {
  9378. switch( strtolower( $prop )) {
  9379. case 'attach': // may occur multiple times, below
  9380. while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9381. $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri';
  9382. unset( $content['params']['VALUE'] );
  9383. _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
  9384. }
  9385. break;
  9386. case 'attendee':
  9387. while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9388. if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
  9389. if( $langComp )
  9390. $content['params']['LANGUAGE'] = $langComp;
  9391. elseif( $langCal )
  9392. $content['params']['LANGUAGE'] = $langCal;
  9393. }
  9394. _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
  9395. }
  9396. break;
  9397. case 'exdate':
  9398. while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9399. $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time';
  9400. unset( $content['params']['VALUE'] );
  9401. _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
  9402. }
  9403. break;
  9404. case 'freebusy':
  9405. while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9406. if( is_array( $content ) && isset( $content['value']['fbtype'] )) {
  9407. $content['params']['FBTYPE'] = $content['value']['fbtype'];
  9408. unset( $content['value']['fbtype'] );
  9409. }
  9410. _addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] );
  9411. }
  9412. break;
  9413. case 'request-status':
  9414. while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9415. if( !isset( $content['params']['LANGUAGE'] )) {
  9416. if( $langComp )
  9417. $content['params']['LANGUAGE'] = $langComp;
  9418. elseif( $langCal )
  9419. $content['params']['LANGUAGE'] = $langCal;
  9420. }
  9421. _addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] );
  9422. }
  9423. break;
  9424. case 'rdate':
  9425. while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9426. $type = 'date-time';
  9427. if( isset( $content['params']['VALUE'] )) {
  9428. if( 'DATE' == $content['params']['VALUE'] )
  9429. $type = 'date';
  9430. elseif( 'PERIOD' == $content['params']['VALUE'] )
  9431. $type = 'period';
  9432. }
  9433. unset( $content['params']['VALUE'] );
  9434. _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
  9435. }
  9436. break;
  9437. case 'categories':
  9438. case 'comment':
  9439. case 'contact':
  9440. case 'description':
  9441. case 'related-to':
  9442. case 'resources':
  9443. while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9444. if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
  9445. if( $langComp )
  9446. $content['params']['LANGUAGE'] = $langComp;
  9447. elseif( $langCal )
  9448. $content['params']['LANGUAGE'] = $langCal;
  9449. }
  9450. _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
  9451. }
  9452. break;
  9453. case 'x-prop':
  9454. while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
  9455. _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
  9456. break;
  9457. case 'created': // single occurence below, if set
  9458. case 'completed':
  9459. case 'dtstamp':
  9460. case 'last-modified':
  9461. $utcDate = TRUE;
  9462. case 'dtstart':
  9463. case 'dtend':
  9464. case 'due':
  9465. case 'recurrence-id':
  9466. if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9467. $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time';
  9468. unset( $content['params']['VALUE'] );
  9469. if(( isset( $content['params']['TZID'] ) && empty( $content['params']['TZID'] )) || @is_null( $content['params']['TZID'] ))
  9470. unset( $content['params']['TZID'] );
  9471. _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
  9472. }
  9473. unset( $utcDate );
  9474. break;
  9475. case 'duration':
  9476. if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9477. if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] ))
  9478. $content['params']['RELATED'] = 'END';
  9479. _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] );
  9480. }
  9481. break;
  9482. case 'rrule':
  9483. while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
  9484. _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
  9485. break;
  9486. case 'class':
  9487. case 'location':
  9488. case 'status':
  9489. case 'summary':
  9490. case 'transp':
  9491. case 'tzid':
  9492. case 'uid':
  9493. if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9494. if((( 'location' == $prop ) || ( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) {
  9495. if( $langComp )
  9496. $content['params']['LANGUAGE'] = $langComp;
  9497. elseif( $langCal )
  9498. $content['params']['LANGUAGE'] = $langCal;
  9499. }
  9500. _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
  9501. }
  9502. break;
  9503. case 'geo':
  9504. if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
  9505. _addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] );
  9506. break;
  9507. case 'organizer':
  9508. if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
  9509. if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
  9510. if( $langComp )
  9511. $content['params']['LANGUAGE'] = $langComp;
  9512. elseif( $langCal )
  9513. $content['params']['LANGUAGE'] = $langCal;
  9514. }
  9515. _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
  9516. }
  9517. break;
  9518. case 'percent-complete':
  9519. case 'priority':
  9520. case 'sequence':
  9521. if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
  9522. _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
  9523. break;
  9524. case 'tzurl':
  9525. case 'url':
  9526. if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
  9527. _addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] );
  9528. break;
  9529. } // end switch( $prop )
  9530. } // end foreach( $props as $prop )
  9531. /** fix subComponent properties, if any */
  9532. foreach( $subComps as $subCompName ) {
  9533. while( FALSE !== ( $subcomp = $component->getComponent( $subCompName ))) {
  9534. $child2 = $child->addChild( $subCompName );
  9535. $properties = $child2->addChild( 'properties' );
  9536. $langComp = $subcomp->getConfig( 'language' );
  9537. $subCompProps = $subcomp->getConfig( 'setPropertyNames' );
  9538. foreach( $subCompProps as $prop ) {
  9539. switch( strtolower( $prop )) {
  9540. case 'attach': // may occur multiple times, below
  9541. while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
  9542. $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri';
  9543. unset( $content['params']['VALUE'] );
  9544. _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
  9545. }
  9546. break;
  9547. case 'attendee':
  9548. while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
  9549. if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
  9550. if( $langComp )
  9551. $content['params']['LANGUAGE'] = $langComp;
  9552. elseif( $langCal )
  9553. $content['params']['LANGUAGE'] = $langCal;
  9554. }
  9555. _addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
  9556. }
  9557. break;
  9558. case 'comment':
  9559. case 'tzname':
  9560. while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
  9561. if( !isset( $content['params']['LANGUAGE'] )) {
  9562. if( $langComp )
  9563. $content['params']['LANGUAGE'] = $langComp;
  9564. elseif( $langCal )
  9565. $content['params']['LANGUAGE'] = $langCal;
  9566. }
  9567. _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
  9568. }
  9569. break;
  9570. case 'rdate':
  9571. while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
  9572. $type = 'date-time';
  9573. if( isset( $content['params']['VALUE'] )) {
  9574. if( 'DATE' == $content['params']['VALUE'] )
  9575. $type = 'date';
  9576. elseif( 'PERIOD' == $content['params']['VALUE'] )
  9577. $type = 'period';
  9578. }
  9579. unset( $content['params']['VALUE'] );
  9580. _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
  9581. }
  9582. break;
  9583. case 'x-prop':
  9584. while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
  9585. _addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
  9586. break;
  9587. case 'action': // single occurence below, if set
  9588. case 'description':
  9589. case 'summary':
  9590. if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
  9591. if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
  9592. if( $langComp )
  9593. $content['params']['LANGUAGE'] = $langComp;
  9594. elseif( $langCal )
  9595. $content['params']['LANGUAGE'] = $langCal;
  9596. }
  9597. _addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
  9598. }
  9599. break;
  9600. case 'dtstart':
  9601. if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
  9602. unset( $content['value']['tz'], $content['params']['VALUE'] ); // always local time
  9603. _addXMLchild( $properties, $prop, 'date-time', $content['value'], $content['params'] );
  9604. }
  9605. break;
  9606. case 'duration':
  9607. if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
  9608. _addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] );
  9609. break;
  9610. case 'repeat':
  9611. if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
  9612. _addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
  9613. break;
  9614. case 'trigger':
  9615. if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
  9616. if( isset( $content['value']['year'] ) &&
  9617. isset( $content['value']['month'] ) &&
  9618. isset( $content['value']['day'] ))
  9619. $type = 'date-time';
  9620. else {
  9621. $type = 'duration';
  9622. if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] ))
  9623. $content['params']['RELATED'] = 'END';
  9624. }
  9625. _addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
  9626. }
  9627. break;
  9628. case 'tzoffsetto':
  9629. case 'tzoffsetfrom':
  9630. if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
  9631. _addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] );
  9632. break;
  9633. case 'rrule':
  9634. while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
  9635. _addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
  9636. break;
  9637. } // switch( $prop )
  9638. } // end foreach( $subCompProps as $prop )
  9639. } // end while( FALSE !== ( $subcomp = $component->getComponent( subCompName )))
  9640. } // end foreach( $subCombs as $subCompName )
  9641. } // end while( FALSE !== ( $component = $calendar->getComponent( $compName )))
  9642. } // end foreach( $comps as $compName)
  9643. return $xml->asXML();
  9644. }
  9645. /**
  9646. * Add children to a SimpleXMLelement
  9647. *
  9648. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9649. * @since 2.15.5 - 2012-10-19
  9650. * @param object $parent, reference to a SimpleXMLelement node
  9651. * @param string $name, new element node name
  9652. * @param string $type, content type, subelement(-s) name
  9653. * @param string $content, new subelement content
  9654. * @param array $params, new element 'attributes'
  9655. * @return void
  9656. */
  9657. function _addXMLchild( & $parent, $name, $type, $content, $params=array()) {
  9658. /** create new child node */
  9659. $name = strtolower( $name );
  9660. $child = $parent->addChild( $name );
  9661. if( isset( $params['VALUE'] ))
  9662. unset( $params['VALUE'] );
  9663. if( !empty( $params )) {
  9664. $parameters = $child->addChild( 'parameters' );
  9665. foreach( $params as $param => $parVal ) {
  9666. $param = strtolower( $param );
  9667. if( 'x-' == substr( $param, 0, 2 )) {
  9668. $p1 = $parameters->addChild( $param );
  9669. $p2 = $p1->addChild( 'unknown', htmlspecialchars( $parVal ));
  9670. }
  9671. else {
  9672. $p1 = $parameters->addChild( $param );
  9673. switch( $param ) {
  9674. case 'altrep':
  9675. case 'dir': $ptype = 'uri'; break;
  9676. case 'delegated-from':
  9677. case 'delegated-to':
  9678. case 'member':
  9679. case 'sent-by': $ptype = 'cal-address'; break;
  9680. case 'rsvp': $ptype = 'boolean'; break ;
  9681. default: $ptype = 'text'; break;
  9682. }
  9683. if( is_array( $parVal )) {
  9684. foreach( $parVal as $pV )
  9685. $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV ));
  9686. }
  9687. else
  9688. $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal ));
  9689. }
  9690. }
  9691. }
  9692. if( empty( $content ) && ( '0' != $content ))
  9693. return;
  9694. /** store content */
  9695. switch( $type ) {
  9696. case 'binary':
  9697. $v = $child->addChild( $type, $content );
  9698. break;
  9699. case 'boolean':
  9700. break;
  9701. case 'cal-address':
  9702. $v = $child->addChild( $type, $content );
  9703. break;
  9704. case 'date':
  9705. if( array_key_exists( 'year', $content ))
  9706. $content = array( $content );
  9707. foreach( $content as $date ) {
  9708. $str = sprintf( '%04d-%02d-%02d', $date['year'], $date['month'], $date['day'] );
  9709. $v = $child->addChild( $type, $str );
  9710. }
  9711. break;
  9712. case 'date-time':
  9713. if( array_key_exists( 'year', $content ))
  9714. $content = array( $content );
  9715. foreach( $content as $dt ) {
  9716. if( !isset( $dt['hour'] )) $dt['hour'] = 0;
  9717. if( !isset( $dt['min'] )) $dt['min'] = 0;
  9718. if( !isset( $dt['sec'] )) $dt['sec'] = 0;
  9719. $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d', $dt['year'], $dt['month'], $dt['day'], $dt['hour'], $dt['min'], $dt['sec'] );
  9720. if( isset( $dt['tz'] ) && ( 'Z' == $dt['tz'] ))
  9721. $str .= 'Z';
  9722. $v = $child->addChild( $type, $str );
  9723. }
  9724. break;
  9725. case 'duration':
  9726. $output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ? '-' : '';
  9727. $v = $child->addChild( $type, $output.iCalUtilityFunctions::_duration2str( $content ) );
  9728. break;
  9729. case 'geo':
  9730. $v1 = $child->addChild( 'latitude', number_format( (float) $content['latitude'], 6, '.', '' ));
  9731. $v1 = $child->addChild( 'longitude', number_format( (float) $content['longitude'], 6, '.', '' ));
  9732. break;
  9733. case 'integer':
  9734. $v = $child->addChild( $type, $content );
  9735. break;
  9736. case 'period':
  9737. if( !is_array( $content ))
  9738. break;
  9739. foreach( $content as $period ) {
  9740. $v1 = $child->addChild( $type );
  9741. $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'] );
  9742. if( isset( $period[0]['tz'] ) && ( 'Z' == $period[0]['tz'] ))
  9743. $str .= 'Z';
  9744. $v2 = $v1->addChild( 'start', $str );
  9745. if( array_key_exists( 'year', $period[1] )) {
  9746. $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'] );
  9747. if( isset($period[1]['tz'] ) && ( 'Z' == $period[1]['tz'] ))
  9748. $str .= 'Z';
  9749. $v2 = $v1->addChild( 'end', $str );
  9750. }
  9751. else
  9752. $v2 = $v1->addChild( 'duration', iCalUtilityFunctions::_duration2str( $period[1] ));
  9753. }
  9754. break;
  9755. case 'recur':
  9756. foreach( $content as $rulelabel => $rulevalue ) {
  9757. $rulelabel = strtolower( $rulelabel );
  9758. switch( $rulelabel ) {
  9759. case 'until':
  9760. if( isset( $rulevalue['hour'] ))
  9761. $str = sprintf( '%04d-%02d-%02dT%02d:%02d:%02dZ', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'], $rulevalue['hour'], $rulevalue['min'], $rulevalue['sec'] );
  9762. else
  9763. $str = sprintf( '%04d-%02d-%02d', $rulevalue['year'], $rulevalue['month'], $rulevalue['day'] );
  9764. $v = $child->addChild( $rulelabel, $str );
  9765. break;
  9766. case 'bysecond':
  9767. case 'byminute':
  9768. case 'byhour':
  9769. case 'bymonthday':
  9770. case 'byyearday':
  9771. case 'byweekno':
  9772. case 'bymonth':
  9773. case 'bysetpos': {
  9774. if( is_array( $rulevalue )) {
  9775. foreach( $rulevalue as $vix => $valuePart )
  9776. $v = $child->addChild( $rulelabel, $valuePart );
  9777. }
  9778. else
  9779. $v = $child->addChild( $rulelabel, $rulevalue );
  9780. break;
  9781. }
  9782. case 'byday': {
  9783. if( isset( $rulevalue['DAY'] )) {
  9784. $str = ( isset( $rulevalue[0] )) ? $rulevalue[0] : '';
  9785. $str .= $rulevalue['DAY'];
  9786. $p = $child->addChild( $rulelabel, $str );
  9787. }
  9788. else {
  9789. foreach( $rulevalue as $valuePart ) {
  9790. if( isset( $valuePart['DAY'] )) {
  9791. $str = ( isset( $valuePart[0] )) ? $valuePart[0] : '';
  9792. $str .= $valuePart['DAY'];
  9793. $p = $child->addChild( $rulelabel, $str );
  9794. }
  9795. else
  9796. $p = $child->addChild( $rulelabel, $valuePart );
  9797. }
  9798. }
  9799. break;
  9800. }
  9801. case 'freq':
  9802. case 'count':
  9803. case 'interval':
  9804. case 'wkst':
  9805. default:
  9806. $p = $child->addChild( $rulelabel, $rulevalue );
  9807. break;
  9808. } // end switch( $rulelabel )
  9809. } // end foreach( $content as $rulelabel => $rulevalue )
  9810. break;
  9811. case 'rstatus':
  9812. $v = $child->addChild( 'code', number_format( (float) $content['statcode'], 2, '.', ''));
  9813. $v = $child->addChild( 'description', htmlspecialchars( $content['text'] ));
  9814. if( isset( $content['extdata'] ))
  9815. $v = $child->addChild( 'data', htmlspecialchars( $content['extdata'] ));
  9816. break;
  9817. case 'text':
  9818. if( !is_array( $content ))
  9819. $content = array( $content );
  9820. foreach( $content as $part )
  9821. $v = $child->addChild( $type, htmlspecialchars( $part ));
  9822. break;
  9823. case 'time':
  9824. break;
  9825. case 'uri':
  9826. $v = $child->addChild( $type, $content );
  9827. break;
  9828. case 'utc-offset':
  9829. if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) {
  9830. $str = substr( $content, 0, 1 );
  9831. $content = substr( $content, 1 );
  9832. }
  9833. else
  9834. $str = '+';
  9835. $str .= substr( $content, 0, 2 ).':'.substr( $content, 2, 2 );
  9836. if( 4 < strlen( $content ))
  9837. $str .= ':'.substr( $content, 4 );
  9838. $v = $child->addChild( $type, $str );
  9839. break;
  9840. case 'unknown':
  9841. default:
  9842. if( is_array( $content ))
  9843. $content = implode( '', $content );
  9844. $v = $child->addChild( 'unknown', htmlspecialchars( $content ));
  9845. break;
  9846. }
  9847. }
  9848. /**
  9849. * parse xml string into iCalcreator instance
  9850. *
  9851. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9852. * @since 2.11.2 - 2012-01-31
  9853. * @param string $xmlstr
  9854. * @param array $iCalcfg iCalcreator config array (opt)
  9855. * @return mixed iCalcreator instance or FALSE on error
  9856. */
  9857. function & XMLstr2iCal( $xmlstr, $iCalcfg=array()) {
  9858. libxml_use_internal_errors( TRUE );
  9859. $xml = simplexml_load_string( $xmlstr );
  9860. if( !$xml ) {
  9861. $str = '';
  9862. $return = FALSE;
  9863. foreach( libxml_get_errors() as $error ) {
  9864. switch ( $error->level ) {
  9865. case LIBXML_ERR_FATAL: $str .= ' FATAL '; break;
  9866. case LIBXML_ERR_ERROR: $str .= ' ERROR '; break;
  9867. case LIBXML_ERR_WARNING:
  9868. default: $str .= ' WARNING '; break;
  9869. }
  9870. $str .= PHP_EOL.'Error when loading XML';
  9871. if( !empty( $error->file ))
  9872. $str .= ', file:'.$error->file.', ';
  9873. $str .= ', line:'.$error->line;
  9874. $str .= ', ('.$error->code.') '.$error->message;
  9875. }
  9876. error_log( $str );
  9877. if( LIBXML_ERR_WARNING != $error->level )
  9878. return $return;
  9879. libxml_clear_errors();
  9880. }
  9881. return xml2iCal( $xml, $iCalcfg );
  9882. }
  9883. /**
  9884. * parse xml file into iCalcreator instance
  9885. *
  9886. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9887. * @since 2.11.2 - 2012-01-20
  9888. * @param string $xmlfile
  9889. * @param array$iCalcfg iCalcreator config array (opt)
  9890. * @return mixediCalcreator instance or FALSE on error
  9891. */
  9892. function & XMLfile2iCal( $xmlfile, $iCalcfg=array()) {
  9893. libxml_use_internal_errors( TRUE );
  9894. $xml = simplexml_load_file( $xmlfile );
  9895. if( !$xml ) {
  9896. $str = '';
  9897. foreach( libxml_get_errors() as $error ) {
  9898. switch ( $error->level ) {
  9899. case LIBXML_ERR_FATAL: $str .= 'FATAL '; break;
  9900. case LIBXML_ERR_ERROR: $str .= 'ERROR '; break;
  9901. case LIBXML_ERR_WARNING:
  9902. default: $str .= 'WARNING '; break;
  9903. }
  9904. $str .= 'Failed loading XML'.PHP_EOL;
  9905. if( !empty( $error->file ))
  9906. $str .= ' file:'.$error->file.', ';
  9907. $str .= 'line:'.$error->line.PHP_EOL;
  9908. $str .= '('.$error->code.') '.$error->message.PHP_EOL;
  9909. }
  9910. error_log( $str );
  9911. if( LIBXML_ERR_WARNING != $error->level )
  9912. return FALSE;
  9913. libxml_clear_errors();
  9914. }
  9915. return xml2iCal( $xml, $iCalcfg );
  9916. }
  9917. /**
  9918. * parse SimpleXMLElement instance into iCalcreator instance
  9919. *
  9920. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9921. * @since 2.11.2 - 2012-01-27
  9922. * @param object $xmlobj SimpleXMLElement
  9923. * @param array $iCalcfg iCalcreator config array (opt)
  9924. * @return mixed iCalcreator instance or FALSE on error
  9925. */
  9926. function & XML2iCal( $xmlobj, $iCalcfg=array()) {
  9927. $iCal = new vcalendar( $iCalcfg );
  9928. foreach( $xmlobj->children() as $icalendar ) { // vcalendar
  9929. foreach( $icalendar->children() as $calPart ) { // calendar properties and components
  9930. if( 'components' == $calPart->getName()) {
  9931. foreach( $calPart->children() as $component ) { // single components
  9932. if( 0 < $component->count())
  9933. _getXMLComponents( $iCal, $component );
  9934. }
  9935. }
  9936. elseif(( 'properties' == $calPart->getName()) && ( 0 < $calPart->count())) {
  9937. foreach( $calPart->children() as $calProp ) { // calendar properties
  9938. $propName = $calProp->getName();
  9939. if(( 'calscale' != $propName ) && ( 'method' != $propName ) && ( 'x-' != substr( $propName,0,2 )))
  9940. continue;
  9941. $params = array();
  9942. foreach( $calProp->children() as $calPropElem ) { // single calendar property
  9943. if( 'parameters' == $calPropElem->getName())
  9944. $params = _getXMLParams( $calPropElem );
  9945. else
  9946. $iCal->setProperty( $propName, reset( $calPropElem ), $params );
  9947. } // end foreach( $calProp->children() as $calPropElem )
  9948. } // end foreach( $calPart->properties->children() as $calProp )
  9949. } // end if( 0 < $calPart->properties->count())
  9950. } // end foreach( $icalendar->children() as $calPart )
  9951. } // end foreach( $xmlobj->children() as $icalendar )
  9952. return $iCal;
  9953. }
  9954. /**
  9955. * parse SimpleXMLElement instance property parameters and return iCalcreator property parameter array
  9956. *
  9957. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9958. * @since 2.11.2 - 2012-01-15
  9959. * @param object $parameters SimpleXMLElement
  9960. * @return array iCalcreator property parameter array
  9961. */
  9962. function _getXMLParams( & $parameters ) {
  9963. if( 1 > $parameters->count())
  9964. return array();
  9965. $params = array();
  9966. foreach( $parameters->children() as $parameter ) { // single parameter key
  9967. $key = strtoupper( $parameter->getName());
  9968. $value = array();
  9969. foreach( $parameter->children() as $paramValue ) // skip parameter value type
  9970. $value[] = reset( $paramValue );
  9971. if( 2 > count( $value ))
  9972. $params[$key] = html_entity_decode( reset( $value ));
  9973. else
  9974. $params[$key] = $value;
  9975. }
  9976. return $params;
  9977. }
  9978. /**
  9979. * parse SimpleXMLElement instance components, create iCalcreator component and update
  9980. *
  9981. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  9982. * @since 2.11.2 - 2012-01-15
  9983. * @param array $iCal iCalcreator calendar instance
  9984. * @param object $component SimpleXMLElement
  9985. * @return void
  9986. */
  9987. function _getXMLComponents( & $iCal, & $component ) {
  9988. $compName = $component->getName();
  9989. $comp = & $iCal->newComponent( $compName );
  9990. $subComponents = array( 'valarm', 'standard', 'daylight' );
  9991. foreach( $component->children() as $compPart ) { // properties and (opt) subComponents
  9992. if( 1 > $compPart->count())
  9993. continue;
  9994. if( in_array( $compPart->getName(), $subComponents ))
  9995. _getXMLComponents( $comp, $compPart );
  9996. elseif( 'properties' == $compPart->getName()) {
  9997. foreach( $compPart->children() as $property ) // properties as single property
  9998. _getXMLProperties( $comp, $property );
  9999. }
  10000. } // end foreach( $component->children() as $compPart )
  10001. }
  10002. /**
  10003. * parse SimpleXMLElement instance property, create iCalcreator component property
  10004. *
  10005. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  10006. * @since 2.11.2 - 2012-01-27
  10007. * @param array $iCal iCalcreator calendar instance
  10008. * @param object $component SimpleXMLElement
  10009. * @return void
  10010. */
  10011. function _getXMLProperties( & $iCal, & $property ) {
  10012. $propName = $property->getName();
  10013. $value = $params = array();
  10014. $valueType = '';
  10015. foreach( $property->children() as $propPart ) { // calendar property parameters (opt) and value(-s)
  10016. $valueType = $propPart->getName();
  10017. if( 'parameters' == $valueType) {
  10018. $params = _getXMLParams( $propPart );
  10019. continue;
  10020. }
  10021. switch( $valueType ) {
  10022. case 'binary':
  10023. $value = reset( $propPart );
  10024. break;
  10025. case 'boolean':
  10026. break;
  10027. case 'cal-address':
  10028. $value = reset( $propPart );
  10029. break;
  10030. case 'date':
  10031. $params['VALUE'] = 'DATE';
  10032. case 'date-time':
  10033. if(( 'exdate' == $propName ) || ( 'rdate' == $propName ))
  10034. $value[] = reset( $propPart );
  10035. else
  10036. $value = reset( $propPart );
  10037. break;
  10038. case 'duration':
  10039. $value = reset( $propPart );
  10040. break;
  10041. // case 'geo':
  10042. case 'latitude':
  10043. case 'longitude':
  10044. $value[$valueType] = reset( $propPart );
  10045. break;
  10046. case 'integer':
  10047. $value = reset( $propPart );
  10048. break;
  10049. case 'period':
  10050. if( 'rdate' == $propName )
  10051. $params['VALUE'] = 'PERIOD';
  10052. $pData = array();
  10053. foreach( $propPart->children() as $periodPart )
  10054. $pData[] = reset( $periodPart );
  10055. if( !empty( $pData ))
  10056. $value[] = $pData;
  10057. break;
  10058. // case 'rrule':
  10059. case 'freq':
  10060. case 'count':
  10061. case 'until':
  10062. case 'interval':
  10063. case 'wkst':
  10064. $value[$valueType] = reset( $propPart );
  10065. break;
  10066. case 'bysecond':
  10067. case 'byminute':
  10068. case 'byhour':
  10069. case 'bymonthday':
  10070. case 'byyearday':
  10071. case 'byweekno':
  10072. case 'bymonth':
  10073. case 'bysetpos':
  10074. $value[$valueType][] = reset( $propPart );
  10075. break;
  10076. case 'byday':
  10077. $byday = reset( $propPart );
  10078. if( 2 == strlen( $byday ))
  10079. $value[$valueType][] = array( 'DAY' => $byday );
  10080. else {
  10081. $day = substr( $byday, -2 );
  10082. $key = substr( $byday, 0, ( strlen( $byday ) - 2 ));
  10083. $value[$valueType][] = array( $key, 'DAY' => $day );
  10084. }
  10085. break;
  10086. // case 'rstatus':
  10087. case 'code':
  10088. $value[0] = reset( $propPart );
  10089. break;
  10090. case 'description':
  10091. $value[1] = reset( $propPart );
  10092. break;
  10093. case 'data':
  10094. $value[2] = reset( $propPart );
  10095. break;
  10096. case 'text':
  10097. $text = str_replace( array( "\r\n", "\n\r", "\r", "\n"), '\n', reset( $propPart ));
  10098. $value['text'][] = html_entity_decode( $text );
  10099. break;
  10100. case 'time':
  10101. break;
  10102. case 'uri':
  10103. $value = reset( $propPart );
  10104. break;
  10105. case 'utc-offset':
  10106. $value = str_replace( ':', '', reset( $propPart ));
  10107. break;
  10108. case 'unknown':
  10109. default:
  10110. $value = html_entity_decode( reset( $propPart ));
  10111. break;
  10112. } // end switch( $valueType )
  10113. } // end foreach( $property->children() as $propPart )
  10114. if( 'freebusy' == $propName ) {
  10115. $fbtype = $params['FBTYPE'];
  10116. unset( $params['FBTYPE'] );
  10117. $iCal->setProperty( $propName, $fbtype, $value, $params );
  10118. }
  10119. elseif( 'geo' == $propName )
  10120. $iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params );
  10121. elseif( 'request-status' == $propName ) {
  10122. if( !isset( $value[2] ))
  10123. $value[2] = FALSE;
  10124. $iCal->setProperty( $propName, $value[0], $value[1], $value[2], $params );
  10125. }
  10126. else {
  10127. if( isset( $value['text'] ) && is_array( $value['text'] )) {
  10128. if(( 'categories' == $propName ) || ( 'resources' == $propName ))
  10129. $value = $value['text'];
  10130. else
  10131. $value = reset( $value['text'] );
  10132. }
  10133. $iCal->setProperty( $propName, $value, $params );
  10134. }
  10135. }
  10136. /*********************************************************************************/
  10137. /* Additional functions to use with vtimezone components */
  10138. /*********************************************************************************/
  10139. /**
  10140. * For use with
  10141. * iCalcreator (kigkonsult.se/iCalcreator/index.php)
  10142. * copyright (c) 2011 Yitzchok Lavi
  10143. * icalcreator@onebigsystem.com
  10144. *
  10145. * This library is free software; you can redistribute it and/or
  10146. * modify it under the terms of the GNU Lesser General Public
  10147. * License as published by the Free Software Foundation; either
  10148. * version 2.1 of the License, or (at your option) any later version.
  10149. *
  10150. * This library is distributed in the hope that it will be useful,
  10151. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10152. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10153. * Lesser General Public License for more details.
  10154. *
  10155. * You should have received a copy of the GNU Lesser General Public
  10156. * License along with this library; if not, write to the Free Software
  10157. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  10158. */
  10159. /**
  10160. * Additional functions to use with vtimezone components
  10161. *
  10162. * Before calling the functions, set time zone 'GMT' ('date_default_timezone_set')!
  10163. *
  10164. * @author Yitzchok Lavi <icalcreator@onebigsystem.com>
  10165. * adjusted for iCalcreator Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  10166. * @version 1.0.2 - 2011-02-24
  10167. *
  10168. */
  10169. /**
  10170. * Returns array with the offset information from UTC for a (UTC) datetime/timestamp in the
  10171. * timezone, according to the VTIMEZONE information in the input array.
  10172. *
  10173. * $param array $timezonesarray, output from function getTimezonesAsDateArrays (below)
  10174. * $param string $tzid, time zone identifier
  10175. * $param mixed $timestamp, timestamp or a UTC datetime (in array format)
  10176. * @return array, time zone data with keys for 'offsetHis', 'offsetSec' and 'tzname'
  10177. *
  10178. */
  10179. function getTzOffsetForDate($timezonesarray, $tzid, $timestamp) {
  10180. if( is_array( $timestamp )) {
  10181. //$disp = sprintf( '%04d%02d%02d %02d%02d%02d', $timestamp['year'], $timestamp['month'], $timestamp['day'], $timestamp['hour'], $timestamp['min'], $timestamp['sec'] ); // test ###
  10182. $timestamp = gmmktime(
  10183. $timestamp['hour'],
  10184. $timestamp['min'],
  10185. $timestamp['sec'],
  10186. $timestamp['month'],
  10187. $timestamp['day'],
  10188. $timestamp['year']
  10189. ) ;
  10190. // echo '<td colspan="4">&nbsp;'."\n".'<tr><td>&nbsp;<td class="r">'.$timestamp.'<td class="r">'.$disp.'<td colspan="4">&nbsp;'."\n".'<tr><td colspan="3">&nbsp;'; // test ###
  10191. }
  10192. $tzoffset = array();
  10193. // something to return if all goes wrong (such as if $tzid doesn't find us an array of dates)
  10194. $tzoffset['offsetHis'] = '+0000';
  10195. $tzoffset['offsetSec'] = 0;
  10196. $tzoffset['tzname'] = '?';
  10197. if( !isset( $timezonesarray[$tzid] ))
  10198. return $tzoffset;
  10199. $tzdatearray = $timezonesarray[$tzid];
  10200. if ( is_array($tzdatearray) ) {
  10201. sort($tzdatearray); // just in case
  10202. if ( $timestamp < $tzdatearray[0]['timestamp'] ) {
  10203. // our date is before the first change
  10204. $tzoffset['offsetHis'] = $tzdatearray[0]['tzbefore']['offsetHis'] ;
  10205. $tzoffset['offsetSec'] = $tzdatearray[0]['tzbefore']['offsetSec'] ;
  10206. $tzoffset['tzname'] = $tzdatearray[0]['tzbefore']['offsetHis'] ; // we don't know the tzname in this case
  10207. } elseif ( $timestamp >= $tzdatearray[count($tzdatearray)-1]['timestamp'] ) {
  10208. // our date is after the last change (we do this so our scan can stop at the last record but one)
  10209. $tzoffset['offsetHis'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetHis'] ;
  10210. $tzoffset['offsetSec'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetSec'] ;
  10211. $tzoffset['tzname'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['tzname'] ;
  10212. } else {
  10213. // our date somewhere in between
  10214. // 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
  10215. // we don't include the last date in our loop as there isn't one after it to check
  10216. for ( $i = 0 ; $i <= count($tzdatearray)-2 ; $i++ ) {
  10217. if(( $timestamp >= $tzdatearray[$i]['timestamp'] ) && ( $timestamp < $tzdatearray[$i+1]['timestamp'] )) {
  10218. $tzoffset['offsetHis'] = $tzdatearray[$i]['tzafter']['offsetHis'] ;
  10219. $tzoffset['offsetSec'] = $tzdatearray[$i]['tzafter']['offsetSec'] ;
  10220. $tzoffset['tzname'] = $tzdatearray[$i]['tzafter']['tzname'] ;
  10221. break;
  10222. }
  10223. }
  10224. }
  10225. }
  10226. return $tzoffset;
  10227. }
  10228. /**
  10229. * Returns an array containing all the timezone data in the vcalendar object
  10230. *
  10231. * @param object $vcalendar, iCalcreator calendar instance
  10232. * @return array, time zone transition timestamp, array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname)
  10233. * based on the timezone data in the vcalendar object
  10234. *
  10235. */
  10236. function getTimezonesAsDateArrays($vcalendar) {
  10237. $timezonedata = array();
  10238. while( $vtz = $vcalendar->getComponent( 'vtimezone' )) {
  10239. $tzid = $vtz->getProperty('tzid');
  10240. $alltzdates = array();
  10241. while ( $vtzc = $vtz->getComponent( 'standard' )) {
  10242. $newtzdates = expandTimezoneDates($vtzc);
  10243. $alltzdates = array_merge($alltzdates, $newtzdates);
  10244. }
  10245. while ( $vtzc = $vtz->getComponent( 'daylight' )) {
  10246. $newtzdates = expandTimezoneDates($vtzc);
  10247. $alltzdates = array_merge($alltzdates, $newtzdates);
  10248. }
  10249. sort($alltzdates);
  10250. $timezonedata[$tzid] = $alltzdates;
  10251. }
  10252. return $timezonedata;
  10253. }
  10254. /**
  10255. * Returns an array containing time zone data from vtimezone standard/daylight instances
  10256. *
  10257. * @param object $vtzc, an iCalcreator calendar standard/daylight instance
  10258. * @return array, time zone data; array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname)
  10259. *
  10260. */
  10261. function expandTimezoneDates($vtzc) {
  10262. $tzdates = array();
  10263. // prepare time zone "description" to attach to each change
  10264. $tzbefore = array();
  10265. $tzbefore['offsetHis'] = $vtzc->getProperty('tzoffsetfrom') ;
  10266. $tzbefore['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzbefore['offsetHis']);
  10267. if(( '-' != substr( (string) $tzbefore['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzbefore['offsetSec'], 0, 1 )))
  10268. $tzbefore['offsetSec'] = '+'.$tzbefore['offsetSec'];
  10269. $tzafter = array();
  10270. $tzafter['offsetHis'] = $vtzc->getProperty('tzoffsetto') ;
  10271. $tzafter['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzafter['offsetHis']);
  10272. if(( '-' != substr( (string) $tzafter['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzafter['offsetSec'], 0, 1 )))
  10273. $tzafter['offsetSec'] = '+'.$tzafter['offsetSec'];
  10274. if( FALSE === ( $tzafter['tzname'] = $vtzc->getProperty('tzname')))
  10275. $tzafter['tzname'] = $tzafter['offsetHis'];
  10276. // find out where to start from
  10277. $dtstart = $vtzc->getProperty('dtstart');
  10278. $dtstarttimestamp = mktime(
  10279. $dtstart['hour'],
  10280. $dtstart['min'],
  10281. $dtstart['sec'],
  10282. $dtstart['month'],
  10283. $dtstart['day'],
  10284. $dtstart['year']
  10285. ) ;
  10286. if( !isset( $dtstart['unparsedtext'] )) // ??
  10287. $dtstart['unparsedtext'] = sprintf( '%04d%02d%02dT%02d%02d%02d', $dtstart['year'], $dtstart['month'], $dtstart['day'], $dtstart['hour'], $dtstart['min'], $dtstart['sec'] );
  10288. if ( $dtstarttimestamp == 0 ) {
  10289. // it seems that the dtstart string may not have parsed correctly
  10290. // let's set a timestamp starting from 1902, using the time part of the original string
  10291. // so that the time will change at the right time of day
  10292. // at worst we'll get midnight again
  10293. $origdtstartsplit = explode('T',$dtstart['unparsedtext']) ;
  10294. $dtstarttimestamp = strtotime("19020101",0);
  10295. $dtstarttimestamp = strtotime($origdtstartsplit[1],$dtstarttimestamp);
  10296. }
  10297. // the date (in dtstart and opt RDATE/RRULE) is ALWAYS LOCAL (not utc!!), adjust from 'utc' to 'local' timestamp
  10298. $diff = -1 * $tzbefore['offsetSec'];
  10299. $dtstarttimestamp += $diff;
  10300. // add this (start) change to the array of changes
  10301. $tzdates[] = array(
  10302. 'timestamp' => $dtstarttimestamp,
  10303. 'tzbefore' => $tzbefore,
  10304. 'tzafter' => $tzafter
  10305. );
  10306. $datearray = getdate($dtstarttimestamp);
  10307. // save original array to use time parts, because strtotime (used below) apparently loses the time
  10308. $changetime = $datearray ;
  10309. // generate dates according to an RRULE line
  10310. $rrule = $vtzc->getProperty('rrule') ;
  10311. if ( is_array($rrule) ) {
  10312. if ( $rrule['FREQ'] == 'YEARLY' ) {
  10313. // calculate transition dates starting from DTSTART
  10314. $offsetchangetimestamp = $dtstarttimestamp;
  10315. // calculate transition dates until 10 years in the future
  10316. $stoptimestamp = strtotime("+10 year",time());
  10317. // if UNTIL is set, calculate until then (however far ahead)
  10318. if ( isset( $rrule['UNTIL'] ) && ( $rrule['UNTIL'] != '' )) {
  10319. $stoptimestamp = mktime(
  10320. $rrule['UNTIL']['hour'],
  10321. $rrule['UNTIL']['min'],
  10322. $rrule['UNTIL']['sec'],
  10323. $rrule['UNTIL']['month'],
  10324. $rrule['UNTIL']['day'],
  10325. $rrule['UNTIL']['year']
  10326. ) ;
  10327. }
  10328. $count = 0 ;
  10329. $stopcount = isset( $rrule['COUNT'] ) ? $rrule['COUNT'] : 0 ;
  10330. $daynames = array(
  10331. 'SU' => 'Sunday',
  10332. 'MO' => 'Monday',
  10333. 'TU' => 'Tuesday',
  10334. 'WE' => 'Wednesday',
  10335. 'TH' => 'Thursday',
  10336. 'FR' => 'Friday',
  10337. 'SA' => 'Saturday'
  10338. );
  10339. // repeat so long as we're between DTSTART and UNTIL, or we haven't prepared COUNT dates
  10340. while ( $offsetchangetimestamp < $stoptimestamp && ( $stopcount == 0 || $count < $stopcount ) ) {
  10341. // break up the timestamp into its parts
  10342. $datearray = getdate($offsetchangetimestamp);
  10343. if ( isset( $rrule['BYMONTH'] ) && ( $rrule['BYMONTH'] != 0 )) {
  10344. // set the month
  10345. $datearray['mon'] = $rrule['BYMONTH'] ;
  10346. }
  10347. if ( isset( $rrule['BYMONTHDAY'] ) && ( $rrule['BYMONTHDAY'] != 0 )) {
  10348. // set specific day of month
  10349. $datearray['mday'] = $rrule['BYMONTHDAY'];
  10350. } elseif ( is_array($rrule['BYDAY']) ) {
  10351. // find the Xth WKDAY in the month
  10352. // the starting point for this process is the first of the month set above
  10353. $datearray['mday'] = 1 ;
  10354. // turn $datearray as it is now back into a timestamp
  10355. $offsetchangetimestamp = mktime(
  10356. $datearray['hours'],
  10357. $datearray['minutes'],
  10358. $datearray['seconds'],
  10359. $datearray['mon'],
  10360. $datearray['mday'],
  10361. $datearray['year']
  10362. );
  10363. if ($rrule['BYDAY'][0] > 0) {
  10364. // to find Xth WKDAY in month, we find last WKDAY in month before
  10365. // we do that by finding first WKDAY in this month and going back one week
  10366. // then we add X weeks (below)
  10367. $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp);
  10368. $offsetchangetimestamp = strtotime("-1 week",$offsetchangetimestamp);
  10369. } else {
  10370. // to find Xth WKDAY before the end of the month, we find the first WKDAY in the following month
  10371. // we do that by going forward one month and going to WKDAY there
  10372. // then we subtract X weeks (below)
  10373. $offsetchangetimestamp = strtotime("+1 month",$offsetchangetimestamp);
  10374. $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp);
  10375. }
  10376. // now move forward or back the appropriate number of weeks, into the month we want
  10377. $offsetchangetimestamp = strtotime($rrule['BYDAY'][0] . " week",$offsetchangetimestamp);
  10378. $datearray = getdate($offsetchangetimestamp);
  10379. }
  10380. // convert the date parts back into a timestamp, setting the time parts according to the
  10381. // original time data which we stored
  10382. $offsetchangetimestamp = mktime(
  10383. $changetime['hours'],
  10384. $changetime['minutes'],
  10385. $changetime['seconds'] + $diff,
  10386. $datearray['mon'],
  10387. $datearray['mday'],
  10388. $datearray['year']
  10389. );
  10390. // add this change to the array of changes
  10391. $tzdates[] = array(
  10392. 'timestamp' => $offsetchangetimestamp,
  10393. 'tzbefore' => $tzbefore,
  10394. 'tzafter' => $tzafter
  10395. );
  10396. // update counters (timestamp and count)
  10397. $offsetchangetimestamp = strtotime("+" . (( isset( $rrule['INTERVAL'] ) && ( $rrule['INTERVAL'] != 0 )) ? $rrule['INTERVAL'] : 1 ) . " year",$offsetchangetimestamp);
  10398. $count += 1 ;
  10399. }
  10400. }
  10401. }
  10402. // generate dates according to RDATE lines
  10403. while ($rdates = $vtzc->getProperty('rdate')) {
  10404. if ( is_array($rdates) ) {
  10405. foreach ( $rdates as $rdate ) {
  10406. // convert the explicit change date to a timestamp
  10407. $offsetchangetimestamp = mktime(
  10408. $rdate['hour'],
  10409. $rdate['min'],
  10410. $rdate['sec'] + $diff,
  10411. $rdate['month'],
  10412. $rdate['day'],
  10413. $rdate['year']
  10414. ) ;
  10415. // add this change to the array of changes
  10416. $tzdates[] = array(
  10417. 'timestamp' => $offsetchangetimestamp,
  10418. 'tzbefore' => $tzbefore,
  10419. 'tzafter' => $tzafter
  10420. );
  10421. }
  10422. }
  10423. }
  10424. return $tzdates;
  10425. }
  10426. ?>