class Console(webapp2.RequestHandler):
  @account_login_required
  def get(self):
    """ Render dashboard """
    current_session = Session().get_current_session(self)
    
    account = current_session.get_account_entity()
    api_key = account.apiKey
    
    template_values = {'dashboard_main' : True,
                       'account_name': current_session.get_email(),
                       'api_key': api_key}
    self.response.out.write(template.render(constants.TEMPLATE_PATHS.CONSOLE_DASHBOARD, template_values)) class Users(db.Model):
  userid = db.StringProperty(required=True)
  isEnabled = db.StringProperty(required=True, choices=set(["yes","no"]))
  creationDate = db.DateTimeProperty(auto_now_add=True, indexed=False)
  modifiedDate = db.DateTimeProperty(auto_now=True, indexed=False)
  accountRef = db.ReferenceProperty(reference_class=Accounts, required=True)
  points = db.IntegerProperty(default=0)
  rank = db.IntegerProperty(default=constants.NOT_RANKED)
  last_time_ranked = db.DateTimeProperty(indexed=False)
  profileName = db.StringProperty(default="Anonymous", indexed=False)
  profileLink = db.TextProperty(indexed=False)
  profileImg = db.TextProperty(default="http://i.imgur.com/hsDiZ.jpg", indexed=False)
  userNotes = db.TextProperty(indexed=False)
  tags = db.StringProperty() class NewsLetterSignUp(webapp2.RequestHandler):
  def post(self):
    clean = XssCleaner()
    email = self.request.get('email')    
    email = clean.strip(email)
    newemail = Email(email=email)
    newemail.put()
    ret = {"success":"true"}
    ret = json.dumps(ret) 
    self.response.out.write(ret) class SignUp(webapp2.RequestHandler):
  """
  get: is used to activate an account.
  post: is used to handle sign up requests coming from web form.
  """
  
  def get(self):
    """Account activation via email"""
    
    values = {'error_message' : "Activation not successful.",
              'error': True}
    
    id = self.request.get("activate")
    error_message = self.request.get("error_msg") 
    if id == None or id == "":
      if error_message:
        values['error_message'] = error_message
      logging.error("Activation attempted without ID")
    else:
      """Look up the account in pending creates table"""
      try:
        pending_entity = Pending_Create.get_by_key_name(id)
        account = None
        if pending_entity != None:
          """ Look up corresponding account entity """
          email = pending_entity.email
          account = memcache_db.get_entity(email, "Accounts")
        else:
          logging.error("Pending entity could not be looked up.")
          
        if account != None:
          if account.isEnabled == ACCOUNT_STATUS.PENDING_CREATE:
            update_fields = {"isEnabled" : ACCOUNT_STATUS.ENABLED }
            memcache_db.update_fields(email, "Accounts", update_fields)
            
            try:
              """ remove item from pending creates """
              pending_entity.delete()
            except NotSavedError:
              logging.error("Entity with id: " + id + " was not in data store...")
              
            values = {'activation' : True}
          else:
            logging.error("Account status is not pending create")
            
      except:
        logging.error("Activation tried and failed with ID: " + id)
    
    """ render with values set above """
    self.response.out.write(template.render(constants.TEMPLATE_PATHS.CONSOLE_LOGIN, values))
    
  def post(self):
    email = self.request.get("email")
    password = self.request.get("password")
    repeat_password = self.request.get('repeat_password')
    show_links = self.request.get("show_links")
    if not utils.validEmail(email):
      values = {"success" : False,
                "message" : "ERROR: You need to provide a valid email address."}
      if show_links == "yes":
        values['givelinks'] = True 
      self.response.out.write(template.render(constants.TEMPLATE_PATHS.CONSOLE_SIGN_UP, values))
      logging.error("Bad email %s"%email)
      return
    if password != repeat_password:
      values = {"success" : False,
                "message" : "ERROR: Passwords did not match."}
      if show_links == "yes":
        values['givelinks'] = True 
      logging.error("Bad passwords for email %s"%email)
      self.response.out.write(template.render(constants.TEMPLATE_PATHS.CONSOLE_SIGN_UP, values))
      return   
    ent_type = 'Accounts'
    existing_account = memcache_db.get_entity(email, ent_type)
    if existing_account != None:
      logging.error('An account already exists with that email: ' + existing_account.email)
 
      """ if the account is a test account, activate the account """
      if email in constants.TEST_ACCOUNTS and environment.is_dev():
        logging.debug("Account is a valid test account")
        
        memcache_db.delete_entity(existing_account, email)
        accounts_dao.create_account(email, password, True)
        
        message = "Your test account has been activated!"
        values = {"success" : True,
                "message" : message}
        if show_links == "yes":
          values['givelinks'] = True 
      elif existing_account.isEnabled == ACCOUNT_STATUS.PENDING_CREATE:
        """ REPEAT SIGN UP WITH UNACTIVATED ACCOUNT!!!!!!!!! """
        """ send the email again with the same activation ID """
        pc = pending_create_dao.get_id_by_email(email)
        activate_url = get_activate_url(pc.id)
        email_sent = send_email(email, activate_url)
        logging.info("Repeat sign up for account that was not activated yet. An email will be sent to with same activation link. Email: " + email + ", activation link: " + activate_url)
        message = ""
        if email_sent:
          message = "An email has been sent to you with a link to activate your account!"
        else:
          message = "There was an error during account creation. Please send an email to support@cloudcaptive.com"
        values = {"success" : True,
                  "message" : message}
        if show_links == "yes":
          values['givelinks'] = True 
        
      else:
        message = "ERROR: An account using this email address already exists. Contact support@cloudcaptive for support."
        values = {"success" : False,
                "message" : message}
        if show_links == "yes":
          values['givelinks'] = True 
    else:    
      """create an account and send an email for validation"""
      accounts_dao.create_account(email, password)
      
      """Add email to pending creates table"""
      id = str(uuid.uuid4())
      pending_create = Pending_Create(key_name=id, id=id, email=email)
      pending_create.put()
          
      
      """send an email to user to complete set up, get arguments in the string will be email and cookie ID"""
      activate_url = get_activate_url(id)
      logging.info("Activation URL for account: " + email + " is " + activate_url)
      email_sent = send_email(email, activate_url)
  
      message = ""
      if email_sent:
        message = "Sign up was a success. An activation link has been sent to your email address."
      else:
        message = "There was an error during account creation. Please send an email to support@cloudcaptive.com"
      values = {"success" : True,
                "message" : message}
      if show_links == "yes":
        values['givelinks'] = True 
    """ Render result with whatever values were filled in above """  
    self.response.out.write(template.render(constants.TEMPLATE_PATHS.CONSOLE_SIGN_UP, values)) class UploadBadge(blobstore_handlers.BlobstoreUploadHandler):
  @account_login_required
  def post(self):
    #TODO
    # IN the future we should try to move this logic to data access utility layer
    # and have the handler portion be in console.py
    current_session = Session().get_current_session(self)
    account = current_session.get_account_entity()
    if not account:
      self.response.out.write("Problem with your account. Please email support.")
      return  

    #TODO make sure badge name is not taken, or warn if overwritting
    badge_name = self.request.get("badgename")
    badge_name = string.replace(badge_name, " ", "_")
    badge_name = string.replace(badge_name, "-", "_")

    badge_theme = self.request.get("badgetheme")
    badge_theme= string.replace(badge_theme, "-", "_")
    badge_theme = string.replace(badge_theme, " ", "_")

    badge_des = self.request.get("badgedescription")

    upload_files = self.get_uploads('file')
    blob_info = upload_files[0]

    badge_file_name = blob_info.filename
    badge_ext = get_file_ext(badge_file_name)     
    if badge_ext not in constants.IMAGE_PARAMS.VALID_EXT_TYPES:
      delete_blob(blob_info)
      self.redirect('/adminconsole/badges?error=BadImageType')
      return 
      
    logging.info("File ext:"+badge_ext)
    if not badge_name:
      delete_blob(blob_info)
      self.redirect('/adminconsole/badges?error=NoNameGiven')
      return 
    if not badge_des:
      delete_blob(blob_info)
      self.redirect('/adminconsole/badges?error=NoDescriptionGiven')
      return 
    if not badge_theme: 
      delete_blob(blob_info)
      self.redirect('/adminconsole/badges?error=NoThemeGiven')
      return 
    if not blob_info:
      delete_blob(blob_info)
      self.redirect('/adminconsole/badges?error=InternalError')
      return 
    if blob_info.size > constants.MAX_BADGE_SIZE:
      delete_blob(blob_info)
      self.redirect('/adminconsole/badges?error=FileTooLarge')
      return 
    perm = "private"
    if account.email == constants.ADMIN_ACCOUNT:
      perm = "public" 

    badge_key = badges_dao.create_badge_key(account.email, badge_theme, badge_name, perm)

    badge = badges_dao.create_badge_type(badge_key,
                      badge_name,
                      badge_des,
                      account,
                      badge_theme,
                      badge_ext,
                      blob_info=blob_info)
    self.redirect('/adminconsole/badges')

  def get(self):
    self.redirect('/adminconsole/badges') class DownloadBadge(webapp2.RequestHandler):
  def get(self):
    badge_id = self.request.get("bk")
    if not badge_id:
      self.redirect('/images/default.jpg')
      return 
    badge = badges_dao.get_badge_image(badge_id)
    if not badge:
      logging.error("Download badge: %s key not found"%badge_id) 
      self.redirect('/images/default.jpg')
      return 
    self.response.headers['Content-Type'] = "image/" + str(badge.imgType)
    self.response.out.write(badge.image) class SeeTheme(webapp2.RequestHandler):
  @account_login_required
  def get(self):
    clean = XssCleaner()
    badge_theme = self.request.get("theme")
    clean_badge_theme = clean.strip(badge_theme)
    if badge_theme != clean_badge_theme:
      logging.info("Cleaning: %s to %s"%(badge_theme, clean_badge_theme))
    badges = db.GqlQuery("SELECT * FROM Badges where theme=:1", 
                         clean_badge_theme)
    badgeset = []
    for b in badges:
      item = {"name": b.name, 
       "description": b.description, 
       "alt":b.altText, 
       "key":b.key().name(),
       "perm":b.permissions }
      badgeset.append(item)

    values = {"badgetheme":clean_badge_theme,
              "badges": badgeset}
    path = os.path.join(os.path.dirname(__file__), 'templates/badgetheme.html')
    # TODO fix this for when you hit refresh, dont go 404 on them
    self.response.out.write(template.render(path, values)) class GetAnalytics(webapp2.RequestHandler):
  @account_login_required
  def get(self):
    current_session = Session().get_current_session(self)
    acc = current_session.get_account_entity()
    a_type = self.request.get("type")
    if a_type not in VALID_ANALYTICS:
      return json.dumps({'success':'false'})

    values = getattr(self, a_type + "_values")(acc)
    self.response.out.write(json.dumps(values))


  @account_login_required
  def post(self):
    current_session = Session().get_current_session(self)
    acc = current_session.get_account_entity()
    a_type = self.request.get("type")
    if a_type not in VALID_ANALYTICS:
      return json.dumps({'success':'false'})

    values = getattr(self, a_type + "_values")(acc)
    self.response.out.write(json.dumps(values))

  def badges_values(self, acc):
    q = BadgeBatch.all().filter("account_key =", acc.key().name())
    values = {"success": "true"}
    res = q.fetch(1000) 
    values['total'] = q.count() or 1
    values['entry'] = []
    values['badges'] = []
    values['numbadges'] = 0
    badges = set()
    for ii in res:
      ent = {'date':ii.date.strftime("%Y-%m-%d"),
             'count':str(ii.counter),
             'badgeid':ii.badgeid}
      values['entry'].append(ent)
      badges.add(ii.badgeid)
    else:
      ent = {'date':datetime.datetime.now().strftime("%Y-%m-%d"),
             'count':"0",
             'badgeid':"none"}
      values['entry'].append(ent)
      badges.add("none")
             
    badges = list(badges)
    #badges = badges_dao.get_all_badges_for_account(acc)
    for ii in badges:
      values['badges'].append(ii)
      values['numbadges'] += 1
    return values

  def badgepoints_values(self, acc):
    q = BadgePointsBatch.all().filter("account_key =", acc.key().name())
    values = {"success": "true"}
    res = q.fetch(1000) 
    values['total'] = q.count() or 1
    values['entry'] = []
    values['badges'] = []
    values['numbadges'] = 0
    badges = set()
    for ii in res:
      ent = {'date':ii.date.strftime("%Y-%m-%d"),
             'count':str(ii.counter),
             'badgeid':ii.badgeid}
      values['entry'].append(ent)
      badges.add(ii.badgeid) 
    else:
      ent = {'date':datetime.datetime.now().strftime("%Y-%m-%d"),
             'count':"0",
             'badgeid':"none"}
      values['entry'].append(ent)
      badges.add("none")
 
    # = badges_dao.get_all_badges_for_account(acc)
    badges = list(badges)
    for ii in badges:
      values['badges'].append(ii)
      values['numbadges'] += 1
    return values

  def points_values(self, acc):
    q = PointBatch.all().filter("account_key =", acc.key().name())
    values = {"success": "true"}
    res = q.fetch(1000) 
    values['total'] = q.count() or 1
    values['entry'] = []
    for ii in res:
      ent = {'date':ii.date.strftime("%Y-%m-%d"),
             'count':str(ii.counter)}
      values['entry'].append(ent)
    else:
      ent = {'date':datetime.datetime.now().strftime("%Y-%m-%d"),
             'count':"0"}
      values['entry'].append(ent)
    return values

  def apicalls_values(self, acc):
    q = APICountBatch.all().filter("account_key =", acc.key().name())
    values = {"success": "true"}
    res = q.fetch(1000) 
    values['total'] = q.count() or 1
    values['entry'] = []
    for ii in res:
      ent = {'date':ii.date.strftime("%Y-%m-%d"),
             'count':str(ii.counter)}
      values['entry'].append(ent)
    else:
      ent = {'date':datetime.datetime.now().strftime("%Y-%m-%d"),
             'count':"0"}
      values['entry'].append(ent)
    return values class RunAnalytics(webapp2.RequestHandler):
  def get(self):
    now = datetime.datetime.now()
    a_day_ago = now - datetime.timedelta(days=1)

    context = {}
    context['start_time'] = str(stripMilSecs(a_day_ago))
    context['end_time'] = str(stripMilSecs(now))
    fsm.startStateMachine('CountAwardedBadges', [context])
    fsm.startStateMachine('CountAwardedPoints', [context])
    fsm.startStateMachine('CountAwardedBadgePoints', [context])
    fsm.startStateMachine('CountAPICalls', [context]) APP_NAME = "cloudcaptive-userinfuser" #os.environ['APPLICATION_ID']
APP_OWNER_EMAIL = "raj@cloudcaptive.com"
TEST_ACCOUNTS = ["raj@cloudcaptive.com","shanrandhawa@gmail.com","shan@cloudcaptive.com"]
ADMIN_ACCOUNT = "admin@" + APP_NAME + ".appspot.com"
DEBUG = True
ACCOUNT_TYPES = ["admin", "bronze", "silver", "gold", "platinum"]
PAYMENT_TYPES = ["free", "trial", "paid", "exempt"]
DEV_URL = "http://localhost:8080"
PRODUCTION_URL= "http://"+APP_NAME+".appspot.com"
SECURE_PRODUCTION_URL = "https://"+APP_NAME+".appspot.com"
CONSOLE_GET_WIDGET_DEV = "http://localhost:8080/api/1/getwidget"
CONSOLE_GET_WIDGET_PROD = "https://"+APP_NAME+".appspot.com/api/1/getwidget"
AES_ENCRYPTION_KEYNAME = "aes_encryption_key"
ENCRYPTION_KEYNAME = "encryption_key"
PROTECTED_DB_TYPES = ["Accounts", "Badges", "Users", "BadgeInstance", "BadgeImage", "TrophyCase", "Milestones", "Points", "Notifier", "Rank", "Leaderboard", "PassPhrase"]
  CLIENT_EVENT = ["signin",
             "logout",
             "dropaccount",
             "startpayments",
             "upgrade",
             "downgrade",
             "dropuser",
             "addbadge",
             "removebadge",
             "error",
             "viewanalytics",
             "viewbadges",
             "signup",
             "test"]
  API_EVENT = ["awardpoints",
             "awardbadge",
             "awardbadgepoints",
             "badgeawarded", # if required badge points met
             "removebadge",
             "viewwidget",
             "prefetchwidget",
             "clickbadge",
             "updateuser",
             "loginuser",
             "createuser",
             "getuserdata",
             "notify_badge",
             "notify_points"]
  SECRET_KEYNAME = "secret_log_key"
  PATH = '/logevents'
  SECRET_KEYNAME = "secret_update_key"
  PATH = '/updateaccount'
  DEFAULT_SIZE = 150
  LEADER_SIZE = 90 
  VALID_EXT_TYPES = ['png','jpg','jpeg','gif','PNG','JPG','JPEG','GIF']
  USER_AVATAR = "http://i.imgur.com/cXhDa.png"
  POINTS_IMAGE = "http://cdn1.iconfinder.com/data/icons/nuove/128x128/actions/edit_add.png"
  LOGIN_IMAGE = "http://www.iconfinder.com/icondetails/8794/128/cryptography_key_lock_log_in_login_password_security_icon" 
  NOT_AUTH = 1 # Not authorized error, either bad account or api key
  BADGE_NOT_FOUND = 2
  USER_NOT_FOUND = 3
  INTERNAL_ERROR = 4 
  BAD_ARGS = 5
  BAD_USER = 6
  ENABLED = "enabled"
  PENDING_CREATE = "pending_create"
  DISABLED = "disabled"
  RANGE_OF_VALUES = [ENABLED,
                     PENDING_CREATE,
                     DISABLED]
  CONSOLE_LOGIN = os.path.join(os.path.dirname(__file__), '../static/console_templates/login.html')
  CONSOLE_FORGOTTEN_PASSWORD = os.path.join(os.path.dirname(__file__), '../static/console_templates/forgot.html')
  CONSOLE_DASHBOARD =  os.path.join(os.path.dirname(__file__), '../static/console_templates/dashboard.html')
  CONSOLE_USERS =  os.path.join(os.path.dirname(__file__), '../static/console_templates/users.html')
  CONSOLE_SIGN_UP =  os.path.join(os.path.dirname(__file__), '../static/console_templates/signup_console.html')
  RENDER_TROPHY_CASE = os.path.join(os.path.dirname(__file__), 'api/widgets/v1.0/trophy_case.html')
  RENDER_NOTIFIER = os.path.join(os.path.dirname(__file__), 'api/widgets/v1.0/notifier.html')
  RENDER_MILESTONES = os.path.join(os.path.dirname(__file__), 'api/widgets/v1.0/milestones.html')
  RENDER_POINTS = os.path.join(os.path.dirname(__file__), 'api/widgets/v1.0/points.html')
  RENDER_RANK = os.path.join(os.path.dirname(__file__), 'api/widgets/v1.0/rank.html')
  RENDER_LEADERBOARD = os.path.join(os.path.dirname(__file__), 'api/widgets/v1.0/leaderboard.html')
  COOKIE_EMAIL_PARAM = "key" # simply email, however, we don't want people their address is set in the cookie
  COOKIE_KEY_PARAM = "ssid" # same as the cookie_key param in our Accounts DB
  COOKIE_EXPIRATION = "esid" # expiration
  VALID_FOR_SECONDS = 86400 # make session valid for a day 
  POST_DATA = ""
  ACTIVATE_URL = ""
  REDIRECT_SIGNUP_SUCCESS = ""
  REDIRECT_SIGNUP_FAIL = ""
  REDIRECT_ACTIVATE_SUCCESS = ""
  REDIRECT_ACTIVATE_FAIL = ""
  REDIRECT_HOME = ""
  REDIRECT_SIGNUP = ""
    POST_DATA = "/signup"
    ACTIVATE_URL = "/signup"
    REDIRECT_SIGNUP_SUCCESS = "/html/signup_success.html"
    REDIRECT_SIGNUP_FAIL = "/html/signup_failed.html"
    REDIRECT_ACTIVATE_SUCCESS = "/html/activation_success.html"
    REDIRECT_ACTIVATE_FAIL = "/html/activation_fail.html"
    REDIRECT_HOME = "/html/index.html"
    REDIRECT_SIGNUP = "/html/signup.html"
    POST_DATA = SECURE_PRODUCTION_URL + "/signup" # need to use https URL for posting credentials
    ACTIVATE_URL = SECURE_PRODUCTION_URL + "/signup"
    REDIRECT_SIGNUP_SUCCESS = PRODUCTION_URL + "/html/signup_success.html"
    REDIRECT_SIGNUP_FAIL = PRODUCTION_URL + "/html/signup_failed.html"
    REDIRECT_ACTIVATE_SUCCESS = PRODUCTION_URL + "/html/activation_success.html"
    REDIRECT_ACTIVATE_FAIL = PRODUCTION_URL + "/html/activation_fail.html"
    REDIRECT_HOME = PRODUCTION_URL + "/html/index.html"
    REDIRECT_SIGNUP = PRODUCTION_URL + "/html/signup.html"
ADMINPASSWD = "u8u8u9i9i"
ADMINKEY = "u8u89i9i"
VALID_WIDGETS = ["trophy_case", "notifier", "milestones", "points", "leaderboard", "rank"]
WIDGETS_THAT_DONT_NEED_A_USER = ["milestones", "leaderboard"]
ANONYMOUS_USER = "__ui__anonymous__"
LOCAL_URL = "http://localhost:8080/"
MAX_BADGE_SIZE = 2<<16 # 128k
NOTIFIER_SIZE_DEFAULT = 180
NOT_RANKED = -1 # For unranked users
NUMBER_RANKED = "10000" #Anyone not in the top 10k is not ranked
TOP_USERS = "10"
class LOGGING:
class UPDATE:
class IMAGE_PARAMS:
class API_ERROR_CODES:
class ACCOUNT_STATUS:
class TEMPLATE_PATHS:
class WEB_ADMIN_PARAMS:
class WEB_SIGNUP_URLS:
import wsgiref.handlers
import cgi
import webapp2
import logging
import os
from google.appengine.ext.webapp import template
from serverside.console import Console
from serverside.entities.users import Users
from serverside.account import Accounts
from serverside.signup import NewsLetterSignUp
from serverside.signup import SignUp
from serverside.signin import SignIn
from serverside.logout import LogOut
from serverside.badge import UploadBadge
from serverside.badge import DownloadBadge 
from serverside.badge import SeeTheme
from serverside.analytics import GetAnalytics
from serverside.analytics import RunAnalytics
from serverside import constants
from google.appengine.ext import db
from google.appengine.api import users
from serverside.analytics import *
# Copyright (C) 2011, CloudCaptive
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

class IndexPage(webapp2.RequestHandler):
  def get(self):
    self.redirect('/html/signup.html')

class HelloWorld(webapp2.RequestHandler):
  def get(self):
    self.response.out.write("hi!")

app = webapp2.WSGIApplication([
  ('/', IndexPage),
  ('/account', Accounts),
  ('/login', SignIn),
  ('/signup', SignUp),
  ('/logout', LogOut),

  ('/badge/u', UploadBadge),