# Kanbanara Admin Component
# Written by Rebecca Shalfield between 2013 and 2017
# Copyright (c) Rebecca Shalfield 2013-2017
# Released under the GNU AGPL v3

'''Kanbanara's Admin Component'''

import ast
import datetime
import logging
import os
import re
import socket
import urllib.parse

import cherrypy
from kanbanara import Kanbanara
import lipsum
import mako
from mako.template import Template
import pygal
import pymongo
from pymongo import MongoClient
import scss


class Admin(Kanbanara):
    '''Kanbanara's Admin Component'''

    @cherrypy.expose
    def contactus(self):
        """Creates the Contact Us page"""
        Kanbanara.check_authentication(f'/{self.component}/contactus')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "contactus","Contact Us"))
        content.append(self.filter_bar('contactus'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document,"contactus","Contact Us"))
        content.append(Template(filename=os.path.join(self.current_dir, '..', 'templates', 'contactus.tpl')).render(footer=Kanbanara.footer(self)))
        return "".join(content)

    @cherrypy.expose
    def help(self, component, destination):
        """Toggles the help facility on or off"""
        Kanbanara.check_authentication(f'/{self.component}')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        if 'help' in session_document and session_document['help'] == 'enabled':
            session_document["help"] = 'disabled'
        else:
            session_document["help"] = 'enabled'

        self.sessions_collection.save(session_document)
        raise cherrypy.HTTPRedirect(f'/{component}/{destination}', 302)

    @cherrypy.expose
    def libraries(self):
        """Displays versioning information for required python libraries"""
        Kanbanara.check_authentication(f'/{self.component}/libraries')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "libraries", "Libraries"))
        content.append(self.filter_bar('libraries'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, "libraries", "Libraries"))
        content.append('<table class="admin"><tr><th>Python Library</th><th>Currently Installed Version</th><th>Recommendation</th></tr>')
        
        cherrypy_version = cherrypy.__version__
        content.append(f'<tr><td>cherrypy</td><td>{cherrypy_version}</td><td></td></tr>')

        try:
            lipsum_version = lipsum.__version__
        except:
            lipsum_version = '&lt;Unknown&gt;'

        content.append(f'<tr><td>lipsum</td><td>{lipsum_version}</td><td></td></tr>')
        
        mako_version = mako.__version__
        content.append(f'<tr><td>mako</td><td>{mako_version}</td><td></td></tr>')

        pygal_version = pygal.__version__
        content.append(f'<tr><td>pygal</td><td>{pygal_version}</td><td></td></tr>')

        pymongo_version = pymongo.__version__
        content.append(f'<tr><td>pymongo</td><td>{pymongo_version}</td><td></td></tr>')

        pyscss_version = scss.__version__
        content.append(f'<tr><td>pyscss</td><td>{pyscss_version}</td><td></td></tr>')

        content.append('</table>')
        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def licences(self):
        """Assemble page of Licences"""
        Kanbanara.check_authentication(f'/{self.component}/licences')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "licences", "Licences"))
        content.append(self.filter_bar('licences'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, "licences", "Licences"))
        content.append('<div class="tabs" class="ui-tabs ui-widget ui-widget-content ui-corner-all">')
        content.append('<ul class="tabs" class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all">')
        for licenced_product in os.listdir(os.path.join(self.current_dir, '..', 'licences')):
            content.append(f'<li class="ui-state-default ui-corner-top"><a href="#{licenced_product}tab">{licenced_product}</a></li>')

        content.append('</ul>')

        for licenced_product in os.listdir(os.path.join(self.current_dir, '..', 'licences')):
            content.append(f'<div class="tab_content" id="{licenced_product}tab" class="ui-tabs-panel ui-widget-content ui-corner-bottom">')
            for licence_file in os.listdir(os.path.join(self.current_dir, '..', 'licences',
                                                        licenced_product)):
                content.append(f'<h2>{licence_file}</h2>')
                licence_path = os.path.join(self.current_dir, '..', 'licences', licenced_product,
                                            licence_file)
                with open(licence_path, "r") as handle:
                    licencetext = handle.read()
                    licencetext = licencetext.replace('<', '&lt;')
                    licencetext = licencetext.replace('>', '&gt;')
                    content.append('<div align="left"><pre>')
                    content.append(licencetext)
                    content.append('</pre></div>')

            content.append('</div>')

        content.append('</div>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    def __init__(self):
        """Initialisation"""
        self.component = 'admin'

        Kanbanara.__init__(self)

        self.current_dir = os.path.dirname(os.path.abspath(__file__))

        logging.basicConfig(level=logging.DEBUG,
                            format='%(asctime)s - %(levelname)s - %(message)s',
                            filename=os.path.join(self.current_dir, '..', 'logs', 'kanbanara.log'),
                            filemode='w'
                           )

        self.filter_bar_component_statistics = {'card':           ('Card',             'dynamic'),
                                                'category':       ('Category',         'dynamic'),
                                                'classofservice': ('Class Of Service', 'dynamic'),
                                                'columns':        ('Columns',          'static'),
                                                'customer':       ('Customer',         'dynamic'),
                                                'fontsize':       ('Font Size',        'dynamic'),
                                                'hashtag':        ('Hashtag',          'dynamic'),
                                                'iteration':      ('Iteration',        'dynamic'),
                                                'kanbanboard':    ('Kanban Board',     'static'),
                                                'priority':       ('Priority',         'dynamic'),
                                                'project':        ('Project',          'static'),
                                                'release':        ('Release',          'dynamic'),
                                                'severity':       ('Severity',         'dynamic'),
                                                'subteam':        ('Subteam',          'dynamic'),
                                                'swimlanes':      ('Swim Lanes',       'dynamic'),
                                                'teammember':     ('Team Member',      'static'),
                                                'type':           ('Type',             'static')}

        Kanbanara.read_administrator_ini_file(self)

        # Initialisation Settings
        self.mongodb_host, self.mongodb_port, self.mongodb_username, self.mongodb_password, self.mongodb_bindir = Kanbanara.read_mongodb_ini_file(self)

        # Connect to MongoDB on given host and port
        if self.mongodb_username and self.mongodb_password:
            modified_username = urllib.parse.quote_plus(self.mongodb_username)
            modified_password = urllib.parse.quote_plus(self.mongodb_password)
            connection = MongoClient('mongodb://' + modified_username + ':' + modified_password + '@' +
                                     self.mongodb_host + ':' + str(self.mongodb_port))
        else:
            connection = MongoClient(self.mongodb_host, self.mongodb_port)

        # Connect to 'kanbanara' database, creating if not already exists
        kanbanara_db = connection['kanbanara']

        # Connect to 'projects' collection
        self.projects_collection = kanbanara_db['projects']
        for attribute in ['project']:
            self.projects_collection.create_index(attribute, unique=True, background=True)

        # Connect to 'sessions' collection
        self.sessions_collection = kanbanara_db['sessions']
        for attribute in ['session_id']:
            self.sessions_collection.create_index(attribute, unique=True, background=True)

        for attribute in ['lastaccess']:
            self.sessions_collection.create_index(attribute, unique=False, background=True)

        # Connect to 'members' collection
        self.members_collection = kanbanara_db['members']
        for attribute in ['username']:
            # TODO - username attribute should be unique but get error when unique=true set
            self.members_collection.create_index(attribute, unique=False, background=True)

        # Connect to 'cards' collection
        self.cards_collection = kanbanara_db['cards']
        for attribute in ['id']:
            self.cards_collection.create_index(attribute, unique=True, background=True)

        for attribute in ['description', 'iteration', 'project', 'release', 'state']:
            self.cards_collection.create_index(attribute, unique=False, background=True)

    @cherrypy.expose
    def errors_log(self):
        Kanbanara.check_authentication(f'/{self.component}')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "errors_log", "Errors Log"))
        content.append(self.filter_bar('errors_log'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'errors_log', "Errors Log"))
        content.append('</div>')
        epoch = datetime.datetime.utcnow()
        todays_date = "%4d%02d%02d" % (epoch.year, epoch.month, epoch.day)
        if os.path.exists(os.path.join(self.current_dir, '..', 'logs', f'errors_{todays_date}.log')):
            with open(os.path.join(self.current_dir, '..', 'logs', f'errors_{todays_date}.log'), 'r') as handle:
                errors = handle.read().split('\n\n\n')
                for e, error in enumerate(errors[-6:]):
                    content.append('<pre>'+error+'</pre>')
                    if e < len(errors[-6:])-1:
                        content.append('<hr>')
                        
        else:
            content.append('<h3>Sorry, there have been no errors today yet!</h3>')

        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def upgrade(self):
        Kanbanara.check_authentication(f'/{self.component}/upgrade')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "upgrade", "Upgrade"))
        content.append(self.filter_bar('upgrade'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'upgrade', "Upgrade"))
        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def database_backup(self, adminusername="", adminpassword=""):
        """ This page allows you to backup your MongoDB database """
        username = Kanbanara.check_authentication(f'/{self.component}/database_backup')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        port = ""
        content.append(Kanbanara.header(self, "database_backup", "Database Backup"))
        content.append(self.filter_bar('index'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, "database_backup",
                                                              "Database Backup"))
        if adminusername and adminpassword:
            if adminusername == self.kanbanara_settings['admin_username'] and adminpassword == self.kanbanara_settings['admin_password']:
                if self.mongodb_port != 27017:
                    port = ' --port '+str(self.mongodb_port)

                executable = '"'+self.mongodb_bindir+os.sep+'mongodump.exe" --verbose'+port+' --db kanbanara --out '+self.current_dir+os.sep+'dbdump'
                (status, output) = Kanbanara.getstatusoutput(executable)
                content.append('<table border="0">')
                content.append('<tr><td align="center">'+executable+'</td></tr>')
                content.append('<tr><td align="center"><textarea id="cmdline" bgcolor="black" color="white" rows="20" cols="80" readonly>'+output+'</textarea></td></tr>')
                content.append('<tr><td align="center"><input type="button" id="toggleCmdline" value="Show Output"></td></tr>')
                #if status == 0:
                #    content.append('<tr><td align="center"><p>Your database has been successfully backed-up!</p></td></tr>')
                #else:
                #    content.append('<tr><td align="center"><p>Sorry, your database has NOT been successfully backed-up!</p></td></tr>')

                content.append('</table>')
                socket_hostname = socket.gethostname()
                ip_addr = socket.gethostbyname_ex(socket_hostname)[2][0]
                remote_addr = cherrypy.request.remote.ip
                if remote_addr == ip_addr:
                    content.append(f'<p><form action="http://{ip_addr}:{self.mongodb_port+1000}/" method="get" target="_new"><input type="submit" value="MongoDB Admin Web Console" title="MongoDB Admin Web Console"></form></p>')
            else:
                raise cherrypy.HTTPError(401, 'Unauthorised')

        else:
            content.append('<form action="/admin/database_backup" method="post">')
            content.append('<table border="0"><tr><td align="right">Administrator Username</td><td>')
            content.append('<input type="password" size="40" name="adminusername" placeholder="Username">')
            content.append('</td></tr><tr><td align="right">Administrator Password</td><td><input type="password" size="40" name="adminpassword" placeholder="Password"></td></tr><tr><td align="center" colspan="2"><input class="button" type="submit" value="Enter"></td></tr></table></form>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def database_restore(self, adminusername="", adminpassword=""):
        """ This page allows you to restore a previously backed-up MongoDB database """
        username = Kanbanara.check_authentication(f'/{self.component}/database_restore')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        port = ""
        content.append(Kanbanara.header(self, "database_restore", "Database Restore"))
        content.append(self.filter_bar('index'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, "database_restore",
                                                              "Database Restore"))
        if adminusername and adminpassword:
            if adminusername == self.kanbanara_settings['admin_username'] and adminpassword == self.kanbanara_settings['admin_password']:
                if self.mongodb_port != 27017:
                    port = ' --port '+str(self.mongodb_port)

                executable = '"'+self.mongodb_bindir+os.sep+'mongorestore.exe" --verbose'+port+' '+self.current_dir+os.sep+'dbdump'+os.sep+'kanbanara'
                (status, output) = Kanbanara.getstatusoutput(self, executable)
                content.append('<table border="0">')
                content.append('<tr><td align="center">'+executable+'</td></tr>')
                content.append('<tr><td align="center"><textarea id="cmdline" bgcolor="black" color="white" rows="20" cols="80" readonly>'+output+'</textarea></td></tr>')
                content.append('<tr><td align="center"><input type="button" id="toggleCmdline" value="Show Output"></td></tr>')
                #if status == 0:
                #    content.append('<tr><td align="center"><p>Your database has been successfully restored!</p></td></tr>')
                #else:
                #    content.append('<tr><td align="center"><p>Sorry, your database has NOT been successfully restored!</p></td></tr>')

                content.append('</table>')
                socket_hostname = socket.gethostname()
                ip_addr = socket.gethostbyname_ex(socket_hostname)[2][0]
                remote_addr = cherrypy.request.remote.ip
                if remote_addr == ip_addr:
                    content.append(f'<p><form action="http://{ip_addr}:{self.mongodb_port+1000}/" method="get" target="_new"><input type="submit" value="MongoDB Admin Web Console" title="MongoDB Admin Web Console"></form></p>')
            else:
                raise cherrypy.HTTPError(401, 'Unauthorised')

        else:
            content.append('<form action="/admin/database_restore" method="post">')
            content.append('<table border="0"><tr><td align="right">Administrator Username</td><td>')
            content.append('<input type="password" size="40" name="adminusername" placeholder="Username">')
            content.append('</td></tr><tr><td align="right">Administrator Password</td><td><input type="password" size="40" name="adminpassword" placeholder="Password"></td></tr><tr><td align="center" colspan="2"><input class="button" type="submit" value="Enter"></td></tr></table></form>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def database_delete(self, adminusername="", adminpassword=""):
        """ Remove all the card_documents from the database's members, projects and cards collections """
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "database_delete", "Database Delete"))
        content.append(self.filter_bar('database_delete'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, "database_delete",
                                                              "Database Delete"))
        content.append('<p class="warning">Successfully logging into this page will cause the members, projects and cards collections in your database to be emptied of all card_documents!</p>')
        if adminusername and adminpassword:
            if adminusername == self.kanbanara_settings['admin_username'] and adminpassword == self.kanbanara_settings['admin_password']:
                content.append('<p>Deleting members...</p>')
                count = self.members_collection.find().count()
                if count:
                    content.append(f'<p>{count} member documents found</p>')
                    for member_document in self.members_collection.find():
                        self.members_collection.remove(member_document)

                    count = self.members_collection.find().count()
                    if count:
                        content.append(f'<p>Unfortunately {count} still remain!</p>')
                    else:
                        content.append('<p>All deleted!</p>')

                else:
                    content.append('<p>No member documents found</p>')

                content.append('<p>Deleting cards...</p>')
                count = self.cards_collection.find().count()
                if count:
                    content.append(f'<p>{count} card documents found</p>')
                    for card_document in self.cards_collection.find():
                        self.cards_collection.remove(card_document)

                    count = self.cards_collection.find().count()
                    if count:
                        content.append(f'<p>Unfortunately {count} still remain!</p>')
                    else:
                        content.append('<p>All deleted!</p>')

                else:
                    content.append('<p>No member documents found</p>')

                content.append('<p>Deleting projects...</p>')
                count = self.projects_collection.find().count()
                if count:
                    content.append(f'<p>{count} project documents found</p>')
                    for project_document in self.projects_collection.find():
                        self.projects_collection.remove(project_document)

                    count = self.projects_collection.find().count()
                    if count:
                        content.append(f'<p>Unfortunately {count} still remain!</p>')
                    else:
                        content.append('<p>All deleted!</p>')

                else:
                    content.append('<p>No project documents found</p>')

            else:
                raise cherrypy.HTTPError(401, 'Unauthorised')

        else:
            content.append('<form action="/admin/database_delete" method="post">')
            content.append('<table border="0"><tr><td align="right">Administrator Username</td><td>')
            content.append('<input type="password" size="40" name="adminusername" placeholder="Username">')
            content.append('</td></tr><tr><td align="right">Administrator Password</td><td><input type="password" size="40" name="adminpassword" placeholder="Password"></td></tr><tr><td align="center" colspan="2"><input class="button" type="submit" value="Enter"></td></tr></table></form>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def index(self):
        """Redirects you to the kanban board"""
        raise cherrypy.HTTPRedirect("/kanban", 302)

    @cherrypy.expose
    def members(self, adminusername="", adminpassword=""):
        username = Kanbanara.check_authentication(f'/{self.component}/members')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "members","Members"))
        content.append(self.filter_bar('members'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document,"members","Members"))
        if adminusername and adminpassword:
            if adminusername == self.kanbanara_settings['admin_username'] and adminpassword == self.kanbanara_settings['admin_password']:
                attributes = [('username', 'Username'), ('firstname', 'First Name'),
                              ('lastname', 'Last Name'), ('fullname', 'Full Name'),
                              ('projects', 'Projects')]
                content.append('<table class="admin"><tr>')
                for (attribute, heading) in attributes:
                    content.append('<th>'+heading+'</th>')
                content.append('</tr>')
                for member_document in self.members_collection.find():
                    content.append('<tr>')
                    for (attribute, heading) in attributes:
                        if attribute in member_document:
                            content.append(f'<td>{member_document[attribute]}</td>')
                        else:
                            content.append('<td>Not Present</td>')

                    content.append('</tr>')

                content.append('</table>')
            else:
                raise cherrypy.HTTPError(401, 'Unauthorised')

        else:
            content.append('<form action="/admin/members" method="post">')
            content.append('<table border="0"><tr><td align="right">Administrator Username</td><td>')
            content.append('<input type="password" size="40" name="adminusername" placeholder="Username">')
            content.append('</td></tr><tr><td align="right">Administrator Password</td><td><input type="password" size="40" name="adminpassword" placeholder="Password"></td></tr><tr><td align="center" colspan="2"><input class="button" type="submit" value="Enter"></td></tr></table></form>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def database_rebuild(self, adminusername="", adminpassword=""):
        """ Rebuild database from JSON files """
        username = Kanbanara.check_authentication(f'/{self.component}/database_rebuild')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "database_rebuild", "Database Rebuild"))
        content.append(self.filter_bar('database_rebuild'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, "database_rebuild", "Database Rebuild"))
        if adminusername and adminpassword:
            if adminusername == self.kanbanara_settings['admin_username'] and adminpassword == self.kanbanara_settings['admin_password']:
                if self.members_collection.find().count() > 1 or self.projects_collection.find().count() or self.cards_collection.find().count():
                    # Database needs to be empty except for your own member document
                    content.append('<h3>Sorry, Database is not Empty!</h3>')
                else:
                    # Clears the members collection of all documents including your own
                    for member_document in self.members_collection.find():
                        self.members_collection.remove(member_document)

                    if os.path.exists(os.path.join(self.current_dir, 'database', 'members')):
                        content.append('<table class="foryourinformation">')
                        content.append('<tr><th>Members</th></tr>')
                        for member in os.listdir(os.path.join(self.current_dir, 'database', 'members')):
                            content.append('<tr><td>'+member+'</td></tr>')

                            with open(os.path.join(self.current_dir, 'database', 'members', member), "r") as ip:
                                member_document_as_string = ip.read()

                                try:
                                    member_document = ast.literal_eval(member_document_as_string)
                                    self.members_collection.insert(member_document)
                                    content.append(f'<tr><td>{member_document_as_string}</td></tr>')
                                except:
                                    content.append(f'<tr><td class="warning">ERROR IN {member_document_as_string}</td></tr>')

                        content.append('</table>')

                    for project in os.listdir(os.path.join(self.current_dir, 'database', 'projects')):
                        content.append('<table class="foryourinformation"><tr><th colspan="2">'+project+'</th></tr>')

                        with open(os.path.join(self.current_dir, 'database', 'projects', project, 'project.json'), "r") as ip:
                            project_document_as_string = ip.read()

                        try:
                            project_document = ast.literal_eval(project_document_as_string)
                            self.projects_collection.insert(project_document)
                            content.append(f'<tr><td colspan="2">{project_document_as_string}</td></tr>')
                        except:
                            content.append(f'<tr><td class="warning">ERROR IN {project_document_as_string}</td></tr>')

                        if os.path.exists(os.path.join(self.current_dir, 'database', 'projects', project, 'cards')):
                            for card in os.listdir(os.path.join(self.current_dir, 'database', 'projects', project, 'cards')):
                                try:
                                    content.append('<tr><td>'+card+'</td>')

                                    with open(os.path.join(self.current_dir, 'database', 'projects', project, 'cards', card), "r") as ip:
                                        card_document_as_string = ip.read()

                                        try:
                                            card_document = ast.literal_eval(card_document_as_string)
                                            if 'deleted' not in card_document:
                                                self.cards_collection.insert(card_document)
                                                content.append('<td>'+card_document_as_string+'</td></tr>')
                                        except:
                                            content.append('<tr><td class="warning">ERROR IN '+card_document_as_string+'</td></tr>')

                                except:
                                    pass

                        content.append('</table>')

                    content.append('<h3>Congratulations! Your Database has now been Rebuilt!</h3>')

            else:
                raise cherrypy.HTTPError(401, 'Unauthorised')

        else:
            content.append('<form action="/admin/database_rebuild" method="post">')
            content.append('<table border="0"><tr><td align="right">Administrator Username</td><td>')
            content.append('<input type="password" size="40" name="adminusername" placeholder="Username">')
            content.append('</td></tr><tr><td align="right">Administrator Password</td><td><input type="password" size="40" name="adminpassword" placeholder="Password"></td></tr><tr><td align="center" colspan="2"><input class="button" type="submit" value="Enter"></td></tr></table></form>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def database_relink(self, adminusername="", adminpassword=""):
        """ Restores links between projects and members and between members and projects """
        username = Kanbanara.check_authentication(f'/{self.component}/database_relink')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "database_relink", "Database Relink"))
        content.append(self.filter_bar('database_relink'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, "database_relink", "Database Relink"))
        if adminusername and adminpassword:
            if adminusername == self.kanbanara_settings['admin_username'] and adminpassword == self.kanbanara_settings['admin_password']:
                for project_document in self.projects_collection.find():
                    if 'members' in project_document and project_document['members']:
                        for member in project_document['members']:
                            for member_document in self.members_collection.find({'username': member['username']}):
                                if 'projects' in member_document:
                                    if not self.project_in_projects(project_document['project'], member_document['projects']):
                                        member_document['projects'].append({'project': project_document['project'],
                                                                            'role': member['role']})
                                        self.members_collection.save(member_document)
                                        self.save_member_as_json(member_document)
                                        content.append('<p>Member '+member_document['username']+' has been relinked to project '+project_document['project']+'</p>')

                                else:
                                    member_document['projects'] = [{'project': project_document['project'], 'role': member['role']}]
                                    self.members_collection.save(member_document)
                                    self.save_member_as_json(member_document)
                                    content.append('<p>Member '+member_document['username']+' has been relinked to project '+project_document['project']+'</p>')

                for member_document in self.members_collection.find():
                    if 'projects' in member_document and member_document['projects']:
                        for candidateProject in member_document['projects']:
                            if 'project' in candidateProject and candidateProject['project']:
                                for project_document in self.projects_collection.find({'project': candidateProject['project']}):
                                    if 'members' in project_document:
                                        if not self.username_in_members(member_document['username'], project_document['members']):
                                            project_document['members'].append({'username':member_document['username'],'role':candidateProject['role']})
                                            self.projects_collection.save(project_document)
                                            self.save_project_as_json(project_document)
                                            content.append('<p>Project '+project_document['project']+' has been relinked to member '+member_document['username']+'</p>')

                                    else:
                                        project_document['members'] = [{'username':member_document['username'],'role':candidateProject['role']}]
                                        self.projects_collection.save(project_document)
                                        self.save_project_as_json(project_document)
                                        content.append('<p>Project '+project_document['project']+' has been relinked to member '+member_document['username']+'</p>')

                content.append('<h3>Congratulations! Your Database has now been Relinked!</h3>')
            else:
                raise cherrypy.HTTPError(401, 'Unauthorised')

        else:
            content.append('<form action="/admin/database_relink" method="post">')
            content.append('<table border="0"><tr><td align="right">Administrator Username</td><td>')
            content.append('<input type="password" size="40" name="adminusername" placeholder="Username">')
            content.append('</td></tr><tr><td align="right">Administrator Password</td><td><input type="password" size="40" name="adminpassword" placeholder="Password"></td></tr><tr><td align="center" colspan="2"><input class="button" type="submit" value="Enter"></td></tr></table></form>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def relink(self):
        """ Relink a member to the projects they are a member of """
        username = Kanbanara.check_authentication(f'/{self.component}/relink')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        member_document = Kanbanara.get_member_document(self, session_document)
        save_required = False
        if 'projects' not in member_document:
            member_document['projects'] = []

        for project_document in self.projects_collection.find():
            if 'members' in project_document and project_document['members'] and self.username_in_members(member_document['username'], project_document['members']):
                for projectDictionary in project_document['members']:
                    if projectDictionary['username'] == member_document['username']:
                        member_document['projects'].append({'project':project_document['project'],'role':projectDictionary['role']})
                        save_required = True
                        break

        if save_required:
            self.members_collection.save(member_document)
            self.save_member_as_json(member_document)

        raise cherrypy.HTTPRedirect('/admin', 302)

    @cherrypy.expose
    def sessions(self, adminusername="", adminpassword=""):
        username = Kanbanara.check_authentication(f'/{self.component}/sessions')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, "sessions", "Sessions"))
        content.append(self.filter_bar('sessions'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document,"sessions","Sessions"))
        if adminusername and adminpassword:
            if adminusername == self.kanbanara_settings['admin_username'] and adminpassword == self.kanbanara_settings['admin_password']:
                attributes = [('session_id', 'Session ID'),
                              ('lastaccess', 'Last Access'),
                              ('username', 'Username'),
                              ('firstname', 'First Name'),
                              ('lastname', 'Last Name'),
                              ('fullname', 'Full Name'),
                              ('project', 'Project'),
                              ('release', 'Release'),
                              ('ip_address', 'IP Address'),
                              ('user_agent', 'User Agent')
                             ]
                content.append('<table class="admin"><tr>')
                for (attribute, heading) in attributes:
                    content.append('<th>'+heading+'</th>')

                content.append('</tr>')
                for session_document in self.sessions_collection.find():
                    content.append('<tr>')
                    for (attribute, heading) in attributes:
                        if attribute in session_document:
                            content.append(f'<td>{session_document[attribute]}</td>')
                        else:
                            content.append('<td>Not Present</td>')

                    content.append('</tr>')

                content.append('</table>')
            else:
                raise cherrypy.HTTPError(401, 'Unauthorised')

        else:
            content.append('<form action="/admin/sessions" method="post">')
            content.append('<table border="0"><tr><td align="right">Administrator Username</td><td>')
            content.append('<input type="password" size="40" name="adminusername" placeholder="Username">')
            content.append('</td></tr><tr><td align="right">Administrator Password</td><td><input type="password" size="40" name="adminpassword" placeholder="Password"></td></tr><tr><td align="center" colspan="2"><input class="button" type="submit" value="Enter"></td></tr></table></form>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

currentDir = os.path.dirname(os.path.abspath(__file__))
conf = {'/': {'tools.staticdir.root':   currentDir,
              'tools.sessions.on':      True,
              'tools.sessions.locking': 'explicit'
              }}
for directory in ['css', 'images']:
    if os.path.exists(currentDir+os.sep+directory):
        conf['/'+directory] = {'tools.staticdir.on':  True,
                               'tools.staticdir.dir': directory}

cherrypy.tree.mount(Admin(), '/admin', config=conf)
