comparison mercurial/hgweb/webcommands.py @ 36889:061635d4221c

hgweb: add a sendtemplate() helper function This pattern is common. Let's make a helper function to reduce boilerplate. We store the "global" template on the requestcontext instance and use it. The templater used by the helper function is the same templater that's passed in as an argument to the @webcommand functions. It needs to be this way because various commands are accessing and mutating the defaults on the templater instance. Differential Revision: https://phab.mercurial-scm.org/D2799
author Gregory Szorc <gregory.szorc@gmail.com>
date Sat, 10 Mar 2018 19:41:18 -0800
parents 66f62d120ba2
children ece242db5000
comparison
equal deleted inserted replaced
36888:66f62d120ba2 36889:061635d4221c
57 The functions should populate the ``rctx.res`` object with details 57 The functions should populate the ``rctx.res`` object with details
58 about the HTTP response. 58 about the HTTP response.
59 59
60 The function returns a generator to be consumed by the WSGI application. 60 The function returns a generator to be consumed by the WSGI application.
61 For most commands, this should be the result from 61 For most commands, this should be the result from
62 ``web.res.sendresponse()``. 62 ``web.res.sendresponse()``. Many commands will call ``web.sendtemplate()``
63 to render a template.
63 64
64 Usage: 65 Usage:
65 66
66 @webcommand('mycommand') 67 @webcommand('mycommand')
67 def mycommand(web, req, tmpl): 68 def mycommand(web, req, tmpl):
149 yield {"line": t, 150 yield {"line": t,
150 "lineid": "l%d" % (lineno + 1), 151 "lineid": "l%d" % (lineno + 1),
151 "linenumber": "% 6d" % (lineno + 1), 152 "linenumber": "% 6d" % (lineno + 1),
152 "parity": next(parity)} 153 "parity": next(parity)}
153 154
154 web.res.setbodygen(tmpl( 155 return web.sendtemplate(
155 'filerevision', 156 'filerevision',
156 file=f, 157 file=f,
157 path=webutil.up(f), 158 path=webutil.up(f),
158 text=lines(), 159 text=lines(),
159 symrev=webutil.symrevorshortnode(req, fctx), 160 symrev=webutil.symrevorshortnode(req, fctx),
160 rename=webutil.renamelink(fctx), 161 rename=webutil.renamelink(fctx),
161 permissions=fctx.manifest().flags(f), 162 permissions=fctx.manifest().flags(f),
162 ishead=int(ishead), 163 ishead=int(ishead),
163 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))) 164 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
164
165 return web.res.sendresponse()
166 165
167 @webcommand('file') 166 @webcommand('file')
168 def file(web, req, tmpl): 167 def file(web, req, tmpl):
169 """ 168 """
170 /file/{revision}[/{path}] 169 /file/{revision}[/{path}]
337 searchfunc = searchfuncs[mode] 336 searchfunc = searchfuncs[mode]
338 337
339 tip = web.repo['tip'] 338 tip = web.repo['tip']
340 parity = paritygen(web.stripecount) 339 parity = paritygen(web.stripecount)
341 340
342 web.res.setbodygen(tmpl( 341 return web.sendtemplate(
343 'search', 342 'search',
344 query=query, 343 query=query,
345 node=tip.hex(), 344 node=tip.hex(),
346 symrev='tip', 345 symrev='tip',
347 entries=changelist, 346 entries=changelist,
348 archives=web.archivelist('tip'), 347 archives=web.archivelist('tip'),
349 morevars=morevars, 348 morevars=morevars,
350 lessvars=lessvars, 349 lessvars=lessvars,
351 modedesc=searchfunc[1], 350 modedesc=searchfunc[1],
352 showforcekw=showforcekw, 351 showforcekw=showforcekw,
353 showunforcekw=showunforcekw)) 352 showunforcekw=showunforcekw)
354
355 return web.res.sendresponse()
356 353
357 @webcommand('changelog') 354 @webcommand('changelog')
358 def changelog(web, req, tmpl, shortlog=False): 355 def changelog(web, req, tmpl, shortlog=False):
359 """ 356 """
360 /changelog[/{revision}] 357 /changelog[/{revision}]
434 nextentry = entries[-1:] 431 nextentry = entries[-1:]
435 entries = entries[:-1] 432 entries = entries[:-1]
436 else: 433 else:
437 nextentry = [] 434 nextentry = []
438 435
439 web.res.setbodygen(tmpl( 436 return web.sendtemplate(
440 'shortlog' if shortlog else 'changelog', 437 'shortlog' if shortlog else 'changelog',
441 changenav=changenav, 438 changenav=changenav,
442 node=ctx.hex(), 439 node=ctx.hex(),
443 rev=pos, 440 rev=pos,
444 symrev=symrev, 441 symrev=symrev,
448 nextentry=nextentry, 445 nextentry=nextentry,
449 archives=web.archivelist('tip'), 446 archives=web.archivelist('tip'),
450 revcount=revcount, 447 revcount=revcount,
451 morevars=morevars, 448 morevars=morevars,
452 lessvars=lessvars, 449 lessvars=lessvars,
453 query=query)) 450 query=query)
454
455 return web.res.sendresponse()
456 451
457 @webcommand('shortlog') 452 @webcommand('shortlog')
458 def shortlog(web, req, tmpl): 453 def shortlog(web, req, tmpl):
459 """ 454 """
460 /shortlog 455 /shortlog
483 The ``changeset`` template is rendered. Contents of the ``changesettag``, 478 The ``changeset`` template is rendered. Contents of the ``changesettag``,
484 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many 479 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
485 templates related to diffs may all be used to produce the output. 480 templates related to diffs may all be used to produce the output.
486 """ 481 """
487 ctx = webutil.changectx(web.repo, req) 482 ctx = webutil.changectx(web.repo, req)
488 web.res.setbodygen(tmpl('changeset', 483
489 **webutil.changesetentry(web, req, tmpl, ctx))) 484 return web.sendtemplate(
490 return web.res.sendresponse() 485 'changeset',
486 **webutil.changesetentry(web, req, tmpl, ctx))
491 487
492 rev = webcommand('rev')(changeset) 488 rev = webcommand('rev')(changeset)
493 489
494 def decodepath(path): 490 def decodepath(path):
495 """Hook for mapping a path in the repository to a path in the 491 """Hook for mapping a path in the repository to a path in the
586 yield {"parity": next(parity), 582 yield {"parity": next(parity),
587 "path": path, 583 "path": path,
588 "emptydirs": "/".join(emptydirs), 584 "emptydirs": "/".join(emptydirs),
589 "basename": d} 585 "basename": d}
590 586
591 web.res.setbodygen(tmpl( 587 return web.sendtemplate(
592 'manifest', 588 'manifest',
593 symrev=symrev, 589 symrev=symrev,
594 path=abspath, 590 path=abspath,
595 up=webutil.up(abspath), 591 up=webutil.up(abspath),
596 upparity=next(parity), 592 upparity=next(parity),
597 fentries=filelist, 593 fentries=filelist,
598 dentries=dirlist, 594 dentries=dirlist,
599 archives=web.archivelist(hex(node)), 595 archives=web.archivelist(hex(node)),
600 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))) 596 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
601
602 return web.res.sendresponse()
603 597
604 @webcommand('tags') 598 @webcommand('tags')
605 def tags(web, req, tmpl): 599 def tags(web, req, tmpl):
606 """ 600 """
607 /tags 601 /tags
626 yield {"parity": next(parity), 620 yield {"parity": next(parity),
627 "tag": k, 621 "tag": k,
628 "date": web.repo[n].date(), 622 "date": web.repo[n].date(),
629 "node": hex(n)} 623 "node": hex(n)}
630 624
631 web.res.setbodygen(tmpl( 625 return web.sendtemplate(
632 'tags', 626 'tags',
633 node=hex(web.repo.changelog.tip()), 627 node=hex(web.repo.changelog.tip()),
634 entries=lambda **x: entries(False, False, **x), 628 entries=lambda **x: entries(False, False, **x),
635 entriesnotip=lambda **x: entries(True, False, **x), 629 entriesnotip=lambda **x: entries(True, False, **x),
636 latestentry=lambda **x: entries(True, True, **x))) 630 latestentry=lambda **x: entries(True, True, **x))
637
638 return web.res.sendresponse()
639 631
640 @webcommand('bookmarks') 632 @webcommand('bookmarks')
641 def bookmarks(web, req, tmpl): 633 def bookmarks(web, req, tmpl):
642 """ 634 """
643 /bookmarks 635 /bookmarks
667 if i: 659 if i:
668 latestrev = i[0][1] 660 latestrev = i[0][1]
669 else: 661 else:
670 latestrev = -1 662 latestrev = -1
671 663
672 web.res.setbodygen(tmpl( 664 return web.sendtemplate(
673 'bookmarks', 665 'bookmarks',
674 node=hex(web.repo.changelog.tip()), 666 node=hex(web.repo.changelog.tip()),
675 lastchange=[{'date': web.repo[latestrev].date()}], 667 lastchange=[{'date': web.repo[latestrev].date()}],
676 entries=lambda **x: entries(latestonly=False, **x), 668 entries=lambda **x: entries(latestonly=False, **x),
677 latestentry=lambda **x: entries(latestonly=True, **x))) 669 latestentry=lambda **x: entries(latestonly=True, **x))
678
679 return web.res.sendresponse()
680 670
681 @webcommand('branches') 671 @webcommand('branches')
682 def branches(web, req, tmpl): 672 def branches(web, req, tmpl):
683 """ 673 """
684 /branches 674 /branches
693 The ``branches`` template is rendered. 683 The ``branches`` template is rendered.
694 """ 684 """
695 entries = webutil.branchentries(web.repo, web.stripecount) 685 entries = webutil.branchentries(web.repo, web.stripecount)
696 latestentry = webutil.branchentries(web.repo, web.stripecount, 1) 686 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
697 687
698 web.res.setbodygen(tmpl( 688 return web.sendtemplate(
699 'branches', 689 'branches',
700 node=hex(web.repo.changelog.tip()), 690 node=hex(web.repo.changelog.tip()),
701 entries=entries, 691 entries=entries,
702 latestentry=latestentry)) 692 latestentry=latestentry)
703
704 return web.res.sendresponse()
705 693
706 @webcommand('summary') 694 @webcommand('summary')
707 def summary(web, req, tmpl): 695 def summary(web, req, tmpl):
708 """ 696 """
709 /summary 697 /summary
770 758
771 desc = web.config("web", "description") 759 desc = web.config("web", "description")
772 if not desc: 760 if not desc:
773 desc = 'unknown' 761 desc = 'unknown'
774 762
775 web.res.setbodygen(tmpl( 763 return web.sendtemplate(
776 'summary', 764 'summary',
777 desc=desc, 765 desc=desc,
778 owner=get_contact(web.config) or 'unknown', 766 owner=get_contact(web.config) or 'unknown',
779 lastchange=tip.date(), 767 lastchange=tip.date(),
780 tags=tagentries, 768 tags=tagentries,
782 branches=webutil.branchentries(web.repo, web.stripecount, 10), 770 branches=webutil.branchentries(web.repo, web.stripecount, 10),
783 shortlog=changelist, 771 shortlog=changelist,
784 node=tip.hex(), 772 node=tip.hex(),
785 symrev='tip', 773 symrev='tip',
786 archives=web.archivelist('tip'), 774 archives=web.archivelist('tip'),
787 labels=web.configlist('web', 'labels'))) 775 labels=web.configlist('web', 'labels'))
788
789 return web.res.sendresponse()
790 776
791 @webcommand('filediff') 777 @webcommand('filediff')
792 def filediff(web, req, tmpl): 778 def filediff(web, req, tmpl):
793 """ 779 """
794 /diff/{revision}/{path} 780 /diff/{revision}/{path}
825 ctx = fctx 811 ctx = fctx
826 else: 812 else:
827 rename = [] 813 rename = []
828 ctx = ctx 814 ctx = ctx
829 815
830 web.res.setbodygen(tmpl( 816 return web.sendtemplate(
831 'filediff', 817 'filediff',
832 file=path, 818 file=path,
833 symrev=webutil.symrevorshortnode(req, ctx), 819 symrev=webutil.symrevorshortnode(req, ctx),
834 rename=rename, 820 rename=rename,
835 diff=diffs, 821 diff=diffs,
836 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))) 822 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
837
838 return web.res.sendresponse()
839 823
840 diff = webcommand('diff')(filediff) 824 diff = webcommand('diff')(filediff)
841 825
842 @webcommand('comparison') 826 @webcommand('comparison')
843 def comparison(web, req, tmpl): 827 def comparison(web, req, tmpl):
900 ctx = fctx 884 ctx = fctx
901 else: 885 else:
902 rename = [] 886 rename = []
903 ctx = ctx 887 ctx = ctx
904 888
905 web.res.setbodygen(tmpl( 889 return web.sendtemplate(
906 'filecomparison', 890 'filecomparison',
907 file=path, 891 file=path,
908 symrev=webutil.symrevorshortnode(req, ctx), 892 symrev=webutil.symrevorshortnode(req, ctx),
909 rename=rename, 893 rename=rename,
910 leftrev=leftrev, 894 leftrev=leftrev,
911 leftnode=hex(leftnode), 895 leftnode=hex(leftnode),
912 rightrev=rightrev, 896 rightrev=rightrev,
913 rightnode=hex(rightnode), 897 rightnode=hex(rightnode),
914 comparison=comparison, 898 comparison=comparison,
915 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))) 899 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
916
917 return web.res.sendresponse()
918 900
919 @webcommand('annotate') 901 @webcommand('annotate')
920 def annotate(web, req, tmpl): 902 def annotate(web, req, tmpl):
921 """ 903 """
922 /annotate/{revision}/{path} 904 /annotate/{revision}/{path}
994 "revdate": f.date()} 976 "revdate": f.date()}
995 977
996 diffopts = webutil.difffeatureopts(req, web.repo.ui, 'annotate') 978 diffopts = webutil.difffeatureopts(req, web.repo.ui, 'annotate')
997 diffopts = {k: getattr(diffopts, k) for k in diffopts.defaults} 979 diffopts = {k: getattr(diffopts, k) for k in diffopts.defaults}
998 980
999 web.res.setbodygen(tmpl( 981 return web.sendtemplate(
1000 'fileannotate', 982 'fileannotate',
1001 file=f, 983 file=f,
1002 annotate=annotate, 984 annotate=annotate,
1003 path=webutil.up(f), 985 path=webutil.up(f),
1004 symrev=webutil.symrevorshortnode(req, fctx), 986 symrev=webutil.symrevorshortnode(req, fctx),
1005 rename=webutil.renamelink(fctx), 987 rename=webutil.renamelink(fctx),
1006 permissions=fctx.manifest().flags(f), 988 permissions=fctx.manifest().flags(f),
1007 ishead=int(ishead), 989 ishead=int(ishead),
1008 diffopts=diffopts, 990 diffopts=diffopts,
1009 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))) 991 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
1010
1011 return web.res.sendresponse()
1012 992
1013 @webcommand('filelog') 993 @webcommand('filelog')
1014 def filelog(web, req, tmpl): 994 def filelog(web, req, tmpl):
1015 """ 995 """
1016 /filelog/{revision}/{path} 996 /filelog/{revision}/{path}
1131 revnav = webutil.filerevnav(web.repo, fctx.path()) 1111 revnav = webutil.filerevnav(web.repo, fctx.path())
1132 nav = revnav.gen(end - 1, revcount, count) 1112 nav = revnav.gen(end - 1, revcount, count)
1133 1113
1134 latestentry = entries[:1] 1114 latestentry = entries[:1]
1135 1115
1136 web.res.setbodygen(tmpl( 1116 return web.sendtemplate(
1137 'filelog', 1117 'filelog',
1138 file=f, 1118 file=f,
1139 nav=nav, 1119 nav=nav,
1140 symrev=webutil.symrevorshortnode(req, fctx), 1120 symrev=webutil.symrevorshortnode(req, fctx),
1141 entries=entries, 1121 entries=entries,
1144 latestentry=latestentry, 1124 latestentry=latestentry,
1145 linerange=linerange, 1125 linerange=linerange,
1146 revcount=revcount, 1126 revcount=revcount,
1147 morevars=morevars, 1127 morevars=morevars,
1148 lessvars=lessvars, 1128 lessvars=lessvars,
1149 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))) 1129 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
1150
1151 return web.res.sendresponse()
1152 1130
1153 @webcommand('archive') 1131 @webcommand('archive')
1154 def archive(web, req, tmpl): 1132 def archive(web, req, tmpl):
1155 """ 1133 """
1156 /archive/{revision}.{format}[/{path}] 1134 /archive/{revision}.{format}[/{path}]
1377 1355
1378 yield entry 1356 yield entry
1379 1357
1380 rows = len(tree) 1358 rows = len(tree)
1381 1359
1382 web.res.setbodygen(tmpl( 1360 return web.sendtemplate(
1383 'graph', 1361 'graph',
1384 rev=rev, 1362 rev=rev,
1385 symrev=symrev, 1363 symrev=symrev,
1386 revcount=revcount, 1364 revcount=revcount,
1387 uprev=uprev, 1365 uprev=uprev,
1394 changesets=count, 1372 changesets=count,
1395 nextentry=nextentry, 1373 nextentry=nextentry,
1396 jsdata=lambda **x: jsdata(), 1374 jsdata=lambda **x: jsdata(),
1397 nodes=lambda **x: nodes(), 1375 nodes=lambda **x: nodes(),
1398 node=ctx.hex(), 1376 node=ctx.hex(),
1399 changenav=changenav)) 1377 changenav=changenav)
1400
1401 return web.res.sendresponse()
1402 1378
1403 def _getdoc(e): 1379 def _getdoc(e):
1404 doc = e[0].__doc__ 1380 doc = e[0].__doc__
1405 if doc: 1381 if doc:
1406 doc = _(doc).partition('\n')[0] 1382 doc = _(doc).partition('\n')[0]
1452 1428
1453 def othercommands(**map): 1429 def othercommands(**map):
1454 for c, doc in other: 1430 for c, doc in other:
1455 yield {'topic': c, 'summary': doc} 1431 yield {'topic': c, 'summary': doc}
1456 1432
1457 web.res.setbodygen(tmpl( 1433 return web.sendtemplate(
1458 'helptopics', 1434 'helptopics',
1459 topics=topics, 1435 topics=topics,
1460 earlycommands=earlycommands, 1436 earlycommands=earlycommands,
1461 othercommands=othercommands, 1437 othercommands=othercommands,
1462 title='Index')) 1438 title='Index')
1463 return web.res.sendresponse()
1464 1439
1465 # Render an index of sub-topics. 1440 # Render an index of sub-topics.
1466 if topicname in helpmod.subtopics: 1441 if topicname in helpmod.subtopics:
1467 topics = [] 1442 topics = []
1468 for entries, summary, _doc in helpmod.subtopics[topicname]: 1443 for entries, summary, _doc in helpmod.subtopics[topicname]:
1470 'topic': '%s.%s' % (topicname, entries[0]), 1445 'topic': '%s.%s' % (topicname, entries[0]),
1471 'basename': entries[0], 1446 'basename': entries[0],
1472 'summary': summary, 1447 'summary': summary,
1473 }) 1448 })
1474 1449
1475 web.res.setbodygen(tmpl( 1450 return web.sendtemplate(
1476 'helptopics', 1451 'helptopics',
1477 topics=topics, 1452 topics=topics,
1478 title=topicname, 1453 title=topicname,
1479 subindex=True)) 1454 subindex=True)
1480 return web.res.sendresponse()
1481 1455
1482 u = webutil.wsgiui.load() 1456 u = webutil.wsgiui.load()
1483 u.verbose = True 1457 u.verbose = True
1484 1458
1485 # Render a page from a sub-topic. 1459 # Render a page from a sub-topic.
1496 try: 1470 try:
1497 doc = helpmod.help_(u, commands, topic, subtopic=subtopic) 1471 doc = helpmod.help_(u, commands, topic, subtopic=subtopic)
1498 except error.Abort: 1472 except error.Abort:
1499 raise ErrorResponse(HTTP_NOT_FOUND) 1473 raise ErrorResponse(HTTP_NOT_FOUND)
1500 1474
1501 web.res.setbodygen(tmpl( 1475 return web.sendtemplate(
1502 'help', 1476 'help',
1503 topic=topicname, 1477 topic=topicname,
1504 doc=doc)) 1478 doc=doc)
1505
1506 return web.res.sendresponse()
1507 1479
1508 # tell hggettext to extract docstrings from these functions: 1480 # tell hggettext to extract docstrings from these functions:
1509 i18nfunctions = commands.values() 1481 i18nfunctions = commands.values()