class AnalyticsReportsAssignmentForm(group.GroupForm): label = _(u'analytics_assignment', default=u'Reports') description = _( u'analytics_assignment_description', default=(u'Configure the reports that are displayed in the Google ' u'Analytics control panel.')) fields = field.Fields(IAnalyticsReportsAssignment)
class AnalyticsTrackingForm(group.GroupForm): fields = field.Fields(IAnalyticsTracking) label = _(u'analytics_tracking', default=u'Tracking') description = _( u'analytics_tracking_description', default=(u'Configure the way Google Analytics tracks statistics ' u'about this site.'))
def __call__(self): """ Gets the token from the URL and takes the appropriate action. """ analytics_tool = getToolByName(self.context, "portal_analytics") plone_utils = getToolByName(self.context, "plone_utils") clients = analytics_tool.getClients() # Check if we are revoking the token. if self.request.get("revoke_token", 0): analytics_tool.auth_token = None try: clients.data.RevokeAuthSubToken() except NonAuthSubToken: # Authorization already revoked pass message = _( u"Authorization revoked. You may now reauthorize with \ a different Google account." ) plone_utils.addPortalMessage(message) # Otherwise, we are setting the token. elif self.request.QUERY_STRING and "token" in self.request: current_url = "%s?%s" % (self.request.ACTUAL_URL, self.request.QUERY_STRING) single_token = gdata.auth.extract_auth_sub_token_from_url(current_url) try: session_token = clients.data.upgrade_to_session_token(single_token) # Save a string representation of the token. analytics_tool.auth_token = unicode(session_token.get_token_string()) # Set the token on the two servcies using SetAuthSubToken. clients.data.SetAuthSubToken(session_token) clients.accounts.SetAuthSubToken(session_token) message = _( u"Authorization succeeded. You may now configure \ Google Analytics for Plone." ) except TokenUpgradeFailed: message = _( u"Authorization failed. Google Analytics for \ Plone received an invalid token." ) plone_utils.addPortalMessage(message) # Redirect back to the control panel. portal_url = getToolByName(self.context, "portal_url") next_url = "%s/portal_analytics/@@analytics-controlpanel" % portal_url() self.request.response.redirect(next_url)
class EditForm(base.EditForm): """Portlet edit form. This is registered with configure.zcml. The form_fields variable tells zope.formlib which fields to display. """ schema = IAnalyticsPortlet label = _(u'Add Analytics Portlet') description = _(u'')
def getPossibleDateRanges(context): """ Return the possible date ranges """ vocab = [ SimpleTerm('week', 'week', _(u"Week")), SimpleTerm('month', 'month', _(u"Month")), SimpleTerm('quarter', 'quarter', _(u"Quarter")), SimpleTerm('year', 'year', _(u"Year")), ] return SimpleVocabulary(vocab)
def __call__(self): """ Gets the token from the URL and takes the appropriate action. """ analytics_tool = getToolByName(self.context, 'portal_analytics') plone_utils = getToolByName(self.context, 'plone_utils') clients = analytics_tool.getClients() # Check if we are revoking the token. if self.request.get('revoke_token', 0): analytics_tool.auth_token = None try: clients.data.RevokeAuthSubToken() except NonAuthSubToken: # Authorization already revoked pass message = _(u'Authorization revoked. You may now reauthorize with \ a different Google account.') plone_utils.addPortalMessage(message) # Otherwise, we are setting the token. elif self.request.QUERY_STRING and 'token' in self.request: current_url = '%s?%s' % (self.request.ACTUAL_URL, self.request.QUERY_STRING) single_token = gdata.auth.extract_auth_sub_token_from_url(current_url) try: session_token = clients.data.upgrade_to_session_token(single_token) # Save a string representation of the token. analytics_tool.auth_token = unicode(session_token.get_token_string()) # Set the token on the two servcies using SetAuthSubToken. clients.data.SetAuthSubToken(session_token) clients.accounts.SetAuthSubToken(session_token) message = _(u'Authorization succeeded. You may now configure \ Google Analytics for Plone.') except TokenUpgradeFailed: message = _(u'Authorization failed. Google Analytics for \ Plone received an invalid token.') plone_utils.addPortalMessage(message) # Redirect back to the control panel. portal_url = getToolByName(self.context, 'portal_url') next_url = '%s/portal_analytics/@@analytics-controlpanel' % portal_url() self.request.response.redirect(next_url)
class AddForm(base.AddForm): """Portlet add form. This is registered in configure.zcml. The form_fields variable tells zope.formlib which fields to display. The create() method actually constructs the assignment that is being added. """ schema = IAnalyticsPortlet label = _(u'Add Analytics Portlet') description = _(u'') def create(self, data): return Assignment(**data)
def extractData(self, setErrors=True): """ Checks to make sure that tracking code is not duplicated in the site configlet. """ data, errors = super(AnalyticsControlPanelForm, self).extractData( setErrors=setErrors ) tracking_web_property = data.get('tracking_web_property', None) if HAS_PLONE5: registry = getUtility(IRegistry) site_records = registry.forInterface(ISiteSchema, prefix='plone') snippet = site_records.webstats_js else: # Plone 4 stores the analytics script in the properties properties_tool = api.portal.get_tool(name="portal_properties") snippet = properties_tool.site_properties.webstats_js snippet = snippet or '' snippet_analytics = '_gat' in snippet or '_gaq' in snippet if tracking_web_property and snippet_analytics: api.portal.show_message( _(u"You have enabled the tracking feature of this product, " u"but it looks like you still have tracking code in the " u"Site control panel. Please remove any Google Analytics " u"tracking code from the Site control panel to avoid " u"conflicts.'"), self.request, type='warning') return data, errors
def __call__(self): """ Gets the token from the URL and takes the appropriate action. """ alsoProvides(self.request, IDisableCSRFProtection) analytics_tool = getToolByName(self.context, 'portal_analytics') plone_utils = getToolByName(self.context, 'plone_utils') # Check if we are revoking the token. if self.request.get('revoke_token', 0): analytics_tool.revoke_token() message = _(u'Authorization revoked. You may now reauthorize with \ a different Google account.') plone_utils.addPortalMessage(message) # Otherwise, we are setting the token. elif self.request.QUERY_STRING and 'code' in self.request: code = self.request.get('code') message = analytics_tool.set_token(code) plone_utils.addPortalMessage(message) # Redirect back to the control panel. portal_url = getToolByName(self.context, 'portal_url') next_url = '%s/portal_analytics/@@analytics-controlpanel' % portal_url() self.request.response.redirect(next_url)
def __call__(self): """ Returns a list of AnalyticsReportResults objects for the selected reports. """ report_ids = self.request.get('report_ids', '').split(',') if not report_ids: return [] analytics_tool = getToolByName(self.context, 'portal_analytics') results = [] for report_id in report_ids: try: report = analytics_tool[report_id] except KeyError: continue try: renderer = getMultiAdapter( (self.context, self.request, report), interface=IAnalyticsReportRenderer ) results.append(renderer()) except error.BadAuthenticationError: return self.bad_auth() except error.RequestTimedOutError: return self.timed_out() date_range_msg = _('Last %s' % analytics_tool.date_range) return '<h2>%s</h2>' % ( self.context.translate(date_range_msg)) + '\n'.join(results)
def __call__(self): """ Gets the token from the URL and takes the appropriate action. """ alsoProvides(self.request, IDisableCSRFProtection) analytics_tool = getToolByName(self.context, 'portal_analytics') plone_utils = getToolByName(self.context, 'plone_utils') # Check if we are revoking the token. if self.request.get('revoke_token', 0): analytics_tool.revoke_token() message = _(u'Authorization revoked. You may now reauthorize with \ a different Google account.') plone_utils.addPortalMessage(message) # Otherwise, we are setting the token. elif self.request.QUERY_STRING and 'code' in self.request: code = self.request.get('code') message = analytics_tool.set_token(code) plone_utils.addPortalMessage(message) # Redirect back to the control panel. portal_url = getToolByName(self.context, 'portal_url') next_url = '%s/portal_analytics/@@analytics-controlpanel' % portal_url( ) self.request.response.redirect(next_url)
def __call__(self): """ Returns a list of AnalyticsReportResults objects for the selected reports. """ report_ids = self.request.get('report_ids', '').split(',') if not report_ids: return [] analytics_tool = getToolByName(self.context, 'portal_analytics') results = [] for report_id in report_ids: try: report = analytics_tool[report_id] except KeyError: continue try: renderer = getMultiAdapter( (self.context, self.request, report), interface=IAnalyticsReportRenderer) results.append(renderer()) except error.BadAuthenticationError: return self.bad_auth() except error.RequestTimedOutError: return self.timed_out() # Once we expose the date range optoin in the UI, we'll need to find a # way to generate this label dynamically, probably by using the variable # date range plugin from one of the report renderers. date_range_msg = _('Last 30 Days') return '<h2>%s</h2>' % ( self.context.translate(date_range_msg)) + '\n'.join(results)
def extractData(self, setErrors=True): """ Checks to make sure that tracking code is not duplicated in the site configlet. """ data, errors = super(AnalyticsControlPanelForm, self).extractData(setErrors=setErrors) tracking_web_property = data.get('tracking_web_property', None) if HAS_PLONE5: registry = getUtility(IRegistry) site_records = registry.forInterface(ISiteSchema, prefix='plone') snippet = site_records.webstats_js else: # Plone 4 stores the analytics script in the properties properties_tool = api.portal.get_tool(name="portal_properties") snippet = properties_tool.site_properties.webstats_js snippet = snippet or '' snippet_analytics = '_gat' in snippet or '_gaq' in snippet if tracking_web_property and snippet_analytics: api.portal.show_message(_( u"You have enabled the tracking feature of this product, " u"but it looks like you still have tracking code in the " u"Site control panel. Please remove any Google Analytics " u"tracking code from the Site control panel to avoid " u"conflicts.'"), self.request, type='warning') return data, errors
def __call__(self): """ Returns a list of AnalyticsReportResults objects for the selected reports. """ report_ids = self.request.get('report_ids', '').split(',') if not report_ids: return [] analytics_tool = getToolByName(self.context, 'portal_analytics') results = [] for report_id in report_ids: try: report = analytics_tool[report_id] except KeyError: continue try: renderer = getMultiAdapter( (self.context, self.request, report), interface=IAnalyticsReportRenderer ) results.append(renderer()) except error.BadAuthenticationError: return self.bad_auth() except error.RequestTimedOutError: return self.timed_out() # Once we expose the date range optoin in the UI, we'll need to find a # way to generate this label dynamically, probably by using the variable # date range plugin from one of the report renderers. date_range_msg = _('Last 30 Days') return '<h2>%s</h2>'%(self.context.translate(date_range_msg)) + '\n'.join(results)
def getWebProperties(context): """ Return list of Google Analytics profiles and web property IDs (e.g. UA-30481-22). """ custom_connection = (_(u'custom tracking code'), '_TRACKING_CODE_CUSTOM') analytics_tool = getToolByName(getSite(), 'portal_analytics') # short circuit if user hasn't authorized yet if not analytics_tool.auth_token: return SimpleVocabulary([ SimpleTerm(custom_connection[1], custom_connection[1], custom_connection[0]) ]) try: accounts = analytics_tool.getAccountsFeed( 'accounts/~all/webproperties/~all/profiles') except error.BadAuthenticationError: choices = [('Please authorize with Google in the Google Analytics \ control panel.', None)] return SimpleVocabulary.fromItems(choices) except error.RequestTimedOutError: choices = [('The request to Google Analytics timed out. Please try \ again later.', None)] return SimpleVocabulary.fromItems(choices) if accounts: unique_choices = {} # In vocabularies, both the terms and the values must be unique. Since # there can be more than one profile for a given web property, we create a list # of all the profiles for each property. (Ideally we would use the URL for the # web property, but Google doesn't expose it through the Analytics API.) for entry in accounts.entry: for prop in entry.property: if prop.name == 'ga:profileName': title = prop.value if not isinstance(title, unicode): title = unicode(title, 'utf-8') if prop.name == 'ga:webPropertyId': webPropertyId = prop.value if not webPropertyId in unique_choices.keys(): unique_choices.update({webPropertyId: title}) else: unique_choices[webPropertyId] += ', ' + title # After we reverse the terms so that the profile name(s) is now the key, we need # to ensure that these keys are unique. So, we pass the resulting list through # dict() and then output a list of items. choices = dict([(crop(title, 40), property_id) for (property_id, title) in unique_choices.items() ]).items() else: choices = [('No profiles available', None)] choices.append(custom_connection) return SimpleVocabulary([SimpleTerm(c[1], c[1], c[0]) for c in choices])
def __call__(self): """ gets the code from URL gets the credentials based on flow and code gets OAuth2 token from credentials takes the appropriate action """ code = self.context.REQUEST.form.get('code', None) analytics_tool = getToolByName(self.context, 'portal_analytics') plone_utils = getToolByName(self.context, 'plone_utils') clients = analytics_tool.getClients() # Check if we are revoking the token. if self.request.get('revoke_token', 0): analytics_tool.auth_token = None clients.data = '' message = _(u'Authorization revoked. You may now reauthorize with \ a different Google account.') plone_utils.addPortalMessage(message) # Otherwise, we are setting the token. elif code is not None: flow = get_flow() credentials = flow.step2_exchange(code) token_response = credentials.token_response access_token = token_response.get('access_token') analytics_tool.auth_token = access_token clients.data = gdata.analytics.service.AnalyticsDataService() clients.accounts = gdata.analytics.service.AccountsService() message = _(u'Authorization succeeded. You may now configure \ Google Analytics for Plone.') plone_utils.addPortalMessage(message) # Redirect back to the control panel. portal_url = getToolByName(self.context, 'portal_url') next_url = '%s/portal_analytics/@@analytics-controlpanel' % \ portal_url() self.request.response.redirect(next_url)
class IAnalyticsPortlet(IPortletDataProvider): """A portlet It inherits from IPortletDataProvider because for this portlet, the data that is being rendered and the portlet assignment itself are the same. """ portlet_title = schema.TextLine( title=_(u'Title'), description=_(u'Enter the title of the portlet.'), required=True, default=u'Google Analytics') profile = schema.Choice( title=_(u"Profile"), vocabulary='collective.googleanalytics.Profiles', description=_( u"Choose the Web property profile from Google Analytics."), required=True) reports = schema.List( title=_(u"Reports"), value_type=schema.Choice( vocabulary='collective.googleanalytics.PortletReports'), min_length=1, description=_(u"Choose the reports to display."), required=True)
def getWebProperties(context): """ Return list of Google Analytics profiles and web property IDs (e.g. UA-30481-22). """ custom_connection = (_(u'custom tracking code'), '_TRACKING_CODE_CUSTOM') analytics_tool = getToolByName(getSite(), 'portal_analytics') # short circuit if user hasn't authorized yet if not analytics_tool.auth_token: return SimpleVocabulary([SimpleTerm(custom_connection[1], custom_connection[1], custom_connection[0])]) try: accounts = analytics_tool.getAccountsFeed('accounts/~all/webproperties/~all/profiles') except error.BadAuthenticationError: choices = [('Please authorize with Google in the Google Analytics \ control panel.', None)] return SimpleVocabulary.fromItems(choices) except error.RequestTimedOutError: choices = [('The request to Google Analytics timed out. Please try \ again later.', None)] return SimpleVocabulary.fromItems(choices) if accounts: unique_choices = {} # In vocabularies, both the terms and the values must be unique. Since # there can be more than one profile for a given web property, we create a list # of all the profiles for each property. (Ideally we would use the URL for the # web property, but Google doesn't expose it through the Analytics API.) for entry in accounts.entry: for prop in entry.property: if prop.name == 'ga:profileName': title = prop.value if not isinstance(title, unicode): title = unicode(title, 'utf-8') if prop.name == 'ga:webPropertyId': webPropertyId = prop.value if not webPropertyId in unique_choices.keys(): unique_choices.update({webPropertyId : title}) else: unique_choices[webPropertyId] += ', ' + title # After we reverse the terms so that the profile name(s) is now the key, we need # to ensure that these keys are unique. So, we pass the resulting list through # dict() and then output a list of items. choices = dict([(crop(title, 40), property_id) for (property_id, title) in unique_choices.items()]).items() else: choices = [('No profiles available', None)] choices.append(custom_connection) return SimpleVocabulary([SimpleTerm(c[1], c[1], c[0]) for c in choices])
def _on_save(self, data={}): """ Checks to make sure that tracking code is not duplicated in the site configlet. """ tracking_web_property = data.get('tracking_web_property', None) properties_tool = getToolByName(self.context, "portal_properties") snippet = properties_tool.site_properties.webstats_js snippet_analytics = '_gat' in snippet or '_gaq' in snippet if tracking_web_property and snippet_analytics: plone_utils = getToolByName(self.context, 'plone_utils') plone_utils.addPortalMessage(_(u'You have enabled the tracking \ feature of this product, but it looks like you still have tracking \ code in the Site control panel. Please remove any Google Analytics \ tracking code from the Site control panel to avoid conflicts.'), 'warning')
def _on_save(self, data={}): """ Checks to make sure that tracking code is not duplicated in the site configlet. """ tracking_web_property = data.get('tracking_web_property', None) properties_tool = getToolByName(self.context, "portal_properties") snippet = properties_tool.site_properties.webstats_js snippet_analytics = '_gat' in snippet or '_gaq' in snippet if tracking_web_property and snippet_analytics: plone_utils = getToolByName(self.context, 'plone_utils') plone_utils.addPortalMessage( _(u'You have enabled the tracking \ feature of this product, but it looks like you still have tracking \ code in the Site control panel. Please remove any Google Analytics \ tracking code from the Site control panel to avoid conflicts.'), 'warning')
def __call__(self): """ Gets the token from the URL and takes the appropriate action. """ analytics_tool = getToolByName(self.context, "portal_analytics") plone_utils = getToolByName(self.context, "plone_utils") # Check if we are revoking the token. if self.request.get("revoke_token", 0): logger.debug("Trying to revoke token") ann = IAnnotations(analytics_tool) try: oauth2_token = ann.get("auth_token", None) if oauth2_token: oauth2_token.revoke() logger.debug("Token revoked successfuly") except OAuth2RevokeError: # Authorization already revoked logger.debug("Token was already revoked") pass except socket.gaierror: logger.debug("There was a connection issue, could not revoke " "token.") raise error.RequestTimedOutError, ("You may not have internet access. Please try again " "later.") ann["auth_token"] = None ann["valid_token"] = False message = _( u"Authorization revoked. You may now reauthorize with \ a different Google account." ) plone_utils.addPortalMessage(message) # Otherwise, we are setting the token. elif self.request.QUERY_STRING and "code" in self.request: code = self.request.get("code") logger.debug("Received callback from Google with code '%s' " % code) ann = IAnnotations(analytics_tool) oauth2_token = ann.get("auth_token", None) try: oauth2_token.get_access_token(code) logger.debug( "Code was valid, got '%s' as access_token and '%s' as " "refresh_token. Token will expire on '%s'" % (oauth2_token.access_token, oauth2_token.refresh_token, oauth2_token.token_expiry) ) message = _( u"Authorization succeeded. You may now configure \ Google Analytics for Plone." ) ann["valid_token"] = True except OAuth2AccessTokenError: logger.debug("Code was invalid, could not get tokens") ann["auth_token"] = None ann["valid_token"] = False message = _( u"Authorization failed. Google Analytics for \ Plone received an invalid token." ) plone_utils.addPortalMessage(message) # Redirect back to the control panel. portal_url = getToolByName(self.context, "portal_url") next_url = "%s/portal_analytics/@@analytics-controlpanel" % portal_url() self.request.response.redirect(next_url)
class AnalyticsSettingsForm(group.GroupForm): fields = field.Fields(IAnalyticsSettings) label = _(u'analytics_settings', default=u'Settings') description = _( u'analytics_settings_description', default=u'Configure the settings of the Google Analytics product.')
class AnalyticsControlPanelForm(ControlPanelForm): """ Google Analytics Control Panel Form """ implements(IAnalyticsControlPanelForm) template = ViewPageTemplateFile('controlpanel.pt') analytics_assignment = FormFieldsets(IAnalyticsReportsAssignment) analytics_assignment.id = 'analytics_assignment' analytics_assignment.label = _(u'analytics_assignment', default=u'Reports') analytics_assignment.description = _( u'analytics_assignment_description', default= u'Configure the reports that are displayed in the Google Analytics control panel.' ) analytics_assignment[ 'reports'].custom_widget = MultiCheckBoxVocabularyWidget analytics_tracking = FormFieldsets(IAnalyticsTracking) analytics_tracking.id = 'analytics_tracking' analytics_tracking.label = _(u'analytics_tracking', default=u'Tracking') analytics_tracking.description = _( u'analytics_tracking_description', default= u'Configure the way Google Analytics tracks statistics about this site.' ) analytics_tracking[ 'tracking_plugin_names'].custom_widget = MultiCheckBoxVocabularyWidget analytics_tracking[ 'tracking_excluded_roles'].custom_widget = MultiCheckBoxVocabularyWidget analytics_settings = FormFieldsets(IAnalyticsSettings) analytics_settings.id = 'analytics_settings' analytics_settings.label = _(u'analytics_settings', default=u'Settings') analytics_settings.description = _( u'analytics_settings_description', default=u'Configure the settings of the Google Analytics product.') form_fields = FormFieldsets(analytics_tracking, analytics_assignment, analytics_settings) label = _(u"Google Analytics") form_name = _("Google Analytics Settings") def authorized(self): """ Returns True if we have an auth token, or false otherwise. """ if self.context.auth_token: return True return False def auth_url(self): """ Returns the URL used to retrieve a Google AuthSub token. """ next = '%s/analytics-auth' % self.context.portal_url() scope = 'https://www.google.com/analytics/feeds/' return gdata.auth.GenerateAuthSubUrl(next, scope, secure=False, session=True) def account_name(self): """ Returns the account name for the currently authorized account. """ analytics_tool = getToolByName(self.context, 'portal_analytics') try: res = analytics_tool.getAccountsFeed('accounts') except error.BadAuthenticationError: return None except error.RequestTimedOutError: return None return res.title.text.split(' ')[-1] def _on_save(self, data={}): """ Checks to make sure that tracking code is not duplicated in the site configlet. """ tracking_web_property = data.get('tracking_web_property', None) properties_tool = getToolByName(self.context, "portal_properties") snippet = properties_tool.site_properties.webstats_js snippet_analytics = '_gat' in snippet or '_gaq' in snippet if tracking_web_property and snippet_analytics: plone_utils = getToolByName(self.context, 'plone_utils') plone_utils.addPortalMessage( _(u'You have enabled the tracking \ feature of this product, but it looks like you still have tracking \ code in the Site control panel. Please remove any Google Analytics \ tracking code from the Site control panel to avoid conflicts.'), 'warning')