Added JSON API.
This commit is contained in:
parent
d5d704d012
commit
010447f9f9
24
lodgeit/controllers/json.py
Normal file
24
lodgeit/controllers/json.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
lodgeit.controllers.json
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The JSON controller
|
||||
|
||||
:copyright: 2008 by Armin Ronacher.
|
||||
:license: BSD
|
||||
"""
|
||||
from lodgeit import local
|
||||
from lodgeit.lib.webapi import json
|
||||
from lodgeit.utils import render_to_response
|
||||
|
||||
|
||||
class JSONController(object):
|
||||
|
||||
def handle_request(self):
|
||||
if local.request.args.get('method'):
|
||||
return json.handle_request()
|
||||
return render_to_response('json.html')
|
||||
|
||||
|
||||
controller = JSONController
|
@ -10,17 +10,17 @@
|
||||
"""
|
||||
from werkzeug.exceptions import NotFound
|
||||
from lodgeit import local
|
||||
from lodgeit.i18n import _
|
||||
from lodgeit.i18n import lazy_gettext
|
||||
from lodgeit.utils import render_to_response
|
||||
from lodgeit.lib.xmlrpc import xmlrpc
|
||||
from lodgeit.lib.webapi import get_public_methods
|
||||
from lodgeit.lib.highlighting import LANGUAGES
|
||||
|
||||
|
||||
HELP_PAGES = [
|
||||
('pasting', _('Pasting')),
|
||||
('advanced', _('Advanced Features')),
|
||||
('xmlrpc', _('Using the XMLRPC Interface')),
|
||||
('integration', _('Scripts and Editor Integration'))
|
||||
('pasting', lazy_gettext('Pasting')),
|
||||
('advanced', lazy_gettext('Advanced Features')),
|
||||
('api', lazy_gettext('Using the LodgeIt API')),
|
||||
('integration', lazy_gettext('Scripts and Editor Integration'))
|
||||
]
|
||||
|
||||
known_help_pages = set(x[0] for x in HELP_PAGES)
|
||||
@ -45,10 +45,9 @@ class StaticController(object):
|
||||
tmpl_name,
|
||||
help_topics=HELP_PAGES,
|
||||
current_topic=topic,
|
||||
xmlrpc_url='http://%s/xmlrpc/' %
|
||||
local.request.environ['SERVER_NAME'],
|
||||
pastebin_url=local.request.host_url,
|
||||
formatters=LANGUAGES,
|
||||
xmlrpc_methods=xmlrpc.get_public_methods()
|
||||
xmlrpc_methods=get_public_methods()
|
||||
)
|
||||
|
||||
|
||||
|
@ -10,13 +10,10 @@
|
||||
"""
|
||||
from lodgeit import local
|
||||
from lodgeit.utils import render_to_response
|
||||
from lodgeit.database import session, Paste
|
||||
from lodgeit.lib.xmlrpc import xmlrpc, exported
|
||||
from lodgeit.lib.highlighting import STYLES, LANGUAGES, get_style, \
|
||||
get_language_for
|
||||
from lodgeit.lib.webapi import xmlrpc
|
||||
|
||||
|
||||
class XmlRpcController(object):
|
||||
class XMLRPCController(object):
|
||||
|
||||
def handle_request(self):
|
||||
if local.request.method == 'POST':
|
||||
@ -24,85 +21,4 @@ class XmlRpcController(object):
|
||||
return render_to_response('xmlrpc.html')
|
||||
|
||||
|
||||
@exported('pastes.newPaste')
|
||||
def pastes_new_paste(language, code, parent_id=None,
|
||||
filename='', mimetype='', private=False):
|
||||
"""Create a new paste. Return the new ID.
|
||||
|
||||
`language` can be None, in which case the language will be
|
||||
guessed from `filename` and/or `mimetype`.
|
||||
"""
|
||||
if not language:
|
||||
language = get_language_for(filename or '', mimetype or '')
|
||||
parent = None
|
||||
if parent_id:
|
||||
parent = Paste.get(parent_id)
|
||||
if parent is None:
|
||||
raise ValueError('parent paste not found')
|
||||
|
||||
paste = Paste(code, language, parent, private=private)
|
||||
session.flush()
|
||||
return paste.identifier
|
||||
|
||||
|
||||
@exported('pastes.getPaste')
|
||||
def pastes_get_paste(paste_id):
|
||||
"""Get all known information about a paste by a given paste id.
|
||||
|
||||
Return a dictionary with these keys:
|
||||
`paste_id`, `code`, `parsed_code`, `pub_date`, `language`,
|
||||
`parent_id`, `url`.
|
||||
"""
|
||||
paste = Paste.get(paste_id)
|
||||
if paste is None:
|
||||
return False
|
||||
return paste.to_xmlrpc_dict()
|
||||
|
||||
|
||||
@exported('pastes.getDiff')
|
||||
def pastes_get_diff(old_id, new_id):
|
||||
"""Compare the two pastes and return an unified diff."""
|
||||
old = Paste.get(old_id)
|
||||
new = Paste.get(new_id)
|
||||
if old is None or new is None:
|
||||
raise ValueError('argument error, paste not found')
|
||||
return old.compare_to(new)
|
||||
|
||||
|
||||
@exported('pastes.getRecent')
|
||||
def pastes_get_recent(amount=5):
|
||||
"""Return information dict (see `getPaste`) about the last
|
||||
`amount` pastes.
|
||||
"""
|
||||
amount = min(amount, 20)
|
||||
return [x.to_xmlrpc_dict() for x in Paste.find_all().limit(amount)]
|
||||
|
||||
|
||||
@exported('pastes.getLast')
|
||||
def pastes_get_last():
|
||||
"""Get information dict (see `getPaste`) for the most recent paste."""
|
||||
rv = pastes_get_recent(1)
|
||||
if rv:
|
||||
return rv[0]
|
||||
return {}
|
||||
|
||||
|
||||
@exported('pastes.getLanguages')
|
||||
def pastes_get_languages():
|
||||
"""Get a list of supported languages."""
|
||||
return LANGUAGES.items()
|
||||
|
||||
|
||||
@exported('styles.getStyles')
|
||||
def styles_get_styles():
|
||||
"""Get a list of supported styles."""
|
||||
return STYLES.items()
|
||||
|
||||
|
||||
@exported('styles.getStylesheet')
|
||||
def styles_get_stylesheet(name):
|
||||
"""Return the stylesheet for a given style."""
|
||||
return get_style(name)
|
||||
|
||||
|
||||
controller = XmlRpcController
|
||||
controller = XMLRPCController
|
||||
|
@ -170,11 +170,10 @@ class Paste(object):
|
||||
|
||||
def to_xmlrpc_dict(self):
|
||||
"""Convert the paste into a dict for XMLRCP."""
|
||||
from lodgeit.lib.xmlrpc import strip_control_chars
|
||||
return {
|
||||
'paste_id': self.paste_id,
|
||||
'code': strip_control_chars(self.code),
|
||||
'parsed_code': strip_control_chars(self.parsed_code),
|
||||
'code': self.code,
|
||||
'parsed_code': self.parsed_code,
|
||||
'pub_date': int(time.mktime(self.pub_date.timetuple())),
|
||||
'language': self.language,
|
||||
'parent_id': self.parent_id,
|
||||
|
BIN
lodgeit/i18n/de/LC_MESSAGES/messages.mo
Normal file
BIN
lodgeit/i18n/de/LC_MESSAGES/messages.mo
Normal file
Binary file not shown.
47
lodgeit/lib/json.py
Normal file
47
lodgeit/lib/json.py
Normal file
@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
lodgeit.lib.json
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
This module implements a simple JSON API.
|
||||
|
||||
:copyright: 2008 by Armin Ronacher.
|
||||
:license: BSD
|
||||
"""
|
||||
from simplejson import dumps, loads
|
||||
from werkzeug import Response
|
||||
from lodgeit import local
|
||||
|
||||
|
||||
class JSONRequestHandler(object):
|
||||
|
||||
def __init__(self):
|
||||
self.funcs = {}
|
||||
|
||||
def register_function(self, func, name=None):
|
||||
self.funcs[name or func.__name__] = func
|
||||
|
||||
def handle_request(self):
|
||||
try:
|
||||
method_name = local.request.args['method']
|
||||
if not local.request.data:
|
||||
args = ()
|
||||
kwargs = {}
|
||||
else:
|
||||
args = loads(local.request.data)
|
||||
if isinstance(args, dict):
|
||||
kwargs = dict((str(key), value) for
|
||||
key, value in args.iteritems())
|
||||
args = ()
|
||||
elif isinstance(args, list):
|
||||
kwargs = {}
|
||||
else:
|
||||
raise TypeError('arguments as object or list expected')
|
||||
response = {
|
||||
'data': self.funcs[method_name](*args, **kwargs),
|
||||
'error': None
|
||||
}
|
||||
except Exception, e:
|
||||
response = {'data': None, 'error': str(e).decode('utf-8')}
|
||||
body = dumps(response, indent=local.request.is_xhr and 2 or 0)
|
||||
return Response(body + '\n', mimetype='application/json')
|
142
lodgeit/lib/webapi.py
Normal file
142
lodgeit/lib/webapi.py
Normal file
@ -0,0 +1,142 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
lodgeit.lib.webapi
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module implements the web api.
|
||||
|
||||
:copyright: Copyright 2008 by Armin Ronacher.
|
||||
:license: BSD.
|
||||
"""
|
||||
import inspect
|
||||
from lodgeit.database import session, Paste
|
||||
from lodgeit.lib.xmlrpc import XMLRPCRequestHandler
|
||||
from lodgeit.lib.json import JSONRequestHandler
|
||||
from lodgeit.lib.highlighting import STYLES, LANGUAGES, get_style, \
|
||||
get_language_for
|
||||
|
||||
|
||||
xmlrpc = XMLRPCRequestHandler()
|
||||
json = JSONRequestHandler()
|
||||
|
||||
|
||||
def exported(name, hidden=False):
|
||||
"""Make a function external available via xmlrpc."""
|
||||
def proxy(f):
|
||||
xmlrpc.register_function(f, name)
|
||||
json.register_function(f, name)
|
||||
f.hidden = hidden
|
||||
return f
|
||||
return proxy
|
||||
|
||||
|
||||
_public_methods = None
|
||||
def get_public_methods():
|
||||
"""Returns the public methods."""
|
||||
global _public_methods
|
||||
if _public_methods is None:
|
||||
result = []
|
||||
for name, f in json.funcs.iteritems():
|
||||
if name.startswith('system.') or f.hidden:
|
||||
continue
|
||||
args, varargs, varkw, defaults = inspect.getargspec(f)
|
||||
if args and args[0] == 'request':
|
||||
args = args[1:]
|
||||
result.append({
|
||||
'name': name,
|
||||
'doc': inspect.getdoc(f) or '',
|
||||
'signature': inspect.formatargspec(
|
||||
args, varargs, varkw, defaults,
|
||||
formatvalue=lambda o: '=' + repr(o)
|
||||
)
|
||||
})
|
||||
result.sort(key=lambda x: x['name'].lower())
|
||||
_public_methods = result
|
||||
return _public_methods
|
||||
|
||||
|
||||
@exported('system.listMethods')
|
||||
def system_list_methods():
|
||||
return [x['name'] for x in get_public_methods()]
|
||||
|
||||
|
||||
@exported('pastes.newPaste')
|
||||
def pastes_new_paste(language, code, parent_id=None,
|
||||
filename='', mimetype='', private=False):
|
||||
"""Create a new paste. Return the new ID.
|
||||
|
||||
`language` can be None, in which case the language will be
|
||||
guessed from `filename` and/or `mimetype`.
|
||||
"""
|
||||
if not language:
|
||||
language = get_language_for(filename or '', mimetype or '')
|
||||
parent = None
|
||||
if parent_id:
|
||||
parent = Paste.get(parent_id)
|
||||
if parent is None:
|
||||
raise ValueError('parent paste not found')
|
||||
|
||||
paste = Paste(code, language, parent, private=private)
|
||||
session.flush()
|
||||
return paste.identifier
|
||||
|
||||
|
||||
@exported('pastes.getPaste')
|
||||
def pastes_get_paste(paste_id):
|
||||
"""Get all known information about a paste by a given paste id.
|
||||
|
||||
Return a dictionary with these keys:
|
||||
`paste_id`, `code`, `parsed_code`, `pub_date`, `language`,
|
||||
`parent_id`, `url`.
|
||||
"""
|
||||
paste = Paste.get(paste_id)
|
||||
if paste is not None:
|
||||
return paste.to_xmlrpc_dict()
|
||||
|
||||
|
||||
@exported('pastes.getDiff')
|
||||
def pastes_get_diff(old_id, new_id):
|
||||
"""Compare the two pastes and return an unified diff."""
|
||||
old = Paste.get(old_id)
|
||||
new = Paste.get(new_id)
|
||||
if old is None or new is None:
|
||||
raise ValueError('argument error, paste not found')
|
||||
return old.compare_to(new)
|
||||
|
||||
|
||||
@exported('pastes.getRecent')
|
||||
def pastes_get_recent(amount=5):
|
||||
"""Return information dict (see `getPaste`) about the last
|
||||
`amount` pastes.
|
||||
"""
|
||||
amount = min(amount, 20)
|
||||
return [x.to_xmlrpc_dict() for x in Paste.find_all().limit(amount)]
|
||||
|
||||
|
||||
@exported('pastes.getLast')
|
||||
def pastes_get_last():
|
||||
"""Get information dict (see `getPaste`) for the most recent paste."""
|
||||
rv = pastes_get_recent(1)
|
||||
if rv:
|
||||
return rv[0]
|
||||
return {}
|
||||
|
||||
|
||||
@exported('pastes.getLanguages')
|
||||
def pastes_get_languages():
|
||||
"""Get a list of supported languages."""
|
||||
# this resolves lazy translations
|
||||
return dict((key, unicode(value)) for
|
||||
key, value in LANGUAGES.iteritems())
|
||||
|
||||
|
||||
@exported('styles.getStyles')
|
||||
def styles_get_styles():
|
||||
"""Get a list of supported styles."""
|
||||
return STYLES.items()
|
||||
|
||||
|
||||
@exported('styles.getStylesheet')
|
||||
def styles_get_stylesheet(name):
|
||||
"""Return the stylesheet for a given style."""
|
||||
return get_style(name)
|
@ -10,7 +10,6 @@
|
||||
"""
|
||||
import sys
|
||||
import re
|
||||
import inspect
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
|
||||
from werkzeug import Response
|
||||
from lodgeit import local
|
||||
@ -28,54 +27,12 @@ class XMLRPCRequestHandler(SimpleXMLRPCDispatcher):
|
||||
SimpleXMLRPCDispatcher.__init__(self)
|
||||
else:
|
||||
SimpleXMLRPCDispatcher.__init__(self, True, 'utf-8')
|
||||
self.funcs['system.listMethods'] = self.list_methods
|
||||
|
||||
def list_methods(self, request):
|
||||
return [x['name'] for x in self.get_public_methods()]
|
||||
|
||||
def handle_request(self):
|
||||
def dispatch(method_name, params):
|
||||
return self.funcs[method_name](*params)
|
||||
rv = self.funcs[method_name](*params)
|
||||
if rv is None:
|
||||
rv = False
|
||||
return rv
|
||||
response = self._marshaled_dispatch(local.request.data, dispatch)
|
||||
return Response(response, mimetype='text/xml')
|
||||
|
||||
def get_public_methods(self):
|
||||
if not hasattr(self, '_public_methods'):
|
||||
# make sure all callbacks are registered
|
||||
import lodgeit.controllers.xmlrpc
|
||||
result = []
|
||||
for name, f in self.funcs.iteritems():
|
||||
if name.startswith('system.'):
|
||||
continue
|
||||
if f.hidden:
|
||||
continue
|
||||
args, varargs, varkw, defaults = inspect.getargspec(f)
|
||||
if args and args[0] == 'request':
|
||||
args = args[1:]
|
||||
result.append({
|
||||
'name': name,
|
||||
'doc': inspect.getdoc(f) or '',
|
||||
'signature': inspect.formatargspec(
|
||||
args, varargs, varkw, defaults,
|
||||
formatvalue=lambda o: '=' + repr(o)
|
||||
)
|
||||
})
|
||||
result.sort(key=lambda x: x['name'].lower())
|
||||
self._public_methods = result
|
||||
return self._public_methods
|
||||
|
||||
|
||||
xmlrpc = XMLRPCRequestHandler()
|
||||
|
||||
|
||||
def exported(name, hidden=False):
|
||||
"""Make a function external available via xmlrpc."""
|
||||
def proxy(f):
|
||||
xmlrpc.register_function(f, name)
|
||||
f.hidden = hidden
|
||||
return f
|
||||
return proxy
|
||||
|
||||
|
||||
def strip_control_chars(s):
|
||||
return _strip_re.sub('', s or '')
|
||||
return Response(_strip_re.sub('', response), mimetype='text/xml')
|
||||
|
@ -327,6 +327,7 @@ ul.paste_list pre {
|
||||
padding: 4px;
|
||||
font-family: 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 13px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
ul.paste_list li {
|
||||
|
@ -28,8 +28,9 @@ urlmap = Map([
|
||||
Rule('/all/', endpoint='pastes/show_all'),
|
||||
Rule('/all/<int:page>/', endpoint='pastes/show_all'),
|
||||
|
||||
# xmlrpc
|
||||
# xmlrpc and json
|
||||
Rule('/xmlrpc/', endpoint='xmlrpc/handle_request'),
|
||||
Rule('/json/', endpoint='json/handle_request'),
|
||||
|
||||
# static pages
|
||||
Rule('/about/', endpoint='static/about'),
|
||||
|
60
lodgeit/views/help/api.html
Normal file
60
lodgeit/views/help/api.html
Normal file
@ -0,0 +1,60 @@
|
||||
{% extends "help/layout.html" %}
|
||||
{% block help_body %}
|
||||
<h3>{% trans %}Using the LodegeIt API{% endtrans %}</h3>
|
||||
<p>{% trans %}
|
||||
LodgeIt supports currently two APIs: XMLRPC and good old JSON.
|
||||
{% endtrans %}</p>
|
||||
<p>{{ _('API URLs:') }}</p>
|
||||
<ul>
|
||||
<li><strong>XMLRPC:</strong> <tt>{{ pastebin_url }}xmlrpc/</tt></li>
|
||||
<li><strong>JSON:</strong> <tt>{{ pastebin_url }}json/</tt></li>
|
||||
</ul>
|
||||
<p><small>{{ _('Note the trailing slash in both URLs!') }}</small></p>
|
||||
<h3>{% trans %}XMLRPC Quickstart{% endtrans %}</h3>
|
||||
<p>{% trans %}
|
||||
You can connect to the XMLRPC interface with any XMLRPC library. If you're
|
||||
using Python the following piece of code connects you to the XMLRPC service:{% endtrans %}
|
||||
</p>
|
||||
<pre class="sample">{% filter escape %}
|
||||
>>> from xmlrpclib import ServerProxy
|
||||
>>> s = ServerProxy('{{ pastebin_url|escape }}xmlrpc/')
|
||||
{% endfilter %}</pre>
|
||||
<p>
|
||||
{% trans %}For example if you want to fetch one paste from the server you can do this:{% endtrans %}
|
||||
</p>
|
||||
<pre class="sample">{% filter escape %}
|
||||
>>> paste = s.pastes.getPaste(23)
|
||||
>>> print paste['code']
|
||||
'{{ "{% if users %}" }}\n...'
|
||||
{% endfilter %}</pre>
|
||||
<h3>{% trans %}JSON Quickstart{% endtrans %}</h3>
|
||||
<p>{% trans %}
|
||||
Alternatively you can use the JSON API. Basically what you do is sending
|
||||
the function arguments as a serialized JSON object or array to the JSON URL
|
||||
from above with the method name as URL parameter. You can do this for
|
||||
example by using the curl command line tool:
|
||||
{% endtrans %}</p>
|
||||
<pre class="sample">
|
||||
$ curl -d '{"paste_id": 23}' -H 'Content-Type: application/json'
|
||||
'http://localhost:5000/json/?method=pastes.getPaste'
|
||||
{
|
||||
"data": {
|
||||
"code": '{{ '{% if users %}' }}\n...',
|
||||
"parsed_code": ...,
|
||||
"language": 'html+django',
|
||||
"url": "/show/23/",
|
||||
"parent_id": null,
|
||||
"paste_id": 23
|
||||
},
|
||||
"error": null
|
||||
}</pre>
|
||||
<h3>{% trans %}Methods{% endtrans %}</h3>
|
||||
<p>{% trans %}For a list of all supported methods see the list below.{% endtrans %}</p>
|
||||
<ul class="xmlrpc-method-list">
|
||||
{% for method in xmlrpc_methods %}
|
||||
<li class="{{ loop.cycle('even', 'odd') }}"><p class="signature"><strong>{{
|
||||
method.name|escape }}</strong><em>{{ method.signature|escape }}</em></p>
|
||||
<p class="docstring">{{ method.doc|e }}</p></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
@ -1,34 +0,0 @@
|
||||
{% extends "help/layout.html" %}
|
||||
{% block help_body %}
|
||||
<h3>{% trans %}Using the XMLRPC Interface{% endtrans %}</h3>
|
||||
<p>{% trans xmlrpc_url=xmlrpc_url|escape %}
|
||||
The XMLRPC Interface is available at <tt>{{ xmlrpc_url }}</tt>.
|
||||
(Note the trailing slash!){% endtrans %}
|
||||
</p>
|
||||
<h3>{% trans %}Quickstart{% endtrans %}</h3>
|
||||
<p>{% trans %}
|
||||
You can connect to the XMLRPC interface with any XMLRPC library. If you're
|
||||
using Python the following piece of code connects you to the XMLRPC service:{% endtrans %}
|
||||
</p>
|
||||
<pre class="sample">{% filter escape %}
|
||||
>>> from xmlrpclib import ServerProxy
|
||||
>>> s = ServerProxy('{{ xmlrpc_url|escape }}')
|
||||
{% endfilter %}</pre>
|
||||
<p>
|
||||
{% trans %}For example if you want to fetch one paste from the server you can do this:{% endtrans %}
|
||||
</p>
|
||||
<pre class="sample">{% filter escape %}
|
||||
>>> paste = s.pastes.getPaste(23)
|
||||
>>> print paste['code']
|
||||
'{{ "{% if users %}" }}\n...'
|
||||
{% endfilter %}</pre>
|
||||
<p>{% trans %}For a list of all supported methods see the list below.{% endtrans %}</p>
|
||||
<h3>{% trans %}Methods{% endtrans %}</h3>
|
||||
<ul class="xmlrpc-method-list">
|
||||
{% for method in xmlrpc_methods %}
|
||||
<li class="{{ loop.cycle('even', 'odd') }}"><p class="signature"><strong>{{
|
||||
method.name|escape }}</strong><em>{{ method.signature|escape }}</em></p>
|
||||
<p class="docstring">{{ method.doc|e }}</p></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
16
lodgeit/views/json.html
Normal file
16
lodgeit/views/json.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends "layout.html" %}
|
||||
{% set page_title = _('JSON') %}
|
||||
{% set active_page = 'about' %}
|
||||
{% block body %}
|
||||
<div class="text">
|
||||
<h3>{% trans %}JSON Entrypoint{% endtrans %}</h3>
|
||||
<p>{% trans %}
|
||||
This is the entrypoint for the JSON API. If you're interested
|
||||
in using it head over to the <a href="/help/api/">API documentation</a>.
|
||||
{%- endtrans %}
|
||||
</p>
|
||||
<p>{% trans %}
|
||||
Alternatively you can also use the <a href="/xmlrpc/">XMLRPC</a> service.
|
||||
{% endtrans %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
@ -6,8 +6,11 @@
|
||||
<h3>{% trans %}XMLRPC Entrypoint{% endtrans %}</h3>
|
||||
<p>{% trans %}
|
||||
This is the entrypoint for the XMLRPC system. If you're interested
|
||||
in using it head over to the <a href="/help/xmlrpc/">XMLRPC documentation</a>.
|
||||
in using it head over to the <a href="/help/api/">API documentation</a>.
|
||||
{%- endtrans %}
|
||||
</p>
|
||||
<p>{% trans %}
|
||||
Alternatively you can also use the <a href="/json/">JSON</a> API.
|
||||
{% endtrans %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user