GMaps related JS changed to use new google namespace.
Google is going to change permanently in the future the way to
load its services, so better stay safe.
Also this commit shows uses of the new melange.js module.
Fixes Issue 634.
#!/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.
#
"""Classes for common kinds, including Contact, Message, and Event.
Most of these kinds are based on the gd namespace "kinds" from GData:
http://code.google.com/apis/gdata/common-elements.html
"""
import types
import urlparse
from xml.sax import saxutils
from google.appengine.datastore import datastore_pb
from google.appengine.api import datastore
from google.appengine.api import datastore_errors
from google.appengine.api import datastore_types
class GdKind(datastore.Entity):
""" A base class for gd namespace kinds.
This class contains common logic for all gd namespace kinds. For example,
this class translates datastore (app id, kind, key) tuples to tag:
URIs appropriate for use in <key> tags.
"""
HEADER = u"""<entry xmlns:gd='http://schemas.google.com/g/2005'>
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://schemas.google.com/g/2005#%s' />"""
FOOTER = u"""
</entry>"""
_kind_properties = set()
_contact_properties = set()
def __init__(self, kind, title, kind_properties, contact_properties=[]):
""" Ctor.
title is the name of this particular entity, e.g. Bob Jones or Mom's
Birthday Party.
kind_properties is a list of property names that should be included in
this entity's XML encoding as first-class XML elements, instead of
<property> elements. 'title' and 'content' are added to kind_properties
automatically, and may not appear in contact_properties.
contact_properties is a list of property names that are Keys that point to
Contact entities, and should be included in this entity's XML encoding as
<gd:who> elements. If a property name is included in both kind_properties
and contact_properties, it is treated as a Contact property.
Args:
kind: string
title: string
kind_properties: list of strings
contact_properties: list of string
"""
datastore.Entity.__init__(self, kind)
if not isinstance(title, types.StringTypes):
raise datastore_errors.BadValueError(
'Expected a string for title; received %s (a %s).' %
(title, datastore_types.typename(title)))
self['title'] = title
self['content'] = ''
self._contact_properties = set(contact_properties)
assert not self._contact_properties.intersection(self.keys())
self._kind_properties = set(kind_properties) - self._contact_properties
self._kind_properties.add('title')
self._kind_properties.add('content')
def _KindPropertiesToXml(self):
""" Convert the properties that are part of this gd kind to XML. For
testability, the XML elements in the output are sorted alphabetically
by property name.
Returns:
string # the XML representation of the gd kind properties
"""
properties = self._kind_properties.intersection(set(self.keys()))
xml = u''
for prop in sorted(properties):
prop_xml = saxutils.quoteattr(prop)[1:-1]
value = self[prop]
has_toxml = (hasattr(value, 'ToXml') or
isinstance(value, list) and hasattr(value[0], 'ToXml'))
for val in self._XmlEscapeValues(prop):
if has_toxml:
xml += '\n %s' % val
else:
xml += '\n <%s>%s</%s>' % (prop_xml, val, prop_xml)
return xml
def _ContactPropertiesToXml(self):
""" Convert this kind's Contact properties kind to XML. For testability,
the XML elements in the output are sorted alphabetically by property name.
Returns:
string # the XML representation of the Contact properties
"""
properties = self._contact_properties.intersection(set(self.keys()))
xml = u''
for prop in sorted(properties):
values = self[prop]
if not isinstance(values, list):
values = [values]
for value in values:
assert isinstance(value, datastore_types.Key)
xml += """
<gd:who rel="http://schemas.google.com/g/2005#%s.%s>
<gd:entryLink href="%s" />
</gd:who>""" % (self.kind().lower(), prop, value.ToTagUri())
return xml
def _LeftoverPropertiesToXml(self):
""" Convert all of this entity's properties that *aren't* part of this gd
kind to XML.
Returns:
string # the XML representation of the leftover properties
"""
leftovers = set(self.keys())
leftovers -= self._kind_properties
leftovers -= self._contact_properties
if leftovers:
return u'\n ' + '\n '.join(self._PropertiesToXml(leftovers))
else:
return u''
def ToXml(self):
""" Returns an XML representation of this entity, as a string.
"""
xml = GdKind.HEADER % self.kind().lower()
xml += self._KindPropertiesToXml()
xml += self._ContactPropertiesToXml()
xml += self._LeftoverPropertiesToXml()
xml += GdKind.FOOTER
return xml
class Message(GdKind):
"""A message, such as an email, a discussion group posting, or a comment.
Includes the message title, contents, participants, and other properties.
This is the gd Message kind. See:
http://code.google.com/apis/gdata/common-elements.html#gdMessageKind
These properties are meaningful. They are all optional.
property name property type meaning
-------------------------------------
title string message subject
content string message body
from Contact* sender
to Contact* primary recipient
cc Contact* CC recipient
bcc Contact* BCC recipient
reply-to Contact* intended recipient of replies
link Link* attachment
category Category* tag or label associated with this message
geoPt GeoPt* geographic location the message was posted from
rating Rating* message rating, as defined by the application
* means this property may be repeated.
The Contact properties should be Keys of Contact entities. They are
represented in the XML encoding as linked <gd:who> elements.
"""
KIND_PROPERTIES = ['title', 'content', 'link', 'category', 'geoPt', 'rating']
CONTACT_PROPERTIES = ['from', 'to', 'cc', 'bcc', 'reply-to']
def __init__(self, title, kind='Message'):
GdKind.__init__(self, kind, title, Message.KIND_PROPERTIES,
Message.CONTACT_PROPERTIES)
class Event(GdKind):
"""A calendar event.
Includes the event title, description, location, organizer, start and end
time, and other details.
This is the gd Event kind. See:
http://code.google.com/apis/gdata/common-elements.html#gdEventKind
These properties are meaningful. They are all optional.
property name property type meaning
-------------------------------------
title string event name
content string event description
author string the organizer's name
where string* human-readable location (not a GeoPt)
startTime timestamp start time
endTime timestamp end time
eventStatus string one of the Event.Status values
link Link* page with more information
category Category* tag or label associated with this event
attendee Contact* attendees and other related people
* means this property may be repeated.
The Contact properties should be Keys of Contact entities. They are
represented in the XML encoding as linked <gd:who> elements.
"""
KIND_PROPERTIES = ['title', 'content', 'author', 'where', 'startTime',
'endTime', 'eventStatus', 'link', 'category']
CONTACT_PROPERTIES = ['attendee']
class Status:
CONFIRMED = 'confirmed'
TENTATIVE = 'tentative'
CANCELED = 'canceled'
def __init__(self, title, kind='Event'):
GdKind.__init__(self, kind, title, Event.KIND_PROPERTIES,
Event.CONTACT_PROPERTIES)
def ToXml(self):
""" Override GdKind.ToXml() to special-case author, gd:where, gd:when, and
gd:eventStatus.
"""
xml = GdKind.HEADER % self.kind().lower()
self._kind_properties = set(Contact.KIND_PROPERTIES)
xml += self._KindPropertiesToXml()
if 'author' in self:
xml += """
<author><name>%s</name></author>""" % self['author']
if 'eventStatus' in self:
xml += """
<gd:eventStatus value="http://schemas.google.com/g/2005#event.%s" />""" % (
self['eventStatus'])
if 'where' in self:
lines = ['<gd:where valueString="%s" />' % val
for val in self._XmlEscapeValues('where')]
xml += '\n ' + '\n '.join(lines)
iso_format = '%Y-%m-%dT%H:%M:%S'
xml += '\n <gd:when'
for key in ['startTime', 'endTime']:
if key in self:
xml += ' %s="%s"' % (key, self[key].isoformat())
xml += ' />'
self._kind_properties.update(['author', 'where', 'startTime', 'endTime',
'eventStatus'])
xml += self._ContactPropertiesToXml()
xml += self._LeftoverPropertiesToXml()
xml += GdKind.FOOTER
return xml
class Contact(GdKind):
"""A contact: a person, a venue such as a club or a restaurant, or an
organization.
This is the gd Contact kind. See:
http://code.google.com/apis/gdata/common-elements.html#gdContactKind
Most of the information about the contact is in the <gd:contactSection>
element; see the reference section for that element for details.
These properties are meaningful. They are all optional.
property name property type meaning
-------------------------------------
title string contact's name
content string notes
email Email* email address
geoPt GeoPt* geographic location
im IM* IM address
phoneNumber Phonenumber* phone number
postalAddress PostalAddress* mailing address
link Link* link to more information
category Category* tag or label associated with this contact
* means this property may be repeated.
"""
CONTACT_SECTION_HEADER = """
<gd:contactSection>"""
CONTACT_SECTION_FOOTER = """
</gd:contactSection>"""
KIND_PROPERTIES = ['title', 'content', 'link', 'category']
CONTACT_SECTION_PROPERTIES = ['email', 'geoPt', 'im', 'phoneNumber',
'postalAddress']
def __init__(self, title, kind='Contact'):
GdKind.__init__(self, kind, title, Contact.KIND_PROPERTIES)
def ToXml(self):
""" Override GdKind.ToXml() to put some properties inside a
gd:contactSection.
"""
xml = GdKind.HEADER % self.kind().lower()
self._kind_properties = set(Contact.KIND_PROPERTIES)
xml += self._KindPropertiesToXml()
xml += Contact.CONTACT_SECTION_HEADER
self._kind_properties = set(Contact.CONTACT_SECTION_PROPERTIES)
xml += self._KindPropertiesToXml()
xml += Contact.CONTACT_SECTION_FOOTER
self._kind_properties.update(Contact.KIND_PROPERTIES)
xml += self._LeftoverPropertiesToXml()
xml += GdKind.FOOTER
return xml