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}] |
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}] |
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() |