vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 gGroupCalendar_Database =
2 {
3 Format = 11,
4 Databases = {},
5 };
6  
7 gGroupCalendar_UserDatabase = nil;
8  
9 gGroupCalendar_MaximumEventAge = 30;
10 gGroupCalendar_MinimumEventDate = nil;
11  
12 gGroupCalendar_PlayerCharacters = {};
13  
14 gGroupCalendar_40ManLimits =
15 {
16 mClassLimits =
17 {
18 P = {mMin = 4, mMax = 6},
19 R = {mMin = 4, mMax = 6},
20 D = {mMin = 4, mMax = 6},
21 W = {mMin = 4, mMax = 6},
22 H = {mMin = 4, mMax = 6},
23 K = {mMin = 4, mMax = 6},
24 M = {mMin = 4, mMax = 6},
25 L = {mMin = 4, mMax = 6},
26 S = {mMin = 4, mMax = 6},
27 },
28  
29 mMaxAttendance = 40,
30 };
31  
32 gGroupCalendar_20ManLimits =
33 {
34 mClassLimits =
35 {
36 P = {mMin = 2, mMax = 3},
37 R = {mMin = 2, mMax = 3},
38 D = {mMin = 2, mMax = 3},
39 W = {mMin = 2, mMax = 3},
40 H = {mMin = 2, mMax = 3},
41 K = {mMin = 2, mMax = 3},
42 M = {mMin = 2, mMax = 3},
43 L = {mMin = 2, mMax = 3},
44 S = {mMin = 2, mMax = 3},
45 },
46  
47 mMaxAttendance = 20,
48 };
49  
50 gGroupCalendar_15ManLimits =
51 {
52 mClassLimits =
53 {
54 P = {mMin = 1, mMax = 3},
55 R = {mMin = 1, mMax = 3},
56 D = {mMin = 1, mMax = 3},
57 W = {mMin = 1, mMax = 3},
58 H = {mMin = 1, mMax = 3},
59 K = {mMin = 1, mMax = 3},
60 M = {mMin = 1, mMax = 3},
61 L = {mMin = 1, mMax = 3},
62 S = {mMin = 1, mMax = 3},
63 },
64  
65 mMaxAttendance = 15,
66 };
67  
68 gGroupCalendar_10ManLimits =
69 {
70 mClassLimits =
71 {
72 P = {mMin = 1, mMax = 2},
73 R = {mMin = 1, mMax = 2},
74 D = {mMin = 1, mMax = 2},
75 W = {mMin = 1, mMax = 2},
76 H = {mMin = 1, mMax = 2},
77 K = {mMin = 1, mMax = 2},
78 M = {mMin = 1, mMax = 2},
79 L = {mMin = 1, mMax = 2},
80 S = {mMin = 1, mMax = 2},
81 },
82  
83 mMaxAttendance = 10,
84 };
85  
86 gGroupCalendar_5ManLimits =
87 {
88 mClassLimits =
89 {
90 P = {mMax = 1},
91 R = {mMax = 1},
92 D = {mMax = 1},
93 W = {mMax = 1},
94 H = {mMax = 1},
95 K = {mMax = 1},
96 M = {mMax = 1},
97 L = {mMax = 1},
98 S = {mMax = 1},
99 },
100  
101 mMaxAttendance = 5,
102 };
103  
104 gGroupCalendar_EventTypes =
105 {
106 General =
107 {
108 Title = GroupCalendar_cGeneralEventGroup,
109 MenuHint = "FLAT",
110 Events =
111 {
112 {id="Meet", name=GroupCalendar_cMeetingEventName},
113 {id="Birth", name=GroupCalendar_cBirthdayEventName},
114 },
115 },
116  
117 Dungeon =
118 {
119 Title = GroupCalendar_cDungeonEventGroup,
120 MenuHint = "FLAT",
121 Events =
122 {
123 {id="AQT", name = GroupCalendar_cAQTEventName, limits = gGroupCalendar_40ManLimits},
124 {id="AQR", name = GroupCalendar_cAQREventName, limits = gGroupCalendar_20ManLimits},
125 {id="BWL", name = GroupCalendar_cBWLEventName, limits = gGroupCalendar_40ManLimits},
126 {id="MC", name = GroupCalendar_cMCEventName, limits = gGroupCalendar_40ManLimits},
127 {id="Onyxia", name = GroupCalendar_cOnyxiaEventName, limits = gGroupCalendar_40ManLimits},
128 {id="ZG", name = GroupCalendar_cZGEventName, limits = gGroupCalendar_20ManLimits},
129 {id="UBRS", name = GroupCalendar_cUBRSEventName, limits = gGroupCalendar_15ManLimits},
130 {id="Scholo", name = GroupCalendar_cScholoEventName, limits = gGroupCalendar_5ManLimits},
131 {id="DM", name = GroupCalendar_cDMEventName, limits = gGroupCalendar_5ManLimits},
132 {id="Strath", name = GroupCalendar_cStrathEventName, limits = gGroupCalendar_5ManLimits},
133 {id="LBRS", name = GroupCalendar_cLBRSEventName, limits = gGroupCalendar_5ManLimits},
134 {id="BRD", name = GroupCalendar_cBRDEventName, limits = gGroupCalendar_5ManLimits},
135 {id="ST", name = GroupCalendar_cSTEventName, limits = gGroupCalendar_5ManLimits},
136 {id="ZF", name = GroupCalendar_cZFEventName, limits = gGroupCalendar_5ManLimits},
137 {id="Mara", name = GroupCalendar_cMaraEventName, limits = gGroupCalendar_5ManLimits},
138 {id="Uld", name = GroupCalendar_cUldEventName, limits = gGroupCalendar_5ManLimits},
139 {id="RFD", name = GroupCalendar_cRFDEventName, limits = gGroupCalendar_5ManLimits},
140 {id="SM", name = GroupCalendar_cSMEventName, limits = gGroupCalendar_5ManLimits},
141 {id="RFK", name = GroupCalendar_cRFKEventName, limits = gGroupCalendar_5ManLimits},
142 {id="Gnomer", name = GroupCalendar_cGnomerEventName, limits = gGroupCalendar_5ManLimits},
143 {id="BFD", name = GroupCalendar_cBFDEventName, limits = gGroupCalendar_5ManLimits},
144 {id="Stockades", name = GroupCalendar_cStockadesEventName, limits = gGroupCalendar_5ManLimits},
145 {id="SFK", name = GroupCalendar_cSFKEventName, limits = gGroupCalendar_5ManLimits},
146 {id="WC", name = GroupCalendar_cWCEventName, limits = gGroupCalendar_5ManLimits},
147 {id="Deadmines", name = GroupCalendar_cDeadminesEventName, limits = gGroupCalendar_5ManLimits},
148 {id="RFC", name = GroupCalendar_cRFCEventName, limits = gGroupCalendar_5ManLimits},
149 },
150 },
151  
152 Battleground =
153 {
154 Title = GroupCalendar_cBattlegroundEventGroup,
155 MenuHint = "HIER",
156 Events =
157 {
158 {id="AV", name=GroupCalendar_cAVEventName},
159 {id="AB", name=GroupCalendar_cABEventName},
160 {id="WSG", name=GroupCalendar_cWSGEventName},
161 },
162 },
163  
164 Reset =
165 {
166 Title = nil,
167 Events =
168 {
169 {id="RSOny", name=GroupCalendar_cOnyxiaResetEventName}, -- Onyxia reset
170 {id="RSMC", name=GroupCalendar_cMCResetEventName}, -- MC reset
171 {id="RSBWL", name=GroupCalendar_cBWLResetEventName}, -- BWL reset
172 {id="RSZG", name=GroupCalendar_cZGResetEventName}, -- ZG reset
173 {id="RSAQT", name=GroupCalendar_cAQTResetEventName}, -- AQT reset
174 {id="RSAQR", name=GroupCalendar_cAQRResetEventName}, -- AQR reset
175 {id="RSXmut", name=GroupCalendar_cTransmuteCooldownEventName}, -- Transmute
176 {id="RSSalt", name=GroupCalendar_cSaltShakerCooldownEventName}, -- Salt shaker
177 {id="RSMoon", name=GroupCalendar_cMoonclothCooldownEventName}, -- Mooncloth
178 {id="RSSnow", name=GroupCalendar_cSnowmasterCooldownEventName}, -- Snowmaster 9000
179 },
180  
181 ResetEventInfo =
182 {
183 RSZG = {left = 0.0, top = 0.25, right = 0.25, bottom = 0.5, isDungeon = true, name=GroupCalendar_cRaidInfoZGName, largeIcon="ZG"},
184 RSOny = {left = 0.25, top = 0.25, right = 0.5, bottom = 0.5, isDungeon = true, name=GroupCalendar_cRaidInfoOnyxiaName, largeIcon="Onyxia"},
185 RSMC = {left = 0.5, top = 0.25, right = 0.75, bottom = 0.5, isDungeon = true, name=GroupCalendar_cRaidInfoMCName, largeIcon="MC"},
186 RSBWL = {left = 0.75, top = 0.25, right = 1.0, bottom = 0.5, isDungeon = true, name=GroupCalendar_cRaidInfoBWLName, largeIcon="BWL"},
187 RSAQT = {left = 0.0, top = 0.5, right = 0.25, bottom = 1.0, isDungeon = true, name=GroupCalendar_cRaidInfoAQTName, largeIcon="AQT"},
188 RSAQR = {left = 0.25, top = 0.5, right = 0.5, bottom = 1.0, isDungeon = true, name=GroupCalendar_cRaidInfoAQRName, largeIcon="AQR"},
189 RSXmut = {left = 0.50, top = 0, right = 0.75, bottom = 0.25, isTradeskill = true, id="Alchemy", largeSysIcon="Interface\\Icons\\Trade_Alchemy"},
190 RSSalt = {left = 0.25, top = 0, right = 0.5, bottom = 0.25, isTradeskill = true, id="Leatherworking", largeSysIcon="Interface\\Icons\\Trade_Leatherworking"},
191 RSMoon = {left = 0, top = 0, right = 0.25, bottom = 0.25, isTradeskill = true, id="Tailoring", largeSysIcon="Interface\\Icons\\Trade_Tailoring"},
192 RSSnow = {left = 0.75, top = 0, right = 1.0, bottom = 0.25, isTradeskill = true, id="Snowmaster", largeSysIcon="Interface\\Icons\\Spell_Frost_WindWalkOn"},
193 },
194 },
195 };
196  
197 gGroupCalendar_ClassInfoByClassCode =
198 {
199 D = {name = GroupCalendar_cDruidClassName, color = GroupCalendar_cDruidClassColorName, element = "Druid"},
200 H = {name = GroupCalendar_cHunterClassName, color = GroupCalendar_cHunterClassColorName, element = "Hunter"},
201 M = {name = GroupCalendar_cMageClassName, color = GroupCalendar_cMageClassColorName, element = "Mage"},
202 L = {name = GroupCalendar_cPaladinClassName, color = GroupCalendar_cPaladinClassColorName, element = "Paladin", faction="Alliance"},
203 P = {name = GroupCalendar_cPriestClassName, color = GroupCalendar_cPriestClassColorName, element = "Priest"},
204 R = {name = GroupCalendar_cRogueClassName, color = GroupCalendar_cRogueClassColorName, element = "Rogue"},
205 S = {name = GroupCalendar_cShamanClassName, color = GroupCalendar_cShamanClassColorName, element = "Shaman", faction="Horde"},
206 K = {name = GroupCalendar_cWarlockClassName, color = GroupCalendar_cWarlockClassColorName, element = "Warlock"},
207 W = {name = GroupCalendar_cWarriorClassName, color = GroupCalendar_cWarriorClassColorName, element = "Warrior"},
208 };
209  
210 gGroupCalendar_RaceNamesByRaceCode =
211 {
212 A = {name = GroupCalendar_cDraeneiRaceName, faction="Alliance"},
213 D = {name = GroupCalendar_cDwarfRaceName, faction="Alliance"},
214 G = {name = GroupCalendar_cGnomeRaceName, faction="Alliance"},
215 H = {name = GroupCalendar_cHumanRaceName, faction="Alliance"},
216 N = {name = GroupCalendar_cNightElfRaceName, faction="Alliance"},
217 B = {name = GroupCalendar_cBloodElfRaceName, faction="Horde"},
218 O = {name = GroupCalendar_cOrcRaceName, faction="Horde"},
219 T = {name = GroupCalendar_cTaurenRaceName, faction="Horde"},
220 R = {name = GroupCalendar_cTrollRaceName, faction="Horde"},
221 U = {name = GroupCalendar_cUndeadRaceName, faction="Horde"},
222 };
223  
224 function EventDatabase_DatabaseIsVisible(pDatabase)
225 return pDatabase
226 and pDatabase.Realm == gGroupCalendar_RealmName
227 and (pDatabase.IsPlayerOwned
228 or (pDatabase.LocalUsers
229 and pDatabase.LocalUsers[gGroupCalendar_PlayerName]));
230 end
231  
232 function EventDatabase_GetDatabase(pUserName, pCreate)
233 if not pUserName then
234 Calendar_DebugMessage("EventDatabase_GetDatabase: pUserName is nil");
235 return;
236 end
237  
238 local vDatabase = gGroupCalendar_Database.Databases[gGroupCalendar_RealmName.."_"..pUserName];
239  
240 if not vDatabase then
241 if pCreate then
242 vDatabase = {};
243  
244 vDatabase.UserName = pUserName;
245 vDatabase.IsPlayerOwned = pUserName == gGroupCalendar_PlayerName;
246 vDatabase.CurrentEventID = 0;
247 vDatabase.Realm = gGroupCalendar_RealmName;
248 vDatabase.Events = {};
249 vDatabase.Changes = nil;
250 vDatabase.RSVPs = nil;
251 vDatabase.LocalUsers = {};
252 vDatabase.Guild = gGroupCalendar_PlayerGuild;
253  
254 gGroupCalendar_Database.Databases[gGroupCalendar_RealmName.."_"..pUserName] = vDatabase;
255  
256 if vDatabase.IsPlayerOwned then
257 gGroupCalendar_PlayerCharacters[gGroupCalendar_PlayerName] = true;
258 end
259 else
260 return nil;
261 end
262 end
263  
264 if not vDatabase.IsPlayerOwned
265 and (not vDatabase.LocalUsers
266 or not vDatabase.LocalUsers[gGroupCalendar_PlayerName]) then
267 if not pCreate then
268 return nil;
269 end
270  
271 if not vDatabase.LocalUsers then
272 vDatabase.LocalUsers = {};
273 end
274  
275 vDatabase.LocalUsers[gGroupCalendar_PlayerName] = true;
276 end
277  
278 if vDatabase
279 and not vDatabase.IsPlayerOwned
280 and pUserName == gGroupCalendar_PlayerName then
281 vDatabase.IsPlayerOwned = true;
282 end
283  
284 return vDatabase;
285 end
286  
287 function EventDatabase_GetOwnedDatabases()
288 local vOwnedDatabases = {};
289  
290 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
291 if EventDatabase_DatabaseIsVisible(vDatabase)
292 and vDatabase.IsPlayerOwned
293 and vDatabase.PlayerLevel then -- Skip databases which haven't been visited since this version
294 table.insert(vOwnedDatabases, vDatabase);
295 end
296 end
297  
298 return vOwnedDatabases;
299 end
300  
301 function EventDatabase_AssumeDatabase(pUserName)
302 local vDatabase = gGroupCalendar_Database.Databases[gGroupCalendar_RealmName.."_"..pUserName];
303  
304 if not vDatabase then
305 return nil;
306 end
307  
308 if not vDatabase.IsPlayerOwned
309 and not vDatabase.LocalUsers[gGroupCalendar_PlayerName] then
310 vDatabase.LocalUsers[gGroupCalendar_PlayerName] = true;
311 GroupCalendar_MajorDatabaseChange(vDatabase);
312 end
313  
314 return vDatabase;
315 end
316  
317 function EventDatabase_DeleteDatabase(pUserName)
318 local vDatabase = gGroupCalendar_Database.Databases[gGroupCalendar_RealmName.."_"..pUserName];
319  
320 if not vDatabase then
321 return;
322 end
323  
324 if vDatabase.LocalUsers then
325 vDatabase.LocalUsers[gGroupCalendar_PlayerName] = nil;
326 end
327  
328 if Calendar_ArrayIsEmpty(vDatabase.LocalUsers) then
329 gGroupCalendar_Database.Databases[gGroupCalendar_RealmName.."_"..pUserName] = nil;
330 end
331  
332 GroupCalendar_MajorDatabaseChange(vDatabase);
333 end
334  
335 function EventDatabase_GetChanges(pDatabase)
336 local vChanges = pDatabase.Changes;
337  
338 if not vChanges then
339 vChanges = CalendarChanges_New();
340 pDatabase.Changes = vChanges;
341 end
342  
343 return vChanges;
344 end
345  
346 function EventDatabase_SetUserName(pUserName)
347 gGroupCalendar_UserDatabase = EventDatabase_GetDatabase(gGroupCalendar_PlayerName, true);
348  
349 gGroupCalendar_UserDatabase.PlayerRaceCode = EventDatabase_GetRaceCodeByRace(UnitRace("PLAYER"));
350 gGroupCalendar_UserDatabase.PlayerClassCode = EventDatabase_GetClassCodeByClass(UnitClass("PLAYER"));
351 end
352  
353 function EventDatabase_NewEvent(pDatabase, pDate)
354 local vEvent = {};
355  
356 vEvent.mType = nil;
357 vEvent.mTitle = nil;
358  
359 vEvent.mTime = 1140;
360 vEvent.mDate = pDate;
361 vEvent.mDuration = 120;
362  
363 vEvent.mDescription = nil;
364  
365 vEvent.mMinLevel = 0;
366 vEvent.mAttendance = nil;
367  
368 vEvent.mPrivate = nil;
369  
370 vEvent.mManualConfirm = false;
371 vEvent.mLimits = nil;
372  
373 pDatabase.CurrentEventID = pDatabase.CurrentEventID + 1;
374 vEvent.mID = pDatabase.CurrentEventID;
375  
376 return vEvent;
377 end
378  
379 function EventDatabase_AddEvent(pDatabase, pEvent, pSilent)
380 local vSchedule = pDatabase.Events[pEvent.mDate];
381  
382 if vSchedule == nil then
383 vSchedule = {};
384 pDatabase.Events[pEvent.mDate] = vSchedule;
385 end
386  
387 -- append the event
388  
389 table.insert(vSchedule, pEvent);
390  
391 if not pSilent then
392 EventDatabase_EventAdded(pDatabase, pEvent);
393 end
394 end
395  
396 function EventDatabase_GetDateSchedule(pDate)
397 return gGroupCalendar_UserDatabase.Events[pDate];
398 end
399  
400 function EventDatabase_GetCompiledSchedule(pDate)
401 local vCompiledSchedule = {};
402  
403 if gGroupCalendar_Settings.ShowEventsInLocalTime then
404 local vDate2 = nil;
405  
406 if gGroupCalendar_ServerTimeZoneOffset < 0 then
407 vDate2 = pDate + 1;
408 elseif gGroupCalendar_ServerTimeZoneOffset > 0 then
409 vDate2 = pDate - 1;
410 end
411  
412 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
413 if EventDatabase_DatabaseIsVisible(vDatabase) then
414 for vDateIndex = 1, 2 do
415 local vDate;
416  
417 if vDateIndex == 1 then
418 vDate = pDate;
419 else
420 if not vDate2 then
421 break;
422 end
423  
424 vDate = vDate2;
425 end
426  
427 local vSchedule = vDatabase.Events[vDate];
428  
429 if vSchedule then
430 for vIndex, vEvent in vSchedule do
431 -- Calculate the local date/time and see if it's still the right date
432  
433 local vLocalDate, vLocalTime = Calendar_GetLocalDateTimeFromServerDateTime(vDate, vEvent.mTime);
434  
435 if vLocalDate == pDate then
436 local vCompiledEvent = {mOwner = vDatabase.UserName, mEvent = vEvent};
437  
438 table.insert(vCompiledSchedule, vCompiledEvent);
439 end
440 end
441 end
442 end
443 end
444 end
445 else
446 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
447 if EventDatabase_DatabaseIsVisible(vDatabase) then
448 local vSchedule = vDatabase.Events[pDate];
449  
450 if vSchedule then
451 for vIndex, vEvent in vSchedule do
452 local vCompiledEvent = {mOwner = vDatabase.UserName, mEvent = vEvent};
453  
454 table.insert(vCompiledSchedule, vCompiledEvent);
455 end
456 end
457 end
458 end
459 end
460  
461 table.sort(vCompiledSchedule, EventDatabase_CompareCompiledEvents);
462  
463 return vCompiledSchedule;
464 end
465  
466 function EventDatabase_CompareCompiledEvents(pCompiledEvent1, pCompiledEvent2)
467 return EventDatabase_CompareEvents(pCompiledEvent1.mEvent, pCompiledEvent2.mEvent);
468 end
469  
470 function EventDatabase_GetEventDisplayName(pEvent)
471 if pEvent.mTitle and pEvent.mTitle ~= "" then
472 return Calendar_UnescapeString(pEvent.mTitle);
473 else
474 local vName = EventDatabase_GetEventNameByID(pEvent.mType);
475  
476 if vName ~= nil then
477 return vName;
478 else
479 return "";
480 end
481 end
482 end
483  
484 function EventDatabase_CompareEvents(pEvent1, pEvent2)
485 -- If either event has nil for a time (all day event) then
486 -- sort based on time or display name
487  
488 if not pEvent1.mTime or not pEvent2.mTime then
489 if pEvent1.mTime == pEvent2.mTime then
490 return EventDatabase_GetEventDisplayName(pEvent1) < EventDatabase_GetEventDisplayName(pEvent2);
491 elseif pEvent1.mTime == nil then
492 return true;
493 else
494 return false;
495 end
496  
497 -- Otherwise compare dates first
498  
499 elseif pEvent1.mDate < pEvent2.mDate then
500 return true;
501 elseif pEvent1.mDate > pEvent2.mDate then
502 return false;
503  
504 -- Dates are the same, compare times
505  
506 elseif pEvent1.mTime == pEvent2.mTime then
507 return EventDatabase_GetEventDisplayName(pEvent1) < EventDatabase_GetEventDisplayName(pEvent2);
508 else
509 return pEvent1.mTime < pEvent2.mTime;
510 end
511 end
512  
513 function EventDatabase_GetEventIndex(pSchedule, pEvent)
514 for vIndex, vEvent in pSchedule do
515 if vEvent == pEvent then
516 return vIndex;
517 end
518 end
519  
520 return 0;
521 end
522  
523 function EventDatabase_ScheduleIsEmpty(pSchedule)
524 for vIndex, vEvent in pSchedule do
525 return false;
526 end
527  
528 return true;
529 end
530  
531 function EventDatabase_FindEventByID(pDatabase, pEventID)
532 for vDate, vSchedule in pDatabase.Events do
533 for vEventIndex, vEvent in vSchedule do
534 if vEvent.mID == pEventID then
535 return vEvent, vDate;
536 end
537 end
538 end
539  
540 return nil;
541 end
542  
543 function EventDatabase_DeleteEvent(pDatabase, pEvent, pSilent)
544 return EventDatabase_DeleteEventFromDate(pDatabase, pEvent.mDate, pEvent, pSilent);
545 end
546  
547 function EventDatabase_DeleteEventFromDate(pDatabase, pDate, pEvent, pSilent)
548 -- Get the event's schedule
549  
550 local vSchedule = pDatabase.Events[pDate];
551  
552 if vSchedule == nil then
553 return false;
554 end
555  
556 -- Find the event index
557  
558 local vEventIndex = EventDatabase_GetEventIndex(vSchedule, pEvent);
559  
560 if vEventIndex == 0 then
561 return false;
562 end
563  
564 -- Notify that the event is being removed
565  
566 if not pSilent
567 and pDatabase.IsPlayerOwned then
568 CalendarNetwork_RemovingEvent(pDatabase, pEvent);
569 end
570  
571 -- Remove any pending RSVPs for the event
572  
573 EventDatabase_RemoveAllRSVPsForEvent(pDatabase, pEvent, false);
574  
575 -- Remove the event
576  
577 table.remove(vSchedule, vEventIndex);
578  
579 if EventDatabase_ScheduleIsEmpty(vSchedule) then
580 pDatabase.Events[pDate] = nil;
581 vSchedule = nil;
582 end
583  
584 -- Notify that the schedule changed
585  
586 GroupCalendar_ScheduleChanged(pDatabase, pDate);
587  
588 return true;
589 end
590  
591 function EventDatabase_GetEventInfoByID(pID)
592 for vGroupID, vEventGroup in gGroupCalendar_EventTypes do
593 for vIndex, vEventInfo in vEventGroup.Events do
594 if vEventInfo.id == pID then
595 return vEventInfo;
596 end
597 end
598 end
599  
600 return nil;
601 end
602  
603 function EventDatabase_GetEventNameByID(pID)
604 local vEventInfo = EventDatabase_GetEventInfoByID(pID);
605  
606 if not vEventInfo then
607 return nil;
608 end
609  
610 return vEventInfo.name;
611 end
612  
613 function EventDatabase_GetStandardLimitsByID(pID)
614 local vEventInfo = EventDatabase_GetEventInfoByID(pID);
615  
616 if not vEventInfo
617 or not vEventInfo.limits then
618 return nil;
619 end
620  
621 -- Remove limit for classes from the "wrong" faction
622  
623 for vClassCode, vClassLimit in vEventInfo.limits.mClassLimits do
624 local vClassInfo = gGroupCalendar_ClassInfoByClassCode[vClassCode];
625  
626 if vClassInfo.faction
627 and vClassInfo.faction ~= gGroupCalendar_PlayerFactionGroup then
628 vEventInfo.limits.mClassLimits[vClassCode] = nil;
629 end
630 end
631  
632 --
633  
634 return vEventInfo.limits;
635 end
636  
637 function EventDatabase_EventAdded(pDatabase, pEvent)
638 -- Notify the calendar
639  
640 GroupCalendar_ScheduleChanged(pDatabase, pEvent.mDate);
641  
642 -- Notify the network
643  
644 CalendarNetwork_NewEvent(pDatabase, pEvent);
645 end
646  
647 function EventDatabase_EventChanged(pDatabase, pEvent, pChangedFields)
648 -- If the date changed then move the event to the appropriate slot
649  
650 if pChangedFields and pChangedFields.mDate then
651 local vEvent, vDate = EventDatabase_FindEventByID(pDatabase, pEvent.mID);
652  
653 if vDate ~= pEvent.mDate then
654 EventDatabase_DeleteEventFromDate(pDatabase, vDate, pEvent, true);
655 EventDatabase_AddEvent(pDatabase, pEvent, true);
656 end
657 end
658  
659 -- Update pending RSVPs based on event contents
660  
661 if pChangedFields and pChangedFields.mAttendance then
662 if pChangedFields.mAttendance.op == "UPD" then
663 for vAttendeeName, vRSVPString in pChangedFields.mAttendance.val do
664 local vDatabase = EventDatabase_GetDatabase(vAttendeeName, false);
665  
666 if vDatabase then
667 -- Remove any older (or same) RSVP for this person
668  
669 local vRSVP = EventDatabase_UnpackEventRSVP(pDatabase.UserName, vAttendeeName, pEvent.mID, vRSVPString);
670  
671 EventDatabase_RemoveOlderRSVP(vDatabase, vRSVP)
672 end
673 end
674 else
675 Calendar_DumpArray("EventDatabase_EventChanged: ", pChangedFields);
676 Calendar_DebugMessage("EventDatabase_EventChanged: Attendance op "..pChangedFields.mAttendance.op.." not recognized");
677 end
678 end
679  
680 -- Notify the calendar
681  
682 GroupCalendar_EventChanged(pDatabase, pEvent, pChangedFields);
683  
684 -- Notify the network
685  
686 CalendarNetwork_EventChanged(pDatabase, pEvent, pChangedFields);
687 end
688  
689 function EventDatabase_GetCurrentChangeList(pDatabase)
690 local vChanges = EventDatabase_GetChanges(pDatabase);
691 local vChangeList, vRevisionChanged = CalendarChanges_GetCurrentChangeList(vChanges);
692  
693 if vRevisionChanged and pDatabase.IsPlayerOwned then
694 CalendarNetwork_DBRevisionChanged(pDatabase);
695 end
696  
697 return vChangeList;
698 end
699  
700 function EventDatabase_GetDBRevisionPath(pUserName, pDatabaseID, pRevision, pAuthRevision)
701 return CalendarChanges_GetRevisionPath("DB", pUserName, pDatabaseID, pRevision, pAuthRevision);
702 end
703  
704 function EventDatabase_GetRSVPRevisionPath(pUserName, pDatabaseID, pRevision, pAuthRevision)
705 return CalendarChanges_GetRevisionPath("RAT", pUserName, pDatabaseID, pRevision, pAuthRevision);
706 end
707  
708 function EventDatabase_GetEventPath(pEvent)
709 return "EVT:"..pEvent.mID.."/";
710 end
711  
712 function EventDatabase_GenerateEventChangeString(pOpcode, pEvent, pEventPath)
713 local vChange;
714  
715 -- Basic fields: type, date, time, duration, minLevel, maxLevel
716  
717 vChange = pEventPath..pOpcode..":";
718  
719 if pEvent.mType ~= nil then
720 vChange = vChange..pEvent.mType..",";
721 else
722 vChange = vChange..",";
723 end
724  
725 if pEvent.mDate ~= nil then
726 vChange = vChange..pEvent.mDate..",";
727 else
728 vChange = vChange..",";
729 end
730  
731 if pEvent.mTime ~= nil then
732 vChange = vChange..pEvent.mTime..",";
733 else
734 vChange = vChange..",";
735 end
736  
737 if pEvent.mDuration ~= nil then
738 vChange = vChange..pEvent.mDuration..",";
739 else
740 vChange = vChange..",";
741 end
742  
743 if pEvent.mMinLevel ~= nil then
744 vChange = vChange..pEvent.mMinLevel..",";
745 else
746 vChange = vChange..",";
747 end
748  
749 if pEvent.mMaxLevel ~= nil then
750 vChange = vChange..pEvent.mMaxLevel;
751 end
752  
753 return vChange;
754 end
755  
756 function EventDatabase_AppendNewEvent(pChangeList, pEvent, pEventPath)
757 -- Basic fields: type, date, time, duration, minLevel, maxLevel
758  
759 table.insert(pChangeList, EventDatabase_GenerateEventChangeString("NEW", pEvent, pEventPath));
760  
761 -- Title
762  
763 if pEvent.mTitle then
764 table.insert(pChangeList, pEventPath.."TIT:"..pEvent.mTitle);
765 end
766  
767 if pEvent.mDescription ~= nil then
768 table.insert(pChangeList, pEventPath.."DSC:"..pEvent.mDescription);
769 end
770  
771 if pEvent.mManualConfirm then
772 table.insert(pChangeList, pEventPath.."CNF:MAN");
773 elseif pEvent.mLimits then
774 local vConfConfigString = "CNF:AUT";
775  
776 if pEvent.mLimits.mMaxAttendance then
777 vConfConfigString = vConfConfigString.."/MAX:"..pEvent.mLimits.mMaxAttendance;
778 end
779  
780 if pEvent.mLimits.mClassLimits then
781 for vClassCode, vClassLimit in pEvent.mLimits.mClassLimits do
782 vConfConfigString = vConfConfigString.."/"..vClassCode..":";
783  
784 if vClassLimit.mMin then
785 vConfConfigString = vConfConfigString..vClassLimit.mMin;
786 end
787  
788 if vClassLimit.mMax then
789 vConfConfigString = vConfConfigString..","..vClassLimit.mMax;
790 end
791 end
792 end
793  
794 table.insert(pChangeList, pEventPath..vConfConfigString);
795 end
796  
797 -- Add attendance info
798  
799 if pEvent.mAttendance then
800 for vAttendeeName, vAttendance in pEvent.mAttendance do
801 table.insert(pChangeList, pEventPath.."ATT:"..vAttendeeName..","..vAttendance);
802 end
803 end
804  
805 table.insert(pChangeList, pEventPath.."END");
806 end
807  
808 function EventDatabase_AppendEventUpdate(pChangeList, pEvent, pEventPath, pChangedFields)
809 local vNeedUpdateWrapper = false;
810  
811 -- See if fields sent in the NEW or UPD wrapper are being changed. If so, the
812 -- wrapper needs to be sent, otherwise it can be omitted to save bandwidth
813  
814 if pChangedFields.mType
815 or pChangedFields.mDate
816 or pChangedFields.mTime
817 or pChangedFields.mDuration
818 or pChangedFields.mMinLevel
819 or pChangedFields.mMaxLevel then
820 vNeedUpdateWrapper = true;
821 end
822  
823 -- Basic fields: type, date, time, duration, minLevel, maxLevel
824  
825 if vNeedUpdateWrapper then
826 table.insert(pChangeList, EventDatabase_GenerateEventChangeString("UPD", pEvent, pEventPath));
827 end
828  
829 -- Title
830  
831 if pChangedFields.mTitle ~= nil then
832 if pEvent.mTitle then
833 table.insert(pChangeList, pEventPath.."TIT:"..pEvent.mTitle);
834 end
835 end
836  
837 if pChangedFields.mDescription ~= nil then
838 if pEvent.mDescription ~= nil then
839 table.insert(pChangeList, pEventPath.."DSC:"..pEvent.mDescription);
840 end
841 end
842  
843 if pChangedFields.mManualConfirm ~= nil
844 or pChangedFields.mLimits ~= nil then
845 if pEvent.mManualConfirm then
846 table.insert(pChangeList, pEventPath.."CNF:MAN");
847 elseif pEvent.mLimits then
848 local vConfConfigString = "CNF:AUT";
849  
850 if pEvent.mLimits.mMaxAttendance then
851 vConfConfigString = vConfConfigString.."/MAX:"..pEvent.mLimits.mMaxAttendance;
852 end
853  
854 if pEvent.mLimits.mClassLimits then
855 for vClassCode, vClassLimit in pEvent.mLimits.mClassLimits do
856 vConfConfigString = vConfConfigString.."/"..vClassCode..":";
857  
858 if vClassLimit.mMin then
859 vConfConfigString = vConfConfigString..vClassLimit.mMin;
860 end
861  
862 if vClassLimit.mMax then
863 vConfConfigString = vConfConfigString..","..vClassLimit.mMax;
864 end
865 end
866 end
867  
868 table.insert(pChangeList, pEventPath..vConfConfigString);
869 end
870 end
871  
872 if pChangedFields.mAttendance ~= nil then
873 if pChangedFields.mAttendance.op == "UPD" then
874 for vAttendeeName, vEventRSVPString in pChangedFields.mAttendance.val do
875 local vAttendeeRSVPString = pEvent.mAttendance[vAttendeeName];
876 local vAttendeePath = pEventPath.."ATT:"..vAttendeeName;
877  
878 if not vAttendeeRSVPString then
879 table.insert(pChangeList, vAttendeePath);
880 else
881 table.insert(pChangeList, vAttendeePath..","..vAttendeeRSVPString);
882 end
883 end
884 else
885 Calendar_DebugMessage("EventDatabase_AppendEventUpdate: Unknown attendance opcode "..pChangedFields.mAttendance.op);
886 end
887 end
888  
889 if vNeedUpdateWrapper then
890 table.insert(pChangeList, pEventPath.."END");
891 end
892 end
893  
894 function EventDatabase_RemoveEventChanges(pDatabase, pEvent)
895 -- Nothing to do if there are no changes
896  
897 if not pDatabase.Changes then
898 return;
899 end
900  
901 -- Remove all prior occurances for this event
902  
903 for vRevision, vChangeList in pDatabase.Changes.ChangeList do
904 local vEventPath = EventDatabase_GetEventPath(pEvent);
905 local vPathLength = string.len(vEventPath);
906  
907 local vNumChanges = table.getn(vChangeList);
908 local vChangeIndex = 1;
909  
910 while vChangeIndex <= vNumChanges do
911 vChange = vChangeList[vChangeIndex];
912  
913 if vChange ~= nil
914 and string.sub(vChange, 1, vPathLength) == vEventPath then
915 table.remove(vChangeList, vIndex);
916 vNumChanges = vNumChanges - 1;
917 else
918 vChangeIndex = vChangeIndex + 1;
919 end
920 end
921  
922 if vNumChanges == 0 then
923 pDatabase.Changes.ChangeList[vRevision] = nil;
924 end
925 end
926 end
927  
928 function EventDatabase_RebuildPlayerDatabases()
929 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
930 if vDatabase.IsPlayerOwned
931 and vDatabase.Realm == gGroupCalendar_RealmName then
932 EventDatabase_RebuildDatabase(vDatabase);
933 end
934 end -- for vRealmUser, vDatabase
935 end
936  
937 ----------------------------------------
938 -- EventDatabase_CalculateHighestUsedEventID
939 -- Calculates the highest event ID in use by the database
940 ----------------------------------------
941  
942 function EventDatabase_CalculateHighestUsedEventID(pDatabase)
943 local vHighestID = nil;
944  
945 for vDate, vSchedule in pDatabase.Events do
946 for vEventIndex, vEvent in vSchedule do
947 if not vHighestID
948 or vHighestID < vEvent.mID then
949 vHighestID = vEvent.mID;
950 end
951 end -- for vEventIndex, vEvent
952 end -- for vDate, vSchedule
953 end
954  
955 ----------------------------------------
956 -- EventDatabase_RepairEventIDs
957 -- Scans the database an ensures that every event has
958 -- a unique ID
959 ----------------------------------------
960  
961 function EventDatabase_RepairEventIDs(pDatabase)
962 local vCurrentID = EventDatabase_CalculateHighestUsedEventID(pDatabase);
963  
964 -- Just return if there are no events
965  
966 if not vCurrentID then
967 return;
968 end
969  
970 -- Adjust the highest ID if it is lower than current ID
971  
972 if vCurrentID < pDatabase.CurrentEventID then
973 vCurrentID = pDatabase.CurrentEventID;
974 end
975  
976 -- Start making a map of used event IDs and
977 -- use up the next ID if a collision is detected
978  
979 local vUsedIDs = {};
980  
981 for vDate, vSchedule in pDatabase.Events do
982 for vEventIndex, vEvent in vSchedule do
983 if not vUsedIDs[vEvent.mID] then
984 vUsedIDs[vEvent.mID] = true;
985 else
986 -- Collision
987  
988 vCurrentID = vCurrentID + 1;
989 vEvent.mID = vCurrentID;
990 end
991 end -- for vEventIndex, vEvent
992 end -- for vDate, vSchedule
993  
994 -- Save the new current ID
995  
996 pDatabase.CurrentEventID = vCurrentID;
997 end
998  
999 ----------------------------------------
1000 -- EventDatabase_RebuildDatabase
1001 -- Builds a new change history from the existing events
1002 ----------------------------------------
1003  
1004 function EventDatabase_RebuildDatabase(pDatabase)
1005 -- Repair event IDs
1006  
1007 EventDatabase_RepairEventIDs(pDatabase);
1008  
1009 -- Clear the revisions
1010  
1011 pDatabase.Changes = nil;
1012  
1013 -- Start a new revision
1014  
1015 local vChangeList = nil;
1016  
1017 -- Add each event to the revision
1018  
1019 for vDate, vSchedule in pDatabase.Events do
1020 for vEventIndex, vEvent in vSchedule do
1021 if not vChangeList then
1022 vChangeList = EventDatabase_GetCurrentChangeList(pDatabase);
1023 end
1024  
1025 local vEventPath = EventDatabase_GetEventPath(vEvent);
1026  
1027 if not vEvent.mPrivate then
1028 EventDatabase_AppendNewEvent(
1029 vChangeList,
1030 vEvent,
1031 vEventPath);
1032 end
1033 end
1034 end
1035  
1036 if not vChangeList then
1037 CalendarNetwork_SendEmptyChanges(pDatabase.Changes, "DB", pDatabase.UserName)
1038 end
1039  
1040 -- Compact the RSVP list and notify that they're updated
1041  
1042 EventDatabase_RebuildRSVPs(pDatabase);
1043  
1044 -- Notify the calendar that there was a major change
1045  
1046 GroupCalendar_MajorDatabaseChange(pDatabase);
1047 end
1048  
1049 function EventDatabase_RebuildRSVPs(pDatabase)
1050 if pDatabase.RSVPs then
1051 CalendarChanges_Compact(pDatabase.RSVPs);
1052 end
1053  
1054 if not CalendarChanges_IsEmpty(pDatabase.RSVPs) then
1055 CalendarNetwork_RSVPRevisionChanged(pDatabase);
1056 else
1057 CalendarNetwork_SendEmptyChanges(pDatabase.RSVPs, "RAT", pDatabase.UserName)
1058 end
1059 end
1060  
1061 ----------------------------------------
1062 -- EventDatabase_ReconstructDatabase
1063 -- Reconstructs the event records by re-playing the
1064 -- change history
1065 ----------------------------------------
1066  
1067 function EventDatabase_ReconstructDatabase(pDatabase)
1068 -- Clear the events
1069  
1070 pDatabase.Events = {};
1071  
1072 -- Execute each change
1073  
1074 if pDatabase.Changes then
1075 for vRevision, vChangeList in pDatabase.Changes.ChangeList do
1076 if gGroupCalendar_Settings.DebugReconstruct then
1077 Calendar_DebugMessage("EventDatabase_ReconstructDatabase: Reconstruction revision "..vRevision.." in "..pDatabase.UserName);
1078 end
1079  
1080 EventDatabase_ExecuteChangeList(pDatabase, vChangeList, false);
1081 end
1082 end
1083  
1084 GroupCalendar_MajorDatabaseChange(pDatabase);
1085 end
1086  
1087 function EventDatabase_ReprocessAllRSVPs(pDatabase)
1088 local vRSVPs = pDatabase.RSVPs;
1089  
1090 if not vRSVPs then
1091 return;
1092 end
1093  
1094 for vRevision, vChangeList in vRSVPs.ChangeList do
1095 EventDatabase_ExecuteRSVPChangeList(pDatabase, vChangeList, false);
1096 end
1097 end
1098  
1099 function EventDatabase_ExecuteRSVPChangeList(pDatabase, pChangeList, pNotifyChanges)
1100 pChangeList.IsOpen = nil; -- Make sure IsOpen is cleared, a bug may have caused it to remain open
1101  
1102 local vIndex = 1;
1103 local vNumChanges = table.getn(pChangeList);
1104  
1105 while vIndex <= vNumChanges do
1106 local vChange = pChangeList[vIndex];
1107  
1108 if vChange then
1109 local vCommands = CalendarNetwork_ParseCommandSubString("/"..vChange);
1110  
1111 if not vCommands then
1112 Calendar_DebugMessage("Invalid change entry found in RSVPs for "..pDatabase.UserName);
1113 return;
1114 end
1115  
1116 local vOpcode = vCommands[1].opcode;
1117 local vOperands = vCommands[1].operands;
1118  
1119 table.remove(vCommands, 1);
1120  
1121 if vOpcode == "EVT" then
1122 local vRSVP = EventDatabase_UnpackRSVPFieldArray(vOperands, pDatabase.UserName);
1123  
1124 if EventDatabase_ProcessRSVP(pDatabase, vRSVP) then
1125 -- The request was consumed, remove it from the list
1126  
1127 table.remove(pChangeList, vIndex);
1128  
1129 vIndex = vIndex - 1;
1130 vNumChanges = vNumChanges - 1;
1131 end
1132 elseif gGroupCalendar_Settings.DebugErrors then
1133 Calendar_DebugMessage("GroupCalendar: Unknown RSVP opcode "..vOpcode);
1134 end
1135 end
1136  
1137 vIndex = vIndex + 1;
1138 end
1139 end
1140  
1141 function EventDatabase_ExecuteChangeList(pDatabase, pChangeList, pNotifyChanges)
1142 local vEvent = nil;
1143 local vNewEvent = false;
1144 local vQuickEvent = false;
1145 local vEventDateChanged = false;
1146  
1147 pChangeList.IsOpen = nil; -- Make sure IsOpen is cleared, a bug may have caused it to remain open
1148  
1149 for vIndex, vChange in pChangeList do
1150 local vCommands = CalendarNetwork_ParseCommandSubString("/"..vChange);
1151  
1152 if not vCommands then
1153 Calendar_DebugMessage("Invalid change entry found in database for "..pDatabase.UserName);
1154 return;
1155 end
1156  
1157 local vOpcode = vCommands[1].opcode;
1158 local vOperands = vCommands[1].operands;
1159  
1160 table.remove(vCommands, 1);
1161  
1162 if vOpcode == "EVT" then
1163 local vEventID = tonumber(vOperands[1]);
1164  
1165 local vEvtOpcode = vCommands[1].opcode;
1166 local vEvtOperands = vCommands[1].operands;
1167  
1168 table.remove(vCommands, 1);
1169  
1170 if vEvtOpcode == "NEW" then
1171 if vEvent and gGroupCalendar_Settings.DebugErrors then
1172 Calendar_DebugMessage("GroupCalendar: Starting new event while previous event is still open in database for "..pDatabase.UserName);
1173 end
1174  
1175 if gGroupCalendar_Settings.Debug then
1176 Calendar_DebugMessage("Adding event "..vEventID.." for "..pDatabase.UserName);
1177 end
1178  
1179 if not EventDatabase_FindEventByID(pDatabase, vEventID) then
1180 local vDate = tonumber(vEvtOperands[2]);
1181  
1182 if vDate >= gGroupCalendar_MinimumEventDate then
1183 -- Create the event record
1184  
1185 vEvent = {};
1186 vNewEvent = true;
1187  
1188 vEvent.mID = vEventID;
1189 vEvent.mType = vEvtOperands[1];
1190 vEvent.mDate = tonumber(vEvtOperands[2]);
1191 vEvent.mTime = tonumber(vEvtOperands[3]);
1192 vEvent.mDuration = tonumber(vEvtOperands[4]);
1193 vEvent.mMinLevel = tonumber(vEvtOperands[5]);
1194 vEvent.mMaxLevel = tonumber(vEvtOperands[6]);
1195  
1196 EventDatabase_AddEvent(pDatabase, vEvent, true);
1197 end
1198 elseif gGroupCalendar_Settings.DebugErrors then
1199 Calendar_DebugMessage("GroupCalendar: Event "..vEventID.." already exists in database for "..pDatabase.UserName);
1200 end
1201  
1202 elseif vEvtOpcode == "UPD" then
1203 if vEvent and gGroupCalendar_Settings.DebugErrors then
1204 Calendar_DebugMessage("GroupCalendar: Updating event while previous event is still open in database for "..pDatabase.UserName);
1205 end
1206  
1207 vEvent = EventDatabase_FindEventByID(pDatabase, vEventID);
1208  
1209 if vEvent then
1210 local vDate = vEvent.mDate;
1211  
1212 vEvent.mID = vEventID;
1213 vEvent.mType = vEvtOperands[1];
1214 vEvent.mDate = tonumber(vEvtOperands[2]);
1215 vEvent.mTime = tonumber(vEvtOperands[3]);
1216 vEvent.mDuration = tonumber(vEvtOperands[4]);
1217 vEvent.mMinLevel = tonumber(vEvtOperands[5]);
1218 vEvent.mMaxLevel = tonumber(vEvtOperands[6]);
1219  
1220 vNewEvent = false;
1221 vEventDateChanged = vEvent.mDate ~= vDate;
1222 elseif gGroupCalendar_Settings.DebugErrors then
1223 Calendar_DebugMessage("GroupCalendar: Event "..vEventID.." not found in database for "..pDatabase.UserName);
1224 end
1225  
1226 elseif vEvtOpcode == "TIT" then
1227 if not vEvent then
1228 vEvent = EventDatabase_FindEventByID(pDatabase, vEventID);
1229 vQuickEvent = true;
1230 end
1231  
1232 if vEvent then
1233 vEvent.mTitle = vEvtOperands[1];
1234 elseif gGroupCalendar_Settings.DebugErrors then
1235 Calendar_DebugMessage("GroupCalendar: Event "..vEventID.." not found in database for "..pDatabase.UserName);
1236 end
1237  
1238 elseif vEvtOpcode == "DSC" then
1239 if not vEvent then
1240 vEvent = EventDatabase_FindEventByID(pDatabase, vEventID);
1241 vQuickEvent = true;
1242 end
1243  
1244 if vEvent then
1245 vEvent.mDescription = vEvtOperands[1];
1246 elseif gGroupCalendar_Settings.DebugErrors then
1247 Calendar_DebugMessage("GroupCalendar: Event "..vEventID.." not found in database for "..pDatabase.UserName);
1248 end
1249  
1250 elseif vEvtOpcode == "ATT" then
1251 if not vEvent then
1252 vEvent = EventDatabase_FindEventByID(pDatabase, vEventID);
1253 vQuickEvent = true;
1254 end
1255  
1256 if vEvent then
1257 local vAttendeeName = vEvtOperands[1];
1258  
1259 if not vEvent.mAttendance then
1260 vEvent.mAttendance = {};
1261 end
1262  
1263 -- Add/update their attendance
1264  
1265 local vNumOperands = table.getn(vEvtOperands);
1266 local vAttendanceString = nil;
1267  
1268 if vNumOperands > 1 then
1269 for vOperandIndex = 2, vNumOperands do
1270 local vOperand = vEvtOperands[vOperandIndex];
1271  
1272 if vAttendanceString == nil then
1273 vAttendanceString = "";
1274 else
1275 vAttendanceString = vAttendanceString..",";
1276 end
1277  
1278 if vOperand then
1279 vAttendanceString = vAttendanceString..vOperand;
1280 end
1281 end
1282  
1283 end
1284  
1285 vEvent.mAttendance[vAttendeeName] = vAttendanceString;
1286  
1287 -- Remove any older (or same) RSVP for this person
1288  
1289 EventDatabase_RemoveOldAttendeeRSVP(vAttendeeName, pDatabase.UserName, vEventID, vAttendanceString);
1290  
1291 -- Didn't find the specified event
1292  
1293 elseif gGroupCalendar_Settings.DebugErrors then
1294 Calendar_DebugMessage("GroupCalendar: Event "..vEventID.." not found in database for "..pDatabase.UserName);
1295 end
1296  
1297 elseif vEvtOpcode == "END" then
1298 if vEvent then
1299 if pNotifyChanges then
1300 -- Notify the calendar
1301  
1302 if vNewEvent then
1303 GroupCalendar_ScheduleChanged(pDatabase, vEvent.mDate);
1304 else
1305 if vEventDateChanged then
1306 local vEvent2, vDate = EventDatabase_FindEventByID(pDatabase, vEvent.mID);
1307  
1308 if vDate ~= vEvent.mDate then
1309 EventDatabase_DeleteEventFromDate(pDatabase, vDate, pEvent, true);
1310 EventDatabase_AddEvent(pDatabase, vEvent, true);
1311 end
1312 end
1313  
1314 GroupCalendar_EventChanged(pDatabase, vEvent, nil); -- only notify the calendar
1315 end
1316 end
1317  
1318 vEvent = nil;
1319 elseif gGroupCalendar_Settings.DebugErrors then
1320 Calendar_DebugMessage("GroupCalendar: Event not open when attemping to end update for "..pDatabase.UserName);
1321 end
1322  
1323 elseif vEvtOpcode == "DEL" then
1324 vEvent = EventDatabase_FindEventByID(pDatabase, vEventID);
1325 vQuickEvent = true;
1326  
1327 if vEvent then
1328 EventDatabase_DeleteEvent(pDatabase, vEvent, true);
1329 elseif gGroupCalendar_Settings.DebugErrors then
1330 Calendar_DebugMessage("GroupCalendar: Can't delete event "..vEventID..": Event not found in database for "..pDatabase.UserName);
1331 end
1332  
1333 elseif gGroupCalendar_Settings.DebugErrors then
1334 Calendar_DebugMessage("GroupCalendar: Unknown change operator "..vEvtOpcode);
1335 end
1336  
1337 if vQuickEvent then
1338 vEvent = nil;
1339 end
1340 end
1341 end
1342 end
1343  
1344 function EventDatabase_PurgeDatabase(pDatabase, pDatabaseID)
1345 EventDatabase_RemoveAllRSVPsForDatabase(pDatabase, false)
1346  
1347 pDatabase.CurrentEventID = 0;
1348 pDatabase.Events = {};
1349  
1350 pDatabase.Changes = CalendarChanges_New();
1351 pDatabase.Changes.ID = pDatabaseID;
1352  
1353 GroupCalendar_MajorDatabaseChange(pDatabase);
1354 end
1355  
1356 function EventDatabase_PurgeRSVPs(pDatabase, pDatabaseID)
1357 pDatabase.RSVPs = CalendarChanges_New();
1358 pDatabase.RSVPs.ID = pDatabaseID;
1359  
1360 GroupCalendar_MajorDatabaseChange(pDatabase);
1361 end
1362  
1363 function EventDatabase_CheckDatabase(pDatabase)
1364 -- Remove empty RSVP changelists
1365  
1366 if pDatabase.RSVPs then
1367 for vRevision, vChangeList in pDatabase.RSVPs.ChangeList do
1368 if table.getn(vChangeList) == 0 then
1369 pDatabase.RSVPs.ChangeList[vRevision] = nil;
1370 end
1371 end
1372 end
1373  
1374 -- Remove events with duplicate IDs
1375  
1376 for vDate, vEvents in pDatabase.Events do
1377 local vEventIndex = 1;
1378 local vNumEvents = table.getn(vEvents);
1379  
1380 while vEventIndex <= vNumEvents do
1381 local vEvent = vEvents[vEventIndex];
1382  
1383 if not vEvent
1384 or EventDatabase_FindEventByID(pDatabase, vEvent.mID) ~= vEvent then
1385 Calendar_DebugMessage("EventDatabase_CheckDatabase: Removing extra event ID "..vEvent.mID.." from database for "..pDatabase.UserName);
1386  
1387 table.remove(vEvents, vEventIndex);
1388 vNumEvents = vNumEvents - 1;
1389 else
1390 vEventIndex = vEventIndex + 1;
1391 end
1392 end
1393 end
1394 end
1395  
1396 function EventDatabase_ScanForNewlines(pDatabase)
1397 for vDate, vEvents in pDatabase.Events do
1398 for vEventID, vEvent in vEvents do
1399 if vEvent.mDescription then
1400 vEvent.mDescription = string.gsub(vEvent.mDescription, "\n", "&n;");
1401 end
1402 end
1403 end
1404  
1405 if pDatabase.Changes and pDatabase.Changes.ChangeList then
1406 for vRevision, vChanges in pDatabase.Changes.ChangeList do
1407 for vIndex, vChange in vChanges do
1408 if type(vIndex) == "number" then
1409 vChanges[vIndex] = string.gsub(vChange, "\n", "&n;");
1410 end
1411 end
1412 end
1413 end
1414 end
1415  
1416 function EventDatabase_Initialize()
1417 EventDatabase_CheckDatabases();
1418  
1419 -- Update the list of player-owned databases
1420  
1421 gGroupCalendar_PlayerCharacters = {};
1422  
1423 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1424 if EventDatabase_DatabaseIsVisible(vDatabase)
1425 and vDatabase.IsPlayerOwned then
1426 gGroupCalendar_PlayerCharacters[vDatabase.UserName] = true;
1427 end
1428 end
1429 end
1430  
1431 function EventDatabase_CheckDatabases()
1432 -- Upgrade the database to format 4 (just purge all non-owned databases
1433 -- and rebuild the owned ones)
1434  
1435 if gGroupCalendar_Database.Format < 4 then
1436 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1437 if vDatabase.IsPlayerOwned then
1438 EventDatabase_RebuildDatabase(vDatabase);
1439 else
1440 gGroupCalendar_Database.Databases[vRealmUser] = nil;
1441 end
1442 end
1443  
1444 gGroupCalendar_Database.Format = 4;
1445 end
1446  
1447 -- Upgrade the database to format 5 (scan for newlines in event fields and escape them)
1448  
1449 if gGroupCalendar_Database.Format < 5 then
1450 for vRealmUser, vSettings in gGroupCalendar_Settings do
1451 if type(vSettings) == "array" and vSettings.EventTemplates then
1452 for vEventID, vEventTemplate in vSettings.EventTemplates do
1453 if vEventTemplate.mDescription then
1454 vEventTemplate.mDescription = string.gsub(vEventTemplate.mDescription, "\n", "&n;");
1455 end
1456 end
1457 end
1458 end
1459  
1460 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1461 EventDatabase_ScanForNewlines(vDatabase);
1462 end
1463  
1464 gGroupCalendar_Database.Format = 5;
1465 end
1466  
1467 -- Upgrade the database to format 6 (just purge all non-owned databases
1468 -- and rebuild the owned ones again)
1469  
1470 if gGroupCalendar_Database.Format < 6 then
1471 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1472 if vDatabase.IsPlayerOwned then
1473 EventDatabase_RebuildDatabase(vDatabase);
1474 else
1475 gGroupCalendar_Database.Databases[vRealmUser] = nil;
1476 end
1477 end
1478  
1479 gGroupCalendar_Database.Format = 6;
1480 end
1481  
1482 -- Upgrade to format 7 (just purge all non-owned databases)
1483  
1484 if gGroupCalendar_Database.Format < 7 then
1485 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1486 if not vDatabase.IsPlayerOwned then
1487 gGroupCalendar_Database.Databases[vRealmUser] = nil;
1488 end
1489 end
1490  
1491 gGroupCalendar_Database.Format = 7;
1492 end
1493  
1494 -- Upgrade to format 8 (rebuild all owned databases to force
1495 -- them to the new version numbering system)
1496  
1497 if gGroupCalendar_Database.Format < 8 then
1498 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1499 if vDatabase.IsPlayerOwned then
1500 EventDatabase_RebuildDatabase(vDatabase);
1501 end
1502 end
1503  
1504 gGroupCalendar_Database.Format = 8;
1505 end
1506  
1507 -- Upgrade to format 9 (reconstruct non-owned databases to correct
1508 -- parsing errors)
1509  
1510 if gGroupCalendar_Database.Format < 9 then
1511 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1512 if not vDatabase.IsPlayerOwned then
1513 EventDatabase_ReconstructDatabase(vDatabase);
1514 end
1515 end
1516  
1517 gGroupCalendar_Database.Format = 9;
1518 end
1519  
1520 -- Upgrade the database to format 10 (just purge all non-owned databases)
1521  
1522 if gGroupCalendar_Database.Format < 10 then
1523 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1524 if not vDatabase.IsPlayerOwned then
1525 gGroupCalendar_Database.Databases[vRealmUser] = nil;
1526 end
1527 end
1528  
1529 gGroupCalendar_Database.Format = 10;
1530 end
1531  
1532 -- Upgrade the database to format 11 (ensure that RSVPs for deleted events have been removed
1533 -- and that event IDs are numbers and not strings)
1534  
1535 if gGroupCalendar_Database.Format < 11 then
1536 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1537 -- Convert all event IDs to a number to fix a bug
1538 -- caused by earlier versions
1539  
1540 for vDate, vEvents in vDatabase.Events do
1541 for vIndex, vEvent in vEvents do
1542 vEvent.mID = tonumber(vEvent.mID);
1543 end
1544 end
1545 end
1546  
1547 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1548 if vDatabase.IsPlayerOwned then
1549 EventDatabase_RemoveObsoleteRSVPs(vDatabase);
1550 EventDatabase_RebuildRSVPs(vDatabase);
1551 else
1552 EventDatabase_ReprocessAllRSVPs(vDatabase);
1553 end
1554 end
1555  
1556 gGroupCalendar_Database.Format = 11;
1557 end
1558  
1559 -- Make sure the realm is set
1560  
1561 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1562 if not vDatabase.Realm then
1563 local vStartIndex, vEndIndex, vRealmName = string.find(vRealmUser, "([^_]+)");
1564  
1565 if vStartIndex ~= nil then
1566 vDatabase.Realm = vRealmName;
1567 end
1568 end
1569  
1570 EventDatabase_CheckDatabase(vDatabase);
1571 end
1572  
1573 -- Remove old events
1574  
1575 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1576 EventDatabase_DeleteOldEvents(vDatabase);
1577 end
1578 end
1579  
1580 function EventDatabase_CheckDatabaseTrust()
1581 -- Return if they're in a guild but the roster isn't loaded
1582 -- so that we don't go and delete a bunch of guildie calendars
1583 -- by mistake
1584  
1585 if IsInGuild() and GetNumGuildMembers() == 0 then
1586 if gGroupCalendar_Settings.DebugInit then
1587 Calendar_DebugMessage("CheckDatabaseTrust: Roster isn't loaded, scheduling a load");
1588 end
1589  
1590 CalendarNetwork_LoadGuildRoster();
1591 return;
1592 end
1593  
1594 if gGroupCalendar_Settings.DebugInit then
1595 Calendar_DebugMessage("CheckDatabaseTrust: Verifying trust");
1596 end
1597  
1598 -- Verify that each database is still trusted
1599  
1600 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1601 -- Only check databases for the current realm
1602  
1603 if EventDatabase_DatabaseIsVisible(vDatabase) then
1604  
1605 -- See if they're still trusted
1606  
1607 if not vDatabase.IsPlayerOwned
1608 and not CalendarTrust_UserIsTrusted(vDatabase.UserName) then
1609 -- not trusted anymore, remove their database
1610  
1611 EventDatabase_DeleteDatabase(vDatabase.UserName);
1612 end
1613 end
1614 end
1615 end
1616  
1617 function EventDatabase_GetDateTime60Stamp()
1618 local vYear, vMonth, vDay, vHour, vMinute, vSecond = Calendar_GetCurrentYearMonthDayHourMinute();
1619  
1620 local vDate = Calendar_ConvertMDYToDate(vMonth, vDay, vYear);
1621 local vTime60 = Calendar_ConvertHMSToTime60(vHour, vMinute, vSecond);
1622  
1623 return vDate, vTime60;
1624 end
1625  
1626 function EventDatabase_GetServerDateTime60Stamp()
1627 local vYear, vMonth, vDay, vHour, vMinute, vSecond = Calendar_GetCurrentYearMonthDayHourMinute();
1628  
1629 local vDate = Calendar_ConvertMDYToDate(vMonth, vDay, vYear);
1630 local vTime = Calendar_ConvertHMToTime(vHour, vMinute);
1631  
1632 vDate, vTime = Calendar_GetServerDateTimeFromLocalDateTime(vDate, vTime);
1633  
1634 return vDate, vTime * 60 + vSecond;
1635 end
1636  
1637 function EventDatabase_GetRSVPs(pDatabase)
1638 local vRSVPs = pDatabase.RSVPs;
1639  
1640 if not vRSVPs then
1641 vRSVPs = CalendarChanges_New();
1642 pDatabase.RSVPs = vRSVPs;
1643 end
1644  
1645 return vRSVPs;
1646 end
1647  
1648 function EventDatabase_GetCurrentRSVPChangeList(pDatabase)
1649 local vRSVPs = EventDatabase_GetRSVPs(pDatabase);
1650 local vChangeList, vRevisionChanged = CalendarChanges_GetCurrentChangeList(vRSVPs);
1651  
1652 if vRevisionChanged and pDatabase.IsPlayerOwned then
1653 CalendarNetwork_RSVPRevisionChanged(pDatabase);
1654 end
1655  
1656 return vChangeList;
1657 end
1658  
1659 function EventDatabase_AddEventRSVP(pDatabase, pEvent, pAttendeeName, pRSVP)
1660 -- Verify that the attendance request is newer than the existing one
1661  
1662 local vExistingRSVP = EventDatabase_FindEventRSVP(pDatabase.UserName, pEvent, pAttendeeName);
1663  
1664 if vExistingRSVP then
1665 if vExistingRSVP.mDate > pRSVP.mDate
1666 or (vExistingRSVP.mDate == pRSVP.mDate
1667 and vExistingRSVP.mTime > pRSVP.mTime) then
1668 if gGroupCalendar_Settings.Debug then
1669 Calendar_DebugMessage("EventDatabase_AddEventRSVP: Ignore event attendance request for "..pAttendeeName.." for ".." event "..pRSVP.mEventID..": Request is out-of-date");
1670 end
1671  
1672 return;
1673 end
1674 end
1675  
1676 -- Update the event attendance list
1677  
1678 if gGroupCalendar_Settings.Debug then
1679 Calendar_DebugMessage("EventDatabase_AddEventRSVP: Updating event attendance for "..pAttendeeName.." for ".." event "..pRSVP.mEventID);
1680 end
1681  
1682 if not pEvent.mAttendance then
1683 pEvent.mAttendance = {};
1684 end
1685  
1686 local vEventRSVPString = EventDatabase_PackEventRSVP(pRSVP);
1687  
1688 pEvent.mAttendance[pAttendeeName] = vEventRSVPString;
1689  
1690 -- Notify the network of the change
1691  
1692 local vChangedFields =
1693 {
1694 mAttendance =
1695 {
1696 op = "UPD",
1697 val =
1698 {
1699 }
1700 }
1701 };
1702  
1703 vChangedFields.mAttendance.val[pAttendeeName] = vEventRSVPString;
1704  
1705 EventDatabase_EventChanged(pDatabase, pEvent, vChangedFields);
1706 end
1707  
1708 function EventDatabase_RemoveEventRSVP(pDatabase, pEvent, pAttendeeName)
1709 if not pEvent.mAttendance then
1710 return;
1711 end
1712  
1713 pEvent.mAttendance[pAttendeeName] = nil;
1714  
1715 -- Notify the network of the change
1716  
1717 local vChangedFields =
1718 {
1719 mAttendance =
1720 {
1721 op = "UPD",
1722 val =
1723 {
1724 [pAttendeeName] = "-",
1725 }
1726 }
1727 };
1728  
1729 EventDatabase_EventChanged(pDatabase, pEvent, vChangedFields);
1730 end
1731  
1732 function EventDatabase_AddRSVPRequest(pDatabase, pRSVP)
1733 -- Remove any existing RSVP for the same event
1734  
1735 if not EventDatabase_RemoveOlderRSVP(pDatabase, pRSVP) then
1736 if gGroupCalendar_Settings.Debug then
1737 Calendar_DebugMessage("EventDatabase_AddRSVPRequest: Ignoring "..pDatabase.UserName..","..pRSVP.mEventID..": "..pRSVP.mStatus);
1738 end
1739  
1740 return; -- A newer request already exists so disregard this one
1741 end
1742  
1743 -- Add the new RSVP
1744  
1745 local vChangeList = EventDatabase_GetCurrentRSVPChangeList(pDatabase);
1746  
1747 local vRSVPString = EventDatabase_PackRSVPRequest(pRSVP);
1748 local vRSVPAltsString = EventDatabase_GetRSVPAltsString(pRSVP);
1749  
1750 if vRSVPAltsString then
1751 vRSVPString = vRSVPString.."/ALTS:"..vRSVPAltsString;
1752 end
1753  
1754 if gGroupCalendar_Settings.Debug then
1755 Calendar_DebugMessage("EventDatabase_AddRSVPRequest: Adding string "..vRSVPString);
1756 end
1757  
1758 table.insert(vChangeList, vRSVPString);
1759  
1760 GroupCalendar_MajorDatabaseChange(pDatabase);
1761 end
1762  
1763 function EventDatabase_GetRSVPOriginalDateTime(pRSVP)
1764 if pRSVP.mOriginalDate then
1765 return pRSVP.mOriginalDate, pRSVP.mOriginalTime;
1766 else
1767 return pRSVP.mDate, pRSVP.mTime;
1768 end
1769 end
1770  
1771 function EventDatabase_RemoveAllRSVPsForEvent(pDatabase, pEvent, pOwnedDatabasesOnly)
1772 local vPrefix = "EVT:"..pDatabase.UserName..","..pEvent.mID..",";
1773 local vPrefixLength = string.len(vPrefix);
1774  
1775 EventDatabase_RemoveAllRSVPsByPrefix(pDatabase.Realm, vPrefix, vPrefixLength, pOwnedDatabasesOnly);
1776 end
1777  
1778 function EventDatabase_RemoveAllRSVPsForDatabase(pDatabase, pOwnedDatabasesOnly)
1779 local vPrefix = "EVT:"..pDatabase.UserName..",";
1780 local vPrefixLength = string.len(vPrefix);
1781  
1782 EventDatabase_RemoveAllRSVPsByPrefix(pDatabase.Realm, vPrefix, vPrefixLength, pOwnedDatabasesOnly);
1783 end
1784  
1785 function EventDatabase_RemoveAllRSVPsByPrefix(pRealm, pPrefix, pPrefixLength, pOwnedDatabasesOnly)
1786 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
1787 if vDatabase.Realm == pRealm
1788 and (not pOwnedDatabasesOnly or vDatabase.IsPlayerOwned) then
1789 EventDatabase_RemoveRSVPsByPrefix(vDatabase, pPrefix, pPrefixLength);
1790 end
1791 end
1792 end
1793  
1794 function EventDatabase_RemoveRSVPsByPrefix(pDatabase, pPrefix, pPrefixLength)
1795 local vRSVPString, vIndex, vRevision = EventDatabase_FindRSVPPrefixString(pDatabase, pPrefix, pPrefixLength)
1796  
1797 while vRSVPString do
1798 local vChangeList = pDatabase.RSVPs.ChangeList[vRevision];
1799  
1800 table.remove(vChangeList, vIndex);
1801  
1802 if table.getn(vChangeList) == 0 then
1803 pDatabase.RSVPs.ChangeList[vRevision] = nil;
1804 end
1805  
1806 vRSVPString, vIndex, vRevision = EventDatabase_FindRSVPPrefixString(pDatabase, pPrefix, pPrefixLength)
1807 end
1808 end
1809  
1810 function EventDatabase_ProcessRSVP(pDatabase, pRSVP)
1811 local vEventDatabase = EventDatabase_GetDatabase(pRSVP.mOrganizerName, false);
1812  
1813 -- Nothing to do if the database isn't one we own
1814  
1815 if not vEventDatabase or not vEventDatabase.IsPlayerOwned then
1816 return false;
1817 end
1818  
1819 -- Process the request into our database
1820  
1821 local vEvent = EventDatabase_FindEventByID(vEventDatabase, pRSVP.mEventID);
1822  
1823 if not vEvent then
1824 if gGroupCalendar_Settings.Debug then
1825 Calendar_DebugMessage("EventDatabase_ProcessRSVP: Discarding request from "..pDatabase.UserName.." for ".." event "..pRSVP.mEventID..": Event no longer exists");
1826 end
1827  
1828 return true; -- Have the request deleted
1829 end
1830  
1831 -- Look up an existing RSVP
1832  
1833 local vExistingRSVP = EventDatabase_FindEventRSVP(pDatabase.UserName, vEvent, pRSVP.mName);
1834  
1835 -- If the player has been banned (removed) from the event then ignore any
1836 -- requests from them
1837  
1838 if vExistingRSVP
1839 and vExistingRSVP.mStatus == "-" then
1840 return true; -- Delete the request without processing it
1841 end
1842  
1843 -- If the player is requesting attendance then figure out how to handle the request
1844  
1845 if pRSVP.mStatus == "Y" then
1846 -- If they're already accepted on the list then preserve their current
1847 -- status
1848  
1849 if vExistingRSVP
1850 and (vExistingRSVP.mStatus == "Y"
1851 or vExistingRSVP.mStatus == "S") then
1852 pRSVP.mOriginalDate, pRSVP.mOriginalTime = EventDatabase_GetRSVPOriginalDateTime(vExistingRSVP);
1853 pRSVP.mStatus = vExistingRSVP.mStatus;
1854  
1855 -- Otherwise put them on standby if using manual confirmations
1856  
1857 elseif vEvent.mManualConfirm then
1858 pRSVP.mStatus = "S";
1859  
1860 -- Check availablility to determine how to handle automatic confirmations
1861  
1862 else
1863 local vAvailableSlots = EventAvailableSlots_CountSlots(pDatabase, vEvent);
1864  
1865 if not EventAvailableSlots_AcceptPlayer(vAvailableSlots, pRSVP.mClassCode) then
1866 pRSVP.mStatus = "S";
1867 end
1868 end
1869 end
1870  
1871 -- Add the RSVP
1872  
1873 EventDatabase_AddEventRSVP(vEventDatabase, vEvent, pDatabase.UserName, pRSVP);
1874  
1875 return true;
1876 end
1877  
1878 function EventDatabase_AddRSVP(pDatabase, pRSVP)
1879 if gGroupCalendar_Settings.Debug then
1880 if not pRSVP.mName then
1881 Calendar_DumpArray("Missing attendee: ", pRSVP);
1882 return;
1883 end
1884 end
1885  
1886 if not EventDatabase_ProcessRSVP(pDatabase, pRSVP) then
1887 EventDatabase_AddRSVPRequest(pDatabase, pRSVP);
1888 end
1889 end
1890  
1891 function EventDatabase_RemoveOldAttendeeRSVP(pAttendeeName, pOrganizerName, pEventID, pAttendanceString)
1892 local vAttendeeDatabase = EventDatabase_GetDatabase(pAttendeeName, false);
1893  
1894 -- Just leave if we've never heard of them
1895  
1896 if not vAttendeeDatabase then
1897 return;
1898 end
1899  
1900 -- Just leave if it's a delete request, we don't know how to handle these yet
1901  
1902 if not pAttendanceString then
1903 return;
1904 end
1905  
1906 -- Remove the RSVP request if it exists in their database
1907  
1908 local vRSVP = EventDatabase_UnpackEventRSVP(pOrganizerName, pAttendeeName, pEventID, pAttendanceString);
1909  
1910 EventDatabase_RemoveOlderRSVP(vAttendeeDatabase, vRSVP)
1911 end
1912  
1913 function EventDatabase_RemoveOlderRSVP(pDatabase, pRSVP)
1914 if gGroupCalendar_Settings.Debug then
1915 Calendar_DebugMessage("EventDatabase_RemoveOlderRSVP: Removing RSVP for "..pRSVP.mName.." from "..pDatabase.UserName..","..pRSVP.mEventID);
1916 end
1917  
1918 local vRSVPString, vIndex, vRevision = EventDatabase_FindRSVPRequestString(pDatabase, pRSVP.mOrganizerName, pRSVP.mEventID);
1919  
1920 while vRSVPString ~= nil do
1921 if gGroupCalendar_Settings.Debug then
1922 Calendar_DebugMessage("EventDatabase_RemoveOlderRSVP: "..pRSVP.mOrganizerName..","..pRSVP.mEventID.." from position "..vRevision..","..vIndex);
1923 end
1924  
1925 -- If the existing RSVP is newer than the specified date/time then disregard the request
1926  
1927 if pRSVP.mDate ~= nil then
1928 local vRSVP = EventDatabase_UnpackRSVPRequest(vRSVPString, pRSVP.mName);
1929  
1930 if vRSVP.mDate > pRSVP.mDate
1931 or (vRSVP.mDate == pRSVP.mDate and vRSVP.mTime > pRSVP.mTime) then
1932 if gGroupCalendar_Settings.Debug then
1933 Calendar_DebugMessage("EventDatabase_RemoveOlderRSVP: Newer request already exists");
1934 end
1935  
1936 return false; -- Fail to indicate that a newer request is already in the database
1937 end
1938 end
1939  
1940 -- Remove the old one
1941  
1942 local vChangeList = pDatabase.RSVPs.ChangeList[vRevision];
1943 table.remove(vChangeList, vIndex);
1944  
1945 if table.getn(vChangeList) == 0 then
1946 pDatabase.RSVPs.ChangeList[vRevision] = nil;
1947 end
1948  
1949 -- Keep removing in case the database has become corrupted with multiple copies
1950  
1951 vRSVPString, vIndex, vRevision = EventDatabase_FindRSVPRequestString(pDatabase, pRSVP.mOrganizerName, pRSVP.mEventID);
1952 end
1953  
1954 return true; -- Everything's ok, no newer RSVP was found
1955 end
1956  
1957 function EventDatabase_FindRSVPRequestString(pDatabase, pOrganizerName, pEventID)
1958 if not pEventID then
1959 Calendar_DebugMessage("EventDatabase_FindRSVPRequestString: pEventID IS NIL!");
1960 end
1961  
1962 if not pOrganizerName then
1963 Calendar_DebugMessage("EventDatabase_FindRSVPRequestString: pOrganizerName IS NIL!");
1964 end
1965  
1966 local vRSVPPrefix = "EVT:"..pOrganizerName..","..pEventID..",";
1967 local vRSVPPrefixLength = string.len(vRSVPPrefix);
1968  
1969 return EventDatabase_FindRSVPPrefixString(pDatabase, vRSVPPrefix, vRSVPPrefixLength);
1970 end
1971  
1972 function EventDatabase_FindRSVPPrefixString(pDatabase, pPrefixString, pPrefixLength)
1973 local vRSVPs = EventDatabase_GetRSVPs(pDatabase);
1974  
1975 for vRevision, vChangeList in vRSVPs.ChangeList do
1976 local vNumChanges = table.getn(vChangeList);
1977  
1978 for vIndex = 1, vNumChanges do
1979 local vRSVP = vChangeList[vIndex];
1980  
1981 if string.sub(vRSVP, 1, pPrefixLength) == pPrefixString then
1982 return vRSVP, vIndex, vRevision;
1983 end
1984 end
1985 end
1986  
1987 return nil, nil, nil;
1988 end
1989  
1990 function EventDatabase_FindRSVPRequestData(pDatabase, pOrganizerName, pEventID)
1991 if not pEventID then
1992 Calendar_DebugMessage("EventDatabase_FindRSVPRequestData: pEventID is nil!");
1993 end
1994  
1995 if not pOrganizerName then
1996 Calendar_DebugMessage("EventDatabase_FindRSVPRequestData: pOrganizerName IS NIL!");
1997 end
1998  
1999 local vRSVPString = EventDatabase_FindRSVPRequestString(pDatabase, pOrganizerName, pEventID);
2000  
2001 if not vRSVPString then
2002 return nil;
2003 end
2004  
2005 return EventDatabase_UnpackRSVPRequest(vRSVPString, pDatabase.UserName);
2006 end
2007  
2008 function EventDatabase_FindEventRSVPString(pEvent, pAttendeeName)
2009 if not pEvent.mAttendance then
2010 return nil;
2011 end
2012  
2013 return pEvent.mAttendance[pAttendeeName];
2014 end
2015  
2016 function EventDatabase_FindEventRSVP(pEventOwner, pEvent, pAttendeeName)
2017 local vEventRSVPString = EventDatabase_FindEventRSVPString(pEvent, pAttendeeName);
2018  
2019 if not vEventRSVPString then
2020 return nil;
2021 end
2022  
2023 return EventDatabase_UnpackEventRSVP(pEventOwner, pAttendeeName, pEvent.ID, vEventRSVPString);
2024 end
2025  
2026 function EventDatabase_EventExists(pEventOwner, pEventID)
2027 local vDatabase = EventDatabase_GetDatabase(pEventOwner, false);
2028  
2029 if not vDatabase then
2030 return false;
2031 end
2032  
2033 if not EventDatabase_FindEventByID(vDatabase, pEventID) then
2034 return false;
2035 end
2036  
2037 return true;
2038 end
2039  
2040 function EventDatabase_RemoveObsoleteRSVPs(pDatabase)
2041 local vRSVPs = EventDatabase_GetRSVPs(pDatabase);
2042  
2043 for vRevision, vChangeList in vRSVPs.ChangeList do
2044 local vNumChanges = table.getn(vChangeList);
2045 local vIndex = 1;
2046  
2047 while vIndex <= vNumChanges do
2048 local vRSVP = EventDatabase_UnpackRSVPRequest(vChangeList[vIndex], pDatabase.UserName);
2049  
2050 if not EventDatabase_EventExists(vRSVP.mOrganizerName, vRSVP.mEventID) then
2051 table.remove(vChangeList, vIndex);
2052 vNumChanges = vNumChanges - 1;
2053 else
2054 vIndex = vIndex + 1;
2055 end
2056 end
2057  
2058 if vNumChanges == 0 then
2059 vRSVPs.ChangeList[vRevision] = nil;
2060 end
2061 end
2062 end
2063  
2064 function EventDatabase_GetRSVPAltsString(pRSVP)
2065 local vAltsString = nil;
2066  
2067 if not pRSVP.mAlts then
2068 return nil;
2069 end
2070  
2071 for vPlayerName, _ in pRSVP.mAlts do
2072 if not vAltsString then
2073 vAltsString = vPlayerName;
2074 else
2075 vAltsString = vAltsString..","..vPlayerName;
2076 end
2077 end
2078  
2079 return vAltsString;
2080 end
2081  
2082 function EventDatabase_PackRSVPRequest(pRSVP)
2083 local vRequest = "EVT:"..pRSVP.mOrganizerName..","..pRSVP.mEventID..","..pRSVP.mDate..","..pRSVP.mTime..","..pRSVP.mStatus..","..EventDatabase_PackCharInfo(pRSVP)..",";
2084  
2085 if pRSVP.mComment then
2086 vRequest = vRequest..pRSVP.mComment;
2087 end
2088  
2089 if pRSVP.mGuild then
2090 vRequest = vRequest..","..pRSVP.mGuild..","..pRSVP.mGuildRank;
2091 else
2092 vRequest = vRequest..",,";
2093 end
2094  
2095 return vRequest;
2096 end
2097  
2098 function EventDatabase_UnpackRSVPRequest(pRSVPString, pAttendee)
2099 local vCommands = CalendarNetwork_ParseCommandSubString("/"..pRSVPString);
2100 local vOpcode = vCommands[1].opcode;
2101  
2102 if vOpcode ~= "EVT" then
2103 return false;
2104 end
2105  
2106 local vOperands = vCommands[1].operands;
2107  
2108 return EventDatabase_UnpackRSVPFieldArray(vOperands, pAttendee);
2109 end
2110  
2111 function EventDatabase_FillInRSVPGuildInfo(pRSVP)
2112 if pRSVP.mGuild then
2113 return;
2114 end
2115  
2116 vIsInGuild, vRankIndex = CalendarNetwork_UserIsInSameGuild(pRSVP.mName);
2117  
2118 if not vIsInGuild then
2119 return;
2120 end
2121  
2122 pRSVP.mGuild = gGroupCalendar_PlayerGuild;
2123 pRSVP.mGuildRank = vRankIndex;
2124 end
2125  
2126 function EventDatabase_UnpackRSVPFieldArray(pArray, pAttendee)
2127 local vRSVP =
2128 {
2129 mOrganizerName = pArray[1],
2130 mName = pAttendee;
2131 mEventID = tonumber(pArray[2]),
2132 mDate = tonumber(pArray[3]),
2133 mTime = tonumber(pArray[4]),
2134 mStatus = pArray[5],
2135 mComment = pArray[7],
2136 mGuild = pArray[8],
2137 mGuildRank = tonumber(pArray[9]),
2138 };
2139  
2140 if vRSVP.mGuild == "" then
2141 vRSVP.mGuild = nil;
2142 end
2143  
2144 EventDatabase_UnpackCharInfo(pArray[6], vRSVP);
2145  
2146 EventDatabase_FillInRSVPGuildInfo(vRSVP);
2147  
2148 return vRSVP;
2149 end
2150  
2151 function EventDatabase_PackEventRSVP(pEventRSVP)
2152 local vEventRSVPString = ""..pEventRSVP.mDate..","..pEventRSVP.mTime..","..pEventRSVP.mStatus..","..EventDatabase_PackCharInfo(pEventRSVP)..",";
2153  
2154 if pEventRSVP.mComment then
2155 vEventRSVPString = vEventRSVPString..pEventRSVP.mComment;
2156 end
2157  
2158 if pEventRSVP.mGuild then
2159 vEventRSVPString = vEventRSVPString..","..pEventRSVP.mGuild..","..pEventRSVP.mGuildRank;
2160 else
2161 vEventRSVPString = vEventRSVPString..",,";
2162 end
2163  
2164 if pEventRSVP.mOriginalDate then
2165 vEventRSVPString = vEventRSVPString..","..pEventRSVP.mOriginalDate..","..pEventRSVP.mOriginalTime;
2166 else
2167 vEventRSVPString = vEventRSVPString..",,";
2168 end
2169  
2170 return vEventRSVPString;
2171 end
2172  
2173 function EventDatabase_UnpackEventRSVP(pOrganizerName, pAttendeeName, pEventID, pEventRSVPString)
2174 local vEventParameters = CalendarNetwork_ParseParameterString(pEventRSVPString);
2175  
2176 local vRSVPFields =
2177 {
2178 mOrganizerName = pOrganizerName,
2179 mName = pAttendeeName,
2180 mEventID = pEventID,
2181 mDate = tonumber(vEventParameters[1]),
2182 mTime = tonumber(vEventParameters[2]),
2183 mStatus = vEventParameters[3],
2184 mComment = vEventParameters[5],
2185 mGuild = vEventParameters[6],
2186 mGuildRank = tonumber(vEventParameters[7]),
2187 mOriginalDate = tonumber(vEventParameters[8]),
2188 mOriginalTime = tonumber(vEventParameters[9]),
2189 };
2190  
2191 if vRSVPFields.mGuild == "" then
2192 vRSVPFields.mGuild = nil;
2193 end
2194  
2195 EventDatabase_UnpackCharInfo(vEventParameters[4], vRSVPFields);
2196  
2197 EventDatabase_FillInRSVPGuildInfo(vRSVPFields);
2198  
2199 return vRSVPFields;
2200 end
2201  
2202 function EventDatabase_PackCharInfo(pCharInfo)
2203 local vRaceCode, vClassCode, vLevel;
2204  
2205 vRaceCode = pCharInfo.mRaceCode;
2206  
2207 if not vRaceCode then
2208 vRaceCode = "?";
2209 end
2210  
2211 vClassCode = pCharInfo.mClassCode;
2212  
2213 if not vClassCode then
2214 vClassCode = "?";
2215 end
2216  
2217 if pCharInfo.mLevel then
2218 vLevel = pCharInfo.mLevel;
2219 else
2220 vLevel = 0;
2221 end
2222  
2223 return vRaceCode..vClassCode..vLevel;
2224 end
2225  
2226 function EventDatabase_GetRaceCodeByRace(pRace)
2227 for vRaceCode, vRaceInfo in gGroupCalendar_RaceNamesByRaceCode do
2228 if pRace == vRaceInfo.name then
2229 return vRaceCode;
2230 end
2231 end
2232  
2233 return "?";
2234 end
2235  
2236 function EventDatabase_GetRaceByRaceCode(pRaceCode)
2237 local vRaceInfo = gGroupCalendar_RaceNamesByRaceCode[pRaceCode];
2238  
2239 if not vRaceInfo then
2240 return nil;
2241 end
2242  
2243 return vRaceInfo.name;
2244 end
2245  
2246 function EventDatabase_GetClassCodeByClass(pClass)
2247 for vClassCode, vClassInfo in gGroupCalendar_ClassInfoByClassCode do
2248 if pClass == vClassInfo.name then
2249 return vClassCode;
2250 end
2251 end
2252  
2253 return "?";
2254 end
2255  
2256 function EventDatabase_GetClassByClassCode(pClassCode)
2257 local vClassInfo = gGroupCalendar_ClassInfoByClassCode[pClassCode];
2258  
2259 if not vClassInfo then
2260 if pClassCode then
2261 return "Unknown ("..pClassCode..")";
2262 else
2263 return "Unknown (nil)";
2264 end
2265 else
2266 return vClassInfo.name;
2267 end
2268 end
2269  
2270 function EventDatabase_UnpackCharInfo(pString, rCharInfo)
2271 if not pString then
2272 rCharInfo.mRaceCode = "?";
2273 rCharInfo.mClassCode = "?";
2274 rCharInfo.mLevel = 0;
2275 else
2276 rCharInfo.mRaceCode = string.sub(pString, 1, 1);
2277 rCharInfo.mClassCode = string.sub(pString, 2, 2);
2278 rCharInfo.mLevel = tonumber(string.sub(pString, 3));
2279 end
2280 end
2281  
2282 function EventDatabase_IsQuestingEventType(pEventType)
2283 return pEventType ~= "Meet" and pEventType ~= "Birth";
2284 end
2285  
2286 function EventDatabase_IsResetEventType(pEventType)
2287 if not pEventType then
2288 return false;
2289 end
2290  
2291 return gGroupCalendar_EventTypes.Reset.ResetEventInfo[pEventType] ~= nil;
2292 end
2293  
2294 function EventDatabase_GetResetIconCoords(pEventType)
2295 if not pEventType then
2296 return nil;
2297 end
2298  
2299 return gGroupCalendar_EventTypes.Reset.ResetEventInfo[pEventType];
2300 end
2301  
2302 function EventDatabase_GetResetEventLargeIconPath(pEventType)
2303 if not pEventType then
2304 return nil;
2305 end
2306  
2307 local vResetEventInfo = gGroupCalendar_EventTypes.Reset.ResetEventInfo[pEventType];
2308  
2309 if vResetEventInfo.largeIcon then
2310 return vResetEventInfo.largeIcon, false;
2311 elseif vResetEventInfo.largeSysIcon then
2312 return vResetEventInfo.largeSysIcon, true;
2313 else
2314 return nil;
2315 end
2316 end
2317  
2318 function EventDatabase_IsDungeonResetEventType(pEventType)
2319 if not pEventType then
2320 return false;
2321 end
2322  
2323 local vResetEventInfo = gGroupCalendar_EventTypes.Reset.ResetEventInfo[pEventType];
2324  
2325 if not vResetEventInfo then
2326 return false;
2327 end
2328  
2329 return vResetEventInfo.isDungeon;
2330 end
2331  
2332 function EventDatabase_LookupDungeonResetEventTypeByName(pName)
2333 for vEventType, vResetEventInfo in gGroupCalendar_EventTypes.Reset.ResetEventInfo do
2334 if vResetEventInfo.isDungeon then
2335 if vResetEventInfo.name == pName then
2336 return vEventType;
2337 end
2338 end
2339 end
2340  
2341 return nil;
2342 end
2343  
2344 function EventDatabase_LookupTradeskillEventTypeByID(pID)
2345 for vEventType, vResetEventInfo in gGroupCalendar_EventTypes.Reset.ResetEventInfo do
2346 if vResetEventInfo.isTradeskill then
2347 if vResetEventInfo.id == pID then
2348 return vEventType;
2349 end
2350 end
2351 end
2352  
2353 return nil;
2354 end
2355  
2356 function EventDatabase_EventTypeUsesAttendance(pEventType)
2357 if pEventType == "Birth"
2358 or EventDatabase_IsResetEventType(pEventType) then
2359 return false;
2360 end
2361  
2362 return true;
2363 end
2364  
2365 function EventDatabase_EventTypeUsesLevelLimits(pEventType)
2366 if pEventType == "Birth"
2367 or pEventType == "Meet"
2368 or EventDatabase_IsResetEventType(pEventType) then
2369 return false;
2370 end
2371  
2372 return true;
2373 end
2374  
2375 function EventDatabase_EventTypeUsesTime(pEventType)
2376 if pEventType == "Birth" then
2377 return false;
2378 end
2379  
2380 return true;
2381 end
2382  
2383 function CalendarChanges_New(pID)
2384 local vID;
2385  
2386 if pID then
2387 vID = pID;
2388 else
2389 vID = Calendar_GetCurrentDateTimeUT60();
2390 end
2391  
2392 return
2393 {
2394 ID = vID,
2395 Revision = 0,
2396 AuthRevision = 0,
2397 ChangeList = {},
2398 };
2399 end
2400  
2401 function CalendarChanges_Rebuild(pChanges)
2402 pChanges.ID = Calendar_GetCurrentDateTimeUT60();
2403  
2404 pChanges.Revision = 0;
2405 pChanges.AuthRevision = 0;
2406 pChanges.ChangeList = {};
2407 end
2408  
2409 function CalendarChanges_Compact(pChanges)
2410 local vNewChanges = nil;
2411  
2412 for vRevision, vChanges in pChanges.ChangeList do
2413 for vIndex, vChange in vChanges do
2414 if not vNewChanges then
2415 vNewChanges = {};
2416 end
2417  
2418 if type(vIndex) == "number" then
2419 table.insert(vNewChanges, vChange);
2420 end
2421 end
2422 end
2423  
2424 pChanges.ID = Calendar_GetCurrentDateTimeUT60();
2425 pChanges.ChangeList = {};
2426  
2427 if vNewChanges then
2428 pChanges.Revision = 1;
2429 pChanges.AuthRevision = 0;
2430 pChanges.ChangeList[1] = vNewChanges;
2431 else
2432 pChanges.Revision = 0;
2433 pChanges.AuthRevision = 0;
2434 end
2435 end
2436  
2437 function CalendarChanges_Open(pChanges, pRevision)
2438 local vChangeList = pChanges.ChangeList[pRevision];
2439  
2440 if not vChangeList then
2441 vChangeList = {};
2442 pChanges.ChangeList[pRevision] = vChangeList;
2443 pChanges.Revision = pRevision;
2444 end
2445  
2446 vChangeList.IsOpen = true;
2447  
2448 return vChangeList;
2449 end
2450  
2451 function CalendarChanges_Close(pChanges, pRevision)
2452 local vChangeList = pChanges.ChangeList[pRevision];
2453  
2454 if not vChangeList then
2455 return;
2456 end
2457  
2458 vChangeList.IsOpen = nil;
2459 end
2460  
2461 function CalendarChanges_SetChangeList(pChanges, pRevision, pChangeList)
2462 pChanges.ChangeList[pRevision] = pChangeList;
2463  
2464 if pRevision > pChanges.Revision then
2465 pChanges.Revision = pRevision;
2466 end
2467 end
2468  
2469 function CalendarChanges_GetCurrentChangeList(pChanges)
2470 local vChangeList = nil;
2471 local vRevisionChanged = false;
2472  
2473 if pChanges.Revision > 0 then
2474 vChangeList = pChanges.ChangeList[pChanges.Revision];
2475 end
2476  
2477 if vChangeList == nil
2478 or not vChangeList.IsOpen then
2479 pChanges.Revision = pChanges.Revision + 1;
2480  
2481 vChangeList = {};
2482 pChanges.ChangeList[pChanges.Revision] = vChangeList;
2483  
2484 vChangeList.IsOpen = true;
2485  
2486 vRevisionChanged = true;
2487 end
2488  
2489 return vChangeList, vRevisionChanged;
2490 end
2491  
2492 function CalendarChanges_LockdownCurrentChangeList(pChanges)
2493 -- Just return if there are no changes yet
2494  
2495 if pChanges.Revision == 0 then
2496 return;
2497 end
2498  
2499 -- See if the current list exists and is open
2500  
2501 local vChangeList = pChanges.ChangeList[pChanges.Revision];
2502  
2503 if not vChangeList
2504 or not vChangeList.IsOpen then
2505 return;
2506 end
2507  
2508 -- Close the change list
2509  
2510 vChangeList.IsOpen = nil;
2511 end
2512  
2513 function CalendarChanges_GetRevisionPath(pLabel, pUserName, pDatabaseID, pRevision, pAuthRevision)
2514 local vPath = pLabel..":"..pUserName..",";
2515  
2516 if pDatabaseID then
2517 vPath = vPath..pDatabaseID;
2518 end
2519  
2520 vPath = vPath..","..pRevision;
2521  
2522 if pAuthRevision then
2523 vPath = vPath..","..pAuthRevision;
2524 end
2525  
2526 return vPath.."/";
2527 end
2528  
2529 function CalendarChanges_IsEmpty(pChanges)
2530 if not pChanges
2531 or pChanges.Revision == 0 then
2532 return true;
2533 end
2534  
2535 for vRevision, vChanges in pChanges.ChangeList do
2536 return false;
2537 end
2538  
2539 return true;
2540 end
2541  
2542 function CalendarChanges_GetChangeList(pChanges, pRevision)
2543 return pChanges.ChangeList[pRevision];
2544 end
2545  
2546 function CalendarAttendanceList_New()
2547 return
2548 {
2549 NumCategories = 0,
2550 NumPlayers = 0,
2551 NumAttendees = 0,
2552 Categories = {},
2553 SortedCategories = {},
2554 ClassTotals = {},
2555 Items = {},
2556 };
2557 end
2558  
2559 function CalendarAttendanceList_RemoveCategory(pAttendanceList, pCategoryID)
2560 local vClassInfo = pAttendanceList.Categories[pCategoryID];
2561  
2562 if not vClassInfo then
2563 return false;
2564 end
2565  
2566 pAttendanceList.NumPlayers = pAttendanceList.NumPlayers - table.getn(vClassInfo.mAttendees);
2567 pAttendanceList.NumCategories = pAttendanceList.NumCategories - 1;
2568  
2569 -- Remove it from the sorted categories
2570  
2571 for vIndex, vCategoryID in pAttendanceList.SortedCategories do
2572 if vCategoryID == pCategoryID then
2573 table.remove(pAttendanceList.SortedCategories, vIndex);
2574 end
2575 end
2576  
2577 pAttendanceList.Categories[pCategoryID] = nil;
2578 return true;
2579 end
2580  
2581 function CalendarAttendanceList_AddItem(pAttendanceList, pCategoryID, pItem)
2582 if not pItem then
2583 Calendar_ErrorMessage("CalendarAttendanceList_AddItem: pItem is nil");
2584 return;
2585 end
2586  
2587 if not pCategoryID then
2588 Calendar_ErrorMessage("CalendarAttendanceList_AddItem: pCategoryID is nil");
2589 return;
2590 end
2591  
2592 local vClassInfo = pAttendanceList.Categories[pCategoryID];
2593  
2594 if not vClassInfo then
2595 vClassInfo = {mCount = 1, mClassCode = pCategoryID, mAttendees = {}};
2596 pAttendanceList.Categories[pCategoryID] = vClassInfo;
2597 table.insert(pAttendanceList.SortedCategories, pCategoryID);
2598  
2599 pAttendanceList.NumCategories = pAttendanceList.NumCategories + 1;
2600 else
2601 vClassInfo.mCount = vClassInfo.mCount + 1;
2602 end
2603  
2604 pAttendanceList.NumPlayers = pAttendanceList.NumPlayers + 1;
2605  
2606 table.insert(vClassInfo.mAttendees, pItem);
2607 end
2608  
2609 function CalendarAttendanceList_AddPlayer(pAttendanceList, pPlayer)
2610 return CalendarAttendanceList_AddItem(pAttendanceList, pPlayer.mClassCode, pPlayer);
2611 end
2612  
2613 function CalendarAttendanceList_AddWhisper(pAttendanceList, pPlayerName, pWhispers)
2614 local vPlayer =
2615 {
2616 mName = pPlayerName,
2617 mWhispers = pWhispers.mWhispers,
2618 };
2619  
2620 local vGuildMemberIndex = CalendarNetwork_GetGuildMemberIndex(pPlayerName);
2621  
2622 if vGuildMemberIndex then
2623 local vMemberName, vRank, vRankIndex,
2624 vLevel, vClass, vZone, vNote,
2625 vOfficerNote, vOnline = GetGuildRosterInfo(vGuildMemberIndex);
2626  
2627 vPlayer.mLevel = vLevel;
2628 vPlayer.mClassCode = EventDatabase_GetClassCodeByClass(vClass);
2629 vPlayer.mZone = vZone;
2630 vPlayer.mOnline = vOnline;
2631 end
2632  
2633 vPlayer.mDate = pWhispers.mDate;
2634 vPlayer.mTime = pWhispers.mTime;
2635 vPlayer.mType = "Whisper";
2636  
2637 return CalendarAttendanceList_AddItem(
2638 pAttendanceList,
2639 "WHISPERS",
2640 vPlayer);
2641 end
2642  
2643 function CalendarAttendanceList_AddEventAttendanceItems(pAttendanceList, pDatabase, pEvent)
2644 if not pEvent.mAttendance then
2645 return;
2646 end
2647  
2648 for vAttendeeName, vRSVPString in pEvent.mAttendance do
2649 local vRSVP = EventDatabase_UnpackEventRSVP(pDatabase.UserName, vAttendeeName, pEvent.mID, vRSVPString);
2650  
2651 pAttendanceList.Items[vRSVP.mName] = vRSVP;
2652 end
2653 end
2654  
2655 function CalendarAttendanceList_AddPendingRequests(pAttendanceList, pDatabase, pEvent)
2656 for vRealmUser, vDatabase in gGroupCalendar_Database.Databases do
2657 if EventDatabase_DatabaseIsVisible(vDatabase) then
2658 local vPendingRSVP = EventDatabase_FindRSVPRequestData(vDatabase, pDatabase.UserName, pEvent.mID);
2659  
2660 if vPendingRSVP then
2661 if vPendingRSVP.mStatus == "Y" then
2662 vPendingRSVP.mStatus = "P";
2663 end
2664  
2665 CalendarAttendanceList_AddItem(pAttendanceList, "PENDING", vPendingRSVP);
2666 end
2667 end
2668 end
2669 end
2670  
2671 function CalendarAttendanceList_CalculateClassTotals(pAttendanceList, pIsAttendingFunction)
2672 pAttendanceList.ClassTotals = {};
2673  
2674 for vName, vItem in pAttendanceList.Items do
2675 if vItem.mClassCode
2676 and pIsAttendingFunction(vItem) then
2677 local vTotal = pAttendanceList.ClassTotals[vItem.mClassCode];
2678  
2679 if not vTotal then
2680 pAttendanceList.ClassTotals[vItem.mClassCode] = 1;
2681 else
2682 pAttendanceList.ClassTotals[vItem.mClassCode] = vTotal + 1;
2683 end
2684 end
2685 end
2686 end
2687  
2688 function CalendarAttendanceList_RSVPIsAttending(pItem)
2689 return pItem.mStatus ~= "-"
2690 and pItem.mStatus ~= "N"
2691 and pItem.mStatus ~= "C";
2692 end
2693  
2694 function CalendarAttendanceList_FindItem(pAttendanceList, pFieldName, pFieldValue, pCategoryID)
2695 if not pAttendanceList then
2696 return nil;
2697 end
2698  
2699 if not pFieldValue then
2700 Calendar_DebugMessage("CalendarAttendanceList_FindItem: pFieldValue is nil for "..pFieldName);
2701 return nil;
2702 end
2703  
2704 local vLowerFieldValue = strlower(pFieldValue);
2705  
2706 -- Search all categories if none is specified
2707  
2708 if not pCategoryID then
2709 for vCategoryID, vCategoryInfo in pAttendanceList.Categories do
2710 for vIndex, vItem in vCategoryInfo.mAttendees do
2711 local vItemFieldValue = vItem[pFieldName];
2712  
2713 if vItemFieldValue
2714 and strlower(vItemFieldValue) == vLowerFieldValue then
2715 return vItem;
2716 end
2717 end
2718 end
2719  
2720 -- Search the specified category
2721  
2722 else
2723 local vCategoryInfo = pAttendanceList.Categories[pCategoryID];
2724  
2725 if not vCategoryInfo then
2726 return nil;
2727 end
2728  
2729 for vIndex, vItem in vCategoryInfo.mAttendees do
2730 if strlower(vItem[pFieldName]) == vLowerFieldValue then
2731 return vItem;
2732 end
2733 end
2734 end
2735  
2736 return nil;
2737 end
2738  
2739 GroupCalendar_cSortByFlags =
2740 {
2741 Date = {Class = true, Rank = false},
2742 Rank = {Class = true, Rank = true},
2743 Name = {Class = false, Rank = false, Name = true},
2744  
2745 Status = {Class = true, Rank = false},
2746 ClassRank = {Class = true, Rank = true},
2747 DateRank = {Class = true, Rank = true},
2748 };
2749  
2750 function CalendarAttendanceList_SortIntoCategories(pAttendanceList, pGetItemCategoryFunction)
2751 -- Clear the existing categories
2752  
2753 pAttendanceList.Categories = {};
2754 pAttendanceList.SortedCategories = {};
2755  
2756 --
2757  
2758 local vTotalAttendees = 0;
2759  
2760 for vName, vItem in pAttendanceList.Items do
2761 local vCategoryID = pGetItemCategoryFunction(vItem);
2762  
2763 if vCategoryID then
2764 if vCategoryID ~= "NO" then
2765 vTotalAttendees = vTotalAttendees + 1;
2766 end
2767  
2768 CalendarAttendanceList_AddItem(pAttendanceList, pGetItemCategoryFunction(vItem), vItem);
2769 end
2770 end
2771  
2772 pAttendanceList.NumAttendees = vTotalAttendees;
2773 end
2774  
2775 function CalendarAttendanceList_GetRSVPStatusCategory(pItem)
2776 -- Skip RSVPs which are supposed to be ignored
2777  
2778 if pItem.mStatus == "-"
2779 or pItem.mStatus == "C" then
2780 return nil;
2781 end
2782  
2783 if pItem.mStatus == "N" then
2784 return "NO";
2785 elseif pItem.mStatus == "S" then
2786 return "STANDBY";
2787 elseif pItem.mStatus == "Q" then
2788 return "QUEUED";
2789 else
2790 return "YES";
2791 end
2792 end
2793  
2794 function CalendarAttendanceList_GetRSVPClassCategory(pItem)
2795 local vCategoryID = CalendarAttendanceList_GetRSVPStatusCategory(pItem);
2796  
2797 if not vCategoryID then
2798 return nil;
2799 end
2800  
2801 if vCategoryID ~= "YES" then
2802 return vCategoryID;
2803 end
2804  
2805 if pItem.mClassCode then
2806 return pItem.mClassCode;
2807 end
2808  
2809 return "?";
2810 end
2811  
2812 function CalendarAttendanceList_GetRSVPRankCategory(pItem)
2813 local vCategoryID = CalendarAttendanceList_GetRSVPStatusCategory(pItem);
2814  
2815 if not vCategoryID then
2816 return nil;
2817 end
2818  
2819 if vCategoryID ~= "YES" then
2820 return vCategoryID;
2821 end
2822  
2823 vCategoryID = EventDatabase_MapGuildRank(pItem.mGuild, pItem.mGuildRank);
2824  
2825 if vCategoryID then
2826 return vCategoryID;
2827 end
2828  
2829 return "?";
2830 end
2831  
2832 function CalendarEvent_GetAttendanceCounts(pDatabase, pEvent, pUseClassCategories)
2833 local vAttendanceList = CalendarAttendanceList_New();
2834  
2835 -- Fill in the items list
2836  
2837 CalendarAttendanceList_AddEventAttendanceItems(vAttendanceList, pDatabase, pEvent);
2838  
2839 -- Sort into categories
2840  
2841 local vGetItemCategoryFunction;
2842  
2843 if pUseClassCategories then
2844 vGetItemCategoryFunction = CalendarAttendanceList_GetRSVPClassCategory;
2845 else
2846 vGetItemCategoryFunction = CalendarAttendanceList_GetRSVPStatusCategory;
2847 end
2848  
2849 CalendarAttendanceList_SortIntoCategories(vAttendanceList, vGetItemCategoryFunction);
2850  
2851 -- Add pending requests
2852  
2853 CalendarAttendanceList_AddPendingRequests(vAttendanceList, pDatabase, pEvent);
2854  
2855 -- Calculate class totals
2856  
2857 CalendarAttendanceList_CalculateClassTotals(vAttendanceList, CalendarAttendanceList_RSVPIsAttending);
2858  
2859 -- Done
2860  
2861 return vAttendanceList;
2862 end
2863  
2864 function CalendarEvent_SortAttendanceCounts(pAttendanceCounts, pSortBy)
2865 local vSortByClass, vSortByRank, vSortByName;
2866  
2867 if pSortBy then
2868 local vSortByFlags = GroupCalendar_cSortByFlags[pSortBy];
2869  
2870 if not vSortByFlags then
2871 Calendar_ErrorMessage("CalendarEvent_SortAttendanceCounts: Unknown sort "..pSortBy);
2872 return;
2873 end
2874  
2875 vSortByClass = vSortByFlags.Class;
2876 vSortByRank = vSortByFlags.Rank;
2877 vSortByName = vSortByFlags.Name;
2878 else
2879 vSortByClass = EventDatabase_IsQuestingEventType(pEvent.mType);
2880 vSortByRank = false;
2881 vSortByName = false;
2882 end
2883  
2884 -- Sort the categories
2885  
2886 if vSortByClass then
2887 table.sort(pAttendanceCounts.SortedCategories, EventDatabase_CompareClassCodes);
2888 else
2889 table.sort(pAttendanceCounts.SortedCategories, EventDatabase_CompareRankCodes);
2890 end
2891  
2892 -- Sort the attendance within each category
2893  
2894 local vCompareFunction;
2895  
2896 if vSortByName then
2897 vCompareFunction = EventDatabase_CompareRSVPsByName;
2898 elseif vSortByClass and vSortByRank then
2899 vCompareFunction = EventDatabase_CompareRSVPsByRankAndDate;
2900 else
2901 vCompareFunction = EventDatabase_CompareRSVPsByDate;
2902 end
2903  
2904 if vSortByClass and vSortByRank then
2905 end
2906  
2907 for vCategory, vClassInfo in pAttendanceCounts.Categories do
2908 table.sort(vClassInfo.mAttendees, vCompareFunction);
2909 end
2910 end
2911  
2912 function EventAvailableSlots_InitializeFromLimits(pLimits)
2913 local vAvailableSlots = {ClassInfo = {}};
2914 local vMinTotal = 0;
2915  
2916 for vClassCode, vClassCodeInfo in gGroupCalendar_ClassInfoByClassCode do
2917 local vClassAvailable = nil;
2918 local vClassExtrasAvailable = nil;
2919  
2920 if pLimits
2921 and pLimits.mClassLimits then
2922 local vClassLimits = pLimits.mClassLimits[vClassCode];
2923  
2924 if vClassLimits then
2925 if vClassLimits.mMin then
2926 vClassAvailable = vClassLimits.mMin;
2927  
2928 if vClassAvailable < 0 then
2929 vClassAvailable = 0;
2930 end
2931  
2932 vMinTotal = vMinTotal + vClassLimits.mMin;
2933 end
2934  
2935 if vClassLimits.mMax then
2936 vClassExtrasAvailable = vClassLimits.mMax;
2937  
2938 if vClassAvailable then
2939 vClassExtrasAvailable = vClassExtrasAvailable - vClassAvailable;
2940 end
2941  
2942 if vClassExtrasAvailable < 0 then
2943 vClassExtrasAvailable = 0;
2944 end
2945 end
2946  
2947 vClassMax = vClassLimits.mMax;
2948 end
2949 end
2950  
2951 if vClassAvailable
2952 or vClassExtrasAvailable then
2953 vAvailableSlots.ClassInfo[vClassCode] = {};
2954 vAvailableSlots.ClassInfo[vClassCode].Available = vClassAvailable;
2955 vAvailableSlots.ClassInfo[vClassCode].ExtrasAvailable = vClassExtrasAvailable;
2956 end
2957 end
2958  
2959 if pLimits
2960 and pLimits.mMaxAttendance then
2961 vAvailableSlots.TotalExtras = pLimits.mMaxAttendance - vMinTotal;
2962 if vAvailableSlots.TotalExtras < 0 then
2963 vAvailableSlots.TotalExtras = 0;
2964 end
2965 else
2966 vAvailableSlots.TotalExtras = nil;
2967 end
2968  
2969 return vAvailableSlots;
2970 end
2971  
2972 function CalendarEvent_PlayerClassAdded(pAvailableSlots, pClassCode)
2973 CalendarEvent_PlayerClassMultiAdded(pAvailableSlots, pClassCode, 1);
2974 end
2975  
2976 function CalendarEvent_PlayerClassMultiAdded(pAvailableSlots, pClassCode, pNumAdded)
2977 local vNumAdded = pNumAdded;
2978 local vClassInfo = pAvailableSlots.ClassInfo[pClassCode];
2979  
2980 if vNumAdded == 0 then
2981 return;
2982 end
2983  
2984 -- If the class hasn't reached its minimum yet then accept the request
2985  
2986 if vClassInfo
2987 and vClassInfo.Available
2988 and vClassInfo.Available > 0 then
2989 local vAvailableUsed = vNumAdded;
2990  
2991 if vAvailableUsed > vClassInfo.Available then
2992 vAvailableUsed = vClassInfo.Available;
2993 end
2994  
2995 vClassInfo.Available = vClassInfo.Available - vAvailableUsed;
2996 vNumAdded = vNumAdded - vAvailableUsed;
2997  
2998 if vNumAdded == 0 then
2999 return;
3000 end
3001 end
3002  
3003 -- Nothing to do if there are no limits
3004  
3005 if not pAvailableSlots.TotalExtras then
3006 return;
3007 end
3008  
3009 -- Have to bail if total extras is now zero
3010  
3011 if pAvailableSlots.TotalExtras == 0 then
3012 return;
3013 end
3014  
3015 -- Decrease the space for the class
3016  
3017 if vClassInfo
3018 and vClassInfo.ExtrasAvailable
3019 and vClassInfo.ExtrasAvailable > 0 then
3020 if vNumAdded > vClassInfo.ExtrasAvailable then
3021 vNumAdded = vClassInfo.ExtrasAvailable;
3022 end
3023  
3024 vClassInfo.ExtrasAvailable = vClassInfo.ExtrasAvailable - vNumAdded;
3025 end
3026  
3027 -- Decrease the total available extras
3028  
3029 pAvailableSlots.TotalExtras = pAvailableSlots.TotalExtras - vNumAdded;
3030 end
3031  
3032 function EventAvailableSlots_AcceptPlayer(pAvailableSlots, pClassCode)
3033 local vClassInfo = pAvailableSlots.ClassInfo[pClassCode];
3034  
3035 -- If the class hasn't reached its minimum yet then accept the request
3036  
3037 if vClassInfo
3038 and vClassInfo.Available
3039 and vClassInfo.Available > 0 then
3040 vClassInfo.Available = vClassInfo.Available - 1;
3041 return true;
3042 end
3043  
3044 -- If the minimum has been reached and the extra slots haven't all
3045 -- been filled then accept the request
3046  
3047 if pAvailableSlots.TotalExtras then
3048 -- Put them on standby if the extras slots have all been filled
3049  
3050 if pAvailableSlots.TotalExtras == 0 then
3051 return false;
3052 end
3053  
3054 -- If the class has a max and it's been reached then put them
3055 -- on standby
3056  
3057 if vClassInfo
3058 and vClassInfo.ExtrasAvailable then
3059 if vClassInfo.ExtrasAvailable == 0 then
3060 return false;
3061 end
3062  
3063 vClassInfo.ExtrasAvailable = vClassInfo.ExtrasAvailable - 1;
3064 end
3065  
3066 pAvailableSlots.TotalExtras = pAvailableSlots.TotalExtras - 1;
3067 return true;
3068 end
3069  
3070 -- No limits at all, just accept them
3071  
3072 return true;
3073 end
3074  
3075 function EventAvailableSlots_CountSlots(pDatabase, pEvent)
3076 local vAvailableSlots = EventAvailableSlots_InitializeFromLimits(pEvent.mLimits);
3077 local vAttendanceCounts = CalendarEvent_GetAttendanceCounts(pDatabase, pEvent, true);
3078  
3079 for vCategory, vClassInfo in vAttendanceCounts.Categories do
3080 if vCategory ~= "NO"
3081 and vCategory ~= "STANDBY" then
3082 CalendarEvent_PlayerClassMultiAdded(vAvailableSlots, vCategory, table.getn(vClassInfo.mAttendees));
3083 end
3084 end
3085  
3086 return vAvailableSlots;
3087 end
3088  
3089 function EventDatabase_CompareRSVPsByDate(pRSVP1, pRSVP2)
3090 local vRSVP1Date, vRSVP1Time = EventDatabase_GetRSVPOriginalDateTime(pRSVP1);
3091 local vRSVP2Date, vRSVP2Time = EventDatabase_GetRSVPOriginalDateTime(pRSVP2);
3092  
3093 if not vRSVP1Date then
3094 return false;
3095 elseif not vRSVP2Date then
3096 return true;
3097 end
3098  
3099 if vRSVP1Date < vRSVP2Date then
3100 return true;
3101 elseif vRSVP1Date > vRSVP2Date then
3102 return false;
3103 elseif vRSVP1Time ~= vRSVP2Time then
3104 return vRSVP1Time < vRSVP2Time;
3105 else
3106 return EventDatabase_CompareRSVPsByName(pRSVP1, pRSVP2);
3107 end
3108 end
3109  
3110 function EventDatabase_CompareRSVPsByName(pRSVP1, pRSVP2)
3111 return pRSVP1.mName < pRSVP2.mName;
3112 end
3113  
3114 function EventDatabase_CompareRSVPsByRankAndDate(pRSVP1, pRSVP2)
3115 local vRank1 = EventDatabase_MapGuildRank(pRSVP1.mGuild, pRSVP1.mGuildRank);
3116 local vRank2 = EventDatabase_MapGuildRank(pRSVP2.mGuild, pRSVP2.mGuildRank);
3117  
3118 if not vRank1 then
3119 if not vRank2 then
3120 return EventDatabase_CompareRSVPsByDate(pRSVP1, pRSVP2);
3121 else
3122 return false;
3123 end
3124 elseif not vRank2 then
3125 return true;
3126 end
3127  
3128 if vRank1 == vRank2 then
3129 return EventDatabase_CompareRSVPsByDate(pRSVP1, pRSVP2);
3130 else
3131 return vRank1 < vRank2;
3132 end
3133 end
3134  
3135 Calendar_cClassCodeSortOrder =
3136 {
3137 WHISPERS = 4,
3138 PENDING = 3,
3139 QUEUED = 2,
3140 YES = 1,
3141 STANDBY = -1,
3142 NO = -2,
3143 };
3144  
3145 function EventDatabase_CompareClassCodes(pClassCode1, pClassCode2)
3146 local vSortPriority1 = Calendar_cClassCodeSortOrder[pClassCode1];
3147 local vSortPriority2 = Calendar_cClassCodeSortOrder[pClassCode2];
3148  
3149 if not vSortPriority1 then
3150 if not vSortPriority2 then
3151 return EventDatabase_GetClassByClassCode(pClassCode1) < EventDatabase_GetClassByClassCode(pClassCode2);
3152 else
3153 return vSortPriority2 < 0;
3154 end
3155 elseif not vSortPriority2 then
3156 return vSortPriority1 > 0;
3157 else
3158 return vSortPriority1 > vSortPriority2;
3159 end
3160 end
3161  
3162 function EventDatabase_CompareRankCodes(pRank1, pRank2)
3163 local vIsRank1 = type(pRank1) == "number";
3164 local vIsRank2 = type(pRank2) == "number";
3165  
3166 if vIsRank1 and vIsRank2 then
3167 return pRank1 < pRank2;
3168 end
3169  
3170 if not vIsRank1 then
3171 if not vIsRank2 then
3172 return EventDatabase_CompareClassCodes(pRank1, pRank2);
3173 else
3174 return false;
3175 end
3176 end
3177  
3178 return true;
3179 end
3180  
3181 function EventDatabase_CreatePlayerRSVP(
3182 pDatabase, pEvent,
3183 pPlayerName,
3184 pPlayerRace, pPlayerClass, pPlayeLevel,
3185 pStatus,
3186 pComment,
3187 pGuild,
3188 pGuildRank,
3189 pAlts)
3190 local vDate, vTime60 = EventDatabase_GetServerDateTime60Stamp();
3191 local vAlts = nil;
3192  
3193 if pAlts then
3194 for vPlayerName, _ in pAlts do
3195 if vPlayerName ~= pPlayerName then
3196 if not vAlts then
3197 vAlts = {};
3198 end
3199  
3200 vAlts[vPlayerName] = true;
3201 end
3202 end
3203 end
3204  
3205 return
3206 {
3207 mName = pPlayerName,
3208 mOrganizerName = pDatabase.UserName,
3209 mEventID = pEvent.mID,
3210 mDate = vDate,
3211 mTime = vTime60,
3212 mStatus = pStatus,
3213 mComment = pComment,
3214 mRaceCode = pPlayerRace,
3215 mClassCode = pPlayerClass,
3216 mLevel = pPlayeLevel,
3217 mGuild = pGuild,
3218 mGuildRank = pGuildRank,
3219 mAlts = vAlts,
3220 };
3221 end
3222  
3223 function EventDatabase_PlayerLevelChanged(pPlayerLevel)
3224 if not gGroupCalendar_UserDatabase then
3225 return;
3226 end
3227  
3228 gGroupCalendar_UserDatabase.PlayerLevel = pPlayerLevel;
3229 end
3230  
3231 function EventDatabase_PlayerIsQualifiedForEvent(pEvent, pPlayerLevel)
3232 if not pPlayerLevel then
3233 return false;
3234 end
3235  
3236 if pEvent.mMinLevel and
3237 pPlayerLevel < pEvent.mMinLevel then
3238 return false;
3239 end
3240  
3241 if pEvent.mMaxLevel and
3242 pPlayerLevel > pEvent.mMaxLevel then
3243 return false;
3244 end
3245  
3246 return true;
3247 end
3248  
3249 function EventDatabase_RescheduleEvent(pDatabase, pEvent, pNewDate)
3250 local vNewEvent = EventDatabase_NewEvent(pDatabase, pNewDate);
3251  
3252 vNewEvent.mType = pEvent.mType;
3253 vNewEvent.mTitle = pEvent.mTitle;
3254  
3255 vNewEvent.mTime = pEvent.mTime;
3256 vNewEvent.mDuration = pEvent.mDuration;
3257  
3258 vNewEvent.mDescription = pEvent.mDescription;
3259  
3260 vNewEvent.mMinLevel = pEvent.mMinLevel;
3261 vNewEvent.mAttendance = pEvent.mAttendance;
3262  
3263 EventDatabase_AddEvent(pDatabase, vNewEvent);
3264  
3265 return EventDatabase_DeleteEvent(pDatabase, pEvent);
3266 end
3267  
3268 function EventDatabase_DeleteOldEvents(pDatabase)
3269 if not pDatabase.Events then
3270 return;
3271 end
3272  
3273 for vDate, vEvents in pDatabase.Events do
3274 if vDate < gGroupCalendar_MinimumEventDate then
3275 -- Remove or reschedule the events for this date
3276  
3277 local vNumEvents = table.getn(vEvents);
3278 local vEventIndex = 1;
3279  
3280 for vIndex = 1, vNumEvents do
3281 local vEvent = vEvents[vEventIndex];
3282  
3283 if pDatabase.IsPlayerOwned and vEvent.mType == "Birth" then
3284 Calendar_DebugMessage("GroupCalendar: Rescheduling birthday event "..vEvent.mID.." for "..pDatabase.UserName);
3285  
3286 local vMonth, vDay, vYear = Calendar_ConvertDateToMDY(vDate);
3287 vYear = vYear + 1;
3288 local vNewDate = Calendar_ConvertMDYToDate(vMonth, vDay, vYear);
3289  
3290 if not EventDatabase_RescheduleEvent(pDatabase, vEvent, vNewDate) then
3291 Calendar_DebugMessage("GroupCalendar: Can't reschedule event "..vEvent.mID.." for "..pDatabase.UserName..": Unknown error");
3292 vEventIndex = vEventIndex + 1;
3293 end
3294 elseif not EventDatabase_DeleteEvent(pDatabase, vEvent) then
3295 Calendar_DebugMessage("GroupCalendar: Can't delete old event "..vEvent.mID.." for "..pDatabase.UserName..": Unknown error");
3296 vEventIndex = vEventIndex + 1;
3297 end
3298 end
3299 end
3300 end
3301 end
3302  
3303 function EventDatabase_PlayerIsAttendingEvent(pEventOwner, pEvent)
3304 for vPlayerName, vPlayerValue in gGroupCalendar_PlayerCharacters do
3305 local vPlayerDatabase = EventDatabase_GetDatabase(vPlayerName, false);
3306 local vRSVP = nil;
3307  
3308 if vPlayerDatabase then
3309 vRSVP = EventDatabase_FindRSVPRequestData(vPlayerDatabase, pEventOwner, pEvent.mID);
3310 end
3311  
3312 if not vRSVP then
3313 vRSVP = EventDatabase_FindEventRSVP(pEventOwner, pEvent, vPlayerName);
3314 end
3315  
3316 if vRSVP then
3317 local vStatus1 = string.sub(vRSVP.mStatus, 1, 1);
3318  
3319 if vStatus1 == "Y" then
3320 return true;
3321 end
3322 end
3323 end
3324  
3325  
3326 return false;
3327 end
3328  
3329 function EventDatabase_RemoveSavedInstanceEvents(pDatabase)
3330 for vDate, vSchedule in pDatabase.Events do
3331 local vEventIndex = 1;
3332 local vNumEvents = table.getn(vSchedule);
3333  
3334 while vEventIndex <= vNumEvents do
3335 local vEvent = vSchedule[vEventIndex];
3336  
3337 if EventDatabase_IsDungeonResetEventType(vEvent.mType) then
3338 EventDatabase_DeleteEvent(pDatabase, vEvent);
3339 vNumEvents = vNumEvents - 1;
3340 else
3341 vEventIndex = vEventIndex + 1;
3342 end
3343 end
3344 end
3345 end
3346  
3347 function EventDatabase_RemoveTradeskillEventByType(pDatabase, pEventType)
3348 for vDate, vSchedule in pDatabase.Events do
3349 local vEventIndex = 1;
3350 local vNumEvents = table.getn(vSchedule);
3351  
3352 while vEventIndex <= vNumEvents do
3353 local vEvent = vSchedule[vEventIndex];
3354  
3355 if vEvent.mType == pEventType then
3356 EventDatabase_DeleteEvent(pDatabase, vEvent);
3357 vNumEvents = vNumEvents - 1;
3358 else
3359 vEventIndex = vEventIndex + 1;
3360 end
3361 end
3362 end
3363 end
3364  
3365 function EventDatabase_ScheduleResetEvent(pDatabase, pType, pResetDate, pResetTime)
3366 local vEvent = EventDatabase_NewEvent(pDatabase, pResetDate);
3367  
3368 vEvent.mType = pType;
3369 vEvent.mPrivate = true;
3370 vEvent.mTime = pResetTime;
3371 vEvent.mDuration = nil;
3372  
3373 EventDatabase_AddEvent(pDatabase, vEvent);
3374 end
3375  
3376 function EventDatabase_ScheduleSavedInstanceEvent(pDatabase, pName, pResetDate, pResetTime)
3377 local vType = EventDatabase_LookupDungeonResetEventTypeByName(pName);
3378  
3379 if not vType then
3380 Calendar_DebugMessage("GroupCalendar: Can't schedule reset event for "..pName..": The instance name is not recognized");
3381 return;
3382 end
3383  
3384 EventDatabase_ScheduleResetEvent(pDatabase, vType, pResetDate, pResetTime);
3385 end
3386  
3387 function EventDatabase_ScheduleTradeskillCooldownEvent(pDatabase, pTradeskillID, pCooldownSeconds)
3388 local vType = EventDatabase_LookupTradeskillEventTypeByID(pTradeskillID);
3389 local vResetDate, vResetTime = Calendar_GetServerDateTimeFromSecondsOffset(pCooldownSeconds);
3390  
3391 EventDatabase_RemoveTradeskillEventByType(pDatabase, vType);
3392 EventDatabase_ScheduleResetEvent(pDatabase, vType, vResetDate, vResetTime);
3393 end
3394  
3395 function EventDatabase_UpdateTradeskillCooldown(pDatabase, pTradeskillID)
3396 local vCooldown = Calendar_GetTradeskillCooldown(pTradeskillID);
3397  
3398 if vCooldown then
3399 EventDatabase_ScheduleTradeskillCooldownEvent(pDatabase, pTradeskillID, vCooldown)
3400 end
3401 end
3402  
3403 function EventDatabase_UpdateCurrentTradeskillCooldown()
3404 local vTradeskillName, vCurrentLevel, vMaxLevel = GetTradeSkillLine();
3405  
3406 if not vTradeskillName then
3407 return;
3408 end
3409  
3410 local vTradeskillID = Calendar_LookupTradeskillIDByName(vTradeskillName);
3411  
3412 if not vTradeskillID then
3413 return;
3414 end
3415  
3416 EventDatabase_UpdateTradeskillCooldown(gGroupCalendar_UserDatabase, vTradeskillID);
3417 end
3418  
3419 function EventDatabase_MapGuildRank(pFromGuild, pFromRank)
3420 if not pFromGuild
3421 or not pFromRank then
3422 return nil;
3423 end
3424  
3425 -- If it's the same guild then just return the rank
3426  
3427 if pFromGuild == gGroupCalendar_PlayerGuild then
3428 return pFromRank;
3429 end
3430  
3431 -- Force to zero if not in any guild
3432  
3433 if not IsInGuild() then
3434 return nil;
3435 end
3436  
3437 -- Just cover our eyes if the roster isn't loaded yet
3438  
3439 if GetNumGuildMembers() == 0 then
3440 CalendarNetwork_LoadGuildRoster();
3441 return pFromRank;
3442 end
3443  
3444 local vMaxGuildRank = GuildControlGetNumRanks() - 1;
3445  
3446 -- Get the mapping
3447  
3448 local vToRankMap;
3449  
3450 if gGroupCalendar_RealmSettings.RankMap then
3451 vToRankMap = gGroupCalendar_RealmSettings.RankMap[gGroupCalendar_PlayerGuild];
3452 end
3453  
3454 local vRankMap;
3455  
3456 if vToRankMap then
3457 vRankMap = vToRankMap[pFromGuild];
3458 end
3459  
3460 if vRankMap then
3461 local vToRank = vRankMap[pFromRank];
3462  
3463 if vToRank then
3464 return vToRank;
3465 end
3466  
3467 -- If there's not a mapping for this rank, map it to the
3468 -- same value as the next highest rank
3469  
3470 for vFromRank, vToRank in vRankMap do
3471 if vFromRank > pFromRank then
3472 return vToRank;
3473 end
3474 end
3475 end
3476  
3477 -- Do a dumb mapping which simply ensures that the rank index
3478 -- is valid for the current guild
3479  
3480 if pFromRank > vMaxGuildRank then
3481 return vMaxGuildRank;
3482 else
3483 return pFromRank;
3484 end
3485 end
3486  
3487 function EventDatabase_SetGuildRankMapping(pFromGuild, pFromRank, pToRank)
3488 if not pFromGuild
3489 or pFromGuild == ""
3490 or not pFromRank then
3491 return;
3492 end
3493  
3494 -- If it's the same guild then there's nothing to do
3495  
3496 if pFromGuild == gGroupCalendar_PlayerGuild then
3497 return;
3498 end
3499  
3500 -- Make sure the maps exist
3501  
3502 if not gGroupCalendar_RealmSettings.RankMap then
3503 gGroupCalendar_RealmSettings.RankMap = {};
3504 end
3505  
3506 -- Make sure the to guild map exists
3507  
3508 local vToGuildMap = gGroupCalendar_RealmSettings.RankMap[gGroupCalendar_PlayerGuild];
3509  
3510 if not vToGuildMap then
3511 vToGuildMap = {};
3512 gGroupCalendar_RealmSettings.RankMap[gGroupCalendar_PlayerGuild] = vToGuildMap;
3513 end
3514  
3515 -- Make sure the from guild map exists
3516  
3517 local vGuildMap = vToGuildMap[pFromGuild];
3518  
3519 if not vGuildMap then
3520 vGuildMap = {};
3521 vToGuildMap[pFromGuild] = vGuildMap;
3522 end
3523  
3524 vGuildMap[pFromRank] = pToRank;
3525 end
3526  
3527 function EventDatabase_UpdateGuildRankCache()
3528 if not gGroupCalendar_PlayerGuild
3529 or not gGroupCalendar_Initialized then
3530 return;
3531 end
3532  
3533 if not gGroupCalendar_RealmSettings.GuildRanks then
3534 gGroupCalendar_RealmSettings.GuildRanks = {};
3535 end
3536  
3537 vGuildRanks = {};
3538  
3539 local vNumRanks = GuildControlGetNumRanks();
3540  
3541 for vIndex = 1, vNumRanks do
3542 vGuildRanks[vIndex - 1] = GuildControlGetRankName(vIndex);
3543 end
3544  
3545 gGroupCalendar_RealmSettings.GuildRanks[gGroupCalendar_PlayerGuild] = vGuildRanks;
3546 end