# Copyright 2010 http://www.collabq.com
# Copyright 2009 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.
 
import logging
import time
 
from common import api
from common import clean
from common import decorator
from common import display
from common import helper
from common import exception
from common import google_contacts
from common import memcache
from common import models
from common import twitter
from common import user
from common import util
from common import validate
from common import views as common_views
 
from operator import itemgetter
from poboxopenid import util as util_externals
 
from django import http
from django import template
from django.conf import settings
from django.utils import simplejson
from django.template import loader
 
ENTRIES_PER_PAGE = 20
CONTACTS_PER_PAGE = 15
CHANNELS_PER_PAGE = 4
 
# This is a decorator to make it a bit easier to deal with the possibility
# the nick coming in via the subdomain
def alternate_nick(f):
  def _wrap(request, *args, **kw):
    if settings.WILDCARD_USER_SUBDOMAINS_ENABLED:
      # grab the nick from the subdomain
      if hasattr(request, 'subdomain'):
        kw['nick'] = request.subdomain
    return f(request, *args, **kw)
  _wrap.func_name = f.func_name
  return _wrap
 
 
def actor_search(request, query=None):
  view = request.user
  if query is None:
    query = request.REQUEST.get('q')
 
  if query: actor_tiles = api.actor_search(request.user, query)
  else: actor_tiles = None
 
  actor_tiles_count = len(actor_tiles)
 
  whose = 'You'
  area = 'user'
 
  c = template.RequestContext(request, locals())
  t = loader.get_template('actor/templates/search.html')
  return http.HttpResponse(t.render(c))
 
def actor_direct_messages(request, inbox='inbox'):
  logging.info('actor_direct_messages inbox: %s' % inbox)
 
  if not request.user:
    redirect_to = '/login?redirect_to=/inbox'
    if inbox == 'sent':
      redirect_to = '/login?redirect_to=/inbox/sent'
    return http.HttpResponseRedirect(redirect_to)
 
  view = request.user
  logging.info('View is: %s' % view)
 
  handled = common_views.handle_view_action(
      request,
      {
        'dm': '/inbox/sent',
        'dm_delete': request.path,
      }
  )
  if handled:
    return handled
 
  per_page = ENTRIES_PER_PAGE
  offset, prev = util.page_offset(request)
 
  if inbox == 'inbox':
    inbox_inbox = api.inbox_get_actor_inbox(request.user, request.user.nick,
                                            limit=(per_page + 1), offset=offset)
  elif inbox == 'sent':
    inbox_inbox = api.inbox_get_actor_sent(request.user, request.user.nick,
                                          limit=(per_page + 1), offset=offset)
 
  actor_streams = api.stream_get_actor_safe(request.user, request.user.nick)
 
  entries, more = helper.get_inbox_entries(request, inbox_inbox, True)
  entries = api.inbox_mark_unreaded(request.user, entries)
 
  contacts, channels, streams, entries = helper.assemble_inbox_data(request,
                                                              entries,
                                                              actor_streams,
                                                              request.user)
 
  friends_keys = api.actor_get_followers(view, view.nick, limit=200)
  friends_refs = api.actor_get_actors(view, friends_keys)
 
  fs = friends_refs.items()
  fs.sort()
 
  reply = request.REQUEST.get('reply', None)
  reply = clean.nick(reply)
  try:
    reply_ref = api.actor_get_safe(api.ROOT, reply)
  except:
    reply_ref = None
 
  if reply in friends_keys:
    let_send = True
  else:
    let_send = False
 
  friends = [value for key, value in fs]
 
  green_top = True
  sidebar_green_top = True
  subtab = 'dm'
 
  # for sidebar info
  channels_count = view.extra.get('channel_count', 0)
  channels_more = channels_count > CHANNELS_PER_PAGE
  followers_count = view.extra.get('follower_count', 0)
  contacts_count = view.extra.get('contact_count', 0)
  contacts_more = contacts_count > CONTACTS_PER_PAGE
 
  c = template.RequestContext(request, locals())
  t = loader.get_template('actor/templates/direct_messages.html')
  return http.HttpResponse(t.render(c))
 
@alternate_nick
def actor_history(request, nick=None, format='html'):
  nick = clean.nick(nick)
  view = api.actor_lookup_nick(request.user, nick)
 
  if not view:
    raise exception.UserDoesNotExistError(nick, request.user)
 
  #twitter
  unauth = twitter.is_unauth(request)
  if 'twitter' in request.POST:
    if unauth:
      return http.HttpResponseRedirect('/twitter/auth?redirect_to=/')
    status = twitter.post_update(request)
    if status:
      flasherror = ["We have experimented some problems trying to post a cc in twitter"]
 
  called_subscribe, sub_ref = common_views.call_api_from_request(
      request, 'subscription_request')
  if called_subscribe:
    if sub_ref.state == 'subscribed':
      message = 'Subscribed.'
    else:
      message = 'Subscription requested.'
    return util.RedirectFlash(view.url(), message)
 
  handled = common_views.handle_view_action(
      request,
      { 'entry_remove': request.path,
        'entry_remove_comment': request.path,
        'entry_mark_as_spam': request.path,
        'subscription_remove': view.url(),
        'actor_add_contact': request.path,
        'actor_remove_contact': request.path,
        'post': request.path,
        'presence_set': request.path,
      }
  )
  if handled:
    return handled
 
  privacy = 'public'
  if request.user:
    if view.nick == request.user.nick:
      privacy = 'private'
    # ROOT because we care whether or not request.user is a contact of
    # the view user's, not whether the request.user can see the contacts
    elif api.actor_has_contact(api.ROOT, view.nick, request.user.nick):
      privacy = 'contacts'
 
  # we're going to hide a bunch of stuff if this user is private and we
  # aren't allowed to see
  user_is_private = False
  if view.privacy < models.PRIVACY_PUBLIC and privacy == 'public':
    user_is_private = True
 
  per_page = ENTRIES_PER_PAGE
  offset, prev = util.page_offset(request)
 
  if privacy == 'public':
    if user_is_private:
      inbox = []
    else:
      inbox = api.inbox_get_actor_public(request.user, view.nick,
                                         limit=(per_page + 1), offset=offset)
  elif privacy == 'contacts':
    inbox = api.inbox_get_actor_contacts(request.user, view.nick,
                                         limit=(per_page + 1), offset=offset)
  elif privacy == 'private':
    inbox = api.inbox_get_actor_private(request.user, view.nick,
                                        limit=(per_page + 1), offset=offset)
 
  actor_streams = api.stream_get_actor_safe(request.user, view.nick)
 
  entries, more = helper.get_inbox_entries(request, inbox)
  contacts, channels, streams, entries = helper.assemble_inbox_data(request,
                                                              entries,
                                                              actor_streams,
                                                              view)
 
  # If not logged in, cannot write
  is_owner = request.user and view.nick == request.user.nick
 
  try:
    presence = api.presence_get(request.user, view.nick)
    presence_stream = api.stream_get_presence(request.user, view.nick)
    last_entry = api.entry_get_last(request.user, presence_stream.keyname())
    view.last_entry = last_entry
  except exception.ApiException:
    pass
 
 
  # for add/remove contact
  if request.user:
    user_is_contact = api.actor_has_contact(request.user,
                                            request.user.nick,
                                            view.nick)
    view.my_contact = user_is_contact
  else:
    user_is_contact = False
 
  show_dm_link = False
  if request.user:
    friends_keys = api.actor_get_followers(api.ROOT, request.user.nick, limit=200)
    if view.nick in friends_keys:
      show_dm_link = True
 
  #@begin zero code HISTORY
  # for sidebar info
  channels_count = view.extra.get('channel_count', 0)
  channels_more = channels_count > CHANNELS_PER_PAGE
  followers_count = view.extra.get('follower_count', 0)
  #@end
 
  # for sidebar_contacts
  contacts_count = view.extra.get('contact_count', 0)
  contacts_more = contacts_count > CONTACTS_PER_PAGE
 
  # Config for the template
  green_top = True
  sidebar_green_top = True
  selectable_icons = display.SELECTABLE_ICONS
  area = 'user'
  subtab = 'profile'
  hide_avatar = True
 
  c = template.RequestContext(request, locals())
 
  if format == 'html':
    t = loader.get_template('actor/templates/history.html')
    return http.HttpResponse(t.render(c))
  elif format == 'json':
    t = loader.get_template('actor/templates/history.json')
    r = util.HttpJsonResponse(t.render(c), request)
    return r
  elif format == 'atom':
    t = loader.get_template('actor/templates/history.atom')
    r = util.HttpAtomResponse(t.render(c), request)
    return r
  elif format == 'rss':
    t = loader.get_template('actor/templates/history.rss')
    r = util.HttpRssResponse(t.render(c), request)
    return r
 
@decorator.login_required
@alternate_nick
def actor_invite(request, nick, format='html'):
  nick = clean.nick(nick)
 
  view = api.actor_lookup_nick(request.user, nick)
  if not view:
    raise exception.UserDoesNotExistError(nick, request.user)
 
  if view.nick != request.user.nick:
    # Bounce the user to their own page (avoids any confusion for the wrong
    # nick in the url).
    return http.HttpResponseRedirect(
        '%s/invite' % request.user.url())
 
  handled = common_views.handle_view_action(
      request,
      { 'invite_request_email': request.path, })
  if handled:
    return handled
 
  if request.user and request.user.nick == view.nick:
    whose = 'You'
  else:
    whose = "%s" % view.display_nick()
 
  area = 'invite'
  c = template.RequestContext(request, locals())
 
  if format == 'html':
    t = loader.get_template('actor/templates/invite.html')
    return http.HttpResponse(t.render(c))
 
@alternate_nick
def actor_overview(request, nick, format='html'):
  nick = clean.nick(nick)
 
  view = api.actor_lookup_nick(request.user, nick)
 
  if not view:
    raise exception.UserDoesNotExistError(nick, request.user)
 
  if not request.user or view.nick != request.user.nick:
    # Instead of displaying the overview, redirect to the public-facing page
    return http.HttpResponseRedirect(view.url())
 
 
  unauth = twitter.is_unauth(request)
  twitter_options = twitter.twitter_options(request)
  if 'twitter' in request.POST:
    if unauth:
      return http.HttpResponseRedirect('/twitter/auth?redirect_to=/%s/overview' % view.display_nick())
    status = twitter.post_update(request)
    if status:
      flasherror = ["We have experimented some problems trying to post a cc in twitter"]
 
  logging.info('Overview request.path: %s' % request.path)
  handled = common_views.handle_view_action(
      request,
      { 
        'entry_remove': request.path,
        'entry_remove_comment': request.path,
        'entry_mark_as_spam': request.path,
        'presence_set': request.path,
        'settings_hide_comments': request.path,
        'post': request.path,
      }
  )
  logging.info('handled: %s' % handled)
  if handled:
    return handled
 
  per_page = ENTRIES_PER_PAGE
  offset, prev = util.page_offset(request)
 
  inbox = api.inbox_get_actor_overview(request.user,
                                       view.nick,
                                       limit=(per_page + 1),
                                       offset=offset)
 
  actor_streams = api.stream_get_actor(request.user, view.nick)
  entries, more = helper.get_inbox_entries(request, inbox, False, per_page, True, view)
  contacts, channels, streams, entries = helper.assemble_inbox_data(request, entries, actor_streams, view)
 
  latest = api.inbox_get_actor_private(request.user, view.nick, per_page)
  latest = api.entry_get_entries(request.user, latest)
  if len(latest) > 0:
    latest = latest[0]
 
  # Check for unconfirmed emails
  unconfirmeds = api.activation_get_actor_email(request.user, view.nick)
  if unconfirmeds:
    unconfirmed_email = unconfirmeds[0].content
 
  first_time, emailform = api.is_first_time(request.user, view.nick)
  loadmodal = first_time
 
  # If not logged in, cannot write
  is_owner = False
  try:
    is_owner = view.nick == request.user.nick
  except:
    pass
  presence = api.presence_get(request.user, view.nick)
 
  # for sidebar streams
  view_streams = dict([(x.key().name(), streams[x.key().name()])
                       for x in actor_streams])
 
  #@begin zero code OVERVIEW
  # for sidebar info
  channels_count = view.extra.get('channel_count', 0)
  channels_more = channels_count > CHANNELS_PER_PAGE
  followers_count = view.extra.get('follower_count', 0)
  #@end
 
  # for sidebar_contacts
  contacts_count = view.extra.get('contact_count', 0)
  contacts_more = contacts_count > CONTACTS_PER_PAGE
 
  # Config for the template
  green_top = True
  sidebar_green_top = True
  selectable_icons = display.SELECTABLE_ICONS
  actor_link = True
 
  area = 'home'
  subtab = 'overview'
 
  # TODO(tyler/termie):  This conflicts with the global settings import.
  # Also, this seems fishy.  Do none of the settings.* items work in templates?
  c = template.RequestContext(request, locals())
 
  if format == 'html':
    t = loader.get_template('actor/templates/overview.html')
    r = http.HttpResponse(t.render(c))
  elif format == 'json':
    t = loader.get_template('actor/templates/overview.json')
    r = util.HttpJsonResponse(t.render(c), request)
  elif format == 'atom':
    t = loader.get_template('actor/templates/overview.atom')
    r = util.HttpAtomResponse(t.render(c), request)
  elif format == 'rss':
    t = loader.get_template('actor/templates/overview.rss')
    r = util.HttpRssResponse(t.render(c), request)
  return r
 
# This is for mentions and is dirty! needs refactor.
# Refactor!
# begin @zero code
@alternate_nick
def actor_mentions(request, nick, format='html'):
  nick = clean.nick(nick)
 
  view = api.actor_lookup_nick(request.user, nick)
 
  if not view:
    raise exception.UserDoesNotExistError(nick, request.user)
 
  if not request.user or view.nick != request.user.nick:
    return http.HttpResponseRedirect(view.url())
 
  unauth = twitter.is_unauth(request)
  twitter_options = twitter.twitter_options(request)
  if 'twitter' in request.POST:
    if unauth:
      return http.HttpResponseRedirect('/twitter/auth?redirect_to=/%s/mentions' % view.display_nick())
    status = twitter.post_update(request)
    if status:
      flasherror = ["We have experimented some problems trying to post a cc in twitter"]
 
  handled = common_views.handle_view_action(
      request,
      { 
        'entry_remove': request.path,
        'entry_remove_comment': request.path,
        'entry_mark_as_spam': request.path,
        'presence_set': request.path,
        'settings_hide_comments': request.path,
        'post': request.path,
      }
  )
  if handled:
    return handled
 
  per_page = ENTRIES_PER_PAGE
  offset, prev = util.page_offset(request)
 
  inbox = api.inbox_get_actor_mentions(request.user,
                                       view.nick,
                                       limit=(per_page + 1), 
                                       offset=offset)
 
  # START inbox generation chaos
  # TODO(termie): refacccttttooorrrrr
  entries = api.entry_get_entries(request.user, inbox)
 
  #begin @zero code
  #if view.extra.get('comments_hide', 0):
    # TODO(tyler): This is certainly not the most eloquent way to filter
    # through entries to remove comments.
    # entries = [x for x in entries if not x.stream.endswith('comments')]
  entries = api.filter_entries_mentions(request.user, entries, view.nick)
 
  per_page = per_page - (len(inbox) - len(entries))
  entries, more = util.page_entries(request, entries, per_page)
 
  stream_keys = [e.stream for e in entries]
  actor_streams = api.stream_get_actor(request.user, view.nick)
  stream_keys += [s.key().name() for s in actor_streams]
  streams = api.stream_get_streams(request.user, stream_keys)
 
  contact_nicks = api.actor_get_contacts(request.user, 
                                         view.nick, 
                                         limit=CONTACTS_PER_PAGE)
  actor_nicks = (contact_nicks +
                 [view.nick] +
                 [s.owner for s in streams.values()] +
                 [e.owner for e in entries] +
                 [e.actor for e in entries])
  actors = api.actor_get_actors(request.user, actor_nicks)
 
  channels = api.actor_get_channels_member(request.user, view.nick,
                                limit=(CHANNELS_PER_PAGE + 1))
 
  # here comes lots of munging data into shape
  # clear deleted contacts
  contacts = [actors[x] for x in contact_nicks if actors[x]]
  streams = display.prep_stream_dict(streams, actors)
  entries = display.prep_entry_list(entries, streams, actors)
 
  # END inbox generation chaos
 
  # Check for unconfirmed emails
  unconfirmeds = api.activation_get_actor_email(request.user, view.nick)
  if unconfirmeds:
    unconfirmed_email = unconfirmeds[0].content
 
  # If not logged in, cannot write
  is_owner = False
  try:
    is_owner = view.nick == request.user.nick
  except:
    pass
  presence = api.presence_get(request.user, view.nick)
 
  # for sidebar streams
  view_streams = dict([(x.key().name(), streams[x.key().name()])
                       for x in actor_streams])
 
  channels_count = view.extra.get('channel_count', 0)
  channels_more = channels_count > CHANNELS_PER_PAGE
  followers_count = view.extra.get('follower_count', 0)
 
  # for sidebar_contacts
  contacts_count = view.extra.get('contact_count', 0)
  contacts_more = contacts_count > CONTACTS_PER_PAGE
 
  # Config for the template
  green_top = True
  sidebar_green_top = True
  selectable_icons = display.SELECTABLE_ICONS
 
  area = 'mentions'
  subtab = 'mentions'
 
  # TODO(tyler/termie):  This conflicts with the global settings import.
  # Also, this seems fishy.  Do none of the settings.* items work in templates?
  import settings
 
  c = template.RequestContext(request, locals())
 
  if format == 'html':
    t = loader.get_template('actor/templates/mentions.html')
    return http.HttpResponse(t.render(c))
 
# end @zerofuxor code
 
@alternate_nick
def actor_twitter(request, nick, format='html'):
  nick = clean.nick(nick)
  view = api.actor_lookup_nick(request.user, nick)
 
  if not view:
    raise exception.UserDoesNotExistError(nick, request.user)
 
  if not request.user or view.nick != request.user.nick:
    # Instead of displaying the twitter tab, redirect to the public-facing page
    return http.HttpResponseRedirect(view.url())
 
  unauth = twitter.is_unauth(request)
  if unauth:
    return http.HttpResponseRedirect('/twitter/auth?redirect_to=/%s/twitter' % view.display_nick())
 
  unauth = twitter.is_unauth(request)
  twitter_options = twitter.twitter_options(request)
  if 'twitter' in request.POST:
    if unauth:
      return http.HttpResponseRedirect('/twitter/auth?redirect_to=/%s/mentions' % view.display_nick())
    status = twitter.post_update(request)
    if status:
      flasherror = ["We have experimented some problems trying to post a cc in twitter"]
 
  logging.info('Twitter request.path: %s' % request.path)
  handled = common_views.handle_view_action(
      request,
      { 
        'post': request.path,
      }
  )
  logging.info('handled: %s' % handled)
  if handled:
    return handled
 
  more = False
  page = util.paging_get_page(request)
  size_entries = ENTRIES_PER_PAGE+ENTRIES_PER_PAGE*(page-1)
 
  #Getting Entries
  twitter_error = False
  try:
    user_info, streams = twitter.twitter_get_entries(request, size_entries+1)
  except:
    twitter_error = True
 
  if not twitter_error:
    if len(streams) > ENTRIES_PER_PAGE:
      streams = streams[0:-1]
      entry_id = streams[size_entries-1].id
      more = page+1
      size_stream = len(streams)
 
  green_top = True
  sidebar_green_top = True
  area = 'actor-twitter'
  subtab = 'twitter'
 
  c = template.RequestContext(request, locals())
 
  if format == 'html':
    t = loader.get_template('actor/templates/twitter_tab.html')
    return http.HttpResponse(t.render(c))
 
 
# Views are named based on their app and whenever possible the url they are
# accessible at, in this case for legacy compatibility there is a requirement
# for using the word "presence" in the url but it is not very closely
# applicable to what the controller does so we have diverged
 
# All views are passed a `request`, it should always be named request
 
# Everything in the `actor` app needs to know which actor it is acting
# on, hence the `nick` argument.
 
# Most views should be presentable in 3-4 formats, 'html' being the default,
# other common formats are JSON, XML, ATOM
@alternate_nick
def actor_item(request, nick=None, item=None, format='html'):
  # The nick passed in the url looks ugly with the escaped @ in it and is
  # generally just shorter if we only use the lead part of the nick
  # however the entire system expects full nicks so we should expand this
  # as soon as possible
  nick = clean.nick(nick)
 
  # Most pages have the concept of a viewer and an actor being viewed,
  # in all cases the viewer is `request.user` and the actor being viewed
  # should be named `view`
  view = api.actor_lookup_nick(request.user, nick)
 
  if not view:
    raise exception.UserDoesNotExistError(nick, request.user)
 
  # With very few exceptions, whenever we are referring to a an
  # instance that is an entity from the datastore we append `_ref`
  # to the variable name to distinguish it from the variable that
  # is simply a string identifier.
  # In the code below `stream_ref` and `entry_ref` are both entity
  # references, while `entry` is simply the string key_name of an entry
  stream_ref = api.stream_get_presence(request.user, view.nick)
  if not stream_ref:
    raise http.Http404()
 
  if item == 'last':
    entry_ref = api.entry_get_last(request.user, stream_ref.keyname())
    return http.HttpResponseRedirect(entry_ref.url())
  else:
    entry = '%s/%s' % (stream_ref.key().name(), item)
    entry_ref = api.entry_get_safe(request.user, entry)
 
  # Most api calls will return None if the entity being looked up does
  # not exist so we usually want to verify the return values
  if not entry_ref:
    raise http.Http404()
 
 
  # When handling user actions the following pattern more or less applies
  # if 'parameter_unique_to_action' in request.(POST|GET|REQUEST):
  #   try:
  #     validate.nonce(request, 'nonce_action')
  #     validate.anything_else_that_is_related_to_ui_rather_than_call()
  #
  #     local_variable = request.(POST|GET|REQUEST).get('request_arg')
  #     # or
  #     params = util.query_dict_to_keywords(request.(POST|GET|REQUEST))
  #
  #     # Our goal is to have most of the logic for any action translate
  #     # directly into an api call on behalf of the requesting user
  #     # such that the api call is responsible for validating all input
  #     # and raising any applicable errors
  #     result = api.some_api_method(request.user,
  #                                  method_variable=local_variable,
  #                                  ...)
  #     # or
  #     result = api.some_api_method(request.user,  **params)
  #
  #     # All actions should issue a redirect with a success message
  #     return util.RedirectFlash('some_url', 'some success message')
  #   except:
  #     exception.handle_exception(request)
  #
  # When an exception occurs we expect the rest of the page to be able
  # to be processed normally as if no action had been taken, the error
  # handling section of the template should display the errors caught
  # by the exception.handle_exception() call
 
  handled = common_views.handle_view_action(
      request,
      {'entry_add_comment': entry_ref.url(request=request),
       'entry_remove': view.url(request=request),
       'entry_remove_comment': entry_ref.url(request=request),
       'entry_mark_as_spam': entry_ref.url(request=request)
       }
      )
  if handled:
    return handled
 
  comments = api.entry_get_comments(request.user, entry_ref.key().name())
 
  # To minimize the number of lookups to the datastore once we know
  # all the data we will be displaying on a page we attempt to make
  # a list of all the actors associated with that data so that we can
  # fetch them all at once
  actor_nicks = [entry_ref.owner, entry_ref.actor] + [c.actor for c in comments]
  actors = api.actor_get_actors(request.user, actor_nicks)
 
  # Creates a copy of actors with lowercase keys (Django #6904: template filter
  # dictsort sorts case sensitive), excluding the currently logged in user.
  participants = {}
  for k, v in actors.iteritems():
    if (v and
        not (hasattr(request.user, 'nick') and request.user.nick == v.nick)):
      participants[k.lower()] = v
 
  # Due to restrictions on Django's templating language most of the time
  # we will have to take an additional step of preparing all of our data
  # for display, this usually translates to attaching references to
  # actor or stream entities.
  # Functions that handle this preparation should be added to the
  # common.display module
  entry = display.prep_entry(entry_ref,
                             {stream_ref.key().name(): stream_ref}, actors)
  comments = display.prep_comment_list(comments, actors)
 
  #To who is the reply for
  owner = request.GET.get('owner', entry.owner)
 
  # Additionally, to minimize more logic in the templates some variables
  # can be defined to configure the output, these are usually template specific
  # though some are common variables for anything that inherits from the
  # base templates
  green_top = True
  sidebar_green_top = True
 
  # The quickest way to make sure we are getting all of the things we care
  # about passed to the template without the temptation of making last minute
  # changes is just to pass `locals()` to the template context
  c = template.RequestContext(request, locals())
 
  # Ideally this is all that should be necessary to add additional output
  # formats, in practice it is yet to be seen whether additional data
  # preparation will be necessary before outputting in JSON or ATOM formats
  if format == 'html':
 
    # We always use the full path to the template to prevent naming conflicts
    # and difficult searches.
    t = loader.get_template('actor/templates/item.html')
    return http.HttpResponse(t.render(c))
 
  elif format == 'json':
    t = loader.get_template('actor/templates/item.json')
    r = http.HttpResponse(t.render(c))
    r['Content-type'] = 'text/javascript'
    return r
 
 
@alternate_nick
def actor_contacts(request, nick=None, format='html'):
  nick = clean.nick(nick)
 
  view = api.actor_lookup_nick(request.user, nick)
 
  if not view:
    raise exception.UserDoesNotExistError(nick, request.user)
 
  handled = common_views.handle_view_action(
      request,
      { 'actor_add_contact': request.path,
        'actor_remove_contact': request.path, })
  if handled:
    return handled
 
  per_page = CONTACTS_PER_PAGE
  offset, prev = util.page_offset_nick(request)
 
  contact_nicks = api.actor_get_contacts(request.user, view.nick,
                                         limit=(per_page + 1), offset=offset)
  actor_nicks = contact_nicks
  actors = api.actor_get_actors(request.user, actor_nicks)
  # clear deleted actors
  actors = dict([(k, v) for k, v in actors.iteritems() if v])
  per_page = per_page - (len(contact_nicks) - len(actors))
 
  # TODO(termie): incorporate this into paging so we only fetch the range
  #               on this page
  # add some extra info so we can let the user do contextual actions
  # on these homeboys
  if request.user and request.user.nick == view.nick:
    # looking at self, find out who of these people follow me so
    # I can highlight them
    for actor in actors:
      if api.actor_is_follower(request.user, view.nick, actor):
        actors[actor].my_follower = True
      actors[actor].my_contact = True
      actors[actor].rel = 'contact'
    whose = 'You'
  elif request.user:
    my_contacts_nicks = api.actor_get_contacts(request.user, request.user.nick)
    for f in my_contacts_nicks:
      try:
        actors[f].my_contact = True
      except:
        pass
    for x in actors:
      actors[x].rel = 'contact'
    whose = "%s" % view.display_nick()
 
  # here comes lots of munging data into shape
  actor_tiles = [actors[x] for x in contact_nicks if x in actors]
 
  actor_tiles_count = view.extra.get('contact_count', 0)
  actor_tiles, actor_tiles_more = util.page_actors(request,
                                                   actor_tiles,
                                                   per_page)
 
  area = 'people'
 
  c = template.RequestContext(request, locals())
 
  if format == 'html':
    t = loader.get_template('actor/templates/contacts.html')
    return http.HttpResponse(t.render(c))
  elif format == 'json':
    t = loader.get_template('actor/templates/contacts.json')
    r = http.HttpResponse(t.render(c))
    r['Content-type'] = 'text/javascript'
    return r
 
 
 
@alternate_nick
def actor_followers(request, nick=None, format='html'):
  nick = clean.nick(nick)
 
  view = api.actor_lookup_nick(request.user, nick)
 
  if not view:
    raise exception.UserDoesNotExistError(nick, request.user)
 
  handled = common_views.handle_view_action(
      request,
      { 'actor_add_contact': request.path,
        'actor_remove_contact': request.path, })
  if handled:
    return handled
 
  per_page = CONTACTS_PER_PAGE
  offset, prev = util.page_offset_nick(request)
 
  follower_nicks = api.actor_get_followers(request.user,
                                           view.nick,
                                           limit=(per_page + 1),
                                           offset=offset)
  actor_nicks = follower_nicks
  actors = api.actor_get_actors(request.user, actor_nicks)
  # clear deleted actors
  actors = dict([(k, v) for k, v in actors.iteritems() if v])
  per_page = per_page - (len(follower_nicks) - len(actors))
 
  # TODO(termie): incorporate this into paging so we only fetch the range
  #               on this page
  # add some extra info so we can let the user do contextual actions
  # on these homeboys
  if request.user and request.user.nick == view.nick:
    for actor in actors:
      if api.actor_is_contact(request.user, view.nick, actor):
        actors[actor].my_contact = True
    whose = 'You'
  else:
    whose = "%s" % view.display_nick()
 
  # here comes lots of munging data into shape
  actor_tiles = [actors[x] for x in follower_nicks if x in actors]
 
  actor_tiles_count = view.extra.get('follower_count', 0)
  actor_tiles, actor_tiles_more = util.page_actors(request,
                                                   actor_tiles,
                                                   per_page)
 
  area = 'people'
 
  c = template.RequestContext(request, locals())
 
  if format == 'html':
    t = loader.get_template('actor/templates/followers.html')
    return http.HttpResponse(t.render(c))
 
@alternate_nick
def actor_settings(request, nick, page='index'):
  """ just a static page that links to the rest"""
  nick = clean.nick(nick)
 
 
  view = api.actor_lookup_nick(api.ROOT, nick)
  if not api.actor_owns_actor(request.user, view):
    raise exception.ApiException(exception.PRIVACY_ERROR,
                                 'Operation not allowed')
 
  logging.info('Actor settings')
  handled = common_views.handle_view_action(
      request,
      {
        'activation_activate_mobile': view.url('/settings/mobile'),
        'activation_request_email': view.url('/settings/email'),
        'activation_request_mobile': view.url('/settings/mobile'),
        'settings_change_notify': view.url('/settings/notifications'),
        'settings_change_privacy': request.path,
        'settings_update_account': view.url('/settings/profile'),
        'actor_remove': '/logout',
        #'oauth_remove_consumer': request.path,
        #'oauth_remove_access_token': request.path
      }
  )
  if handled:
    return handled
 
 
 
  # TODO(tyler/termie):  This conflicts with the global settings import.
  # Also, this seems fishy.  Do none of the settings.* items work in templates?
  import settings
 
  is_admin_user = request.user.nick in settings.ADMINS_POBOX
  # TODO(tyler): Merge this into handle_view_action, if possible
  if 'password' in request.POST:
    try:
      validate.nonce(request, 'change_password')
 
      password = request.POST.get('password', '')
      confirm = request.POST.get('confirm', '')
 
      validate.password_and_confirm(password, confirm, field = 'password')
 
      api.settings_change_password(request.user, view.nick, password)
      response = util.RedirectFlash(view.url() + '/settings/password',
                                    'Password updated')
      request.user.password = util.hash_password(request.user.nick, password)
      # TODO(mikie): change when cookie-auth is changed
      user.set_user_cookie(response, request.user)
      return response
    except:
      exception.handle_exception(request)
 
  if page == 'feeds':
    try:
      if not settings.FEEDS_ENABLED:
        raise exception.DisabledFeatureError('Feeds are currently disabled')
    except:
      exception.handle_exception(request)
 
  if page == 'photo':
    redirect_to = view.url() + '/settings/photo'
    handled = common_views.common_photo_upload(request, redirect_to)
    if handled:
      return handled
 
 
  area = 'settings'
  full_page = page.capitalize()
 
  if page == 'mobile':
    full_page = 'Mobile Number'
 
    mobile = api.mobile_get_actor(request.user, view.nick)
    sms_notify = view.extra.get('sms_notify', False)
 
  elif page == 'im':
    full_page = 'IM Address'
    im_address = api.im_get_actor(request.user, view.nick)
    im_notify = view.extra.get('im_notify', False)
  elif page == 'index':
    email = api.email_get_actor(request.user, view.nick)
    email_notify = view.extra.get('email_notify', False)
    im_address = api.im_get_actor(request.user, view.nick)
    im_notify = view.extra.get('im_notify', False)
  elif page == 'feeds':
    full_page = 'Web Feeds'
  elif page == 'email':
    full_page = 'Email Address'
    email_notify = view.extra.get('email_notify', False)
 
    # check if we already have an email
    email = api.email_get_actor(request.user, view.nick) 
 
    # otherwise look for an unconfirmed one
    if not email:
      unconfirmeds = api.activation_get_actor_email(api.ROOT, view.nick)
      if unconfirmeds:
        unconfirmed_email = unconfirmeds[0].content
 
  elif page == 'design':
    handled = common_views.common_design_update(request, view.nick)
    if handled:
      return handled
    full_page = 'Look and Feel'
 
  elif page == 'notifications':
    email = api.email_get_actor(request.user, view.nick)
    email_notify = view.extra.get('email_notify', False)
    im_address = api.im_get_actor(request.user, view.nick)
    im_notify = view.extra.get('im_notify', False)
    mobile = api.mobile_get_actor(request.user, request.user.nick)
    sms_notify = view.extra.get('sms_notify', False)
 
    sms_confirm = sms_notify and not view.extra.get('sms_confirmed', False)
    # TODO(termie): remove this once we can actually receive sms
    sms_confirm = False
    bio = view.extra.get('bio', False)
 
  elif page == 'twitter':
    unauth = twitter.is_unauth(request)
    if not unauth:
      if request.POST:
        cc_twitter = request.POST.get('cc_twitter', False) and True
        api.settings_twitter_settings(request.user, view.nick, cc_twitter=cc_twitter)
        response = util.RedirectFlash(view.url() + '/settings/twitter',
                                    'Twitter options updated')
        return response
      else:
        post = False
      twitter_options = view.extra.get('twitter_settings', {'cc_twitter':True})
 
  elif page == 'profile':
    # check if we already have an email
    email = api.email_get_actor(request.user, view.nick) 
 
    # otherwise look for an unconfirmed one
    if not email:
      unconfirmeds = api.activation_get_actor_email(api.ROOT, view.nick)
      if unconfirmeds:
        unconfirmed_email = unconfirmeds[0].content
 
  elif page == 'photo':
    avatars = display.DEFAULT_AVATARS
    small_photos = api.image_get_all_keys(request.user, view.nick, size='f')
 
    # TODO(tyler): Fix this avatar nonsense!
    own_photos = [{
        'path' : small_photo.key().name(),
        'name' : small_photo.key().name()[len('image/'):-len('_f.jpg')],
      } for small_photo in small_photos
    ]
 
  elif page == 'privacy':
    PRIVACY_PUBLIC = api.PRIVACY_PUBLIC
    PRIVACY_CONTACTS = api.PRIVACY_CONTACTS
  elif page == 'jsbadge':
    full_page = 'Javascript Badges'
  elif page == 'badge':
    badges = [{'id': 'badge-stream',
               'width': '200',
               'height': '300',
               'src': '/themes/%s/badge.swf' % settings.DEFAULT_THEME,
               'title': 'Stream',
               },
              {'id': 'badge-map',
               'width': '200',
               'height': '255',
               'src': '/themes/%s/badge-map.swf' % settings.DEFAULT_THEME,
               'title': 'Map',
               },
              {'id': 'badge-simple',
               'width': '200',
               'height': '200',
               'src': '/themes/%s/badge-simple.swf' % settings.DEFAULT_THEME,
               'title': 'Simple',
               },
              ]
 
  elif page in ['password', 'delete']:
    # Catch for remaining pages before we generate a 404.
    pass
 
  elif page == 'tags_rel':
    if request.method == 'GET' and 'new' in request.GET:
      form = request.GET['new']
      if form == 'newtag' or form == 'modtag':
        tags = api.site_get_tags(request.user)
    elif request.method == 'POST':
      back_page = request.POST['backpage']
      if  back_page == 'newtag':
        parent = request.POST['parent']
        sons = request.POST['sons'].strip()
        sons = sons.split()
        api.new_tags_relation(parent, sons)
      elif back_page == 'modtag':
        parent_old = request.POST['parent_old']
        parent_new = request.POST['parent_new']
        son = request.POST['son']
        api.mod_tags_relation(parent_old, parent_new, son)
  else:
    return common_views.common_404(request)
 
  # rendering
  c = template.RequestContext(request, locals())
  t = loader.get_template('actor/templates/settings_%s.html' % page)
  return http.HttpResponse(t.render(c))
 
def actor_settings_redirect(request):
  if not request.user:
    return http.HttpResponseRedirect(
        '/login?redirect_to=%s' % request.get_full_path())
  nick = clean.nick(request.user.nick)
  view = api.actor_lookup_nick(request.user, nick)
  return http.HttpResponseRedirect(view.url() + request.get_full_path())
 
@decorator.login_required
def find_contacts(request, nick):
 
  """
  if we have an access token for this user attempt to fetch the contacts
  else if we have a request token attempt to get an access token
  if we have neither
    if we are trying to authorize, grab a request token and redirect to authorize page
    else
      show the page
  """
  redirect_to = request.REQUEST.get('redirect_to', '/')
 
  # these are for the find more contacts bits
  start_index = int(request.REQUEST.get('index', 1))
  max = 100
  token = request.REQUEST.get('token')
  contacts_more = int(request.REQUEST.get('contacts_more', 0))
 
  # this won't be seen unless contacts_more is positive,
  # so no worries about the possible negative value
  contacts_so_far = contacts_more - 1
 
  try:
    if not settings.GOOGLE_CONTACTS_IMPORT_ENABLED:
      raise exception.FeatureDisabledError('Google Contacts import is currently disabled')
 
    if 'lookup_remote_contacts' in request.POST:
      validate.nonce(request, 'lookup_remote_contacts')
 
      next_url = util.qsa(util.here(request),
                          {'redirect_to': redirect_to,
                           'upgrade_auth_token': '',
                           '_nonce': util.create_nonce(request.user,
                                                       'upgrade_auth_token'),
                           }
                          )
      auth_url = google_contacts.auth_sub_url(next_url)
      return http.HttpResponseRedirect(auth_url)
    elif 'actor_add_contacts' in request.POST:
      validate.nonce(request, 'actor_add_contacts')
 
 
      targets = request.POST.getlist('targets')
      owner = request.POST.get('owner', '')
 
      rv = api.actor_add_contacts(request.user, owner, targets)
 
      next_url = util.qsa(util.here(request),
                          {'redirect_to': redirect_to,
                           'contacts_more': contacts_more,
                           'index': start_index,
                           'token': token,
                           }
                          )
 
      return util.RedirectFlash(next_url, 'Contacts added.')
 
    elif 'upgrade_auth_token' in request.GET:
      validate.nonce(request, 'upgrade_auth_token')
 
      auth_token = google_contacts.auth_sub_token_from_request(request)
      session_token = google_contacts.upgrade_to_session_token(auth_token)
 
      next_url = util.qsa(util.here(request),
                          {'redirect_to': redirect_to,
                           'fetch_contacts': '',
                           'token': session_token.get_token_string(),
                           '_nonce': util.create_nonce(request.user,
                                                       'fetch_contacts'),
                           }
                          )
 
      return http.HttpResponseRedirect(next_url)
 
    elif 'fetch_contacts' in request.REQUEST:
      validate.nonce(request, 'fetch_contacts')
 
      # start_index and max are gathered above
      session_token = google_contacts.auth_sub_token_from_request(request)
 
      # check for the "My Contacts" group, otherwise, fetch it
      my_contacts = memcache.client.get('%s/my_contacts' % token)
      if not my_contacts:
        my_contacts = google_contacts.get_system_group(session_token,
                                                       'Contacts')
        memcache.client.set('%s/my_contacts' % token, my_contacts)
 
 
      rv, more = google_contacts.get_contacts_emails(session_token,
                                                     group=my_contacts,
                                                     index=start_index,
                                                     max=max)
 
      contacts = []
 
      for name, email in rv:
        logging.info('looking up "%s" %s', name, email)
        contacts.append(api.actor_lookup_email(request.user, email))
 
      contacts = [x for x in contacts if x]
 
      # for the template
      contacts_found = True
      contacts_more = more
      contacts_so_far = contacts_more - 1
      token = session_token.get_token_string()
      contacts_emails = rv
 
      # if no contacts were found and more are available, try some more
      if not contacts and contacts_more:
        next_url = util.qsa(util.here(request),
                            {'fetch_contacts': '',
                             'contacts_more': contacts_more,
                             'index': contacts_more,
                             'token': token,
                             '_nonce': util.create_nonce(request.user,
                                                         'fetch_contacts'),
                             'redirect_to': redirect_to,
                             }
                            )
        # TODO(termie): this can take a really long time, probably not really
        #               viable until we can do it with javascript
        #return util.MetaRefresh(next_url, message='Still working...', second=1)
        #return http.HttpResponseRedirect(next_url)
 
  except:
    exception.handle_exception(request)
 
 
  # set the progress
  welcome_photo = True
  welcome_mobile = True
 
  view = request.user
  whose = 'You'
  area = 'people'
 
  c = template.RequestContext(request, locals())
 
  t = loader.get_template('actor/templates/findpeople_contacts.html')
  return http.HttpResponse(t.render(c))
 
def actor_twitter_auth(request):
  # Getting the access token
  view = user.get_user_from_cookie_or_legacy_auth(request)
  access_token = None
  if view is not None:
    access_token = view.extra.get('twitter_access_token', None)
 
  redirect_to = request.GET.get('redirect_to', '/')
 
  if access_token is not None:
    if redirect_to == 'autoclose':
      memcache.client.set('redirect_to', redirect_to, 600)
      return http.HttpResponseRedirect('/twitter/callback')
    return http.HttpResponseRedirect(redirect_to)
 
  try:
    authorization_url = twitter.get_authorization_url(redirect_to)
  except:
    response = util.RedirectFlash(redirect_to, 'We can not connect to twitter')
    return response
 
  return http.HttpResponseRedirect(authorization_url)
 
 
def actor_twitter_signin(request):
  redirect_to = request.GET.get('redirect_to', '/')
 
  try:
    get_signin_url = twitter.get_signin_url(redirect_to)
  except:
    response = util.RedirectFlash(redirect_to, 'We can not connect to twitter')
    return response
 
  return http.HttpResponseRedirect(get_signin_url)
 
def actor_twitter_callback(request):
  logging.info("Callback URL")
 
  redirect_to = memcache.client.get('redirect_to')
  memcache.client.delete('redirect_to')
 
  view = user.get_user_from_cookie_or_legacy_auth(request)
  if view is not None:
    if view.extra.get('twitter_access_token') is None:
      twitter_user, token = util_externals.twitter_user()
      logging.info("Creating external profile after creates a token")
      ep_ref = api.create_external_profile(view.nick,
                                             'twitter',
                                             twitter_user.screen_name,
                                             str(twitter_user.id),
                                             'http://twitter.com/%s'%twitter_user.screen_name)
 
      view.extra['twitter_access_token'] = token
      view.put()
 
  if not redirect_to == 'autoclose':
    response = util.RedirectFlash(redirect_to,
                                  'Oauth authentication success')
    return response
 
  c = template.RequestContext(request, locals())
  t = loader.get_template('actor/templates/twitter_callback.html')
  return http.HttpResponse(t.render(c))
 
def actor_post_update(request):
  access_token = request.user.extra.get('twitter_access_token', None)
 
  if access_token is None:
    return http.HttpResponseRedirect('/twitter/auth?redirect_to=/twitter/post_update')
 
  api_twitter = twitter.get_api(access_token)
  user = api_twitter.GetUserInfo()
  tiempo = time.localtime()
  status = api_twitter.PostUpdate('testing from with actor_post_update %s' % tiempo)
 
  c = template.RequestContext(request, locals())
  t = loader.get_template('actor/templates/twitter_callback.html')
  return http.HttpResponse(t.render(c))
 
 
def actor_removing_token(request):
  user = request.user
  access_token = user.extra.pop('twitter_access_token', None)
  user.put()
 
  if access_token is None:
    msg = 'There was not any auth'
  else:
    msg = 'Access revoked successfully'
 
  logging.info(msg)
 
  redirect_to = request.GET.get('redirect_to', None)
 
  if redirect_to is not None:
    response = util.RedirectFlash(redirect_to,
                                'Access revoked successfully')
    return response
 
  c = template.RequestContext(request, locals())
  t = loader.get_template('actor/templates/twitter_removing.html')
  return http.HttpResponse(t.render(c))
 
def actor_email_update(request):
  logging.info("actor_email_update")
 
  if request.method == 'POST':
    if request.is_ajax():
      email = request.REQUEST.get('email', 'none')
      try:
        validate.email(email)
        api.activation_request_email(api.ROOT, request.user.nick, email)
        message = {'message':"success"}
      except exception.ValidationError, inst:
        message = {'message':str(inst)}
      except exception.ApiException, inst:
        message = {'message':str(inst)}
    else:
      message = {'message':'error'}
  else:
    message = {'message':'error'}
 
  response = http.HttpResponse(simplejson.dumps(message))
  response['Content-type']  = 'text/javascript; charset=utf-8'
  return response
 
# Facebook
def actor_facebook_callback(request):
  redirect_to = memcache.client.get('redirect_to', 'None')
  memcache.client.delete('redirect_to')
 
  c = template.RequestContext(request, locals())
  t = loader.get_template('actor/templates/twitter_callback.html')
  return http.HttpResponse(t.render(c))