mercurial/hgweb/webcommands.py
changeset 36883 061635d4221c
parent 36882 66f62d120ba2
child 36884 ece242db5000
equal deleted inserted replaced
36882:66f62d120ba2 36883: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()