18:00:51 #startmeeting 18:00:58 Meeting started Sat Mar 19 18:00:51 2011 UTC. The chair is bleathem. Information about MeetBot at http://wiki.debian.org/MeetBot. 18:00:58 Useful Commands: #action #agreed #help #info #idea #link #topic. 18:01:24 #meetingtopic SEAMFACES-33 18:01:29 jira [3SEAMFACES-33] Create a solution for consolidated page-flow, transactional control, security constraints and URL-rewriting configuration [10Open (Unresolved) Feature Request,7 Blocker,6 Brian Leathem] https://issues.jboss.org/browse/SEAMFACES-33 18:01:38 grr, here is the code that I hate 18:01:42 // Only skip to render response if there are no view parameters 18:01:47 Collection params = 18:01:51 ViewMetadata.getViewParameters(viewRoot); 18:01:56 if (params.isEmpty()) { 18:02:01 facesContext.renderResponse(); 18:02:06 } 18:02:10 why??? 18:02:15 okay, so it's very clear that we need to hook into this 18:02:19 ViewMetadata metadata = vdl.getViewMetadata(facesContext, viewId); 18:02:23 if (metadata != null) { // perhaps it's not supported 18:02:27 // and use it to create the ViewRoot. This will have, at most 18:02:32 // the UIViewRoot and its metadata facet. 18:02:36 viewRoot = metadata.createMetadataView(facesContext); 18:02:40 vdl == view declaration language 18:02:45 #topic which faces event/phase to listen for, to invoke a security check 18:03:09 we are tackling a few things, first, we need to add a dummy view parameter if one doesn't exist 18:03:22 and there are any other components in the facet 18:03:37 so we need to override createMetatdataView 18:03:44 #info we want the security check to take place before any side effects can occur 18:03:48 so how do we register our own vdl 18:04:30 #info viewAction is a source of side effects 18:04:45 view-declaration-language-factory 18:05:12 #action we want to add a dummy view parameter programatically so that the viewAction is always called 18:06:17 #info We want to allow the end user to specify if security should be enforced before/after viewActions are called 18:06:32 #action We will need Seam Security qualifiers for this 18:06:52 guess I should be more clear what "this" is 18:07:20 #action We will need Seam Security qualifiers for enforcing security before/after viewActions are called 18:08:05 mojavelinux the corresponding events to listem to would be ?? and PreRenderViewEvent 18:08:15 hang on, getting to it 18:08:45 ok, that's what you are looking up? 18:09:17 I'm looking up how we are going to tie into this whole thing, almost there 18:10:10 #action implement/resurrect the PreRenderViewEventProcessor chain / SystemEventProcessor to allow sequencing of system events listeners 18:10:27 okay, I think all we need to do is override the ViewHandler 18:10:44 rather than listening for events? 18:10:58 which we don't yet do 18:11:03 here's the idea 18:11:19 the topic is adding a dummy UIViewParameter 18:11:25 because this is the first step in this process 18:11:30 #chair mojavelinux 18:11:34 Current chairs: bleathem mojavelinux 18:11:58 #topic adding a dummy UIViewParameter 18:12:04 #info if we override ViewHandler, then we can return a wrapped version of the ViewDeclarationLanguage 18:12:44 #info when createMetadataView is called, we delegate to parent, then, if there are components in the view metadata, but no UIViewParameter, we add a dummy instance 18:13:21 #info we override view handler by using in faces-config.xml 18:13:41 we can look into other ways to do this, let's just go with this as a prototype 18:13:45 and createMetadataView *is* called even though ni UIViewParamter is present? 18:14:03 meaning, we could go lower and provide a custom ViewDeclarationLanguageFactory and wrap the ViewDeclarationLanguage instead 18:14:16 yes, createMetadataView is always called 18:14:22 ok 18:14:29 i'll reiterate the mojarra code 18:14:36 oh, info that 18:15:11 #info mojavelinux: we could go lower and provide a custom ViewDeclarationLanguageFactory and wrap the ViewDeclarationLanguage instead 18:15:23 #info createMetadataView is always called 18:15:27 if (vdl != null) { 18:15:31 // If we have one, get the ViewMetadata... 18:15:35 ViewMetadata metadata = vdl.getViewMetadata(facesContext, viewId); 18:15:40 if (metadata != null) { // perhaps it's not supported 18:15:44 // and use it to create the ViewRoot. This will have, at most 18:15:48 // the UIViewRoot and its metadata facet. 18:15:53 viewRoot = metadata.createMetadataView(facesContext); 18:15:58 // Only skip to render response if there are no view parameters 18:16:02 Collection params = 18:16:06 ViewMetadata.getViewParameters(viewRoot); 18:16:11 if (params.isEmpty()) { 18:16:15 facesContext.renderResponse(); 18:16:19 } 18:16:23 } 18:16:28 oic, if params.isEmptuy 18:16:32 yeah, so see where we can inject our own stuff 18:16:36 is by overriding createMetadataView 18:16:41 yep 18:16:45 and weaving 18:16:49 * lightguard_jp is having fun going through new hire info :) 18:16:54 hehehe 18:16:58 okay, so that solves our UIViewParameter problem, I'll jira that 18:17:12 lightguard_jp probably the happiest paperwork you had to go through in a long time 18:17:17 #action file jira for adding dummy UIViewParameter if there are other non-UIViewParameter children in the metadata view 18:17:27 Oh, did you start a meeting? 18:17:32 next topic? 18:17:37 yep 18:17:45 #topic where to hook in security restrictions 18:17:49 lightguard_jp ype, the IRC logs for this conversation got *really* long 18:17:56 bleathem: Yeah, and it actually makes sense. I read the NDA and all the legal stuff and it was actually understandable. 18:18:12 i wonder if there is some way to delete my first topic 18:18:21 oh well, we can disregard it 18:18:42 #info disregard first topic on hooking in seam security in favor of subsequent info lines 18:19:23 aha! 18:19:27 idea 18:19:37 we can fire and event for post create metadata view 18:19:41 :) 18:19:47 yeah, more hooks 18:19:51 nice 18:20:01 so we want this one to be really nice ;) 18:20:14 okay, so we have built the view with just the ui view metadata 18:20:28 now is the best time for early security 18:21:00 #idea mojavelinux fire an event for post create metadata view 18:21:04 the next thing that happens in the jsf lifecycle is apply request values to the components in the metadata facet 18:21:20 I guess we could do before too :) 18:21:25 for the event 18:21:30 we'll see if that's useful 18:21:49 but these are critical points in the lifecycle, sure is nice to be able to tie into them 18:21:53 #action fire pre and post create-metadata-view events 18:22:51 so the early seam security check will be post-create-metadataview 18:23:05 and late seam security check will be prerenderview 18:23:20 now here's the question, do you want to transfer the view config into view metadata components at this point? 18:23:26 as though the user defined them in the template? 18:23:31 I would think the other way around 18:23:43 k, it's up to you, either way 18:23:55 I don't think we can define them in the template 18:24:01 one sec 18:24:22 why not? well, you could have s:restrictView for EL restrictions 18:24:27 but it would be different 18:24:39 true, okay, so you are correct, we can't have a 1-to-1 mapping 18:24:43 it will be an aggregate 18:25:09 "Note line 4. The page author must ensure that the element does not appear on a template or included page. It must reside on the root page that corresponds to the viewId." 18:25:15 http://javaserverfaces.java.net/nonav/docs/2.0/pdldocs/facelets/f/metadata.html 18:25:38 yes, but you can still use templating 18:25:56 you just have to pass the into a block 18:26:00 we do this in booking 18:26:05 and weld permalink 18:26:09 right 18:26:13 it's just sort of quirky 18:26:20 but the metadata has to be defined per page 18:26:25 and you can use ui:include inside of 18:26:37 right, that's the trick, ui:include 18:26:41 #info you can use ui:include inside of 18:26:46 you just can't do it outside 18:27:00 it's not ideal, but it can get almost the same result 18:27:08 you just have a lot of repeat ui:include blocks 18:27:18 boilerplate 18:27:30 which is where the @ViewConfig comes in nice 18:27:34 yep, though remember, we can programmatically add them 18:27:38 exactly 18:27:42 f:metadata for prototyping 18:27:47 we could provide view actions view @ViewConfig! 18:27:51 :) 18:27:55 nice 18:28:00 exactly 18:28:04 sweet 18:28:08 this is coming along nicely 18:28:13 very 18:28:17 #idea mojavelinux: we could provide view actions view @ViewConfig! 18:28:21 excellent 18:28:36 have we got our hook points settled? 18:28:56 postCreateMetadataViewEvent and PreRenderView? 18:29:16 just a sec 18:29:24 wife is talking 18:29:31 got it! 18:30:10 there should be a code for that ;) 18:30:31 there are two paths we can take here 18:30:38 Seam Security Qualifiers? 18:30:54 one path is to register a security executor component 18:31:10 as the first component in the view metadata 18:31:14 w/ immediate = true 18:31:26 and let it execute and we tie into that 18:31:50 the other approach is to tie into our own post create metadata view event 18:31:56 the only problem with events is the ordering 18:32:03 corresponds to the postCreateViewMetadataEvent 18:32:21 right 18:32:43 how I worked around this in seam servlet is this 18:32:47 And we would have a corresponding Seam Security qualifier for use in @ViewConfig 18:32:56 I fire two events for a lifecycle point 18:33:07 the first has an @Internal qualifier 18:33:11 We only need one qualifier to correspond to immediate, the default shoud be late 18:33:16 the second is a public one 18:33:25 ok 18:33:48 So we can act on an event before the end-developer can 18:33:52 I think we don't need a qualifier for the immediate , it is jsut an attribute on the security annotation 18:33:57 perhaps 18:34:02 ok 18:34:06 that's better 18:34:12 in a sense, PreRenderViewEvent is perfectly useless 18:34:19 because it's just way too late 18:34:34 however, consider this 18:34:53 the post create metadata view event isn't good either 18:34:59 because that only happens on initial request 18:35:04 useful event, but not for the purpose of security 18:35:11 right 18:35:16 what we really need is post-restore view 18:35:20 we can re-visit the view, with changed data 18:35:24 at that point, we know we have a metadata facet 18:35:37 also, we need to have a security "on-postback" flag 18:35:51 that determines if it's enforced on postback or not 18:35:56 though the default should be that it is 18:36:19 #idea mojavelinux: we need to have a security "on-postback" flag - that determines if it's enforced on postback or not (default true) 18:36:23 view actions are the opposite, default onPostack=false 18:36:36 because they are designed for initial requests 18:36:41 #info view actions are the opposite, default onPostack=false 18:36:45 security is something you always want 18:36:49 or 99% of the time 18:36:53 right makes sense 18:37:06 so I would tie into post-restore view phase for enforcing security 18:37:15 always? 18:37:29 what about for initial requests? 18:37:33 same 18:37:37 oh, for immediate 18:37:42 for immediate security 18:37:47 we won't have application data to inspect 18:37:51 for late security, PreRenderViewEvent is fine 18:37:55 oh, ok 18:38:12 Which should be default? 18:38:16 late? 18:38:20 early 18:38:27 I think most security would need to be checked against application data 18:38:44 why isn't application data available though? 18:38:49 which would be available early on post back 18:38:53 you are saying that things like view parameters aren't yet resolved 18:39:01 an initial request, post restore view 18:39:06 yes 18:39:10 so like what's the blog entry for instance 18:39:14 or which user are we viewing 18:39:19 got it 18:39:37 mabye default late for intial request, early for postback 18:39:41 okay, so early checking would be like this 18:39:48 viewId and current user session 18:39:59 like, okay, you are joe shmo and the viewId is /admin/ 18:40:04 deny 18:40:17 right 18:40:27 thtat would be an early one 18:40:39 then let's say, for PreRenderView 18:40:43 we've looked up the user 18:40:50 let's say the blog entry 18:40:54 no, let's say a document 18:40:59 you are joe shmo and view is /view?entry=JohnsItem 18:41:03 we get a document 18:41:07 in like a view action 18:41:17 then we see if the current user can view that document, perhaps via a rules engine or something 18:41:22 or acl 18:41:27 then deny 18:41:37 we could do security before the view actions 18:41:45 but after the view parameters, but that seems like a very rare condition 18:41:54 chances are, the view actions are pulling the application data 18:42:11 but there is another case 18:42:21 what about on postback, you clicked delete 18:42:26 you can do security in PreRenderView 18:42:30 can't 18:42:36 because the document will be gone by then :) 18:42:40 so then, you need early 18:42:50 which should be available in the application data memory 18:42:55 so then you know what we need 18:43:03 early and late needs to be per initial request or postback 18:43:08 so like this 18:43:12 right, I think a sensible default might be: default late for intial request, early for postback 18:43:19 initial = "late", postback="early" 18:43:26 yep, and then we can have never 18:43:33 initial = "never", postback = "early" 18:43:42 okay, idea that one 18:43:53 wait, what's the "never" 18:43:57 one? 18:45:29 dont' apply security in that case 18:45:33 I think we need 4 options 18:46:02 default: initial = "late", postback="early" 18:46:06 initial | postback = AFTER_RESTORE | BEFORE_INVOKE | BEFORE_RENDER 18:46:15 ok 18:46:25 that sumamrises it nicely 18:46:30 #idea timing for security enforcement is initial | postback = AFTER_RESTORE | BEFORE_INVOKE | BEFORE_RENDER 18:46:48 #idea default timing is initial = BEFORE_RENDER, postback = AFTER_RESTORE 18:46:59 or should it be postback = BEFORE_INVOKE? 18:47:17 for the default? 18:47:21 yeah 18:47:28 yeah, I think so 18:47:33 no side effects 18:47:37 but viewParams are all present 18:48:19 okay, cool, so we can fine tune all that...that's just the details 18:48:46 ratehr than BEFORE_RENDER, We could be AFTER_INVOKE, to make sure we get applied before other BEFORE_RENDER events 18:49:16 yeah, I think so...only trick bit is, we don't have a hook for that in jsf 18:49:21 though we could make one 18:49:25 #idea postback coud be BEFORE_INVOKE rather than AFTER_RESTORE - no side effects, yet viewParams are all present 18:49:29 by adding a metadata component 18:49:34 like securityenforcer 18:49:38 we have the after phase listener 18:49:42 duh 18:50:14 but that is a FINEST details :P 18:50:19 hehehe 18:50:32 okay, so I see these steps 18:50:36 so se have out JSF event tie-ins 18:50:48 what else do we need... 18:50:53 #1 create an security enforcer 18:50:58 #2 hook it into the life cycle 18:51:24 #3 override ViewMetadata and add the dummy UIViewParameter if there are any non-UIViewParameter components but no UIViewParameter 18:51:30 sorry, ViewHandler 18:51:52 override ViewHandler, to wrap ViewDeclarationLanguage to override createMetadataView 18:52:07 that will fix our UIViewAction problem too 18:52:12 Side note: can we get sub-tasks in the JBoss jira? 18:52:27 These would be good issues as sub-tasks of seamfaces-33 18:52:31 oh, we intentially disabled those because they were annoying, do you like them? 18:52:36 true 18:52:40 we just use linked issues 18:52:44 depends-on 18:52:48 they're annoying in that they are only 1 level deep 18:52:53 ok 18:52:57 depends on is fine 18:53:01 let's use that 18:53:26 cool! I think we are much further along now, woot 18:53:37 Is ViewMeta (as a replacement for ViewData) to similar in name to JSF's ViewMetadata? 18:53:46 yes, the path ahead is clear 18:55:26 The initial first pass on this was going to be to use the @Viewconfig, and add in the f:metadata tags later, possibly post 3.0 18:56:07 Are we still ok with this, given that our discussion was f:metadata centric? 18:57:18 And I think we need qualifiers rather than attributes for the timing, since the end developers create the Seam Security annotations themselves. 18:57:30 No qualifier present, = the default behaviour agreed to above 18:58:31 git [12faces] push 10master7 6b0d9f5.. 6Dan Allen ignore faces-config dia files 18:58:35 git [12faces] push 10master7 da97474.. 6Shane Bryzak [maven-release-plugin] prepare release 3.0.0.CR2 18:58:40 git [12faces] push 10master7 142bb53.. 6Shane Bryzak [maven-release-plugin] prepare for next development iteration 18:58:44 git [12faces] push 10master7 5ae0dcb.. 6Dan Allen Merge branch 'master' of github.com:seam/faces 18:58:48 git [12faces] push 10master URL: http://github.com/seam/faces/compare/da0cc20...5ae0dcb 18:58:52 Then when we scan for the SeamSecurity annotations qualified with @SecurityBindingType, we can check for the timing qualifiers @BeforeInvoke, @AfterRestore, @BeforeRender 18:59:18 git [12faces] push 10master URL: http://github.com/seam/faces/compare/5ae0dcb...da0cc20 18:59:24 #commands 18:59:28 Available commands: #accept #accepted #action #agree #agreed #chair #commands #endmeeting #halp #help #idea #info #link #lurk #meetingname #meetingtopic #nick #rejected #restrictlogs #save #startmeeting #topic #unchair #undo #unlurk 18:59:52 #info 18:59:56 git [12faces] push 10master7 1f1e3d8.. 6Dan Allen ignore faces-config dia files 19:00:01 git [12faces] push 10master URL: http://github.com/seam/faces/compare/da0cc20...1f1e3d8 19:00:18 sorry, screwed up that push at first 19:00:36 yep 19:01:23 the f:metadata discussion was important for context, we've determined that we can get our hooks using what's available 19:01:45 well, sort of 19:02:00 remember, without a UIViewParameter, you don't get invoke application on an initial request 19:02:25 but do we need that? 19:02:59 so what are the decided on hook points again? meaning what exactly will you use as the tie in (I know we discussed the available timing, but I'm looking for the actual relationships) 19:03:10 ok 19:03:17 so the after restore is in a phase listener? 19:03:28 yes 19:03:32 I suppose you can use the delegating phase listener 19:03:40 before render could be either phase or system 19:03:45 to launch it, like w/ the transactions 19:03:58 yes, then you get the injection 19:04:19 I think the system event is probably best for pre-render 19:04:30 BEFORE_INVOKE would be skipped on initial request, if there were no viewAction 19:05:08 true, let's just say that for now you need a view action for security to be called at that point on intiial request 19:05:14 general curiosity, why is the system event prefered over the phase listener? 19:05:25 ok 19:05:29 and the view action is current dependent on a view parameter (which we will fix either before or after 3.0) 19:05:43 in essence, BEFORE_INVOKE is meaningless if you have no action to invoke 19:05:48 right 19:05:54 I'd like to see that fixed before 3.0 19:06:06 it'll get lots of questions/complaints in the forum 19:06:11 I can probably take that one...I think I know what needs to be done 19:06:17 that'd be good 19:06:29 we really need jsfunit tests, that's something which we may not have time for though 19:06:37 ok, does that wrap it up? I'll file some jiras, and we're good to go? 19:06:42 but a lot of this testing is manual w/o it 19:06:46 yep 19:06:53 Don't get me started on the need for JSFUnit tests 19:07:00 as for the system event vs phase for prerender 19:07:05 we'll be here another 3 hours! 19:07:33 it's hard to say, probably the same really 19:07:42 just more fine-grained if you do system 19:07:46 one more question: should the Qualifiers we spoke about be Seam Security qualifiers, or Faces Qualifiers? 19:07:51 because you know that you don't interfere with application phase listeners 19:07:59 faces 19:08:08 ok 19:08:20 you might just be able to use what you have, as in @Before @Render 19:08:40 @Before @RenderResponse 19:08:50 only downside is that you might think you can use any combination 19:08:54 Then we might get people trying unsupported combinations 19:08:58 but you could fail at startup 19:09:08 * bleathem is a slower typer than mojavelinux 19:09:12 when processing the enum 19:09:35 you could also just say that the timing is assumed 19:09:40 and then just do @RenderResponse 19:10:00 they tell you which phase, the timing within the phase is fixed 19:10:10 We could ignore unsupported qualifiers, assume the default, and log an error 19:10:22 fail is better 19:10:27 it's like having a bad injection point 19:10:37 ok 19:10:43 how do I do that? 19:10:51 Is there another class I can look at for an example? 19:11:42 the view config extension currently has the code for processing the enum 19:11:49 it does isAnnotationPresent 19:11:56 you would want to do getAnnotations() 19:12:03 #idea use the exisitng Faces qualifiers to qualify the Seam Security annotations (eg. @RenderResponse_ 19:12:10 and make sure that only the allowed phases are there 19:13:09 I would play with some ideas in the jira comments about how it will look for the user 19:13:15 ok, is that done at application startup? 19:13:29 the allowed phases stuff 19:13:36 or when the @ViewConfig is processed 19:13:49 nevermind 19:13:56 yep 19:14:03 getting confused with f:metadat which is processed on first visit 19:14:10 btw, are you going to change @ViewData to @ViewPattern? 19:14:20 @ViewPattern("/admin/*") 19:14:24 I changed ViewData to VeiwMeta 19:14:29 oh wait 19:14:33 ah, right 19:14:37 let me check 19:14:42 actually, you know what 19:14:46 you should do 19:15:07 yeah, I'd done ViewMeta 19:15:11 but I'm not married to it 19:15:15 @ViewMeta(patterns = {"/admin/*", "/admin.xhtml"}) 19:15:20 mojavelinux: You fixed SOLDER-86 as part of SOLDER-85 didn't you? 19:15:24 jira [3SOLDER-86] Split up LoggerProducers [10Open (Unresolved) Task,7 Minor,6 Ken Finnigan] https://issues.jboss.org/browse/SOLDER-86 19:15:28 jira [3SOLDER-85] Better separation of i18n and logging concerns [10Resolved (Done) Enhancement,7 Major,6 Dan Allen] https://issues.jboss.org/browse/SOLDER-85 19:15:33 yep! 19:15:37 cool, will mark it done 19:15:45 or perhaps @ViewMeta(viewIds = {}) 19:16:05 what if you do 19:16:09 What other parameter would go in ViewMeta, that would warrant adding a parameter name? 19:16:33 How about @ViewIds() 19:16:37 ? 19:17:06 ah, in that case, you probably want @ViewPattern 19:17:18 Ok, I'll go with @ViewPattern 19:17:27 but the rest should stay as @ViewMeta I think 19:17:32 and you can do @ViewPatterns({@ViewPattern("/admin/*"), @ViewPattern("/admin.xhtml")}) 19:17:36 It's bigger than just ViewPattern 19:17:44 nice 19:17:53 the thing is, each enum value is the meta 19:17:58 #topic Naming convention 19:18:02 so the annotation can't be ViewMeta 19:18:06 because then that means everything is in that one annotation 19:18:11 when in fact, it's stacked 19:18:19 #idea rename @ViewMeta to @ViewPattern 19:18:29 or ViewMapping 19:18:34 #idea, one can suport @ViewPatterns({@ViewPattern("/admin/*"), @ViewPattern("/admin.xhtml")}) 19:18:41 View mapping is good too 19:18:57 but I like the pattern part to support the "*" 19:19:18 so if you have an admin section of your app, you always secure it 19:19:23 right, the value is a collection of patterns 19:19:47 ok, I'll end the meeting then, and create some jiras. 19:19:51 @ViewMapping({"/admin/*", "/admin.xhtml"}) 19:20:19 I don't feel strongly about either, if you have a preference 19:20:28 not really, we can see what the masses thing 19:20:32 think 19:20:55 btw, internally, ViewMetadata is fine 19:21:00 #idea or maybe instead @ViewMapping({"/admin/*", "/admin.xhtml"}) 19:21:07 rather than ViewMeta? 19:21:11 in a sense, this is a superset, or complement to template view metadata 19:21:25 so it's really a very similar purpose 19:21:29 If they are indeed analagous, then ViewMeatadata is better 19:21:44 I'll go with ViewMetadata then 19:21:48 I would say it's either ViewMetadata or ViewConfiguration 19:21:52 cool 19:21:59 ViewMetadataStore 19:22:12 ViewConfiguration sounds to similar to Viewconfig 19:22:39 yep, though honestly I wouldn't mind @ViewConfiguration 19:22:44 Or we could use ViewConfigStore 19:22:55 Since it is storing the ViewConfig 19:23:03 rahter then ViewmetaDataStore 19:23:22 ^rather than ViewMetadataStore 19:23:26 I like the Store suffix for the holder 19:23:31 that works 19:24:57 we could do @ViewMetadata, but hard to pluralize that 19:25:10 #action renamve @ViewMeta to @ViewMapping 19:25:25 #action rename @ViewMetaStore to @ViewConfigStore 19:25:38 i know, we are saying that the view configuration associates metadata with various views 19:25:55 ok, I got myself all turned around. 19:26:11 I'll mess around with the naming in code, and come up with something that captures the intent nicely 19:26:21 wait, ViewConfigStore isn't an annotation 19:26:25 is it? 19:26:29 it's just like this 19:26:33 No, it's not 19:26:38 I cpoy and paster the previous line 19:26:47 copy and paste alway intorduces error :P 19:27:02 my typing is going downhill altogether at the moment 19:27:13 #closemeeting 19:27:25 #endmeeting