| #!/usr/bin/python |
| # |
| # Copyright 2009 Google 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. |
| |
| """SitesClient extends gdata.client.GDClient to streamline Sites API calls.""" |
| |
| |
| __author__ = 'e.bidelman (Eric Bidelman)' |
| |
| import atom.data |
| import gdata.client |
| import gdata.sites.data |
| import gdata.gauth |
| |
| |
| # Feed URI templates |
| CONTENT_FEED_TEMPLATE = '/feeds/content/%s/%s/' |
| REVISION_FEED_TEMPLATE = '/feeds/revision/%s/%s/' |
| ACTIVITY_FEED_TEMPLATE = '/feeds/activity/%s/%s/' |
| SITE_FEED_TEMPLATE = '/feeds/site/%s/' |
| ACL_FEED_TEMPLATE = '/feeds/acl/site/%s/%s/' |
| |
| |
| class SitesClient(gdata.client.GDClient): |
| |
| """Client extension for the Google Sites API service.""" |
| |
| host = 'sites.google.com' # default server for the API |
| domain = 'site' # default site domain name |
| api_version = '1.1' # default major version for the service. |
| auth_service = 'jotspot' |
| auth_scopes = gdata.gauth.AUTH_SCOPES['jotspot'] |
| ssl = True |
| |
| def __init__(self, site=None, domain=None, auth_token=None, **kwargs): |
| """Constructs a new client for the Sites API. |
| |
| Args: |
| site: string (optional) Name (webspace) of the Google Site |
| domain: string (optional) Domain of the (Google Apps hosted) Site. |
| If no domain is given, the Site is assumed to be a consumer Google |
| Site, in which case the value 'site' is used. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: The other parameters to pass to gdata.client.GDClient |
| constructor. |
| """ |
| gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs) |
| self.site = site |
| if domain is not None: |
| self.domain = domain |
| |
| def __make_kind_category(self, label): |
| if label is None: |
| return None |
| return atom.data.Category( |
| scheme=gdata.sites.data.SITES_KIND_SCHEME, |
| term='%s#%s' % (gdata.sites.data.SITES_NAMESPACE, label), label=label) |
| |
| __MakeKindCategory = __make_kind_category |
| |
| def __upload(self, entry, media_source, auth_token=None, **kwargs): |
| """Uploads an attachment file to the Sites API. |
| |
| Args: |
| entry: gdata.sites.data.ContentEntry The Atom XML to include. |
| media_source: gdata.data.MediaSource The file payload to be uploaded. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: Other parameters to pass to gdata.client.post(). |
| |
| Returns: |
| The created entry. |
| """ |
| uri = self.make_content_feed_uri() |
| return self.post(entry, uri, media_source=media_source, |
| auth_token=auth_token, **kwargs) |
| |
| def _get_file_content(self, uri): |
| """Fetches the file content from the specified URI. |
| |
| Args: |
| uri: string The full URL to fetch the file contents from. |
| |
| Returns: |
| The binary file content. |
| |
| Raises: |
| gdata.client.RequestError: on error response from server. |
| """ |
| server_response = self.request('GET', uri) |
| if server_response.status != 200: |
| raise gdata.client.RequestError, {'status': server_response.status, |
| 'reason': server_response.reason, |
| 'body': server_response.read()} |
| return server_response.read() |
| |
| _GetFileContent = _get_file_content |
| |
| def make_content_feed_uri(self): |
| return CONTENT_FEED_TEMPLATE % (self.domain, self.site) |
| |
| MakeContentFeedUri = make_content_feed_uri |
| |
| def make_revision_feed_uri(self): |
| return REVISION_FEED_TEMPLATE % (self.domain, self.site) |
| |
| MakeRevisionFeedUri = make_revision_feed_uri |
| |
| def make_activity_feed_uri(self): |
| return ACTIVITY_FEED_TEMPLATE % (self.domain, self.site) |
| |
| MakeActivityFeedUri = make_activity_feed_uri |
| |
| def make_site_feed_uri(self, site_name=None): |
| if site_name is not None: |
| return (SITE_FEED_TEMPLATE % self.domain) + site_name |
| else: |
| return SITE_FEED_TEMPLATE % self.domain |
| |
| MakeSiteFeedUri = make_site_feed_uri |
| |
| def make_acl_feed_uri(self): |
| return ACL_FEED_TEMPLATE % (self.domain, self.site) |
| |
| MakeAclFeedUri = make_acl_feed_uri |
| |
| def get_content_feed(self, uri=None, auth_token=None, **kwargs): |
| """Retrieves the content feed containing the current state of site. |
| |
| Args: |
| uri: string (optional) A full URI to query the Content feed with. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: Other parameters to pass to self.get_feed(). |
| |
| Returns: |
| gdata.sites.data.ContentFeed |
| """ |
| if uri is None: |
| uri = self.make_content_feed_uri() |
| return self.get_feed(uri, desired_class=gdata.sites.data.ContentFeed, |
| auth_token=auth_token, **kwargs) |
| |
| GetContentFeed = get_content_feed |
| |
| def get_revision_feed(self, entry_or_uri_or_id, auth_token=None, **kwargs): |
| """Retrieves the revision feed containing the revision history for a node. |
| |
| Args: |
| entry_or_uri_or_id: string or gdata.sites.data.ContentEntry A full URI, |
| content entry node ID, or a content entry object of the entry to |
| retrieve revision information for. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: Other parameters to pass to self.get_feed(). |
| |
| Returns: |
| gdata.sites.data.RevisionFeed |
| """ |
| uri = self.make_revision_feed_uri() |
| if isinstance(entry_or_uri_or_id, gdata.sites.data.ContentEntry): |
| uri = entry_or_uri_or_id.FindRevisionLink() |
| elif entry_or_uri_or_id.find('/') == -1: |
| uri += entry_or_uri_or_id |
| else: |
| uri = entry_or_uri_or_id |
| return self.get_feed(uri, desired_class=gdata.sites.data.RevisionFeed, |
| auth_token=auth_token, **kwargs) |
| |
| GetRevisionFeed = get_revision_feed |
| |
| def get_activity_feed(self, uri=None, auth_token=None, **kwargs): |
| """Retrieves the activity feed containing recent Site activity. |
| |
| Args: |
| uri: string (optional) A full URI to query the Activity feed. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: Other parameters to pass to self.get_feed(). |
| |
| Returns: |
| gdata.sites.data.ActivityFeed |
| """ |
| if uri is None: |
| uri = self.make_activity_feed_uri() |
| return self.get_feed(uri, desired_class=gdata.sites.data.ActivityFeed, |
| auth_token=auth_token, **kwargs) |
| |
| GetActivityFeed = get_activity_feed |
| |
| def get_site_feed(self, uri=None, auth_token=None, **kwargs): |
| """Retrieves the site feed containing a list of sites a user has access to. |
| |
| Args: |
| uri: string (optional) A full URI to query the site feed. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: Other parameters to pass to self.get_feed(). |
| |
| Returns: |
| gdata.sites.data.SiteFeed |
| """ |
| if uri is None: |
| uri = self.make_site_feed_uri() |
| return self.get_feed(uri, desired_class=gdata.sites.data.SiteFeed, |
| auth_token=auth_token, **kwargs) |
| |
| GetSiteFeed = get_site_feed |
| |
| def get_acl_feed(self, uri=None, auth_token=None, **kwargs): |
| """Retrieves the acl feed containing a site's sharing permissions. |
| |
| Args: |
| uri: string (optional) A full URI to query the acl feed. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: Other parameters to pass to self.get_feed(). |
| |
| Returns: |
| gdata.sites.data.AclFeed |
| """ |
| if uri is None: |
| uri = self.make_acl_feed_uri() |
| return self.get_feed(uri, desired_class=gdata.sites.data.AclFeed, |
| auth_token=auth_token, **kwargs) |
| |
| GetAclFeed = get_acl_feed |
| |
| def create_site(self, title, description=None, source_site=None, |
| theme=None, uri=None, auth_token=None, **kwargs): |
| """Creates a new Google Site. |
| |
| Note: This feature is only available to Google Apps domains. |
| |
| Args: |
| title: string Title for the site. |
| description: string (optional) A description/summary for the site. |
| source_site: string (optional) The site feed URI of the site to copy. |
| This parameter should only be specified when copying a site. |
| theme: string (optional) The name of the theme to create the site with. |
| uri: string (optional) A full site feed URI to override where the site |
| is created/copied. By default, the site will be created under |
| the currently set domain (e.g. self.domain). |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: Other parameters to pass to gdata.client.post(). |
| |
| Returns: |
| gdata.sites.data.SiteEntry of the created site. |
| """ |
| new_entry = gdata.sites.data.SiteEntry(title=atom.data.Title(text=title)) |
| |
| if description is not None: |
| new_entry.summary = gdata.sites.data.Summary(text=description) |
| |
| # Add the source link if we're making a copy of a site. |
| if source_site is not None: |
| source_link = atom.data.Link(rel=gdata.sites.data.SITES_SOURCE_LINK_REL, |
| type='application/atom+xml', |
| href=source_site) |
| new_entry.link.append(source_link) |
| |
| if theme is not None: |
| new_entry.theme = gdata.sites.data.Theme(text=theme) |
| |
| if uri is None: |
| uri = self.make_site_feed_uri() |
| |
| return self.post(new_entry, uri, auth_token=auth_token, **kwargs) |
| |
| CreateSite = create_site |
| |
| def create_page(self, kind, title, html='', page_name=None, parent=None, |
| auth_token=None, template=None, **kwargs): |
| """Creates a new page (specified by kind) on a Google Site. |
| |
| Args: |
| kind: string The type of page/item to create. For example, webpage, |
| listpage, comment, announcementspage, filecabinet, etc. The full list |
| of supported kinds can be found in gdata.sites.gdata.SUPPORT_KINDS. |
| title: string Title for the page. |
| html: string (optional) XHTML for the page's content body. |
| page_name: string (optional) The URL page name to set. If not set, the |
| title will be normalized and used as the page's URL path. |
| parent: string or gdata.sites.data.ContentEntry (optional) The parent |
| entry or parent link url to create the page under. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| template: string or gdata.sites.data.ContentEntry (optional) Create page |
| using the given template. Any content elements will be discarded as |
| they are not valid when creating a page from a template. |
| kwargs: Other parameters to pass to gdata.client.post(). |
| |
| Returns: |
| gdata.sites.data.ContentEntry of the created page. |
| """ |
| content = gdata.sites.data.Content(text=html) |
| if template is not None: |
| content = None |
| new_entry = gdata.sites.data.ContentEntry( |
| title=atom.data.Title(text=title), kind=kind, |
| content=content) |
| |
| if page_name is not None: |
| new_entry.page_name = gdata.sites.data.PageName(text=page_name) |
| |
| # Add parent link to entry if it should be uploaded as a subpage. |
| if isinstance(parent, gdata.sites.data.ContentEntry): |
| parent_link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, |
| type='application/atom+xml', |
| href=parent.GetSelfLink().href) |
| new_entry.link.append(parent_link) |
| elif parent is not None: |
| parent_link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, |
| type='application/atom+xml', |
| href=parent) |
| new_entry.link.append(parent_link) |
| |
| # Add template link to entry if present. |
| if isinstance(template, gdata.sites.data.ContentEntry): |
| template_link = atom.data.Link( |
| rel=gdata.sites.data.SITES_TEMPLATE_LINK_REL, |
| type='application/atom+xml', href=template.GetSelfLink().href) |
| new_entry.link.append(template_link) |
| elif template is not None: |
| template_link = atom.data.Link( |
| rel=gdata.sites.data.SITES_TEMPLATE_LINK_REL, |
| type='application/atom+xml', href=template) |
| new_entry.link.append(template_link) |
| |
| return self.post(new_entry, self.make_content_feed_uri(), |
| auth_token=auth_token, **kwargs) |
| |
| CreatePage = create_page |
| |
| def create_webattachment(self, src, content_type, title, parent, |
| description=None, auth_token=None, **kwargs): |
| """Creates a new webattachment within a filecabinet. |
| |
| Args: |
| src: string The url of the web attachment. |
| content_type: string The MIME type of the web attachment. |
| title: string The title to name the web attachment. |
| parent: string or gdata.sites.data.ContentEntry (optional) The |
| parent entry or url of the filecabinet to create the attachment under. |
| description: string (optional) A summary/description for the attachment. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: Other parameters to pass to gdata.client.post(). |
| |
| Returns: |
| gdata.sites.data.ContentEntry of the created page. |
| """ |
| new_entry = gdata.sites.data.ContentEntry( |
| title=atom.data.Title(text=title), kind='webattachment', |
| content=gdata.sites.data.Content(src=src, type=content_type)) |
| |
| if isinstance(parent, gdata.sites.data.ContentEntry): |
| link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, |
| type='application/atom+xml', |
| href=parent.GetSelfLink().href) |
| elif parent is not None: |
| link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, |
| type='application/atom+xml', href=parent) |
| |
| new_entry.link.append(link) |
| |
| # Add file decription if it was specified |
| if description is not None: |
| new_entry.summary = gdata.sites.data.Summary(type='text', |
| text=description) |
| |
| return self.post(new_entry, self.make_content_feed_uri(), |
| auth_token=auth_token, **kwargs) |
| |
| CreateWebAttachment = create_webattachment |
| |
| def upload_attachment(self, file_handle, parent, content_type=None, |
| title=None, description=None, folder_name=None, |
| auth_token=None, **kwargs): |
| """Uploads an attachment to a parent page. |
| |
| Args: |
| file_handle: MediaSource or string A gdata.data.MediaSource object |
| containing the file to be uploaded or the full path name to the |
| file on disk. |
| parent: gdata.sites.data.ContentEntry or string The parent page to |
| upload the file to or the full URI of the entry's self link. |
| content_type: string (optional) The MIME type of the file |
| (e.g 'application/pdf'). This should be provided if file is not a |
| MediaSource object. |
| title: string (optional) The title to name the attachment. If not |
| included, the filepath or media source's filename is used. |
| description: string (optional) A summary/description for the attachment. |
| folder_name: string (optional) The name of an existing folder to upload |
| the attachment to. This only applies when the parent parameter points |
| to a filecabinet entry. |
| auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or |
| OAuthToken which authorizes this client to edit the user's data. |
| kwargs: Other parameters to pass to self.__upload(). |
| |
| Returns: |
| A gdata.sites.data.ContentEntry containing information about the created |
| attachment. |
| """ |
| if isinstance(parent, gdata.sites.data.ContentEntry): |
| link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, |
| type='application/atom+xml', |
| href=parent.GetSelfLink().href) |
| else: |
| link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, |
| type='application/atom+xml', |
| href=parent) |
| |
| if not isinstance(file_handle, gdata.data.MediaSource): |
| ms = gdata.data.MediaSource(file_path=file_handle, |
| content_type=content_type) |
| else: |
| ms = file_handle |
| |
| # If no title specified, use the file name |
| if title is None: |
| title = ms.file_name |
| |
| new_entry = gdata.sites.data.ContentEntry(kind='attachment') |
| new_entry.title = atom.data.Title(text=title) |
| new_entry.link.append(link) |
| |
| # Add file decription if it was specified |
| if description is not None: |
| new_entry.summary = gdata.sites.data.Summary(type='text', |
| text=description) |
| |
| # Upload the attachment to a filecabinet folder? |
| if parent.Kind() == 'filecabinet' and folder_name is not None: |
| folder_category = atom.data.Category( |
| scheme=gdata.sites.data.FOLDER_KIND_TERM, term=folder_name) |
| new_entry.category.append(folder_category) |
| |
| return self.__upload(new_entry, ms, auth_token=auth_token, **kwargs) |
| |
| UploadAttachment = upload_attachment |
| |
| def download_attachment(self, uri_or_entry, file_path): |
| """Downloads an attachment file to disk. |
| |
| Args: |
| uri_or_entry: string The full URL to download the file from. |
| file_path: string The full path to save the file to. |
| |
| Raises: |
| gdata.client.RequestError: on error response from server. |
| """ |
| uri = uri_or_entry |
| if isinstance(uri_or_entry, gdata.sites.data.ContentEntry): |
| uri = uri_or_entry.content.src |
| |
| f = open(file_path, 'wb') |
| try: |
| f.write(self._get_file_content(uri)) |
| except gdata.client.RequestError, e: |
| f.close() |
| raise e |
| f.flush() |
| f.close() |
| |
| DownloadAttachment = download_attachment |