Mercurial > public > mercurial-scm > hg-stable
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() |