* implement (INBOX) thing
[citadel.git] / webcit / roomlist.c
1 /*
2  * $Id: roomlist.c 7751 2009-08-28 21:13:28Z dothebart $
3  * room listings and filters.
4  */
5
6 #include "webcit.h"
7 #include "webserver.h"
8 #include "roomops.h"
9
10 void DeleteFloor(void *vFloor)
11 {
12         floor *Floor;
13         Floor = (floor*) vFloor;
14         FreeStrBuf(&Floor->Name);
15         free(Floor);
16 }
17
18 int SortFloorsByNameOrder(const void *vfloor1, const void *vfloor2) 
19 {
20         floor *f1 = (floor*) GetSearchPayload(vfloor1);
21         floor *f2 = (floor*) GetSearchPayload(vfloor2);
22         
23         /* prefer My floor over alpabetical sort */
24         if (f1->ID == VIRTUAL_MY_FLOOR)
25                 return 1;
26         if (f2->ID == VIRTUAL_MY_FLOOR)
27                 return -1;
28
29         return strcmp(ChrPtr(f1->Name), ChrPtr(f2->Name));
30 }
31
32 HashList *GetFloorListHash(StrBuf *Target, WCTemplputParams *TP) {
33         const char *Err;
34         StrBuf *Buf;
35         HashList *floors;
36         HashPos *it;
37         floor *Floor;
38         void *vFloor;
39         const char *Pos;
40         int i;
41         wcsession *WCC = WC;
42         const char *HashKey;
43         long HKLen;
44
45
46         if (WCC->Floors != NULL)
47                 return WCC->Floors;
48         WCC->Floors = floors = NewHash(1, NULL);
49         Buf = NewStrBuf();
50
51         Floor = malloc(sizeof(floor));
52         Floor->ID = VIRTUAL_MY_FLOOR;
53         Floor->Name = NewStrBufPlain(_("My Folders"), -1);
54         Floor->NRooms = 0;
55         
56         Put(floors, IKEY(Floor->ID), Floor, DeleteFloor);
57
58         serv_puts("LFLR"); /* get floors */
59         StrBufTCP_read_line(Buf, &WC->serv_sock, 0, &Err); /* '100', we hope */
60         if (GetServerStatus(Buf, NULL) == 1) 
61         {
62                 while(StrBufTCP_read_line(Buf, &WC->serv_sock, 0, &Err), strcmp(ChrPtr(Buf), "000")) 
63                 {
64                         
65                         Pos = NULL;
66
67                         Floor = malloc(sizeof(floor));
68                         Floor->ID = StrBufExtractNext_long(Buf, &Pos, '|');
69                         Floor->Name = NewStrBufPlain(NULL, StrLength(Buf));
70                         StrBufExtract_NextToken(Floor->Name, Buf, &Pos, '|');
71                         Floor->NRooms = StrBufExtractNext_long(Buf, &Pos, '|');
72
73                         Put(floors, IKEY(Floor->ID), Floor, DeleteFloor);
74                 }
75         }
76         FreeStrBuf(&Buf);
77         
78         /* now lets pre-sort them alphabeticaly. */
79         i = 1;
80         SortByPayload(floors, SortFloorsByNameOrder);
81         it = GetNewHashPos(floors, 0);
82         while ( GetNextHashPos(floors, it, &HKLen, &HashKey, &vFloor)) 
83                 ((floor*) vFloor)->AlphaN = i++;
84         DeleteHashPos(&it);
85         SortByHashKeyStr(floors);
86
87         return floors;
88 }
89
90 void tmplput_FLOOR_ID(StrBuf *Target, WCTemplputParams *TP) 
91 {
92         floor *Floor = (floor *)(TP->Context);
93
94         StrBufAppendPrintf(Target, "%d", Floor->ID);
95 }
96
97 void tmplput_FLOOR_NAME(StrBuf *Target, WCTemplputParams *TP) 
98 {
99         floor *Floor = (floor *)(TP->Context);
100
101         StrBufAppendTemplate(Target, TP, Floor->Name, 0);
102 }
103
104 void tmplput_FLOOR_NROOMS(StrBuf *Target, WCTemplputParams *TP) 
105 {
106         floor *Floor = (floor *)(TP->Context);
107
108         StrBufAppendPrintf(Target, "%d", Floor->NRooms);
109 }
110 HashList *GetRoomListHashLKRA(StrBuf *Target, WCTemplputParams *TP) 
111 {
112         wcsession *WCC = WC;
113
114         if (WCC->Floors == NULL)
115                 GetFloorListHash(Target, TP);
116         serv_puts("LKRA");
117         return GetRoomListHash(Target, TP);
118 }
119
120 void DeleteFolder(void *vFolder)
121 {
122         int i;
123         folder *room;
124         room = (folder*) vFolder;
125
126         FreeStrBuf(&room->name);
127         ////FreeStrBuf(&room->ACL);
128
129         //// FreeStrBuf(&room->room);
130
131         if (room->RoomNameParts != NULL)
132         {
133                 for (i=0; i < room->nRoomNameParts; i++)
134                         FreeStrBuf(&room->RoomNameParts[i]);
135                 free(room->RoomNameParts);
136         }
137         free(room);
138 }
139
140
141 HashList *GetRoomListHash(StrBuf *Target, WCTemplputParams *TP) 
142 {
143         HashList *rooms;
144         folder *room;
145         StrBuf *Buf;
146         const char *Pos;
147         const char *Err;
148         void *vFloor;
149         wcsession *WCC = WC;
150         CompareFunc SortIt;
151         WCTemplputParams SubTP;
152
153         Buf = NewStrBuf();
154         rooms = NewHash(1, NULL);
155         StrBufTCP_read_line(Buf, &WC->serv_sock, 0, &Err);
156         if (GetServerStatus(Buf, NULL) == 1) 
157         {
158                 while(StrBufTCP_read_line(Buf, &WC->serv_sock, 0, &Err), 
159                       strcmp(ChrPtr(Buf), "000")) 
160                 {
161
162                         Pos = NULL;
163                         room = (folder*) malloc (sizeof(folder));
164                         memset(room, 0, sizeof(folder));
165
166                         /* Load the base data from the server reply */
167                         room->name = NewStrBufPlain(NULL, StrLength(Buf));
168                         StrBufExtract_NextToken(room->name, Buf, &Pos, '|');
169
170                         room->QRFlags = StrBufExtractNext_long(Buf, &Pos, '|');
171                         room->floorid = StrBufExtractNext_long(Buf, &Pos, '|');
172                         room->listorder = StrBufExtractNext_long(Buf, &Pos, '|');
173                         room->QRFlags2 = StrBufExtractNext_long(Buf, &Pos, '|');
174
175                         room->RAFlags = StrBufExtractNext_long(Buf, &Pos, '|');
176
177 /*
178  ACWHUT?
179                         room->ACL = NewStrBufPlain(NULL, StrLength(Buf));
180                         StrBufExtract_NextToken(room->ACL, Buf, &Pos, '|');
181 */
182
183                         room->view = StrBufExtractNext_long(Buf, &Pos, '|');
184                         room->defview = StrBufExtractNext_long(Buf, &Pos, '|');
185                         room->lastchange = StrBufExtractNext_long(Buf, &Pos, '|');
186
187                         /* Evaluate the Server sent data for later use */
188                         /* find out, whether we are in a sub-room */
189                         room->nRoomNameParts = StrBufNum_tokens(room->name, '\\');
190                         if (room->nRoomNameParts > 1)
191                         {
192                                 int i;
193
194                                 Pos = NULL;
195                                 room->RoomNameParts = malloc(sizeof(StrBuf*) * (room->nRoomNameParts + 1));
196                                 memset(room->RoomNameParts, 0, sizeof(StrBuf*) * (room->nRoomNameParts + 1));
197                                 for (i=0; i < room->nRoomNameParts; i++)
198                                 {
199                                         room->RoomNameParts[i] = NewStrBuf();
200                                         StrBufExtract_NextToken(room->RoomNameParts[i],
201                                                                 room->name, &Pos, '\\');
202                                 }
203                         }
204
205                         /* Private mailboxes on the main floor get remapped to the personal folder */
206                         if ((room->QRFlags & QR_MAILBOX) && 
207                             (room->floorid == 0))
208                         {
209                                 room->floorid = VIRTUAL_MY_FLOOR;
210                                 if ((room->nRoomNameParts == 1) && 
211                                     (StrLength(room->name) == 4) && 
212                                     (strcmp(ChrPtr(room->name), "Mail") == 0))
213                                 {
214                                         room->is_inbox = 1;
215                                 }
216
217                         }
218                         /* get a pointer to the floor we're on: */
219                         GetHash(WCC->Floors, IKEY(room->floorid), &vFloor);
220                         room->Floor = (const floor*) vFloor;
221
222
223
224                         /* now we know everything, remember it... */
225                         Put(rooms, SKEY(room->name), room, DeleteFolder);
226                 }
227         }
228 ///     SortByHashKey(rooms, 1);
229
230         SubTP.Filter.ContextType = CTX_ROOMS;
231         SortIt = RetrieveSort(&SubTP, NULL, 0, HKEY("fileunsorted"), 0);
232         if (SortIt != NULL)
233                 SortByPayload(rooms, SortIt);
234         else 
235                 SortByPayload(rooms, SortRoomsByListOrder);
236         FreeStrBuf(&Buf);
237         return rooms;
238 }
239
240 /** Unused function that orders rooms by the listorder flag */
241 int SortRoomsByListOrder(const void *room1, const void *room2) 
242 {
243         folder *r1 = (folder*) GetSearchPayload(room1);
244         folder *r2 = (folder*) GetSearchPayload(room2);
245   
246         if (r1->listorder == r2->listorder) return 0;
247         if (r1->listorder > r2->listorder) return 1;
248         return -1;
249 }
250
251 int CompareRoomListByFloorRoomPrivFirst(const void *room1, const void *room2) 
252 {
253         folder *r1 = (folder*) GetSearchPayload(room1);
254         folder *r2 = (folder*) GetSearchPayload(room2);
255   
256         if ((r1->Floor == NULL)  ||
257             (r2->Floor == NULL))
258                 return 0;
259                 
260         /**
261          * are we on the same floor? else sort by floor.
262          */
263         if (r1->Floor != r2->Floor)
264         {
265                 /**
266                  * the private rooms are first in any case.
267                  */
268                 if (r1->Floor->ID == VIRTUAL_MY_FLOOR)
269                         return -1;
270                 if (r2->Floor->ID == VIRTUAL_MY_FLOOR)
271                         return 1;
272                 /**
273                  * else decide alpaheticaly by floorname
274                  */
275                 return (r1->Floor->AlphaN > r2->Floor->AlphaN)? 1 : -1;
276         }
277
278         /**
279          * if we have different levels of subdirectories, 
280          * we want the toplevel to be first, regardless of sort
281          * sequence.
282          */
283         if (((r1->nRoomNameParts > 1) || 
284             (r2->nRoomNameParts > 1)    )&&
285             (r1->nRoomNameParts != r2->nRoomNameParts))
286         {
287                 int i, ret;
288                 int nparts = (r1->nRoomNameParts > r2->nRoomNameParts)?
289                         r2->nRoomNameParts : r1->nRoomNameParts;
290
291                 for (i=0; i < nparts; i++)
292                 {
293                         ret = strcmp (ChrPtr(r1->name), 
294                                       ChrPtr(r2->name));
295                         /**
296                          * Deltas in common parts? exit here.
297                          */
298                         if (ret != 0) 
299                                 return ret;
300                 }
301
302                 /**
303                  * who's a subdirectory of whom?
304                  */
305                 if (r1->nRoomNameParts > r2->nRoomNameParts)
306                         return 1;
307                 else
308                         return -1;
309
310         }
311
312         /**
313          * else just sort alphabeticaly.
314          */
315         return strcmp (ChrPtr(r1->name), 
316                        ChrPtr(r2->name));
317 }
318
319 int CompareRoomListByFloorRoomPrivFirstRev(const void *room1, const void *room2) 
320 {
321         folder *r1 = (folder*) GetSearchPayload(room1);
322         folder *r2 = (folder*) GetSearchPayload(room2);
323   
324         if ((r1->Floor == NULL)  ||
325             (r2->Floor == NULL))
326                 return 0;
327                 
328         /**
329          * are we on the same floor? else sort by floor.
330          */
331         if (r2->Floor != r1->Floor)
332         {
333                 /**
334                  * the private rooms are first in any case.
335                  */
336                 if (r1->Floor->ID == VIRTUAL_MY_FLOOR)
337                         return -1;
338                 if (r2->Floor->ID == VIRTUAL_MY_FLOOR)
339                         return 1;
340                 /**
341                  * else decide alpaheticaly by floorname
342                  */
343
344                 return (r1->Floor->AlphaN < r2->Floor->AlphaN)? 1 : -1;
345         }
346
347         /**
348          * if we have different levels of subdirectories, 
349          * we want the toplevel to be first, regardless of sort
350          * sequence.
351          */
352         if (((r1->nRoomNameParts > 1) || 
353             (r2->nRoomNameParts > 1)    )&&
354             (r1->nRoomNameParts != r2->nRoomNameParts))
355         {
356                 int i, ret;
357                 int nparts = (r1->nRoomNameParts > r2->nRoomNameParts)?
358                         r2->nRoomNameParts : r1->nRoomNameParts;
359
360                 for (i=0; i < nparts; i++)
361                 {
362                         /**
363                          * special cases if one room is top-level...
364                          */
365                         if (r2->nRoomNameParts == 1)
366                                 ret = strcmp (ChrPtr(r2->name), 
367                                               ChrPtr(r1->RoomNameParts[i]));
368                         else if (r1->nRoomNameParts == 1)
369                                 ret = strcmp (ChrPtr(r2->RoomNameParts[i]),
370                                               ChrPtr(r1->name));
371                         else 
372                                 ret = strcmp (ChrPtr(r2->RoomNameParts[i]), 
373                                               ChrPtr(r1->RoomNameParts[i]));
374                         /**
375                          * Deltas in common parts? exit here.
376                          */
377                         if (ret != 0) 
378                                 return ret;
379                 }
380
381                 /**
382                  * who's a subdirectory of whom?
383                  */
384                 if (r1->nRoomNameParts > r2->nRoomNameParts)
385                         return 1;
386                 else
387                         return -1;
388         }
389
390         return strcmp (ChrPtr(r2->name), 
391                        ChrPtr(r1->name));
392 }
393
394 int GroupchangeRoomListByFloorRoomPrivFirst(const void *room1, const void *room2) 
395 {
396         folder *r1 = (folder*) room1;
397         folder *r2 = (folder*) room2;
398   
399
400         if ((r1->Floor == NULL)  ||
401             (r2->Floor == NULL))
402                 return 0;
403                 
404         if (r1->Floor == r2->Floor)
405                 return 0;
406         else 
407         {
408                 wcsession *WCC = WC;
409                 static int columns = 3;
410                 int boxes_per_column = 0;
411                 int nf;
412
413                 nf = GetCount(WCC->Floors);
414                 while (nf % columns != 0) ++nf;
415                 boxes_per_column = (nf / columns);
416                 if (boxes_per_column < 1)
417                         boxes_per_column = 1;
418                 if (r1->Floor->AlphaN % boxes_per_column == 0)
419                         return 2;
420                 else 
421                         return 1;
422 ///                                     wprintf("</td><td valign=top>\n");
423         }
424 }
425
426
427
428
429
430
431 void tmplput_ROOM_NAME(StrBuf *Target, WCTemplputParams *TP) 
432 {
433         folder *Folder = (folder *)(TP->Context);
434
435         StrBufAppendTemplate(Target, TP, Folder->name, 0);
436 }
437 void tmplput_ROOM_BASENAME(StrBuf *Target, WCTemplputParams *TP) 
438 {
439         folder *room = (folder *)(TP->Context);
440
441         if (room->nRoomNameParts > 1)
442                 StrBufAppendTemplate(Target, TP, 
443                                       room->RoomNameParts[room->nRoomNameParts - 1], 0);
444         else 
445                 StrBufAppendTemplate(Target, TP, room->name, 0);
446 }
447 void tmplput_ROOM_LEVEL_N_TIMES(StrBuf *Target, WCTemplputParams *TP) 
448 {
449         folder *room = (folder *)(TP->Context);
450         int i;
451         const char *AppendMe;
452         long AppendMeLen;
453
454
455         if (room->nRoomNameParts > 1)
456         {
457                 GetTemplateTokenString(Target, TP, 0, &AppendMe, &AppendMeLen);
458                 for (i = 0; i < room->nRoomNameParts; i++)
459                         StrBufAppendBufPlain(Target, AppendMe, AppendMeLen, 0);
460         }
461 }
462
463 void tmplput_ROOM_ACL(StrBuf *Target, WCTemplputParams *TP) 
464 {
465         folder *Folder = (folder *)(TP->Context);
466
467         StrBufAppendPrintf(Target, "%ld", Folder->RAFlags, 0);
468 }
469
470
471 void tmplput_ROOM_QRFLAGS(StrBuf *Target, WCTemplputParams *TP) 
472 {
473         folder *Folder = (folder *)(TP->Context);
474         StrBufAppendPrintf(Target, "%d", Folder->QRFlags);
475 }
476
477
478
479 void tmplput_ROOM_FLOORID(StrBuf *Target, WCTemplputParams *TP) 
480 {
481         folder *Folder = (folder *)(TP->Context);
482         StrBufAppendPrintf(Target, "%d", Folder->floorid);
483 }
484
485 void tmplput_ROOM_LISTORDER(StrBuf *Target, WCTemplputParams *TP) 
486 {
487         folder *Folder = (folder *)(TP->Context);
488         StrBufAppendPrintf(Target, "%d", Folder->listorder);
489 }
490 void tmplput_ROOM_VIEW(StrBuf *Target, WCTemplputParams *TP) 
491 {
492         folder *Folder = (folder *)(TP->Context);
493         StrBufAppendPrintf(Target, "%d", Folder->view);
494 }
495 void tmplput_ROOM_DEFVIEW(StrBuf *Target, WCTemplputParams *TP) 
496 {
497         folder *Folder = (folder *)(TP->Context);
498         StrBufAppendPrintf(Target, "%d", Folder->defview);
499 }
500 void tmplput_ROOM_LASTCHANGE(StrBuf *Target, WCTemplputParams *TP) 
501 {
502         folder *Folder = (folder *)(TP->Context);
503         StrBufAppendPrintf(Target, "%d", Folder->lastchange);
504 }
505 void tmplput_ROOM_FLOOR_ID(StrBuf *Target, WCTemplputParams *TP) 
506 {
507         folder *Folder = (folder *)(TP->Context);
508         const floor *Floor = Folder->Floor;
509
510         if (Floor == NULL)
511                 return;
512
513         StrBufAppendPrintf(Target, "%d", Floor->ID);
514 }
515
516 void tmplput_ROOM_FLOOR_NAME(StrBuf *Target, WCTemplputParams *TP) 
517 {
518         folder *Folder = (folder *)(TP->Context);
519         const floor *Floor = Folder->Floor;
520
521         if (Floor == NULL)
522                 return;
523
524         StrBufAppendTemplate(Target, TP, Floor->Name, 0);
525 }
526
527 void tmplput_ROOM_FLOOR_NROOMS(StrBuf *Target, WCTemplputParams *TP) 
528 {
529         folder *Folder = (folder *)(TP->Context);
530         const floor *Floor = Folder->Floor;
531
532         if (Floor == NULL)
533                 return;
534         StrBufAppendPrintf(Target, "%d", Floor->NRooms);
535 }
536
537
538
539 int ConditionalRoomHas_UA_KNOWN(StrBuf *Target, WCTemplputParams *TP)
540 {
541         folder *Folder = (folder *)(TP->Context);
542         return (Folder->RAFlags & UA_KNOWN) != 0;
543 }
544
545 int ConditionalRoomHas_UA_GOTOALLOWED(StrBuf *Target, WCTemplputParams *TP)
546 {
547         folder *Folder = (folder *)(TP->Context);
548         return (Folder->RAFlags & UA_GOTOALLOWED) != 0;
549 }
550
551 int ConditionalRoomHas_UA_HASNEWMSGS(StrBuf *Target, WCTemplputParams *TP)
552 {
553         folder *Folder = (folder *)(TP->Context);
554         return (Folder->RAFlags & UA_HASNEWMSGS) != 0;
555 }
556
557 int ConditionalRoomHas_UA_ZAPPED(StrBuf *Target, WCTemplputParams *TP)
558 {
559         folder *Folder = (folder *)(TP->Context);
560         return (Folder->RAFlags & UA_ZAPPED) != 0;
561 }
562
563 int ConditionalRoomHas_UA_POSTALLOWED(StrBuf *Target, WCTemplputParams *TP)
564 {
565         folder *Folder = (folder *)(TP->Context);
566         return (Folder->RAFlags & UA_POSTALLOWED) != 0;
567 }
568
569 int ConditionalRoomHas_UA_ADMINALLOWED(StrBuf *Target, WCTemplputParams *TP)
570 {
571         folder *Folder = (folder *)(TP->Context);
572         return (Folder->RAFlags & UA_ADMINALLOWED) != 0;
573 }
574
575 int ConditionalRoomHas_UA_DELETEALLOWED(StrBuf *Target, WCTemplputParams *TP)
576 {
577         folder *Folder = (folder *)(TP->Context);
578         return (Folder->RAFlags & UA_DELETEALLOWED) != 0;
579 }
580
581
582 int ConditionalRoomIsInbox(StrBuf *Target, WCTemplputParams *TP)
583 {
584         folder *Folder = (folder *)(TP->Context);
585         return Folder->is_inbox;
586 }
587
588
589
590
591
592 void jsonRoomFlr(void) 
593 {
594         /* Send as our own (application/json) content type */
595         hprintf("HTTP/1.1 200 OK\r\n");
596         hprintf("Content-type: application/json; charset=utf-8\r\n");
597         hprintf("Server: %s / %s\r\n", PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software));
598         hprintf("Connection: close\r\n");
599         hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
600         begin_burst();
601         DoTemplate(HKEY("json_roomflr"),NULL,&NoCtx);
602         end_burst(); 
603 }
604
605
606
607 void 
608 InitModule_ROOMLIST
609 (void)
610 {
611         WebcitAddUrlHandler(HKEY("json_roomflr"), jsonRoomFlr, 0);
612
613
614         RegisterNamespace("FLOOR:ID", 0, 0, tmplput_FLOOR_ID, CTX_FLOORS);
615         RegisterNamespace("FLOOR:NAME", 0, 1, tmplput_FLOOR_NAME, CTX_FLOORS);
616         RegisterNamespace("FLOOR:NROOMS", 0, 0, tmplput_FLOOR_NROOMS, CTX_FLOORS);
617
618
619
620         RegisterIterator("LKRA", 0, NULL, GetRoomListHashLKRA, NULL, DeleteHash, CTX_ROOMS, CTX_NONE, IT_FLAG_DETECT_GROUPCHANGE);
621
622         RegisterNamespace("ROOM:INFO:FLOORID", 0, 1, tmplput_ROOM_FLOORID, CTX_ROOMS);
623         RegisterNamespace("ROOM:INFO:NAME", 0, 1, tmplput_ROOM_NAME, CTX_ROOMS);
624         RegisterNamespace("ROOM:INFO:BASENAME", 0, 1, tmplput_ROOM_BASENAME, CTX_ROOMS);
625         RegisterNamespace("ROOM:INFO:LEVELNTIMES", 1, 2, tmplput_ROOM_LEVEL_N_TIMES, CTX_ROOMS);
626
627         RegisterNamespace("ROOM:INFO:ACL", 0, 1, tmplput_ROOM_ACL, CTX_ROOMS);
628         RegisterNamespace("ROOM:INFO:QRFLAGS", 0, 1, tmplput_ROOM_QRFLAGS, CTX_ROOMS);
629         RegisterNamespace("ROOM:INFO:LISTORDER", 0, 1, tmplput_ROOM_LISTORDER, CTX_ROOMS);
630         RegisterNamespace("ROOM:INFO:VIEW", 0, 1, tmplput_ROOM_VIEW, CTX_ROOMS);
631         RegisterNamespace("ROOM:INFO:DEFVIEW", 0, 1, tmplput_ROOM_DEFVIEW, CTX_ROOMS);
632         RegisterNamespace("ROOM:INFO:LASTCHANGE", 0, 1, tmplput_ROOM_LASTCHANGE, CTX_ROOMS);
633         RegisterNamespace("ROOM:INFO:FLOOR:ID", 0, 0, tmplput_ROOM_FLOOR_ID, CTX_ROOMS);
634         RegisterNamespace("ROOM:INFO:FLOOR:NAME", 0, 1, tmplput_ROOM_FLOOR_NAME, CTX_ROOMS);
635         RegisterNamespace("ROOM:INFO:FLOOR:NROOMS", 0, 0, tmplput_ROOM_FLOOR_NROOMS, CTX_ROOMS);
636
637
638         RegisterConditional(HKEY("COND:ROOM:INFO:IS_INBOX"), 0, ConditionalRoomIsInbox, CTX_ROOMS);
639         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_KNOWN"), 0, ConditionalRoomHas_UA_KNOWN, CTX_ROOMS);
640         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_GOTOALLOWED"), 0, ConditionalRoomHas_UA_GOTOALLOWED, CTX_ROOMS);
641         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_HASNEWMSGS"), 0, ConditionalRoomHas_UA_HASNEWMSGS, CTX_ROOMS);
642         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_ZAPPED"), 0, ConditionalRoomHas_UA_ZAPPED, CTX_ROOMS);
643         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_POSTALLOWED"), 0, ConditionalRoomHas_UA_POSTALLOWED, CTX_ROOMS);
644         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_ADMINALLOWED"), 0, ConditionalRoomHas_UA_ADMINALLOWED, CTX_ROOMS);
645         RegisterConditional(HKEY("COND:ROOM:FLAGS:UA_DELETEALLOWED"), 0, ConditionalRoomHas_UA_DELETEALLOWED, CTX_ROOMS);
646
647         RegisterSortFunc(HKEY("byfloorroom"),
648                          NULL, 0,
649                          CompareRoomListByFloorRoomPrivFirst,
650                          CompareRoomListByFloorRoomPrivFirstRev,
651                          GroupchangeRoomListByFloorRoomPrivFirst,
652                          CTX_ROOMS);
653
654 }