1 # ===================================================================== 2 # showPage: W-TW page visualizer. 3 # 4 # Copyright (c) 2007-2012 Carlo Strozzi 5 # 6 # This program is free software; you can redistribute it and/or modify 7 # it under the terms of the GNU General Public License as published by 8 # the Free Software Foundation; version 2 dated June, 1991. 9 # 10 # This program is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU General Public License for more details. 14 # 15 # You should have received a copy of the GNU General Public License 16 # along with this program; if not, write to the Free Software 17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 # 19 # ===================================================================== 20 21 # ===================================================================== 22 # Local variables and functions 23 # ===================================================================== 24 25 cgi.group = () 26 cgi.page = () 27 cgi.subcat = () 28 cgi.wikilinks = show # Currently forced. 29 cgi.group.literal = () 30 cgi.page.literal = () 31 cgi.author = () 32 cgi.revision = () 33 cgi.style = () 34 cgi.numeric = () 35 cgi.item.type = page # default. 36 cgi.item = () 37 38 infile = /dev/null 39 outfile = /dev/null 40 41 tmp2 = /dev/null 42 43 do_commit = () 44 new_url = () 45 test_urls = () 46 pls_set = () 47 48 # ===================================================================== 49 # Main program 50 # ===================================================================== 51 52 csaGetArgs GET 53 54 . $CSA_ROOT/lib/group-stuff.rc 55 56 # This is needed by 'logOut' as a last resort. 57 csaSession.set $'cgi.group' 12 58 59 # Now check the second most important arg. Note that this program 60 # does not source page-stuff.rc, because: 61 # 62 # 1) cgi.page is allowed to be null. 63 # 2) it can be called to display other than actual pages. 64 65 if (~ $'cgi.page' ()) { 66 67 ~ $#TNS_GROUP_HOME 1 2 || csaExit.fault 0041 TNS_GROUP_HOME 68 69 # It is the sysadmin responsibility to ensure that a single 70 # value specified for TNS_GROUP_HOME is usable also as a 71 # valid URL component. 72 73 ~ $TNS_GROUP_HOME(2) () && 74 TNS_GROUP_HOME = ($TNS_GROUP_HOME $TNS_GROUP_HOME) 75 76 # I perform redirection even when this program is executed in the 77 # CSA2 context, i.e. when XML-RPC is used; this is partly because 78 # client HTTP libraries usually do support it and, most important, 79 # because the new URL can be anything, not necessarily a different 80 # local page, so we have no choice but to divert the client to the 81 # new URL with the standard HTTP mechanism. Furthermore, an RPC2 82 # client not necessarily is an editing program but can be just 83 # a different way to get pages and display them, so if the latter 84 # are redirected the client must be told to redirect too. 85 86 csaExit.location \ 87 $CSA_RPC_URI/$CSA_LANG/$'cgi.group'/$TNS_GROUP_HOME(2) 88 } 89 90 # Page names beginning with tw-* are reserved for TW use, and usually 91 # refer to views that do not correspond to actual pages on disk. 92 ~ $'cgi.page' tw-* && csaExit.fault 1009 $'cgi.page' 93 94 tw_pstem = $tw_gstem/$'cgi.page' 95 96 # Handle numeric comment targets. 97 ~ $'cgi.item.type' comment && !~ $'cgi.item' () && csaExit.location \ 98 $CSA_RPC_URI/$CSA_LANG/$'cgi.group'/$'cgi.page'/comment/$'cgi.item' 99 100 # Check page required metadata. 101 ~ $'cgi.page.literal' () && csaExit.fault 0041 cgi.page.literal 102 103 # Set page provisional meta-data as supplied by the user. 104 tbl_page.p_name = $'cgi.page.literal' 105 106 # This is useful to be included in output templates for documentation 107 # purposes, i.e. to let the webmaster know how to name externally 108 # included page sections from the "bypage/" subdir of the template 109 # directory. ".unx" vars are also needed to resolve relative static 110 # URLs in page templates. 111 112 tpl.var.tw.page.unx = $'cgi.page' 113 114 . $CSA_ROOT/lib/group-editor.rc 115 116 #~ $REMOTE_ADDR 192.168.1.2 && csaExit.env 117 118 # TW body, if available and if not empty (otherwise it will remain 119 # set to /dev/null). 120 if (csaIsFullPath --exists --quiet $tw_pstem+wki) { 121 if (~ $'cgi.revision' ()) { 122 tpl.include.tw.page = $tw_pstem+wki 123 } else { 124 csaMkTemp tpl.include.tw.page 125 co -q -p -zLT -d$'cgi.revision' \ 126 $tw_pstem+wki > $'tpl.include.tw.page' || csaExit.fault 0003 co 127 tpl.var.tw.page.revision = $'cgi.revision' 128 } 129 130 # Override page meta-data with actual values if available. 131 keysearch $'cgi.page' $tw_gstem/page+dat | 132 csa-tbl2rc --prefix tbl_page. > $tmp1 133 . $tmp1 134 135 # Override page virtual date with RCS date of revision. 136 ~ $'cgi.revision' () || tbl_page.p_vtime = $'cgi.revision' 137 138 # Since this program does not source page-stuff.rc, I need to 139 # repeat the needed subset of the latter here. 140 141 # Set default page expiration timestamp if unset, for backward- 142 # compatibility with page+dat tables still lacking that attribute. 143 144 ~ $'tbl_page.p_etime' () && 145 tbl_page.p_etime = '9999-12-31 23:59:59' 146 147 # Display anchor points in the form of hyperlinks if requested. 148 # Only elements in the form and 149 # are recognised, i.e. basically only those that were inserted 150 # by the (:a:) wiki tag. 151 152 if (~ $'cgi.style' toc && !~ $CSA_PGM(1) CSA2) { 153 sed ' 154 # normalize syntax. 155 s,\(,\1>,g 156 # turn anchor into href. 157 s,\(\)\(\),\1href\2#\3\4(:a:)\5,g 158 # make the resulting link fully-qualified. 159 s,\( $tmp1 || csaExit.fault 0003 sed 161 162 tpl.include.tw.page = $tmp1 163 } 164 } 165 166 # Page meta keywords, if available. 167 csaIsFullPath --exists --quiet $tw_pstem-meta+xml && 168 tpl.include.tw.page.meta = $tw_pstem-meta+xml 169 170 #~ $REMOTE_ADDR 192.168.1.2 && csaExit.pcdata $tmp1 171 #~ $REMOTE_ADDR 192.168.1.2 && csaExit.session 991f62af501231ed55f06db20fa4dc18 172 #~ $REMOTE_ADDR 192.168.1.2 && csaExit.session 173 174 # Set defaults for misc. counters. 175 ~ $'tbl_page.p_ntbk' [0-9]* || tbl_page.p_ntbk = 0 176 ~ $'tbl_page.p_npriv' [0-9]* || tbl_page.p_npriv = 0 177 ~ $'tbl_page.p_npub' [0-9]* || tbl_page.p_npub = 0 178 179 # Set comment counter. 180 if (csaIsFullPath --exists --quiet $tw_pstem+cmt) { 181 tpl.var.tw.page.cmt.count = `{grep -cve '^'$soh $tw_pstem+cmt} 182 } else tpl.var.tw.page.cmt.count = 0 183 184 # Handle redirections by looking for the relevant application-level CPI 185 # markup in the page description. This is done in the page description 186 # rather than in the page body because the former is stored in the 187 # page database and is therefore more readily available at an early 188 # stage, and it is also much easier to handle. Note that if a possible 189 # redirection loop is detected then the redirection is silently ignored. 190 # This code makes use of a few sed(1) and tr(1) processes and it would 191 # become more efficient if moved to showPage.awk. However, since this 192 # section is triggered only occasionally, effciency is not a major issue 193 # here though. 194 # 195 # Note that I perform the redirection even when this program is executed 196 # in the CSA2 context, i.e. when XML-RPC is used; this is partly because 197 # client HTTP libraries usually do support it and, most important, 198 # because the new URL can be anything, not necessarily a different local 199 # page, so we have no choice but to divert the client to the new URL 200 # with the standard HTTP mechanism. 201 202 # Mark an existing page as hidden for the current run, if it 203 # has expired and if it is not already hidden; the expiration 204 # will occur regardless of whether the page has possibly 205 # been redirected, which is irrelevant here. 206 207 !~ $'tbl_page.p_etime' () && !~ $'tbl_page.p_descr' -* && 208 ~ `{expr $CSA_TIME_ISO '>=' $'tbl_page.p_etime'} 1 && 209 tbl_page.p_descr = -$'tbl_page.p_descr' 210 211 # It may be better not to follow the redirection if editor, or editing 212 # without manually typing the trailing "/form/edit" in the URL will 213 # become impossibile. The desired behavior can be set through a relevant 214 # group+cf setting. 215 if (!~ $'tbl_page.p_descr' -* && 216 ~ $'tbl_page.p_descr' *^'(:redirect '^* && 217 {!~ ,$TNS_AUTH_GRP, *,editor,* || csaTrue $CSA_REDIR_PROP(1)}) { 218 219 new_url = `{echo $'tbl_page.p_descr' | 220 sed 's,.*(:redirect[ ]*,,;s,[ ]*:).*,,;s,[ ],%20,g'} 221 222 test_urls = `{echo $new_url$nl$CSA_RPC_HREF$nl$HTTP_REFERER | 223 sed 's,.*(:redirect[ ]*,,;s,[ ]*:).*,,;s,[ ],%20,g' | 224 tr [:upper:] [:lower:]} 225 226 #~ $REMOTE_ADDR 192.168.1.2 && csaExit.env 227 228 # Silently ignore the redirection if a loop is detected. 229 230 ~ $new_url http* && 231 !~ $test_urls(1) $test_urls(2) $test_urls(3) && 232 csaExit.location $new_url(1) 233 } 234 235 #~ $REMOTE_ADDR 192.168.1.2 && csaExit.env 236 237 # Set template vars to their final values. 238 tpl.var.tw.page = $'tbl_page.p_name' 239 tpl.var.tw.page.object = $'tpl.var.tw.page' 240 tpl.var.tw.descr = $'tbl_page.p_descr' 241 tpl.var.tw.author = $'tbl_page.p_modau' 242 tpl.var.tw.page.ping.count = $'tbl_page.p_ntbk' 243 tpl.var.tw.page.att.count.priv = $'tbl_page.p_npriv' 244 tpl.var.tw.page.vtime = $'tbl_page.p_vtime' 245 tpl.var.html.title = $'tpl.var.tw.group'/$'tpl.var.tw.page' 246 247 # Page description defaults to group/page name. 248 ~ $'tpl.var.tw.descr' () && tpl.var.tw.descr = $'tpl.var.html.title' 249 250 # Let's be very careful about the public upload directory, as it is 251 # located in an area wich is accessible to the webmaster through FTP. 252 # In theory, the user could remove the directory and re-create it with 253 # different permissions (at least while the directory is still empty). 254 # If he did, then our next touch(1) will fail and we will abort the 255 # operation with complaints. A user can remove a non-owned directory from 256 # an owned directory tree only if the directory is empty, so we touch a 257 # placeholder into it first, just in case. All this should give us enough 258 # security. 259 260 if (!~ $TNS_ATTACH_PUBDIR () && !csaIsFullPath --quiet --exist \ 261 $CSA_DOCROOT/$CSA_LANG/$TNS_ATTACH_PUBDIR/.touch) { 262 263 test -d $CSA_DOCROOT/$CSA_LANG/$TNS_ATTACH_PUBDIR || 264 csaExit.fault 0008 $CSA_DOCROOT/$CSA_LANG/$TNS_ATTACH_PUBDIR 265 266 touch $CSA_DOCROOT/$CSA_LANG/$TNS_ATTACH_PUBDIR/.touch || 267 csaExit.fault 0009 \ 268 $CSA_DOCROOT/$CSA_LANG/$TNS_ATTACH_PUBDIR/.touch 269 } 270 271 if (csaIsFullPath --exists --quiet $CSA_TPL_ROOT/tw-show-page.txt) { 272 # custom page-display template. 273 tpl.include.html.body = $CSA_TPL_ROOT/tw-show-page.txt 274 } else { 275 # default page-display template. 276 tpl.include.html.body = $tw_dstem/tw-show-page.txt 277 } 278 279 if (csaIsFullPath --exists --quiet $CSA_TPL_ROOT/tw-show-page.rdf) { 280 # custom extra RDF page section. 281 tpl.include.tw.page.rdf = $CSA_TPL_ROOT/tw-show-page.rdf 282 } else { 283 # default extra RDF page section. 284 tpl.include.tw.page.rdf = $tw_dstem/tw-show-page.rdf 285 } 286 287 # Policy checks should always come after the inclusion of group 288 # (and possibly also page) meta-data and group+cf file. 289 290 # Set page name to return to after successful authentication, in case 291 # the requested page requires restricted access. Note the further 292 # escaping of selected URI-encoded characters, see the CSA library 293 # function '_uriencode()' for more information. 294 295 csaIsInteractive && csaSession.set \ 296 ``$nl{echo $CSA_REQUEST_URI | sed 's/%\(2[6Ff]\|3[dDfF]\)/%25\1/g'} 18 297 298 # Check authorizations. An empty group list means that everyone can read 299 # the page. Editors are granted access in any case. 300 301 if (!~ $'tbl_page.p_allow' ()) { 302 303 if (!csvMatch $TNS_AUTH_GRP $'tbl_page.p_allow',editor) { 304 305 # Show self-registration form if appropriate. 306 csaIsInteractive && !csaTrue $CSA_AUTH_OK && 307 ~ $TNS_SELFREG_AUTH *[a-z]* && csaTrue $TNS_SELFREG_AUTOASK && 308 csaExit.location $CSA_RPC_URI'?0=showStatic&x-csa-lang='$CSA_LANG^'&1='$'cgi.group'^'&2=tw-reg-form' 309 310 csaExit.needauth 311 } 312 } 313 314 if (!~ $'cgi.revision' () && csaTrue $TNS_HISTORY_RESTRICT) { 315 316 # Non-editors will anyway be blocked by 'updatePage', but blocking 317 # them already at this stage will save a lot of resources in most 318 # cases, like the useless download of bulky WYSIWYG AJAX editor code 319 # and things like that. 320 321 ~ ,$TNS_AUTH_GRP, *,editor,* || csaExit.needauth 322 } 323 324 # page does not exist yet ? 325 if (~ $'tpl.include.tw.page' /dev/null) { 326 327 #~ $REMOTE_ADDR 192.168.1.2 && csaExit.env 328 329 cgi.wikilinks = () # tell _userproc() not to bother. 330 331 # There isn't really much else I can do if non-interactive. 332 csaIsInteractive || csaExit.fault 1011 $'cgi.page.literal' 333 334 if (csaIsFullPath --exists --quiet $CSA_TPL_ROOT/tw-new-page.txt) { 335 # custom page-creation template. 336 tpl.include.html.body = $CSA_TPL_ROOT/tw-new-page.txt 337 } else { 338 # default page-creation template. 339 tpl.include.html.body = $tw_dstem/tw-new-page.txt 340 } 341 342 # Build the list of URLs to existing page names which sound 343 # like the missing one, in case the editor actually meant 344 # one of those. This should help with keeping things tidy. 345 346 * = `{echo $soh^pagename$nl$'tpl.var.tw.page' | soundex --no-header} 347 348 csaMkTemp tpl.include.tw.page 349 350 getcolumn --input $tw_gstem/page+dat p_name p_uri | 351 soundex --soundex-column soundex_ | 352 awktable -- 'BEGIN{ print "" }' > $'tpl.include.tw.page' 365 366 } else { 367 368 tpl.var.tw.node = $'tbl_page.k_node' 369 370 # If page exists but its description begins with '-' then do not 371 # display it to non-editors. 372 373 if (!~ ,$TNS_AUTH_GRP, *,editor,* && ~ $'tbl_page.p_descr' -*) { 374 375 if (csaIsFullPath --exists --quiet $CSA_TPL_ROOT/tw-hidden-page.txt) { 376 # hidden-page custom template. 377 tpl.include.html.body = $CSA_TPL_ROOT/tw-hidden-page.txt 378 } else { 379 # hidden-page default template. 380 tpl.include.html.body = $tw_dstem/tw-hidden-page.txt 381 } 382 383 } else { 384 385 # If page exists but is empty use a different template. Note the 386 # use of grep(1) here instead of test(1), as tidy(1) always writes 387 # at least one newline in the page, which is then never completely 388 # zeroed. 389 390 if (!grep -qEe '(>[^<>]+|[^<>]+<)' $'tpl.include.tw.page') { 391 392 if (csaIsFullPath --exists \ 393 --quiet $CSA_TPL_ROOT/tw-empty-page.txt) { 394 # empty-page custom template. 395 tpl.include.html.body = $CSA_TPL_ROOT/tw-empty-page.txt 396 } else { 397 # empty-page default template. 398 tpl.include.html.body = $tw_dstem/tw-empty-page.txt 399 } 400 } 401 } 402 403 # Pingback specs recommend that an HTTP header is used, but not every 404 # client support this so I also insert a element in the 405 # response body. If both headers are present the specs madate that the 406 # header one prevail. 407 408 csaHttp.header --deferred X-Pingback \ 409 $CSA_RPC_URS/$CSA_ID/$CSA_LANG/$'tbl_group.g_uri'/$'tbl_page.p_uri'^'/ping?PUT' 410 411 tpl.var.tw.page.ping.url = '' 412 } 413 414 . $CSA_ROOT/lib/tpl-stuff.rc 415 416 if (!~ $'cgi.wikilinks' ()) { 417 csaMkTemp outfile 418 infile = $'tpl.include.tw.page' 419 tpl.include.tw.page = $outfile 420 } 421 422 if (~ $CSA_PGM(1) CSA2) { 423 tpl.include.tw.page = $'tpl.include.tw.page':p 424 } else tpl.include.tw.page = $'tpl.include.tw.page':c 425 426 # Create page widgets if necessary. 427 428 # Set target year/month for the calendar widget 429 if (~ $'tbl_page.p_vtime' ()) { 430 gencal_month = ($CSA_TIME_LOCAL(1) $CSA_TIME_LOCAL(2)) 431 } else { 432 * = ``(-,){echo -n $'tbl_page.p_vtime'} 433 ~ $#* 4 && shift # strip ranking 434 gencal_month = $* 435 } 436 437 gencal_xml = $tw_gstem/cal-$gencal_month(1)^-$gencal_month(2)^+xml 438 genarch_xml = $tw_gstem/year-links+xml 439 gencat_xml = $tw_gstem/cat-links+xml 440 441 if (!csaIsFullPath --exists --quiet $gencal_xml $genarch_xml $gencat_xml) { 442 443 # Set Principal Lock Semaphore(s) (PLS). 444 csaLock $tw_gstem/page+dat || csaExit.fault 445 pls_set = true 446 447 # Create main table if it does not yet exist. 448 if (!csaIsFullPath --exists --quiet $tw_gstem/page+dat) { 449 maketable --input \ 450 $CSA_ROOT/lib/page.xrf > $tw_gstem/page+dat || 451 csaExit.fault 0003 maketable 452 } 453 454 gencal_rcs = $tw_gstem/RCS/cal-$gencal_month(1)^-$gencal_month(2)^+xml,v 455 genarch_rcs = $tw_gstem/RCS/year-links+xml,v 456 gencat_rcs = $tw_gstem/RCS/cat-links+xml,v 457 458 gencat_mt = $tw_gstem/cat-mt+xml 459 gencat_mw = $tw_gstem/cat-mw+xml 460 461 gencat_mt_rcs = $tw_gstem/RCS/cat-mt+xml,v 462 gencat_mw_rcs = $tw_gstem/RCS/cat-mw+xml,v 463 464 widget_input = $tw_gstem/page+dat 465 466 . $CSA_ROOT/lib/widgets.rc 467 } 468 469 tpl.include.tw.related = $tw_gstem/$'cgi.page'-rel+xml 470 471 # (re)build the "related links" view if necessary. 472 if (!~ $'tbl_page.k_page' () && 473 csaIsFullPath --exists --quiet $tw_gstem/tag+dat && { 474 !csaIsFullPath --exists --quiet $'tpl.include.tw.related' || \ 475 test $tw_gstem/tag+dat -nt $'tpl.include.tw.related'}) { 476 477 # Set Principal Lock Semaphore(s) (PLS) if not done yet. 478 csaTrue $pls_set || csaLock $tw_gstem/page+dat || csaExit.fault 479 pls_set = true 480 481 csaTrapFile $tw_gstem/RCS/$'cgi.page'-rel+xml,v 482 csaOpen --relaxed --fast $'tpl.include.tw.related' || csaExit.fault 483 tmp2 = $CSA_RESULT 484 485 csaAwkCmd pageRelatedPages.awk 486 487 # Note that the relation is established over *all* tags, 488 # whether they are keywords, geo- or local tags, with the 489 # latters being case-sensitive. It may be possible to 490 # handle correlations over local tags case-insensitively 491 # by using "sorttable -f" and "jointable -i", but this needs 492 # to be worked upon. This code needs to be extended to select 493 # the desired topology among the supported ones (keywords, 494 # geo-tag, etc.). This will be done by filtering the input 495 # tags based on the desired topology, and in case this is 496 # geo-tagging the '_awk' script below will compute the vector 497 # of each g:x:y tag and append an additional "tag_topology" 498 # column containing something like g:1234567. The resulting 499 # value will then be zeroed on the least significant digits, 500 # possibly based on a group+cf var that specifies the desired 501 # approximation, and turned into, say, g:1230000, so that 502 # "sorttable" can do its grouping work. This should do the trick. 503 # The "sorttable -u k_page" will probably have to be changed into 504 # "sorttable tag_topology", with "pageRelatedPages.awk" handling 505 # uniqueness internally. For other then geo topology, the 506 # "tag_topology" column will contain the relevant tag unchanged. 507 508 _awk '-vtags_='$'tbl_page.p_tags' -- 'BEGIN{ 509 print "\001k_tag" 510 split(tags_,t_,/ +/) 511 for (i_ in t_) print t_[i_] 512 }' /dev/null | sorttable | jointable $tw_gstem/tag+dat | 513 sorttable -u k_page | $CSA_RESULT > $tmp2 || 514 csaExit.fault 0003 _awk/sorttable/jointable/sorttable/AWK 515 516 do_commit = true 517 } 518 519 # Commit widget creation if necessary. 520 ~ $do_commit () || csaCommit || csaExit.fault 521 522 #if (~ $tpl_name tw-*) { 523 # 524 # # Page names beginning with tw-* are reserved for TW use, and usually 525 # # refer to views that do not correspond to actual pages on disk, so 526 # # we need to toggle unapplicable sections in templates. 527 # 528 # tpl.if.tw.ispage = '(::DEL:)' 529 # tpl.fi.tw.ispage = '(:DEL::)' 530 # tpl.if.tw.editor = '(::DEL:)' 531 # tpl.fi.tw.editor = '(:DEL::)' 532 #} 533 534 # Make sure no-replace mode is used on the page body by prepending 535 # its name with '-', or any user-supplied CSA tags in the text will 536 # be happily parsed by _envtoxml() !! 537 538 tpl.include.tw.page = -$'tpl.include.tw.page' 539 540 if (!~ $CSA_PGM(1) CSA2) { 541 csaMkTemp tpl.include.tw.page.^(header footer) 542 csaExit.ok $tpl_file 543 } 544 545 # RPC2 mode detected, behave accordingly. 546 547 if (~ $'cgi.numeric' ()) { 548 tpl.var.tw.node = ''$CSA_LANG/$'tpl.var.tw.group.unx'/$'tpl.var.tw.page.unx'^'' 549 } else tpl.var.tw.node = ''$'tbl_page.k_node'^'' 550 551 csaMkTemp tpl.include.tw.rpc 552 553 switch ($CSA_PGM($#CSA_PGM)) { 554 555 case blogger.* 556 557 cat <<'EOF' > $'tpl.include.tw.rpc' || csaExit.fault 0003 cat 558 559 560 content 561 $[tpl.include.tw.page] 562 563 564 userId 565 $[tpl.var.tw.page.creau:x] 566 567 568 postId 569 $[tpl.var.tw.node] 570 571 572 dateCreated 573 $[tpl.var.tw.page.ctime:x] 574 575 576 EOF 577 578 case metaWeblog.getPost 579 580 cat <<'EOF' > $'tpl.include.tw.rpc' || csaExit.fault 0003 cat 581 582 583 categories 584 585 $[tpl.var.tw.page.subcat:x] 586 587 588 589 description 590 $[tpl.include.tw.page] 591 592 593 link 594 $[tpl.var.tw.link:x] 595 596 597 userId 598 $[tpl.var.tw.page.creau:x] 599 600 601 postId 602 $[tpl.var.tw.node] 603 604 605 dateCreated 606 $[tpl.var.tw.page.ctime:x] 607 608 609 EOF 610 611 case mt.setPostCategories 612 613 # Although this is implemented as a dummy method (see below), 614 # strictly speaking it requires editing privileges. 615 ~ ,$TNS_AUTH_GRP, *,editor,* || csaExit.needauth 616 } 617 618 # Take a default OK exit even if none of the above switch cases matches, 619 # that is also in the case I have been called as mt.setPostCategories , 620 # which TW implements as a dummy method. 621 622 csaExit.ok 623 624 # End of program.