blob: a0e1245ca80d5a766f391444f6f22f2c4d4306d1 [file]
#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# 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.
#
"""Used to confirm and act on delete requests from the Admin Console."""
import re
import urllib
from google.appengine.api import datastore
from google.appengine.ext import webapp
from google.appengine.ext.datastore_admin import utils
from google.appengine.ext.mapreduce import model
from google.appengine.ext.mapreduce import operation
MAPREDUCE_OBJECTS = [model.MapreduceState.kind(),
model.ShardState.kind()]
XSRF_ACTION = 'delete'
KIND_AND_SIZE_RE = re.compile('^(.*)\|(-?[0-9]+)$')
def DeleteEntity(key):
"""Delete function which deletes all processed entities.
Args:
key: key of the entity to delete.
Yields:
a delete operation if the entity is not an active mapreduce object.
"""
if key.kind() in MAPREDUCE_OBJECTS:
entity = datastore.Get(key)
if entity and not entity["active"]:
yield operation.db.Delete(key)
else:
yield operation.db.Delete(key)
class ConfirmDeleteHandler(webapp.RequestHandler):
"""Handler to deal with requests from the admin console to delete data."""
SUFFIX = 'confirm_delete'
@classmethod
def Render(cls, handler):
"""Rendering method that can be called by main.py or get.
This method executes no action, so the method by which it is accessed is
immaterial. Creating a form with get may be a desirable function. That is,
if this builtin is turned on, anyone can create a form to delete a kind by
simply linking to the ConfirmDeleteHandler like so:
<a href="/_ah/datastore_admin/confirm_delete?kind=trash">
Delete all Trash Objects</a>
Args:
handler: the webapp.RequestHandler invoking the method
"""
namespace = handler.request.get('namespace')
kinds = handler.request.get('kind', allow_multiple=True)
sizes_known, size_total, remainder = utils.ParseKindsAndSizes(kinds)
(namespace_str, kind_str) = utils.GetPrintableStrs(namespace, kinds)
template_params = {
'form_target': DoDeleteHandler.SUFFIX,
'kind_list': kinds,
'remainder': remainder,
'sizes_known': sizes_known,
'size_total': size_total,
'app_id': handler.request.get('app_id'),
'cancel_url': handler.request.get('cancel_url'),
'kind_str': kind_str,
'namespace_str': namespace_str,
'xsrf_token': utils.CreateXsrfToken(XSRF_ACTION),
}
utils.RenderToResponse(handler, 'confirm_delete.html', template_params)
def get(self):
"""Handler for get requests to datastore_admin/confirm_delete."""
ConfirmDeleteHandler.Render(self)
class DoDeleteHandler(webapp.RequestHandler):
"""Handler to deal with requests from the admin console to delete data."""
SUFFIX = 'delete.do'
DELETE_HANDLER = (
'google.appengine.ext.datastore_admin.delete_handler.DeleteEntity')
INPUT_READER = (
'google.appengine.ext.mapreduce.input_readers.DatastoreKeyInputReader')
MAPREDUCE_DETAIL = utils.config.MAPREDUCE_PATH + '/detail?mapreduce_id='
def get(self):
"""Handler for get requests to datastore_admin/delete.do.
Status of executed jobs is displayed.
"""
jobs = self.request.get('job', allow_multiple=True)
error = self.request.get('error', '')
xsrf_error = self.request.get('xsrf_error', '')
template_params = {
'job_list': jobs,
'mapreduce_detail': self.MAPREDUCE_DETAIL,
'error': error,
'xsrf_error': xsrf_error,
}
utils.RenderToResponse(self, 'do_delete.html', template_params)
def post(self):
"""Handler for post requests to datastore_admin/delete.do.
Jobs are executed and user is redirected to the get handler.
"""
namespace = self.request.get('namespace')
kinds = self.request.get('kind', allow_multiple=True)
(namespace_str, kinds_str) = utils.GetPrintableStrs(namespace, kinds)
token = self.request.get('xsrf_token')
jobs = []
if utils.ValidateXsrfToken(token, XSRF_ACTION):
try:
op = utils.StartOperation(
'Deleting %s%s' % (kinds_str, namespace_str))
name_template = 'Delete all %(kind)s objects%(namespace)s'
jobs = utils.RunMapForKinds(
op,
kinds,
name_template,
self.DELETE_HANDLER,
self.INPUT_READER,
{})
error = ''
except Exception, e:
error = self._HandleException(e)
parameters = [('job', job) for job in jobs]
if error:
parameters.append(('error', error))
else:
parameters = [('xsrf_error', '1')]
query = urllib.urlencode(parameters)
self.redirect('%s/%s?%s' % (utils.config.BASE_PATH, self.SUFFIX, query))
def _HandleException(self, e):
"""Make exception handling overrideable by tests.
In normal cases, return only the error string; do not fail to render the
page for user.
"""
return str(e)