Resource Management
No matches found for resource .
Resource Details:
No matches found for proxy resource .
calendarserver-9.1+dfsg/calendarserver/webadmin/delegation.py 0000664 0000000 0000000 00000053373 13150035626 0024571 0 ustar 00root root 0000000 0000000 # -*- test-case-name: calendarserver.webadmin.test.test_resource -*-
##
# Copyright (c) 2009-2014 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
"""
Calendar Server Web Admin UI.
"""
__all__ = [
"WebAdminResource",
"WebAdminPage",
]
import urlparse
from calendarserver.tools.util import (
recordForPrincipalID, proxySubprincipal, action_addProxy,
action_removeProxy, principalForPrincipalID
)
from twistedcaldav.config import config
from twistedcaldav.extensions import DAVFile, ReadOnlyResourceMixIn
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from txweb2.http import Response
from txweb2.http_headers import MimeType
from txweb2.stream import MemoryStream
from twisted.python.modules import getModule
from zope.interface.declarations import implements
from txdav.xml import element as davxml
from twisted.web.iweb import ITemplateLoader
from twisted.web.template import (
Element, renderer, XMLFile, flattenString
)
from twext.who.idirectory import RecordType
from txdav.who.idirectory import RecordType as CalRecordType, AutoScheduleMode
allowedAutoScheduleModes = {
"default": None,
"none": AutoScheduleMode.none,
"accept-always": AutoScheduleMode.accept,
"decline-always": AutoScheduleMode.decline,
"accept-if-free": AutoScheduleMode.acceptIfFree,
"decline-if-busy": AutoScheduleMode.declineIfBusy,
"automatic": AutoScheduleMode.acceptIfFreeDeclineIfBusy,
}
class WebAdminPage(Element):
"""
Web administration renderer for HTML.
@ivar resource: a L{WebAdminResource}.
"""
loader = XMLFile(
getModule(__name__).filePath.sibling("delegation.html")
)
def __init__(self, resource):
super(WebAdminPage, self).__init__()
self.resource = resource
@renderer
def main(self, request, tag):
"""
Main renderer, which fills page-global slots like 'title'.
"""
searchTerm = request.args.get('resourceSearch', [''])[0]
return tag.fillSlots(resourceSearch=searchTerm)
@renderer
@inlineCallbacks
def hasSearchResults(self, request, tag):
"""
Renderer which detects if there are resource search results and
continues if so.
"""
if 'resourceSearch' not in request.args:
returnValue('')
if (yield self.performSearch(request)):
returnValue(tag)
else:
returnValue('')
@renderer
@inlineCallbacks
def noSearchResults(self, request, tag):
"""
Renderer which detects if there are resource search results and
continues if so.
"""
if 'resourceSearch' not in request.args:
returnValue('')
rows = yield self.performSearch(request)
if rows:
returnValue("")
else:
returnValue(tag)
_searchResults = None
@inlineCallbacks
def performSearch(self, request):
"""
Perform a directory search for users, groups, and resources based on the
resourceSearch query parameter. Cache the results of that search so
that it will only be done once per request.
"""
if self._searchResults is not None:
returnValue(self._searchResults)
searchTerm = request.args.get('resourceSearch', [''])[0]
if searchTerm:
results = sorted((yield self.resource.search(searchTerm)),
key=lambda record: record.fullNames[0])
else:
results = []
self._searchResults = results
returnValue(results)
@renderer
def searchResults(self, request, tag):
"""
Renderer which renders resource search results.
"""
d = self.performSearch(request)
return d.addCallback(searchToSlots, tag)
@renderer
@inlineCallbacks
def resourceDetails(self, request, tag):
"""
Renderer which fills slots for details of the resource selected by
the resourceId request parameter.
"""
resourceId = request.args.get('resourceId', [''])[0]
propertyName = request.args.get('davPropertyName', [''])[0]
proxySearch = request.args.get('proxySearch', [''])[0]
if resourceId:
principalResource = yield self.resource.getResourceById(
request, resourceId)
returnValue(
DetailsElement(
resourceId, principalResource, propertyName, proxySearch,
tag, self.resource
)
)
else:
returnValue("")
def searchToSlots(results, tag):
"""
Convert the result of doing a search to an iterable of tags.
"""
for idx, record in enumerate(results):
if hasattr(record, "shortNames"):
shortName = record.shortNames[0]
shortNames = record.shortNames
else:
shortName = "(none)"
shortNames = [shortName]
if hasattr(record, "emailAddresses"):
emailAddresses = record.emailAddresses
else:
emailAddresses = ["(none)"]
yield tag.clone().fillSlots(
rowClass="even" if (idx % 2 == 0) else "odd",
type=record.recordType.description,
shortName=shortName,
name=record.fullNames[0],
typeStr={
RecordType.user: "User",
RecordType.group: "Group",
CalRecordType.location: "Location",
CalRecordType.resource: "Resource",
CalRecordType.address: "Address",
}.get(record.recordType),
shortNames=str(", ".join(shortNames)),
emails=str(", ".join(emailAddresses)),
uid=str(record.uid),
)
class stan(object):
"""
L{ITemplateLoader} wrapper for an existing tag, in the style of Nevow's
'stan' loader.
"""
implements(ITemplateLoader)
def __init__(self, tag):
self.tag = tag
def load(self):
return self.tag
def recordTitle(record):
return u"{} ({} {})".format(record.fullNames[0], record.recordType.description, record.uid)
class DetailsElement(Element):
def __init__(self, resourceId, principalResource, davPropertyName,
proxySearch, tag, adminResource):
self.principalResource = principalResource
self.adminResource = adminResource
self.proxySearch = proxySearch
self.record = principalResource.record
tag.fillSlots(resourceTitle=recordTitle(self.record),
resourceId=resourceId,
davPropertyName=davPropertyName,
proxySearch=proxySearch)
try:
namespace, name = davPropertyName.split("#")
except Exception:
self.namespace = None
self.name = None
if davPropertyName:
self.error = davPropertyName
else:
self.error = None
else:
self.namespace = namespace
self.name = name
self.error = None
super(DetailsElement, self).__init__(loader=stan(tag))
@renderer
def propertyParseError(self, request, tag):
"""
Renderer to display an error when the user specifies an invalid property
name.
"""
if self.error is None:
return ""
else:
return tag.fillSlots(davPropertyName=self.error)
@renderer
@inlineCallbacks
def davProperty(self, request, tag):
"""
Renderer to display an error when the user specifies an invalid property
name.
"""
if self.name is not None:
try:
propval = yield self.principalResource.readProperty(
(self.namespace, self.name), request
)
except:
propval = "No such property: " + "#".join([self.namespace,
self.name])
else:
propval = propval.toxml()
returnValue(tag.fillSlots(value=propval))
else:
returnValue("")
@renderer
def autoSchedule(self, request, tag):
"""
Renderer which elides its tag for non-resource-type principals.
"""
if (
self.record.recordType.description != "user" and
self.record.recordType.description != "group" or
self.record.recordType.description == "user" and
config.Scheduling.Options.AutoSchedule.AllowUsers
):
return tag
return ""
@renderer
def isAutoSchedule(self, request, tag):
"""
Renderer which sets the 'selected' attribute on its tag if the resource
is auto-schedule.
"""
if self.record.autoScheduleMode is not AutoScheduleMode.none:
tag(selected='selected')
return tag
@renderer
def isntAutoSchedule(self, request, tag):
"""
Renderer which sets the 'selected' attribute on its tag if the resource
is not auto-schedule.
"""
if self.record.autoScheduleMode is AutoScheduleMode.none:
tag(selected='selected')
return tag
@renderer
def autoScheduleModeNone(self, request, tag):
"""
Renderer which sets the 'selected' attribute on its tag based on the resource
auto-schedule-mode.
"""
if self.record.autoScheduleMode is AutoScheduleMode.none:
tag(selected='selected')
return tag
@renderer
def autoScheduleModeAcceptAlways(self, request, tag):
"""
Renderer which sets the 'selected' attribute on its tag based on the resource
auto-schedule-mode.
"""
if self.record.autoScheduleMode is AutoScheduleMode.accept:
tag(selected='selected')
return tag
@renderer
def autoScheduleModeDeclineAlways(self, request, tag):
"""
Renderer which sets the 'selected' attribute on its tag based on the resource
auto-schedule-mode.
"""
if self.record.autoScheduleMode is AutoScheduleMode.decline:
tag(selected='selected')
return tag
@renderer
def autoScheduleModeAcceptIfFree(self, request, tag):
"""
Renderer which sets the 'selected' attribute on its tag based on the resource
auto-schedule-mode.
"""
if self.record.autoScheduleMode is AutoScheduleMode.acceptIfFree:
tag(selected='selected')
return tag
@renderer
def autoScheduleModeDeclineIfBusy(self, request, tag):
"""
Renderer which sets the 'selected' attribute on its tag based on the resource
auto-schedule-mode.
"""
if self.record.autoScheduleMode is AutoScheduleMode.declineIfBusy:
tag(selected='selected')
return tag
@renderer
def autoScheduleModeAutomatic(self, request, tag):
"""
Renderer which sets the 'selected' attribute on its tag based on the resource
auto-schedule-mode.
"""
if self.record.autoScheduleMode is AutoScheduleMode.acceptIfFreeDeclineIfBusy:
tag(selected='selected')
return tag
_matrix = None
@inlineCallbacks
def proxyMatrix(self, request):
"""
Compute a matrix of proxies to display in a 2-column table.
This value is cached so that multiple renderers may refer to it without
causing additional back-end queries.
@return: a L{Deferred} which fires with a list of 2-tuples of
(readProxy, writeProxy). If there is an unequal number of read and
write proxies, the tables will be padded out with C{None}s so that
some readProxy or writeProxy values will be C{None} at the end of
the table.
"""
if self._matrix is not None:
returnValue(self._matrix)
(readSubPrincipal, writeSubPrincipal) = (
(yield proxySubprincipal(self.principalResource, "read")),
(yield proxySubprincipal(self.principalResource, "write"))
)
if readSubPrincipal or writeSubPrincipal:
(readMembers, writeMembers) = (
(yield readSubPrincipal.readProperty(davxml.GroupMemberSet,
None)),
(yield writeSubPrincipal.readProperty(davxml.GroupMemberSet,
None))
)
if readMembers.children or writeMembers.children:
# FIXME: 'else' case needs to be handled by separate renderer
readProxies = []
writeProxies = []
def getres(ref):
return self.adminResource.getResourceById(request,
str(proxyHRef))
for proxyHRef in sorted(readMembers.children, key=str):
readProxies.append((yield getres(proxyHRef)))
for proxyHRef in sorted(writeMembers.children, key=str):
writeProxies.append((yield getres(proxyHRef)))
lendiff = len(readProxies) - len(writeProxies)
if lendiff > 0:
writeProxies += [None] * lendiff
elif lendiff < 0:
readProxies += [None] * -lendiff
self._matrix = zip(readProxies, writeProxies)
else:
self._matrix = []
else:
self._matrix = []
returnValue(self._matrix)
@renderer
@inlineCallbacks
def noProxies(self, request, tag):
"""
Renderer which shows its tag if there are no proxies for this resource.
"""
mtx = yield self.proxyMatrix(request)
if mtx:
returnValue("")
returnValue(tag)
@renderer
@inlineCallbacks
def hasProxies(self, request, tag):
"""
Renderer which shows its tag if there are any proxies for this resource.
"""
mtx = yield self.proxyMatrix(request)
if mtx:
returnValue(tag)
returnValue("")
@renderer
@inlineCallbacks
def noProxyResults(self, request, tag):
"""
Renderer which shows its tag if there are no proxy search results for
this request.
"""
if not self.proxySearch:
returnValue("")
results = yield self.performProxySearch()
if results:
returnValue("")
else:
returnValue(tag)
@renderer
@inlineCallbacks
def hasProxyResults(self, request, tag):
"""
Renderer which shows its tag if there are any proxy search results for
this request.
"""
results = yield self.performProxySearch()
if results:
returnValue(tag)
else:
returnValue("")
@renderer
@inlineCallbacks
def proxyRows(self, request, tag):
"""
Renderer which does zipping logic to render read-only and read-write
rows of existing proxies for the currently-viewed resource.
"""
result = []
mtx = yield self.proxyMatrix(request)
for idx, (readProxy, writeProxy) in enumerate(mtx):
result.append(ProxyRow(tag.clone(), idx, readProxy, writeProxy))
returnValue(result)
_proxySearchResults = None
def performProxySearch(self):
if self._proxySearchResults is not None:
return succeed(self._proxySearchResults)
if self.proxySearch:
def nameSorted(records):
self._proxySearchResults = sorted(records, key=lambda rec: rec.fullNames[0])
return records
return self.adminResource.search(
self.proxySearch).addCallback(nameSorted)
else:
return succeed([])
@renderer
def proxySearchRows(self, request, tag):
"""
Renderer which renders search results for the proxy form.
"""
d = self.performProxySearch()
return d.addCallback(searchToSlots, tag)
class ProxyRow(Element):
def __init__(self, tag, index, readProxy, writeProxy):
tag.fillSlots(rowClass="even" if (index % 2 == 0) else "odd")
super(ProxyRow, self).__init__(loader=stan(tag))
self.readProxy = readProxy
self.writeProxy = writeProxy
def proxies(self, proxyResource, tag):
if proxyResource is None:
return ''
return tag.fillSlots(proxy=recordTitle(proxyResource.record),
type=proxyResource.record.recordType.description,
fullName=proxyResource.record.fullNames[0],
uid=proxyResource.record.uid)
def noProxies(self, proxyResource, tag):
if proxyResource is None:
return tag
else:
return ""
@renderer
def readOnlyProxies(self, request, tag):
return self.proxies(self.readProxy, tag)
@renderer
def noReadOnlyProxies(self, request, tag):
return self.noProxies(self.readProxy, tag)
@renderer
def readWriteProxies(self, request, tag):
return self.proxies(self.writeProxy, tag)
@renderer
def noReadWriteProxies(self, request, tag):
return self.noProxies(self.writeProxy, tag)
class WebAdminResource (ReadOnlyResourceMixIn, DAVFile):
"""
Web administration HTTP resource.
"""
def __init__(self, path, root, directory, store, principalCollections=()):
self.root = root
self.directory = directory
self.store = store
super(WebAdminResource, self).__init__(
path,
principalCollections=principalCollections
)
# Only allow administrators to access
def defaultAccessControlList(self):
return davxml.ACL(*config.AdminACEs)
def etag(self):
# Can't be calculated here
return succeed(None)
def contentLength(self):
# Can't be calculated here
return None
def lastModified(self):
return None
def exists(self):
return True
def displayName(self):
return "Web Admin"
def contentType(self):
return MimeType.fromString("text/html; charset=utf-8")
def contentEncoding(self):
return None
def createSimilarFile(self, path):
return DAVFile(path, principalCollections=self.principalCollections())
@inlineCallbacks
def resourceActions(self, request, record):
"""
Take all actions on the given record based on the given request.
"""
def queryValue(arg):
return request.args.get(arg, [""])[0]
def queryValues(arg):
query = urlparse.parse_qs(urlparse.urlparse(request.uri).query,
True)
matches = []
for key in query.keys():
if key.startswith(arg):
matches.append(key[len(arg):])
return matches
autoScheduleMode = queryValue("autoScheduleMode")
makeReadProxies = queryValues("mkReadProxy|")
makeWriteProxies = queryValues("mkWriteProxy|")
removeProxies = queryValues("rmProxy|")
# Update the auto-schedule-mode value if specified.
if autoScheduleMode:
if (
record.recordType != RecordType.user and
record.recordType != RecordType.group or
record.recordType == RecordType.user and
config.Scheduling.Options.AutoSchedule.AllowUsers
):
autoScheduleMode = allowedAutoScheduleModes[autoScheduleMode]
yield record.setAutoScheduleMode(autoScheduleMode)
record.autoScheduleMode = autoScheduleMode
# Update the proxies if specified.
if removeProxies:
yield action_removeProxy(self.store, record, *removeProxies)
if makeReadProxies:
yield action_addProxy(self.store, record, "read", *makeReadProxies)
if makeWriteProxies:
yield action_addProxy(self.store, record, "write", *makeWriteProxies)
@inlineCallbacks
def render(self, request):
"""
Create a L{WebAdminPage} to render HTML content for this request, and
return a response.
"""
resourceId = request.args.get('resourceId', [''])[0]
if resourceId:
record = yield recordForPrincipalID(self.directory, resourceId)
yield self.resourceActions(request, record)
htmlContent = yield flattenString(request, WebAdminPage(self))
response = Response()
response.stream = MemoryStream(htmlContent)
for (header, value) in (
("content-type", self.contentType()),
("content-encoding", self.contentEncoding()),
):
if value is not None:
response.headers.setHeader(header, value)
returnValue(response)
def getResourceById(self, request, resourceId):
if resourceId.startswith("/"):
return request.locateResource(resourceId)
else:
return principalForPrincipalID(resourceId, directory=self.directory)
@inlineCallbacks
def search(self, searchStr):
searchStr = unicode(searchStr, "utf-8") if isinstance(searchStr, str) else searchStr
records = list((yield self.directory.recordsMatchingTokens(searchStr.strip().split())))
returnValue(records)
calendarserver-9.1+dfsg/calendarserver/webadmin/eventsource.py 0000664 0000000 0000000 00000017241 13150035626 0025012 0 ustar 00root root 0000000 0000000 # -*- test-case-name: calendarserver.webadmin.test.test_principals -*-
##
# Copyright (c) 2014-2017 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from __future__ import print_function
"""
Calendar Server principal management web UI.
"""
__all__ = [
"textAsEvent",
"EventSourceResource",
]
from collections import deque
from zope.interface import implementer, Interface
from twistedcaldav.simpleresource import SimpleResource
from twisted.internet.defer import Deferred, succeed
from txweb2.stream import IByteStream, fallbackSplit
from txweb2.http_headers import MimeType
from txweb2.http import Response
def textAsEvent(text, eventID=None, eventClass=None, eventRetry=None):
"""
Format some text as an HTML5 EventSource event. Since the EventSource data
format is text-oriented, this function expects L{unicode}, not L{bytes};
binary data should be encoded as text if it is to be used in an EventSource
stream.
UTF-8 encoded L{bytes} are returned because
http://www.w3.org/TR/eventsource/ states that the only allowed encoding
for C{text/event-stream} is UTF-8.
@param text: The text (ie. the message) to send in the event.
@type text: L{unicode}
@param eventID: An unique identifier for the event.
@type eventID: L{unicode}
@param eventClass: A class name (ie. a categorization) for the event.
@type eventClass: L{unicode}
@param eventRetry: The retry interval (in milliseconds) for the client to
wait before reconnecting if it gets disconnected.
@type eventRetry: L{int}
@return: An HTML5 EventSource event as text.
@rtype: UTF-8 encoded L{bytes}
"""
if text is None:
raise TypeError("text may not be None")
event = []
if eventID is not None:
event.append(u"id: {0}".format(eventID))
if eventClass is not None:
event.append(u"event: {0}".format(eventClass))
if eventRetry is not None:
event.append(u"retry: {0:d}".format(eventRetry))
event.extend(
u"data: {0}".format(l) for l in text.split("\n")
)
return (u"\n".join(event) + u"\n\n").encode("utf-8")
class IEventDecoder(Interface):
"""
An object that can be used to extract data from an application-specific
event object for encoding into an EventSource data stream.
"""
def idForEvent(event):
"""
@return: An unique identifier for the given event.
@rtype: L{unicode}
"""
def classForEvent(event):
"""
@return: A class name (ie. a categorization) for the event.
@rtype: L{unicode}
"""
def textForEvent(event):
"""
@return: The text (ie. the message) to send in the event.
@rtype: L{unicode}
"""
def retryForEvent(event):
"""
@return: The retry interval (in milliseconds) for the client to wait
before reconnecting if it gets disconnected.
@rtype: L{int}
"""
class EventSourceResource(SimpleResource):
"""
Resource that vends HTML5 EventSource events.
Events are stored in a ring buffer and streamed to clients.
"""
addSlash = False
def __init__(self, eventDecoder, principalCollections, bufferSize=400):
"""
@param eventDecoder: An object that can be used to extract data from
an event for encoding into an EventSource data stream.
@type eventDecoder: L{IEventDecoder}
@param bufferSize: The maximum number of events to keep in the ring
buffer.
@type bufferSize: L{int}
"""
super(EventSourceResource, self).__init__(principalCollections, isdir=False)
self._eventDecoder = eventDecoder
self._events = deque(maxlen=bufferSize)
self._streams = set()
def addEvents(self, events):
self._events.extend(events)
# Notify outbound streams that there is new data to vend
for stream in self._streams:
stream.didAddEvents()
def render(self, request):
lastID = request.headers.getRawHeaders(u"last-event-id")
response = Response()
response.stream = EventStream(self._eventDecoder, self._events, lastID)
response.headers.setHeader(
b"content-type", MimeType.fromString(b"text/event-stream")
)
# Keep track of the event streams
def cleanupFilter(_request, _response):
self._streams.remove(response.stream)
return _response
# request.addResponseFilter(cleanupFilter)
self._streams.add(response.stream)
return response
@implementer(IByteStream)
class EventStream(object):
"""
L{IByteStream} that streams out HTML5 EventSource events.
"""
length = None
def __init__(self, eventDecoder, events, lastID):
"""
@param eventDecoder: An object that can be used to extract data from
an event for encoding into an EventSource data stream.
@type eventDecoder: L{IEventDecoder}
@param events: Application-specific event objects.
@type events: sequence of L{object}
@param lastID: The identifier for the last event that was vended from
C{events}. Vending will resume starting from the following event.
@type lastID: L{int}
"""
super(EventStream, self).__init__()
self._eventDecoder = eventDecoder
self._events = events
self._lastID = lastID
self._closed = False
self._deferredRead = None
def didAddEvents(self):
d = self._deferredRead
if d is not None:
self._deferredRead = None
d.callback(None)
def read(self):
if self._closed:
return succeed(None)
lastID = self._lastID
eventID = None
idForEvent = self._eventDecoder.idForEvent
classForEvent = self._eventDecoder.classForEvent
textForEvent = self._eventDecoder.textForEvent
retryForEvent = self._eventDecoder.retryForEvent
for event in self._events:
eventID = idForEvent(event)
# If lastID is not None, skip messages up to and including the one
# referenced by lastID.
if lastID is not None:
if eventID == lastID:
eventID = None
lastID = None
continue
eventClass = classForEvent(event)
eventText = textForEvent(event)
eventRetry = retryForEvent(event)
self._lastID = eventID
return succeed(
textAsEvent(eventText, eventID, eventClass, eventRetry)
)
if eventID is not None:
# We just scanned all the messages, and none are the last one the
# client saw.
self._lastID = None
return succeed(b"")
# # This causes the client to poll, which is undesirable, but the
# # deferred below doesn't seem to work in real use...
# return succeed(None)
d = Deferred()
self._deferredRead = d
d.addCallback(lambda _: self.read())
return d
def split(self, point):
return fallbackSplit(self, point)
def close(self):
self._closed = True
calendarserver-9.1+dfsg/calendarserver/webadmin/landing.py 0000664 0000000 0000000 00000006602 13150035626 0024063 0 ustar 00root root 0000000 0000000 # -*- test-case-name: calendarserver.webadmin.test.test_landing -*-
##
# Copyright (c) 2009-2017 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
"""
Calendar Server Web Admin UI.
"""
__all__ = [
"WebAdminLandingResource",
]
# from twisted.web.template import renderer
from .resource import PageElement, TemplateResource
class WebAdminLandingPageElement(PageElement):
"""
Web administration langing page element.
"""
def __init__(self):
super(WebAdminLandingPageElement, self).__init__(u"landing")
def pageSlots(self):
return {
u"title": u"Server Administration",
}
class WebAdminLandingResource(TemplateResource):
"""
Web administration landing page resource.
"""
addSlash = True
def __init__(self, path, root, directory, store, principalCollections=()):
super(WebAdminLandingResource, self).__init__(WebAdminLandingPageElement, principalCollections, True)
from twistedcaldav.config import config as configuration
self.configuration = configuration
self.directory = directory
self.store = store
# self._path = path
# self._root = root
# self._principalCollections = principalCollections
from .config import ConfigurationResource
self.putChild(u"config", ConfigurationResource(configuration, principalCollections))
from .principals import PrincipalsResource
self.putChild(u"principals", PrincipalsResource(directory, store, principalCollections))
from .logs import LogsResource
self.putChild(u"logs", LogsResource(principalCollections))
from .work import WorkMonitorResource
self.putChild(u"work", WorkMonitorResource(store, principalCollections))
# def getChild(self, name):
# bound = super(WebAdminLandingResource, self).getChild(name)
# if bound is not None:
# return bound
# #
# # Dynamically load and vend child resources not bound using putChild()
# # in __init__(). This is useful for development, since it allows one
# # to comment out the putChild() call above, and then code will be
# # re-loaded for each request.
# #
# from . import config, principals, logs, work
# if name == u"config":
# reload(config)
# return config.ConfigurationResource(self.configuration, self._principalCollections)
# elif name == u"principals":
# reload(principals)
# return principals.PrincipalsResource(self.directory, self.store, self._principalCollections)
# elif name == u"logs":
# reload(logs)
# return logs.LogsResource(self._principalCollections)
# elif name == u"work":
# reload(work)
# return work.WorkMonitorResource(self.store, self._principalCollections)
# return None
calendarserver-9.1+dfsg/calendarserver/webadmin/landing.xhtml 0000664 0000000 0000000 00000000626 13150035626 0024567 0 ustar 00root root 0000000 0000000