/gdata/sites/client.py

http://radioappz.googlecode.com/ · Python · 461 lines · 335 code · 31 blank · 95 comment · 9 complexity · e0e1646759c4463ffb7db099af0cf6f6 MD5 · raw file

  1. #!/usr/bin/python
  2. #
  3. # Copyright 2009 Google Inc. All Rights Reserved.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """SitesClient extends gdata.client.GDClient to streamline Sites API calls."""
  17. __author__ = 'e.bidelman (Eric Bidelman)'
  18. import atom.data
  19. import gdata.client
  20. import gdata.sites.data
  21. import gdata.gauth
  22. # Feed URI templates
  23. CONTENT_FEED_TEMPLATE = '/feeds/content/%s/%s/'
  24. REVISION_FEED_TEMPLATE = '/feeds/revision/%s/%s/'
  25. ACTIVITY_FEED_TEMPLATE = '/feeds/activity/%s/%s/'
  26. SITE_FEED_TEMPLATE = '/feeds/site/%s/'
  27. ACL_FEED_TEMPLATE = '/feeds/acl/site/%s/%s/'
  28. class SitesClient(gdata.client.GDClient):
  29. """Client extension for the Google Sites API service."""
  30. host = 'sites.google.com' # default server for the API
  31. domain = 'site' # default site domain name
  32. api_version = '1.1' # default major version for the service.
  33. auth_service = 'jotspot'
  34. auth_scopes = gdata.gauth.AUTH_SCOPES['jotspot']
  35. def __init__(self, site=None, domain=None, auth_token=None, **kwargs):
  36. """Constructs a new client for the Sites API.
  37. Args:
  38. site: string (optional) Name (webspace) of the Google Site
  39. domain: string (optional) Domain of the (Google Apps hosted) Site.
  40. If no domain is given, the Site is assumed to be a consumer Google
  41. Site, in which case the value 'site' is used.
  42. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  43. OAuthToken which authorizes this client to edit the user's data.
  44. kwargs: The other parameters to pass to gdata.client.GDClient
  45. constructor.
  46. """
  47. gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs)
  48. self.site = site
  49. if domain is not None:
  50. self.domain = domain
  51. def __make_kind_category(self, label):
  52. if label is None:
  53. return None
  54. return atom.data.Category(
  55. scheme=gdata.sites.data.SITES_KIND_SCHEME,
  56. term='%s#%s' % (gdata.sites.data.SITES_NAMESPACE, label), label=label)
  57. __MakeKindCategory = __make_kind_category
  58. def __upload(self, entry, media_source, auth_token=None, **kwargs):
  59. """Uploads an attachment file to the Sites API.
  60. Args:
  61. entry: gdata.sites.data.ContentEntry The Atom XML to include.
  62. media_source: gdata.data.MediaSource The file payload to be uploaded.
  63. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  64. OAuthToken which authorizes this client to edit the user's data.
  65. kwargs: Other parameters to pass to gdata.client.post().
  66. Returns:
  67. The created entry.
  68. """
  69. uri = self.make_content_feed_uri()
  70. return self.post(entry, uri, media_source=media_source,
  71. auth_token=auth_token, **kwargs)
  72. def _get_file_content(self, uri):
  73. """Fetches the file content from the specified URI.
  74. Args:
  75. uri: string The full URL to fetch the file contents from.
  76. Returns:
  77. The binary file content.
  78. Raises:
  79. gdata.client.RequestError: on error response from server.
  80. """
  81. server_response = self.request('GET', uri)
  82. if server_response.status != 200:
  83. raise gdata.client.RequestError, {'status': server_response.status,
  84. 'reason': server_response.reason,
  85. 'body': server_response.read()}
  86. return server_response.read()
  87. _GetFileContent = _get_file_content
  88. def make_content_feed_uri(self):
  89. return CONTENT_FEED_TEMPLATE % (self.domain, self.site)
  90. MakeContentFeedUri = make_content_feed_uri
  91. def make_revision_feed_uri(self):
  92. return REVISION_FEED_TEMPLATE % (self.domain, self.site)
  93. MakeRevisionFeedUri = make_revision_feed_uri
  94. def make_activity_feed_uri(self):
  95. return ACTIVITY_FEED_TEMPLATE % (self.domain, self.site)
  96. MakeActivityFeedUri = make_activity_feed_uri
  97. def make_site_feed_uri(self, site_name=None):
  98. if site_name is not None:
  99. return (SITE_FEED_TEMPLATE % self.domain) + site_name
  100. else:
  101. return SITE_FEED_TEMPLATE % self.domain
  102. MakeSiteFeedUri = make_site_feed_uri
  103. def make_acl_feed_uri(self):
  104. return ACL_FEED_TEMPLATE % (self.domain, self.site)
  105. MakeAclFeedUri = make_acl_feed_uri
  106. def get_content_feed(self, uri=None, auth_token=None, **kwargs):
  107. """Retrieves the content feed containing the current state of site.
  108. Args:
  109. uri: string (optional) A full URI to query the Content feed with.
  110. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  111. OAuthToken which authorizes this client to edit the user's data.
  112. kwargs: Other parameters to pass to self.get_feed().
  113. Returns:
  114. gdata.sites.data.ContentFeed
  115. """
  116. if uri is None:
  117. uri = self.make_content_feed_uri()
  118. return self.get_feed(uri, desired_class=gdata.sites.data.ContentFeed,
  119. auth_token=auth_token, **kwargs)
  120. GetContentFeed = get_content_feed
  121. def get_revision_feed(self, entry_or_uri_or_id, auth_token=None, **kwargs):
  122. """Retrieves the revision feed containing the revision history for a node.
  123. Args:
  124. entry_or_uri_or_id: string or gdata.sites.data.ContentEntry A full URI,
  125. content entry node ID, or a content entry object of the entry to
  126. retrieve revision information for.
  127. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  128. OAuthToken which authorizes this client to edit the user's data.
  129. kwargs: Other parameters to pass to self.get_feed().
  130. Returns:
  131. gdata.sites.data.RevisionFeed
  132. """
  133. uri = self.make_revision_feed_uri()
  134. if isinstance(entry_or_uri_or_id, gdata.sites.data.ContentEntry):
  135. uri = entry_or_uri_or_id.FindRevisionLink()
  136. elif entry_or_uri_or_id.find('/') == -1:
  137. uri += entry_or_uri_or_id
  138. else:
  139. uri = entry_or_uri_or_id
  140. return self.get_feed(uri, desired_class=gdata.sites.data.RevisionFeed,
  141. auth_token=auth_token, **kwargs)
  142. GetRevisionFeed = get_revision_feed
  143. def get_activity_feed(self, uri=None, auth_token=None, **kwargs):
  144. """Retrieves the activity feed containing recent Site activity.
  145. Args:
  146. uri: string (optional) A full URI to query the Activity feed.
  147. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  148. OAuthToken which authorizes this client to edit the user's data.
  149. kwargs: Other parameters to pass to self.get_feed().
  150. Returns:
  151. gdata.sites.data.ActivityFeed
  152. """
  153. if uri is None:
  154. uri = self.make_activity_feed_uri()
  155. return self.get_feed(uri, desired_class=gdata.sites.data.ActivityFeed,
  156. auth_token=auth_token, **kwargs)
  157. GetActivityFeed = get_activity_feed
  158. def get_site_feed(self, uri=None, auth_token=None, **kwargs):
  159. """Retrieves the site feed containing a list of sites a user has access to.
  160. Args:
  161. uri: string (optional) A full URI to query the site feed.
  162. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  163. OAuthToken which authorizes this client to edit the user's data.
  164. kwargs: Other parameters to pass to self.get_feed().
  165. Returns:
  166. gdata.sites.data.SiteFeed
  167. """
  168. if uri is None:
  169. uri = self.make_site_feed_uri()
  170. return self.get_feed(uri, desired_class=gdata.sites.data.SiteFeed,
  171. auth_token=auth_token, **kwargs)
  172. GetSiteFeed = get_site_feed
  173. def get_acl_feed(self, uri=None, auth_token=None, **kwargs):
  174. """Retrieves the acl feed containing a site's sharing permissions.
  175. Args:
  176. uri: string (optional) A full URI to query the acl feed.
  177. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  178. OAuthToken which authorizes this client to edit the user's data.
  179. kwargs: Other parameters to pass to self.get_feed().
  180. Returns:
  181. gdata.sites.data.AclFeed
  182. """
  183. if uri is None:
  184. uri = self.make_acl_feed_uri()
  185. return self.get_feed(uri, desired_class=gdata.sites.data.AclFeed,
  186. auth_token=auth_token, **kwargs)
  187. GetAclFeed = get_acl_feed
  188. def create_site(self, title, description=None, source_site=None,
  189. theme=None, uri=None, auth_token=None, **kwargs):
  190. """Creates a new Google Site.
  191. Note: This feature is only available to Google Apps domains.
  192. Args:
  193. title: string Title for the site.
  194. description: string (optional) A description/summary for the site.
  195. source_site: string (optional) The site feed URI of the site to copy.
  196. This parameter should only be specified when copying a site.
  197. theme: string (optional) The name of the theme to create the site with.
  198. uri: string (optional) A full site feed URI to override where the site
  199. is created/copied. By default, the site will be created under
  200. the currently set domain (e.g. self.domain).
  201. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  202. OAuthToken which authorizes this client to edit the user's data.
  203. kwargs: Other parameters to pass to gdata.client.post().
  204. Returns:
  205. gdata.sites.data.SiteEntry of the created site.
  206. """
  207. new_entry = gdata.sites.data.SiteEntry(title=atom.data.Title(text=title))
  208. if description is not None:
  209. new_entry.summary = gdata.sites.data.Summary(text=description)
  210. # Add the source link if we're making a copy of a site.
  211. if source_site is not None:
  212. source_link = atom.data.Link(rel=gdata.sites.data.SITES_SOURCE_LINK_REL,
  213. type='application/atom+xml',
  214. href=source_site)
  215. new_entry.link.append(source_link)
  216. if theme is not None:
  217. new_entry.theme = gdata.sites.data.Theme(text=theme)
  218. if uri is None:
  219. uri = self.make_site_feed_uri()
  220. return self.post(new_entry, uri, auth_token=auth_token, **kwargs)
  221. CreateSite = create_site
  222. def create_page(self, kind, title, html='', page_name=None, parent=None,
  223. auth_token=None, **kwargs):
  224. """Creates a new page (specified by kind) on a Google Site.
  225. Args:
  226. kind: string The type of page/item to create. For example, webpage,
  227. listpage, comment, announcementspage, filecabinet, etc. The full list
  228. of supported kinds can be found in gdata.sites.gdata.SUPPORT_KINDS.
  229. title: string Title for the page.
  230. html: string (optional) XHTML for the page's content body.
  231. page_name: string (optional) The URL page name to set. If not set, the
  232. title will be normalized and used as the page's URL path.
  233. parent: string or gdata.sites.data.ContentEntry (optional) The parent
  234. entry or parent link url to create the page under.
  235. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  236. OAuthToken which authorizes this client to edit the user's data.
  237. kwargs: Other parameters to pass to gdata.client.post().
  238. Returns:
  239. gdata.sites.data.ContentEntry of the created page.
  240. """
  241. new_entry = gdata.sites.data.ContentEntry(
  242. title=atom.data.Title(text=title), kind=kind,
  243. content=gdata.sites.data.Content(text=html))
  244. if page_name is not None:
  245. new_entry.page_name = gdata.sites.data.PageName(text=page_name)
  246. # Add parent link to entry if it should be uploaded as a subpage.
  247. if isinstance(parent, gdata.sites.data.ContentEntry):
  248. parent_link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL,
  249. type='application/atom+xml',
  250. href=parent.GetSelfLink().href)
  251. new_entry.link.append(parent_link)
  252. elif parent is not None:
  253. parent_link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL,
  254. type='application/atom+xml',
  255. href=parent)
  256. new_entry.link.append(parent_link)
  257. return self.post(new_entry, self.make_content_feed_uri(),
  258. auth_token=auth_token, **kwargs)
  259. CreatePage = create_page
  260. def create_webattachment(self, src, content_type, title, parent,
  261. description=None, auth_token=None, **kwargs):
  262. """Creates a new webattachment within a filecabinet.
  263. Args:
  264. src: string The url of the web attachment.
  265. content_type: string The MIME type of the web attachment.
  266. title: string The title to name the web attachment.
  267. parent: string or gdata.sites.data.ContentEntry (optional) The
  268. parent entry or url of the filecabinet to create the attachment under.
  269. description: string (optional) A summary/description for the attachment.
  270. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  271. OAuthToken which authorizes this client to edit the user's data.
  272. kwargs: Other parameters to pass to gdata.client.post().
  273. Returns:
  274. gdata.sites.data.ContentEntry of the created page.
  275. """
  276. new_entry = gdata.sites.data.ContentEntry(
  277. title=atom.data.Title(text=title), kind='webattachment',
  278. content=gdata.sites.data.Content(src=src, type=content_type))
  279. if isinstance(parent, gdata.sites.data.ContentEntry):
  280. link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL,
  281. type='application/atom+xml',
  282. href=parent.GetSelfLink().href)
  283. elif parent is not None:
  284. link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL,
  285. type='application/atom+xml', href=parent)
  286. new_entry.link.append(link)
  287. # Add file decription if it was specified
  288. if description is not None:
  289. new_entry.summary = gdata.sites.data.Summary(type='text',
  290. text=description)
  291. return self.post(new_entry, self.make_content_feed_uri(),
  292. auth_token=auth_token, **kwargs)
  293. CreateWebAttachment = create_webattachment
  294. def upload_attachment(self, file_handle, parent, content_type=None,
  295. title=None, description=None, folder_name=None,
  296. auth_token=None, **kwargs):
  297. """Uploads an attachment to a parent page.
  298. Args:
  299. file_handle: MediaSource or string A gdata.data.MediaSource object
  300. containing the file to be uploaded or the full path name to the
  301. file on disk.
  302. parent: gdata.sites.data.ContentEntry or string The parent page to
  303. upload the file to or the full URI of the entry's self link.
  304. content_type: string (optional) The MIME type of the file
  305. (e.g 'application/pdf'). This should be provided if file is not a
  306. MediaSource object.
  307. title: string (optional) The title to name the attachment. If not
  308. included, the filepath or media source's filename is used.
  309. description: string (optional) A summary/description for the attachment.
  310. folder_name: string (optional) The name of an existing folder to upload
  311. the attachment to. This only applies when the parent parameter points
  312. to a filecabinet entry.
  313. auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or
  314. OAuthToken which authorizes this client to edit the user's data.
  315. kwargs: Other parameters to pass to self.__upload().
  316. Returns:
  317. A gdata.sites.data.ContentEntry containing information about the created
  318. attachment.
  319. """
  320. if isinstance(parent, gdata.sites.data.ContentEntry):
  321. link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL,
  322. type='application/atom+xml',
  323. href=parent.GetSelfLink().href)
  324. else:
  325. link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL,
  326. type='application/atom+xml',
  327. href=parent)
  328. if not isinstance(file_handle, gdata.data.MediaSource):
  329. ms = gdata.data.MediaSource(file_path=file_handle,
  330. content_type=content_type)
  331. else:
  332. ms = file_handle
  333. # If no title specified, use the file name
  334. if title is None:
  335. title = ms.file_name
  336. new_entry = gdata.sites.data.ContentEntry(kind='attachment')
  337. new_entry.title = atom.data.Title(text=title)
  338. new_entry.link.append(link)
  339. # Add file decription if it was specified
  340. if description is not None:
  341. new_entry.summary = gdata.sites.data.Summary(type='text',
  342. text=description)
  343. # Upload the attachment to a filecabinet folder?
  344. if parent.Kind() == 'filecabinet' and folder_name is not None:
  345. folder_category = atom.data.Category(
  346. scheme=gdata.sites.data.FOLDER_KIND_TERM, term=folder_name)
  347. new_entry.category.append(folder_category)
  348. return self.__upload(new_entry, ms, auth_token=auth_token, **kwargs)
  349. UploadAttachment = upload_attachment
  350. def download_attachment(self, uri_or_entry, file_path):
  351. """Downloads an attachment file to disk.
  352. Args:
  353. uri_or_entry: string The full URL to download the file from.
  354. file_path: string The full path to save the file to.
  355. Raises:
  356. gdata.client.RequestError: on error response from server.
  357. """
  358. uri = uri_or_entry
  359. if isinstance(uri_or_entry, gdata.sites.data.ContentEntry):
  360. uri = uri_or_entry.content.src
  361. f = open(file_path, 'wb')
  362. try:
  363. f.write(self._get_file_content(uri))
  364. except gdata.client.RequestError, e:
  365. f.close()
  366. raise e
  367. f.flush()
  368. f.close()
  369. DownloadAttachment = download_attachment