def decodestring(cookiestring, userdir): """Given a username/password encoded into a string - decode it and check it's validity. It checks the username against the one stored in the user file.. """ # try decoding the string, if it's badly formed then it may raise an excpetion - in which case we just return False try: instring, daynumber, timestamp = pass_dec(cookiestring) except: return False # check it's not a really old (or copied) cookie if not unexpired(daynumber, timestamp, AGETEST): return False # we've extracted the timestamped string from the cookie string. # Let's pull out the username and password hash try: username, pwd_hash = instring.split('||') except ValueError: return False # Now we need to check it's a valid username and check the password if username in RESERVEDNAMES or not os.path.isfile(userdir+username+'.ini'): return False user = ConfigObj(userdir+username+'.ini') stamped_pwd_hash = user['password'] maxage = user['max-age'] cookiepath = ConfigObj(userdir+'config.ini')['cookiepath'] # the password is time stamped - so we need to decode it try: stored_pwd_hash, _, _= pass_dec(stamped_pwd_hash) except: return False if pwd_hash != stored_pwd_hash: return False return user, pwd_hash, cookiepath
def validatelogin(formdict, userdir, thisscript, action): name = formdict['username'] tempstore = ConfigObj(userdir + 'temp.ini') pendinglist = tempstore.get('pending', []) if os.path.isfile(userdir+name+'.ini') or name in pendinglist or name.lower() in RESERVEDNAMES: invalidentry('Username already exists.', formdict, userdir, thisscript, action) for char in name.lower(): if not char in validchars: invalidentry('Username contains invalid characters.', formdict, userdir, thisscript, action) return name
def validatelogin(formdict, userdir, thisscript, action): name = formdict['username'] tempstore = ConfigObj(userdir + 'temp.ini') pendinglist = tempstore.get('pending', []) if os.path.isfile( userdir + name + '.ini') or name in pendinglist or name.lower() in RESERVEDNAMES: invalidentry('Username already exists.', formdict, userdir, thisscript, action) for char in name.lower(): if not char in validchars: invalidentry('Username contains invalid characters.', formdict, userdir, thisscript, action) return name
def doeditconfig(theform, userdir, thisscript, userconfig, action, newcookie): """Receives the submission from the edit config page.""" config = ConfigObj(userdir + 'config.ini') default = ConfigObj(userdir + 'default.ini') loginlink = theform.getfirst('loginlink', '') adminmail = theform.getfirst('adminmail', '') emailsubj = theform.getfirst('emailsubject', '') emailmsg = theform.getfirst('emailmsg', '') maxage = theform.getfirst('maxage', '') editable = theform.getfirst('editable', '') if adminmail and not validemail(adminmail): editconfig(theform, userdir, thisscript, userconfig, action, newcookie, "The adminmail doesn't appear to be a valid address.") if not maxage.isdigit(): editconfig(theform, userdir, thisscript, userconfig, action, newcookie, "maxage must be a number.") if int(maxage) and int(maxage) < MINMAXAGE: editconfig(theform, userdir, thisscript, userconfig, action, newcookie, "maxage must be greater than %s." % MINMAXAGE) if loginlink: config['newloginlink'] = 'Yes' else: config['newloginlink'] = 'No' config['adminmail'] = adminmail config['email_subject'] = emailsubj config['email_message'] = emailmsg config.write() default['max-age'] = maxage if editable: default['editable'] = 'Yes' else: default['editable'] = 'No' default.write() displaymenu(theform, userdir, thisscript, userconfig, action, newcookie) # XXXX should we send a msg here 'COnfig File Edited' ?
def createuser(userdir, realname, username, email, password, adminlev): """Create a new user.""" from time import time from modules.dataenc import pass_enc from modules.configobj import ConfigObj user = ConfigObj(userdir + 'default.ini') user.filename = userdir + username + '.ini' # XXXX this does no checkign htat the name is valid and doesn't already exist !! user['username'] = username user['realname'] = realname user['email'] = email user['admin'] = adminlev user['password'] = pass_enc(password, timestamp=True, daynumber=True) user['created'] = str(time()) user.write()
def createuser(userdir, realname, username, email, password, adminlev): """Create a new user.""" from time import time from modules.dataenc import pass_enc from modules.configobj import ConfigObj user = ConfigObj(userdir+'default.ini') user.filename = userdir + username + '.ini' # XXXX this does no checkign htat the name is valid and doesn't already exist !! user['username'] = username user['realname'] = realname user['email'] = email user['admin'] = adminlev user['password'] = pass_enc(password, timestamp=True, daynumber=True) user['created'] = str(time()) user.write()
def confirm(theform, userdir, thisscript): """Confirm a login. Either from an invite or from a user who has registered.""" from modules.dataenc import pass_dec, pass_enc from login import encodestring fail = False try: theval, daynumber, timestamp = pass_dec(theform['id'].value) except: # FIXME: bare except.... newloginfail() tempstore = ConfigObj(userdir + 'temp.ini') if not tempstore.has_key(theval): newloginfail() uservals = tempstore[theval] del tempstore[theval] username = uservals['username'] if username in tempstore['pending']: tempstore['pending'].remove(username) tempstore.write() # newconfig = ConfigObj(userdir + 'default.ini') newpath = userdir + username + '.ini' if os.path.isfile(newpath): newloginfail() newconfig.filename = newpath # FIXME: should this be '' ? action = None for entry in uservals: if entry == 'action': action = uservals[entry] elif entry == 'password': password = uservals[entry] newconfig[entry] = pass_enc(password, timestamp=True, daynumber=True) else: newconfig[entry] = uservals[entry] newconfig.write() # # next we need to create the cookie header to return it from Cookie import SimpleCookie thecookie = SimpleCookie() thecookie['userid'] = encodestring(newconfig['username'], password) config = ConfigObj(userdir + 'config.ini') maxage = newconfig['max-age'] cookiepath = config['cookiepath'] if maxage and int(maxage): # possible cause of error here if the maxage value in a users file isn't an integer !! thecookie['userid']['max-age'] = int(maxage) if cookiepath: thecookie['userid']['path'] = cookiepath if config['adminmail']: msg = 'A new user has created a login - "%s".\n\n' % thisscript for entry in newconfig: if entry != 'password': msg += entry + ' : ' + newconfig[entry] + '\n' # FIXME: should be mailme sendmailme(config['adminmail'], msg, config['email_subject'], config['adminmail'], html=False) return action, newconfig, thecookie.output()
def displaylogin(userdir, thisscript=None, action=None, failed=False): """This function will display the login page and then exit. Usually called if the user has no cookie or an expired/forged cookie. """ if thisscript == None: thisscript = THISSCRIPT config = ConfigObj(userdir + 'config.ini') templatedir = userdir + ".." + os.sep + config['templatedir'].replace("/",os.sep) loginform = readfile(templatedir+form_nojs) loginform = loginform.replace('**script**', thisscript) loginpage = readfile(templatedir+logintemplate) loginpage = loginpage.replace('**login form**', loginform) if failed: loginpage = loginpage.replace('**login failed**', '<b style="color:red">Wrong user name or password</b>') else: loginpage = loginpage.replace('**login failed**', '') if istrue(config['newloginlink']): loginpage = loginpage.replace('<!-- **commstart**', '') loginpage = loginpage.replace('**commend** -->', '') loginpage = loginpage.replace('**new login link**', thisscript+'?login=newlogin') if action: loginpage = loginpage.replace('<!-- **action** -->', actionline % action) print(serverline) print('\r') print(loginpage) sys.exit()
def checknewlogin(userdir): """Check that new logins are enabled on this setup. Return or display the error message and quit.""" config = ConfigObj(userdir + 'config.ini') if istrue(config['newloginlink']): return error('Sorry, New Logins are Disabled on this System.')
def doinvite(theform, userdir, thisscript, userconfig, action, newcookie): """Receives the submission from the invite/create new user page.""" config = ConfigObj(userdir + 'config.ini') default = ConfigObj(userdir + 'default.ini') create = theform.getfirst('create', '') realname = theform.getfirst('realname', '') username = theform.getfirst('username', '') email = validemail(theform.getfirst('email', '')) pass1 = theform.getfirst('pass1', '') pass2 = theform.getfirst('pass2', '') adminlev = theform.getfirst('adminlev', '') maxadminlev = min(int(userconfig['admin']), MAXADMINLEV) if not email: invite(theform, userdir, thisscript, userconfig, action, newcookie, 'The email address appears to be invalid.') if pass1 != pass2: invite(theform, userdir, thisscript, userconfig, action, newcookie, "The two passwords don't match.") if len(pass1) < 5: invite(theform, userdir, thisscript, userconfig, action, newcookie, "The password must be at least five characters long.") if not realname: invite(theform, userdir, thisscript, userconfig, action, newcookie, 'You must supply a realname.') if not username: invite(theform, userdir, thisscript, userconfig, action, newcookie, 'You must supply a username.') if not adminlev.isdigit(): invite(theform, userdir, thisscript, userconfig, action, newcookie, 'Admin level must be a number.') if int(adminlev) > maxadminlev: invite(theform, userdir, thisscript, userconfig, action, newcookie, 'Admin level is greater than the maximum allowed (%s).' % maxadminlev) # now we need to check if the username already exists tempstore = ConfigObj(userdir + 'temp.ini') pendinglist = tempstore.get('pending', []) if os.path.isfile(userdir+username+'.ini') or username in pendinglist or username.lower() in RESERVEDNAMES: invite(theform, userdir, thisscript, userconfig, action, newcookie, 'username already exists.') for char in username.lower(): if not char in validchars: invite(theform, userdir, thisscript, userconfig, action, newcookie, 'username contains invalid characters.') # now we have verified the values - we *either* need to create a new username *or* send an invitiation if create == 'create': createuser(userdir, realname, username, email, pass1, adminlev) msg = 'New User Created' else: inviteuser(userdir, realname, username, email, pass1, thisscript, adminlev) msg = 'New User Invited' invite(theform, userdir, thisscript, userconfig, action, newcookie, msg, True)
def donewlogin(theform, userdir, thisscript, action=None): """Process the results from new login form submissions.""" loginaction = theform['login'].value if not loginaction == 'donewloginnojs': # only type of newlogin supported so far sys.exit() # check that new logins are enabled on this setup checknewlogin(userdir) allentries = theform.keys() vallist = allentries + [ entry for entry in newloginformkeys if entry not in allentries ] formdict = getform(vallist, theform, nolist=True) for entry in newloginformkeys: if not formdict[entry]: invalidentry( 'Required field missing : "%s"' % newloginformkeys[entry], formdict, userdir, thisscript, action) # missingentry should redisplay the form, with values filled in, and then exit email = validateemail(formdict) if not email: invalidentry('Email address appears to be invalid.', formdict, userdir, thisscript, action) # if the address is invalid it should behave like missingentry # also max one login per email address ? login = validatelogin(formdict, userdir, thisscript, action) # if login name already exists or is invalid password = validatepass(formdict, userdir, thisscript, action) # if passwords don't match or are too short realname = formdict['realname'] # If we've got this far we've got a valid new login and need to save the # details, send the email and print a mesage # config = ConfigObj(userdir + 'config.ini') link_url = savedetails( userdir, formdict, action) # this includes any 'extra' keys, not just the ones we require msg = config['email_message'] + '\n' msg = msg + SCRIPTLOC + thisscript + '?login=confirm&id=' + link_url writefile('log.txt', msg) mailme(email, msg, config['email_subject'], config['mail_server_address'], config['mail_server_host'], config['mail_server_port'], config['mail_server_user'], config['mail_server_password'], html=False) # templatedir = config['templatedir'] logindonepage = readfile(templatedir + logindone) logindonepage = logindonepage.replace('**this script**', thisscript) # print serverline print '\r' print logindonepage # sys.exit()
def doeditaccount(theform, userconfig, userdir, thisscript, action, newcookie): """Process the results from edit account form submissions.""" from modules.dataenc import pass_enc, pass_dec loginaction = theform['login'].value if not loginaction == 'doeditaccountnojs': # only type of newlogin supported so far sys.exit() allentries = theform.keys() vallist = allentries + [ entry for entry in edacckeys if entry not in allentries ] formdict = getform(vallist, theform, nolist=True) # oldpass_hash = pwd_context.hash(formdict['pass0'], salt="") storedpass_hash = pass_dec(userconfig['password'])[0] pass1 = formdict['pass1'] pass2 = formdict['pass2'] # email = validateemail(formdict) oldemail = userconfig['email'] if not email: msg = 'The email address you supplied appears to be invalid.' display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig) if email != oldemail and (not oldpass_hash or oldpass_hash != storedpass_hash): msg = 'You must correctly enter your password to change your email address.' display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig) userconfig['email'] = email if not formdict['realname']: msg = 'You need to enter a name for us to use.' display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig) userconfig['realname'] = formdict['realname'] if pass1 or pass2: if pass1 != pass2: msg = "The two passwords don't match." display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig) if len(pass1) < 5: msg = "The password must be longer than 5 characters." display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig) if not oldpass_hash or oldpass_hash != storedpass_hash: msg = 'You must correctly enter your current password to change it.' display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig) pass1_hash = pwd_context.hash(pass1, salt="") userconfig['password'] = pass_enc(pass1_hash, daynumber=True, timestamp=True) newcookie = makecookie(userconfig, pass1_hash, ConfigObj(userdir + 'config.ini')['cookiepath']) for entry in formdict: if entry not in edacckeys: userconfig[entry] = formdict[entry] userconfig.write() return action, userconfig, newcookie # XXXXX display values changed page
def invite(theform, userdir, thisscript, userconfig, action, newcookie, msg=None, success=None): """Display the screen to create or invite a new user.""" config = ConfigObj(userdir + 'config.ini') templatedir = config['templatedir'] adminpage = readfile(templatedir+adminpage_file) adminpage = adminpage.replace('**this script**', thisscript + '?action=' + action) adminpage = adminpage.replace('**admin menu**', thisscript+'?login=admin'+'&action='+action) # Values to be filled in are : # **create1** and **create2** - the one switched on should be 'checked', the other should be '' if msg: adminpage = adminpage.replace('<br><!- message --> ', '<h2>'+msg+'</h2>') if msg and not success: create = theform.getfirst('create', '') if create == 'create': create1 = 'checked' create2 = '' else: create2 = 'checked' create1 = '' realname = theform.getfirst('realname', '') username = theform.getfirst('username', '') email = theform.getfirst('email', '') pass1 = theform.getfirst('pass1', '') pass2 = theform.getfirst('pass2', '') adminlev = theform.getfirst('adminlev', '') else: create2 = 'checked' create1 = '' realname = '' username = '' email = '' pass1 = randomstring(8) pass2 = pass1 adminlev = '0' invitemenu = readfile(templatedir+admininvite_file) invitemenu = invitemenu.replace('**create1**', create1) invitemenu = invitemenu.replace('**create2**', create2) invitemenu = invitemenu.replace('**realname**', realname) invitemenu = invitemenu.replace('**username**', username) invitemenu = invitemenu.replace('**email**', email) invitemenu = invitemenu.replace('**pass1**', pass1) invitemenu = invitemenu.replace('**pass2**', pass2) invitemenu = invitemenu.replace('**adminlev**', adminlev) invitemenu = invitemenu.replace('**thisscript**', thisscript) invitemenu = invitemenu.replace('**action**', action) adminpage = adminpage.replace('**admin**', invitemenu) print newcookie print serverline print print adminpage sys.exit()
def checkpass(username, password, userdir, thisscript, action): """Check the password from a new login.""" # XXXX log failed login attempts if username in RESERVEDNAMES: return False if not os.path.isfile(userdir+username+'.ini'): return False user = ConfigObj(userdir+username+'.ini') stampedpass = user['password'] cookiepath = ConfigObj(userdir+'config.ini')['cookiepath'] # we need to un-time stamp the password realpass, daynumber, timestamp = pass_dec(stampedpass) if realpass != password: return False #open('xxxtest.txt', 'w').write(str(user)) # if we've got this far then the login was successful and we need to return a cookie thecookie = makecookie(user, password, cookiepath) return action, user, thecookie
def savedetails(userdir, formdict, action=None): """ Given the form from a validated new login, it saves the details to the temporary store. It also cleans up any out of date ones that haven't been used. """ from modules.dateutils import returndate, daycount from modules.dataenc import pass_enc # tempstore = ConfigObj(userdir + 'temp.ini') if action: formdict['action'] = action year, month, day = returndate() today = daycount(year, month, day) # for section in tempstore: if section[4:].isdigit(): if int(section[4:]) > today + 30: name = tempstore[section]['username'] tempstore['pending'].remove(name) del tempstore[section] # ran = randomstring(4) while tempstore.has_key(ran+str(today)): ran = randomstring(4) key = ran+str(today) tempstore[key] = {} store = tempstore[key] for entry in formdict: if entry == 'pass1' or entry == 'pass2': store['password'] = formdict[entry] elif entry == 'login': pass else: store[entry] = formdict[entry] if not tempstore.has_key('pending'): tempstore['pending'] = [] tempstore['pending'].append(formdict['username']) tempstore.write() return pass_enc(key, timestamp=True, daynumber=True)
def update_git_info(user, new_git_username, new_git_password, new_git_2fa=False): o = ConfigObj(prefix + 'users' + os.sep + user + '.ini') o['git_username'] = new_git_username o['git_2fa'] = str(new_git_2fa).lower() try: note = project + ", " + time.ctime() auth = github3.authorize(new_git_username, new_git_password, ['repo'], note, "") o['git_token'] = auth.token o['git_id'] = auth.id if 'git_password' in o: del o['git_password'] o.write() except: # fail silently--would want to display an error ideally, but # users will know to try again if the credentials are wrong pass
def inviteuser(userdir, realname, username, email, password, thisscript, adminlev): """Invite a new user.""" from newlogin import savedetails from configobj import ConfigObj formdict = {'username' : username, 'pass1' : password, 'admin' : adminlev, 'realname' : realname, 'email' : email, 'action' : '' } thekey = savedetails(userdir, formdict) config = ConfigObj(userdir + 'config.ini') msg = config['email_message'] + '\n' msg = msg + SCRIPTLOC + thisscript + '?login=confirm&id=' + thekey + (pass_msg % (username, password)) writefile('log.txt', msg) sendmailme(email, msg, config['email_subject'], email, html=False)
def get_menu(): config = ConfigObj(prefix + "users" + os.sep + "config.ini") if "banner" not in config: return "" banner = config["banner"] if banner.startswith("http"): # Web resource resp = requests.get(banner) return resp.text else: # File name in templates/ dir banner = config["banner"] banner = open(prefix + "templates" + os.sep + banner).read() return banner
def display_edit(formdict, userdir, thisscript, msg, action, newcookie, userconfig=None): """ Display the form for editing account details. This is a different form to creating a new account as it requires confirmation of the old password before doing certain things, like changing email address and password. """ from time import ctime print newcookie config = ConfigObj(userdir + 'config.ini') templatedir = config['templatedir'] # if we are sending the userconfig as the formdict, we needn't explicitly # pass it in if not userconfig: userconfig = formdict # edaccform = unicode(readfile(templatedir + editform_nojs)) edaccform = edaccform.replace('**script**', thisscript) # edaccpage = readfile(templatedir + edacctemplate) edaccpage = edaccpage.replace('**edit account form**', edaccform) edaccpage = filltemplate(edaccpage, formdict) if msg: edaccpage = edaccpage.replace('<!-- **message** -->', '<h2>' + msg + '</h2><br>') edaccpage = edaccpage.replace('**created on**', ctime(float(userconfig['created']))) edaccpage = edaccpage.replace('**num logins**', userconfig['numlogins']) edaccpage = edaccpage.replace('**last used**', ctime(float(userconfig['lastused']))) edaccpage = edaccpage.replace('**num used**', userconfig['numused']) edaccpage = edaccpage.replace('**this script**', thisscript + '?action=' + action) # if action: edaccpage = edaccpage.replace('<!-- **action** -->', actionline % action) print serverline print '\r' print edaccpage # sys.exit()
def get_git_credentials(user, admin, code): global code_2fa code_2fa = code if admin == 0: return scriptpath = os.path.dirname( os.path.realpath(__file__)) + os.sep + ".." + os.sep userdir = scriptpath + "users" + os.sep user_dict = ConfigObj(userdir + user + '.ini') git_username = user_dict[ 'git_username'] if "git_username" in user_dict else "_" git_token = user_dict['git_token'] if "git_token" in user_dict else "_" git_use2fa = user_dict['git_2fa'] if "git_2fa" in user_dict else "false" return git_username, git_token, git_use2fa
def getReleaseNotes(): """return release notes fir the given version string (major.minor.build, EXAMPLE: "2.3.1") if no release notes are found, return None """ # release notes aren't displayed on each start # (eq if the user marked the current notes as # read or disabled release notes altogether) # so import configobj only when needed versionNumbers = None try: from modules.configobj import ConfigObj releaseNotes = ConfigObj(RELEASE_NOTES_FILE_PATH) versionNumbers = releaseNotes['release_notes_section'].keys() except Exception, e: print('info: loading release notes failed') print(e)
def displaymenu(theform, userdir, thisscript, userconfig, action, newcookie): """Display the admin menu page.""" config = ConfigObj(userdir + 'config.ini') templatedir = config['templatedir'] adminpage = readfile(templatedir+adminpage_file) adminpage = adminpage.replace('**this script**', thisscript + '?action=' + action) url = '?login=admin&admin=%s&action=' + action adminmenu = readfile(templatedir+adminmenu_file) adminmenu = adminmenu.replace('**edit config**', thisscript+url % 'editconfig') adminmenu = adminmenu.replace('**edit users**', thisscript+url % 'edituser') adminmenu = adminmenu.replace('**invite**', thisscript+url % 'invite') adminpage = adminpage.replace('**admin**', adminmenu) adminpage = adminpage.replace('**admin menu**', thisscript+'?login=admin'+'&action='+action) print newcookie print serverline print print adminpage sys.exit()
def invalidentry(msg, formdict, userdir, thisscript, action=None): """Display the newlogin form.""" config = ConfigObj(userdir + 'config.ini') templatedir = config['templatedir'] # newloginform = readfile(templatedir + newform_nojs) newloginform = newloginform.replace('**script**', thisscript) # newloginpage = readfile(templatedir + newlogintemplate) newloginpage = newloginpage.replace('**new login form**', newloginform) newloginpage = filltemplate(newloginpage, formdict) newloginpage = newloginpage.replace('<!-- **message** -->', '<h2>' + msg + '</h2><br>') # if action: newloginpage = newloginpage.replace('<!-- **action** -->', actionline % action) print serverline print '\r' print newloginpage # sys.exit()
def newlogin(userdir, thisscript, action=None): """Display the newlogin form.""" config = ConfigObj(userdir + 'config.ini') templatedir = userdir + ".." + os.sep + config['templatedir'].replace( "/", os.sep) # check that new logins are enabled on this setup checknewlogin(userdir) # newloginform = readfile(templatedir + newform_nojs) newloginform = newloginform.replace('**script**', thisscript) # newloginpage = readfile(templatedir + newlogintemplate) newloginpage = newloginpage.replace('**new login form**', newloginform) newloginpage = filltemplate(newloginpage) # if action: newloginpage = newloginpage.replace('<!-- **action** -->', actionline % action) print serverline print '\r' print newloginpage # sys.exit()
def savedetails(userdir, formdict, action=None): """ Given the form from a validated new login, it saves the details to the temporary store. It also cleans up any out of date ones that haven't been used. """ from modules.dateutils import returndate, daycount from modules.dataenc import pass_enc # tempstore = ConfigObj(userdir + 'temp.ini') if action: formdict['action'] = action year, month, day = returndate() today = daycount(year, month, day) # for section in tempstore: if section[4:].isdigit(): if int(section[4:]) > today + 30: name = tempstore[section]['username'] tempstore['pending'].remove(name) del tempstore[section] # ran = randomstring(4) while tempstore.has_key(ran + str(today)): ran = randomstring(4) key = ran + str(today) tempstore[key] = {} store = tempstore[key] for entry in formdict: if entry == 'pass1' or entry == 'pass2': store['password'] = pwd_context.hash(formdict[entry], salt="") elif entry == 'login': pass else: store[entry] = formdict[entry] if not tempstore.has_key('pending'): tempstore['pending'] = [] tempstore['pending'].append(formdict['username']) tempstore.write() return pass_enc(key, timestamp=True, daynumber=True)
''' if mode != "server": cpout = cpout.replace(".py","") return cpout # Main script when running from Apache def structure_main_server(): thisscript = os.environ.get('SCRIPT_NAME', '') action = None theform = cgi.FieldStorage() scriptpath = os.path.dirname(os.path.realpath(__file__)) + os.sep userdir = scriptpath + "users" + os.sep action, userconfig = login(theform, userdir, thisscript, action) user = userconfig["username"] admin = userconfig["admin"] kwargs={} for key in theform: kwargs[key] = theform[key].value print structure_main(user, admin, 'server', **kwargs) scriptpath = os.path.dirname(os.path.realpath(__file__)) + os.sep userdir = scriptpath + "users" + os.sep config = ConfigObj(userdir + 'config.ini') if "/" in os.environ.get('SCRIPT_NAME', ''): mode = "server" else: mode = "local" if mode == "server": structure_main_server()
def structure_main(user, admin, mode, **kwargs): scriptpath = os.path.dirname(os.path.realpath(__file__)) + os.sep userdir = scriptpath + "users" + os.sep theform = kwargs cgitb.enable() ###GRAPHICAL PARAMETERS### top_spacing = 20 layer_spacing = 60 config = ConfigObj(userdir + 'config.ini') templatedir = scriptpath + config['controltemplates'].replace("/", os.sep) template = "main_header.html" header = readfile(templatedir + template) header = header.replace("**page_title**", "Structure editor") header = header.replace("**user**", user) cpout = "" if mode == "server": cpout += "Content-Type: text/html\n\n\n" header = header.replace("**logout_control**", '(<a href="logout.py">log out</a>)') else: header = header.replace("**logout_control**", '') cpout += header if "current_doc" in theform: current_doc = theform["current_doc"] current_project = theform["current_project"] current_guidelines = get_guidelines_url(current_project) else: current_doc = "" current_project = "" current_guidelines = "" UTF8Writer = codecs.getwriter('utf8') sys.stdout = UTF8Writer(sys.stdout) edit_bar = "edit_bar.html" edit_bar = readfile(templatedir + edit_bar) edit_bar = edit_bar.replace("**doc**", current_doc) edit_bar = edit_bar.replace("**project**", current_project) edit_bar = edit_bar.replace("**structure_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**segment_disabled**", '') edit_bar = edit_bar.replace("**relations_disabled**", '') edit_bar = edit_bar.replace("**screenshot_disabled**", '') edit_bar = edit_bar.replace("**quickexp_disabled**", '') edit_bar = edit_bar.replace("**current_guidelines**", current_guidelines) if mode == "server": edit_bar = edit_bar.replace("**submit_target**", 'structure.py') else: edit_bar = edit_bar.replace("**submit_target**", 'structure') edit_bar = edit_bar.replace("**action_type**", 'action') edit_bar = edit_bar.replace("**serve_mode**", mode) edit_bar = edit_bar.replace("**open_disabled**", '') edit_bar = edit_bar.replace( 'id="nav_edit" class="nav_button"', 'id="nav_edit" class="nav_button nav_button_inset"') if admin == "3": edit_bar = edit_bar.replace("**admin_disabled**", '') else: edit_bar = edit_bar.replace("**admin_disabled**", 'disabled="disabled"') cpout += edit_bar help = "help.html" help = readfile(templatedir + help) help_match = re.search(r'(<div id="help_edit".*?</div>)', help, re.MULTILINE | re.DOTALL) help = help_match.group(1) if sys.version_info[0] == 2: cpout += help.decode("utf-8") else: cpout += help about = "about.html" about = readfile(templatedir + about) about = about.replace("**version**", _version.__version__) cpout += about if current_guidelines != "": cpout += '<script>enable_guidelines();</script>' if current_doc == "": cpout += '<p class="warn">No file found - please select a file to open</p>' return cpout cpout += ''' <div id="container" class="container"> <button id="show-all-signals"> Show All Signal Tokens </button> <div class="signal-drawer"> <div id="signal-list"> </div> <div class="signal-drawer__row"> <button id="new-signal" class="signal-drawer__create-new-button"> <i class="fa fa-plus" title="New Signal"> </i> New Signal </button> </div> <div class="signal-drawer__row" style="text-align: center;padding-top:20px;"> <button id="save-signals" class="signal-drawer__save-button"> <i class="fa fa-check"> </i> Save Changes </button> <button id="cancel-signals" class="signal-drawer__cancel-button"> <i class="fa fa-ban"> </i> Cancel </button> </div> </div> ''' cpout += '''<div id="canvas" class="canvas">''' cpout += '\t<p id="document_name">Document: <b>' + current_doc + '</b> (project: <i>' + current_project + '</i>)</p>' cpout += '''<div id="inner_canvas">''' cpout += '<script src="./script/structure.js"></script>' # Remove floating non-terminal nodes if found # (e.g. due to browsing back and re-submitting old actions or other data corruption) clean_floating_nodes(current_doc, current_project, user) rels = get_rst_rels(current_doc, current_project) def_multirel = get_def_rel("multinuc", current_doc, current_project) def_rstrel = get_def_rel("rst", current_doc, current_project) multi_options = "" rst_options = "" rel_kinds = {} for rel in rels: if rel[1] == "multinuc": multi_options += "<option value='" + rel[0] + "'>" + rel[ 0].replace("_m", "") + '</option>\n' rel_kinds[rel[0]] = "multinuc" else: rst_options += "<option value='" + rel[0] + "'>" + rel[0].replace( "_r", "") + '</option>\n' rel_kinds[rel[0]] = "rst" multi_options += "<option value='" + def_rstrel + "'>(satellite...)</option>\n" timestamp = "" if "timestamp" in theform: if len(theform["timestamp"]) > 1: timestamp = theform["timestamp"] refresh = check_refresh(user, timestamp) if "action" in theform and not refresh: if len(theform["action"]) > 1: action_log = theform["action"] if len(action_log) > 0: actions = action_log.split(";") set_timestamp(user, timestamp) for action in actions: action_type = action.split(":")[0] action_params = action.split(":")[1] if len( action.split(":")) > 1 else "" params = action_params.split(",") if action_type == "up": update_parent(params[0], params[1], current_doc, current_project, user) elif action_type == "sp": insert_parent(params[0], "span", "span", current_doc, current_project, user) elif action_type == "mn": insert_parent(params[0], def_multirel, "multinuc", current_doc, current_project, user) elif action_type == "rl": update_rel(params[0], params[1], current_doc, current_project, user) elif action_type == "sg": update_signals( action.split(":")[1:], current_doc, current_project, user) else: cpout += '<script>alert("the action was: " + theform["action"]);</script>\n' if "logging" in theform and not refresh: if len(theform["logging"]) > 1: if get_setting("logging") == "on": logging = theform["logging"] if len(logging) > 0: update_log(current_doc, current_project, user, logging, "structure", str(datetime.datetime.now())) if "reset" in theform or user == "demo": if len(theform["reset"]) > 1 or user == "demo": reset_rst_doc(current_doc, current_project, user) nodes = {} rows = get_rst_doc(current_doc, current_project, user) for row in rows: if row[7] in rel_kinds: relkind = rel_kinds[row[7]] else: relkind = "span" if row[5] == "edu": nodes[row[0]] = NODE(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], relkind) else: nodes[row[0]] = NODE(row[0], 0, 0, row[3], row[4], row[5], row[6], row[7], relkind) for key in nodes: node = nodes[key] get_depth(node, node, nodes) for key in nodes: if nodes[key].kind == "edu": get_left_right(key, nodes, 0, 0, rel_kinds) signals = {} for signal in get_signals(current_doc, current_project, user): s_id, s_type, subtype, tokens = signal if s_id not in signals: signals[s_id] = list() if tokens: tokens = list(map(int, tokens.split(","))) else: tokens = [] signals[s_id].append({ 'type': s_type, 'subtype': subtype, 'tokens': tokens }) cpout += '<script>' cpout += 'window.rstWebSignals = ' + json.dumps(signals) + ';' cpout += 'window.rstWebSignalTypes = ' + json.dumps(get_signal_types_dict( current_doc, current_project), sort_keys=True) + ';' cpout += 'window.rstWebDefaultSignalType = Object.keys(window.rstWebSignalTypes)[0];' cpout += 'window.rstWebDefaultSignalSubtype = window.rstWebSignalTypes[window.rstWebDefaultSignalType][0];' cpout += '</script>' anchors = {} pix_anchors = {} dbpath = os.path.dirname(os.path.realpath(__file__)) + os.sep + "rstweb.db" conn = sqlite3.connect(dbpath) cur = conn.cursor() # Calculate anchor points for nodes # First get proportional position for anchor nodes_data = get_multinuc_nodes_data(cur, current_doc, current_project, user) node_to_lr = {} for node in nodes_data: if not node[1] in node_to_lr: node_to_lr[node[1]] = {'left': {}, 'right': {}} if not node[2] in node_to_lr[node[1]]['left']: node_to_lr[node[1]]['left'][node[2]] = node[0] if not node[3] in node_to_lr[node[1]]['right']: node_to_lr[node[1]]['right'][node[3]] = node[0] for key in sorted(nodes, key=lambda id: nodes[id].depth, reverse=True): node = nodes[key] if node.kind == "edu": anchors[node.id] = "0.5" if node.parent != "0": parent = nodes[node.parent] parent_wid = (parent.right - parent.left + 1) * 100 - 4 child_wid = (node.right - node.left + 1) * 100 - 4 if node.relname == "span": if node.id in anchors: anchors[parent.id] = str(( (node.left - parent.left) * 100) / parent_wid + float(anchors[node.id]) * float(child_wid / parent_wid)) else: anchors[parent.id] = str(( (node.left - parent.left) * 100) / parent_wid + (0.5 * child_wid) / parent_wid) elif node.relkind == "multinuc" and parent.kind == "multinuc": # For multinucs, the anchor is in the middle between leftmost and rightmost of the multinuc children # (not including other rst children) node_lr = node_to_lr[node.parent] lr = [ min(node_lr['left'].keys()), max(node_lr['right'].keys()) ] lr_wid = (lr[0] + lr[1]) / 2 lr_ids = (node_to_lr[node.parent]['left'][lr[0]], node_to_lr[node.parent]['right'][lr[1]]) left_child = lr_ids[0] right_child = lr_ids[1] if left_child == right_child: anchors[parent.id] = "0.5" else: if left_child in anchors and right_child in anchors: #both leftmost and rightmost multinuc children have been found len_left = nodes[left_child].right - nodes[ left_child].left + 1 len_right = nodes[right_child].right - nodes[ right_child].left + 1 anchors[parent.id] = str( ((float(anchors[left_child]) * len_left * 100 + float(anchors[right_child]) * len_right * 100 + (nodes[right_child].left - parent.left) * 100) / 2) / parent_wid) else: anchors[parent.id] = str( (lr_wid - parent.left + 1) / (parent.right - parent.left + 1)) else: if not parent.id in anchors: anchors[parent.id] = "0.5" # Place anchor element to center on proportional position relative to parent for key in nodes: node = nodes[key] pix_anchors[node.id] = str( int(3 + node.left * 100 - 100 - 39 + float(anchors[node.id]) * ((node.right - node.left + 1) * 100 - 4))) # Check that span and multinuc buttons should be used (if the interface is not used for RST, they may be disabled) if int(get_schema()) > 2: use_span_buttons = True if get_setting( "use_span_buttons") == "True" else False use_multinuc_buttons = True if get_setting( "use_multinuc_buttons") == "True" else False else: use_span_buttons = True use_multinuc_buttons = True if sys.version_info[0] == 2: lambda_button = "Λ".decode("utf-8") else: lambda_button = "Λ" tok_count = 0 for key in sorted(nodes.keys(), key=int): node = nodes[key] if node.kind != "edu": g_wid = str(int((node.right - node.left + 1) * 100 - 4)) cpout += '<div id="lg' + node.id + '" class="group" style="left: ' + str( int(node.left * 100 - 100) ) + '; width: ' + g_wid + '; top:' + str( int(top_spacing + layer_spacing + node.depth * layer_spacing) ) + 'px; z-index:1"><div id="wsk' + node.id + '" class="whisker" style="width:' + g_wid + ';"></div></div>' cpout += '<div id="g' + node.id + '" class="num_cont" style="position: absolute; left:' + pix_anchors[ node.id] + 'px; top:' + str( int(4 + top_spacing + layer_spacing + node.depth * layer_spacing) ) + 'px; z-index:' + str( int(200 - (node.right - node.left)) ) + '"><table class="btn_tb"><tr><td rowspan="2"><button id="unlink_' + node.id + '" title="unlink this node" class="minibtn" onclick="act(' + "'up:" + node.id + ",0'" + ');">X</button></td><td rowspan="2"><span class="num_id">' + str( int(node.left)) + "-" + str(int( node.right)) + '</span></td>' if use_span_buttons: cpout += '<td><button id="aspan_' + node.id + '" title="add span above" class="minibtn" onclick="act(' + "'sp:" + node.id + "'" + ');">T</button></td>' cpout += '</tr>' if use_multinuc_buttons: cpout += '<tr><td><button id="amulti_' + node.id + '" title="add multinuc above" class="minibtn" onclick="act(' + "'mn:" + node.id + "'" + ');">' + lambda_button + '</button></td></tr>' cpout += '</table></div><br/>' elif node.kind == "edu": cpout += '<div id="edu' + str( node.id) + '" class="edu" title="' + str( node.id) + '" style="left:' + str( int(node.id) * 100 - 100) + '; top:' + str( int(top_spacing + layer_spacing + node.depth * layer_spacing)) + '; width: 96px">' cpout += '<div id="wsk' + node.id + '" class="whisker" style="width:96px;"></div><div class="edu_num_cont"><table class="btn_tb"><tr><td rowspan="2"><button id="unlink_' + node.id + '" title="unlink this node" class="minibtn" onclick="act(' + "'up:" + node.id + ",0'" + ');">X</button></td><td rowspan="2"><span class="num_id"> ' + str( int(node.left)) + ' </span></td>' if use_span_buttons: cpout += '<td><button id="aspan_' + node.id + '" title="add span above" class="minibtn" onclick="act(' + "'sp:" + node.id + "'" + ');">T</button></td>' cpout += '</tr>' if use_multinuc_buttons: cpout += '<tr><td><button id="amulti_' + node.id + '" title="add multinuc above" class="minibtn" onclick="act(' + "'mn:" + node.id + "'" + ');">' + lambda_button + '</button></td></tr>' cpout += '</table></div>' for tok in node.text.split(" "): tok_count += 1 cpout += '<span id="tok' + str( tok_count) + '" class="tok">' + tok + '</span> ' cpout += '</div>' max_right = get_max_right(current_doc, current_project, user) # Serialize data in hidden input for JavaScript cpout += '<input id="data" name="data" type="hidden" ' hidden_val = "" doc_relname_to_reltype = get_doc_relname_to_reltype( cur, current_doc, current_project) for key in nodes: node = nodes[key] reltype = doc_relname_to_reltype.get(node.relname) if node.relname: safe_relname = node.relname else: safe_relname = "none" if node.kind == "edu": hidden_val += "n" + node.id + ",n" + node.parent + ",e," + str( int(node.left)) + "," + safe_relname + "," + reltype + ";" elif node.kind == "span": hidden_val += "n" + node.id + ",n" + node.parent + ",s,0," + safe_relname + "," + reltype + ";" else: hidden_val += "n" + node.id + ",n" + node.parent + ",m,0," + safe_relname + "," + reltype + ";" hidden_val = hidden_val[0:len(hidden_val) - 1] cpout += 'value="' + hidden_val + '"/>' cpout += '<input id="def_multi_rel" type="hidden" value="' + get_def_rel( "multinuc", current_doc, current_project) + '"/>\n' cpout += '<input id="def_rst_rel" type="hidden" value="' + get_def_rel( "rst", current_doc, current_project) + '"/>\n' cpout += '<input id="undo_log" type="hidden" value=""/>\n' cpout += '<input id="redo_log" type="hidden" value=""/>\n' cpout += '<input id="undo_state" type="hidden" value=""/>\n' cpout += '<input id="logging" type="hidden" value=""/>\n' cpout += '<input id="validations" type="hidden" value="' + get_project_validations( current_project) + '"/>\n' cpout += '<input id="use_span_buttons" type="hidden" value="' + str( use_span_buttons) + '"/>\n' cpout += '<input id="use_multinuc_buttons" type="hidden" value="' + str( use_multinuc_buttons) + '"/>\n' cpout += ''' <script src="./script/jquery.jsPlumb-1.7.5-min.js"></script> <script> ''' cpout += 'function select_my_rel(options,my_rel){' cpout += 'var multi_options = `' + multi_options + '`;\n' cpout += 'var rst_options = `' + rst_options + '`;\n' cpout += 'if (options =="multi"){options = multi_options;} else {options=rst_options;}' cpout += ' return options.replace("<option value=' + "'" + '"' + '+my_rel+' + '"' + "'" + '","<option selected=' + "'" + 'selected' + "'" + ' value=' + "'" + '"' + '+my_rel+' + '"' + "'" + '");' cpout += ' }\n' cpout += '''function make_relchooser(id,option_type,rel){ var s = "<div id='seldiv"+id.replace("n","")+"' style='white-space:nowrap;'>"; s += make_signal_button(id); s += "<select id='sel"+id.replace("n","")+"' class='rst_rel' onchange='crel(" + id.replace("n","") + ",this.options[this.selectedIndex].value);'>" + select_my_rel(option_type,rel) + "</select>"; return $(s); }''' # todo: make a flag that controls whether signals are on cpout += 'var signalsEnabled = ' + ('true;' if get_setting("signals") == "True" else 'false;') cpout += '''function make_signal_button(id) { if (signalsEnabled) { var text = window.rstWebSignals[id] ? window.rstWebSignals[id].length : "S"; var classes = window.rstWebSignals[id] && window.rstWebSignals[id].length > 0 ? "minibtn minibtn--with-signals" : "minibtn"; return '<button title="add signals" class="' + classes + '" onclick="open_signal_drawer(\\'' + id.replace('n','') + '\\')">' + text + '</button>'; } else { return ''; } }''' cpout += ''' jsPlumb.importDefaults({ PaintStyle : { lineWidth:2, strokeStyle: 'rgba(0,0,0,0.5)' }, Endpoints : [ [ "Dot", { radius:1 } ], [ "Dot", { radius:1 } ] ], EndpointStyles : [{ fillStyle:"#000000" }, { fillStyle:"#000000" }], Anchor:"Top", Connector : [ "Bezier", { curviness:50 } ] }) jsPlumb.ready(function() { jsPlumb.setContainer(document.getElementById("inner_canvas")); ''' cpout += "jsPlumb.setSuspendDrawing(true);" for key in nodes: node = nodes[key] if node.kind == "edu": node_id_str = "edu" + node.id else: node_id_str = "g" + node.id cpout += 'jsPlumb.makeSource("' + node_id_str + '", {anchor: "Top", filter: ".num_id", allowLoopback:false});' cpout += 'jsPlumb.makeTarget("' + node_id_str + '", {anchor: "Top", filter: ".num_id", allowLoopback:false});' # Connect nodes for key in nodes: node = nodes[key] if node.parent != "0": parent = nodes[node.parent] if node.kind == "edu": node_id_str = "edu" + node.id else: node_id_str = "g" + node.id if parent.kind == "edu": parent_id_str = "edu" + parent.id else: parent_id_str = "g" + parent.id if node.relname == "span": cpout += 'jsPlumb.connect({source:"' + node_id_str + '",target:"' + parent_id_str + '", connector:"Straight", anchors: ["Top","Bottom"]});' elif parent.kind == "multinuc" and node.relkind == "multinuc": cpout += 'jsPlumb.connect({source:"' + node_id_str + '",target:"' + parent_id_str + '", connector:"Straight", anchors: ["Top","Bottom"], overlays: [ ["Custom", {create:function(component) {return make_relchooser("' + node.id + '","multi","' + node.relname + '");},location:0.2,id:"customOverlay"}]]});' else: cpout += 'jsPlumb.connect({source:"' + node_id_str + '",target:"' + parent_id_str + '", overlays: [ ["Arrow" , { width:12, length:12, location:0.95 }],["Custom", {create:function(component) {return make_relchooser("' + node.id + '","rst","' + node.relname + '");},location:0.1,id:"customOverlay"}]]});' cpout += ''' jsPlumb.setSuspendDrawing(false,true); jsPlumb.bind("connection", function(info) { source = info.sourceId.replace(/edu|g/,"") target = info.targetId.replace(/edu|g/g,"") }); jsPlumb.bind("beforeDrop", function(info) { $(".minibtn").prop("disabled",true); ''' cpout += ''' var node_id = "n"+info.sourceId.replace(/edu|g|lg/,""); var new_parent_id = "n"+info.targetId.replace(/edu|g|lg/,""); nodes = parse_data(); new_parent = nodes[new_parent_id]; relname = nodes[node_id].relname; new_parent_kind = new_parent.kind; if (nodes[node_id].parent != "n0"){ old_parent_kind = nodes[nodes[node_id].parent].kind; } else { old_parent_kind ="none"; } if (info.sourceId != info.targetId){ if (!(is_ancestor(new_parent_id,node_id))){ jsPlumb.select({source:info.sourceId}).detach(); if (new_parent_kind == "multinuc"){ relname = get_multirel(new_parent_id,node_id,nodes); jsPlumb.connect({source:info.sourceId, target:info.targetId, connector:"Straight", anchors: ["Top","Bottom"], overlays: [ ["Custom", {create:function(component) {return make_relchooser(node_id,"multi",relname);},location:0.2,id:"customOverlay"}]]}); } else{ jsPlumb.connect({source:info.sourceId, target:info.targetId, overlays: [ ["Arrow" , { width:12, length:12, location:0.95 }],["Custom", {create:function(component) {return make_relchooser(node_id,"rst",relname);},location:0.1,id:"customOverlay"}]]}); } new_rel = document.getElementById("sel"+ node_id.replace("n","")).value; act('up:' + node_id.replace("n","") + ',' + new_parent_id.replace("n","")); update_rel(node_id,new_rel,nodes); recalculate_depth(parse_data()); } } $(".minibtn").prop("disabled",false); }); }); nodes = parse_data(); show_warnings(nodes); </script> ''' cpout += ''' </div> </div> </div> <div id="anim_catch" class="anim_catch"> </div> </body> </html> ''' if mode != "server": cpout = cpout.replace(".py", "") return cpout
def open_main(user, admin, mode, **kwargs): cpout = "" scriptpath = os.path.dirname(os.path.realpath(__file__)) + os.sep userdir = scriptpath + "users" + os.sep theform = kwargs cgitb.enable() config = ConfigObj(userdir + 'config.ini') templatedir = scriptpath + config['controltemplates'].replace("/", os.sep) template = "main_header.html" header = readfile(templatedir + template) header = header.replace("**page_title**", "Open a file for editing") header = header.replace("**user**", user) if mode == "server": cpout += "Content-Type: text/html\n\n\n" header = header.replace("**logout_control**", '(<a href="logout.py">log out</a>)') else: header = header.replace("**logout_control**", '') cpout += header if "current_doc" in theform: current_doc = theform["current_doc"] current_project = theform["current_project"] else: current_doc = "" current_project = "" edit_bar = "edit_bar.html" edit_bar = readfile(templatedir + edit_bar) edit_bar = edit_bar.replace("**doc**", current_doc) edit_bar = edit_bar.replace("**project**", current_project) edit_bar = edit_bar.replace("**structure_disabled**", '') edit_bar = edit_bar.replace("**segment_disabled**", '') edit_bar = edit_bar.replace("**relations_disabled**", '') if mode == "server": edit_bar = edit_bar.replace("**submit_target**", 'structure.py') else: edit_bar = edit_bar.replace("**submit_target**", 'structure') edit_bar = edit_bar.replace("**action_type**", '') edit_bar = edit_bar.replace("**serve_mode**", mode) edit_bar = edit_bar.replace("**open_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**reset_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**save_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**undo_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**redo_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace( 'id="nav_open" class="nav_button"', 'id="nav_open" class="nav_button nav_button_inset"') if admin == "0": edit_bar = edit_bar.replace("**admin_disabled**", 'disabled="disabled"') else: edit_bar = edit_bar.replace("**admin_disabled**", '') cpout += edit_bar help = "help.html" help = readfile(templatedir + help) help_match = re.search(r'(<div id="help_open".*?</div>)', help, re.MULTILINE | re.DOTALL) help = help_match.group(1) cpout += help about = "about.html" about = readfile(templatedir + about) about = about.replace("**version**", _version.__version__) cpout += about cpout += "<h2>Current Documents</h2>" cpout += '<p>List of documents you are authorized to view:</p>' docs = get_docs_by_project(user) if not docs: cpout += "<p>No documents have been assigned for user name: <b>" + user + "</b></p>" else: cpout += '<select id="doclist" name="doclist" class="doclist" size="15">\n' project_group = "" for doc in docs: if project_group != doc[1]: if project_group != "": cpout += '</optgroup>\n' project_group = doc[1] cpout += '<optgroup label="' + doc[1] + '">\n' cpout += '\t<option value="' + doc[1] + "/" + doc[0] + '">' + doc[ 0] + '</option>\n' cpout += '</optgroup>\n</select>\b<br/>' cpout += '''<button class="nav_button" onclick="do_open(document.getElementById('doclist').value);">Open file</button>''' cpout += ''' </body> </html> ''' if mode != "server": cpout = cpout.replace(".py", "") return cpout
import requests, os, platform from modules.configobj import ConfigObj # Support IIS site prefix on Windows if platform.system() == "Windows": prefix = "transc\\" else: prefix = "" gitdox_root = os.path.dirname(os.path.realpath(__file__)) # to use password authentication, use a netrc file called .netrc in the project root try: ether_url = ConfigObj(gitdox_root + os.sep + "users" + os.sep + "config.ini")["ether_url"] if not ether_url.endswith(os.sep): ether_url += os.sep except KeyError: ether_url = "" def get_menu(): config = ConfigObj(prefix + "users" + os.sep + "config.ini") if "banner" not in config: return "" banner = config["banner"] if banner.startswith("http"): # Web resource resp = requests.get(banner) return resp.text
def get_nlp_credentials(): config = ConfigObj("users" + os.sep + "config.ini") return config["nlp_user"], config["nlp_password"]
def segment_main(user, admin, mode, **kwargs): cpout = "" theform = kwargs scriptpath = os.path.dirname(os.path.realpath(__file__)) + os.sep userdir = scriptpath + "users" + os.sep UTF8Writer = codecs.getwriter('utf8') sys.stdout = UTF8Writer(sys.stdout) cgitb.enable() config = ConfigObj(userdir + 'config.ini') templatedir = scriptpath + config['controltemplates'].replace("/",os.sep) template = "main_header.html" header = readfile(templatedir+template) header = header.replace("**page_title**","Segmentation editor") header = header.replace("**user**",user) header = header.replace("**open_disabled**",'') if admin == "0": header = header.replace("**admin_disabled**",'disabled="disabled"') else: header = header.replace("**admin_disabled**",'') cpout = "" if mode == "server": cpout += "Content-Type: text/html\n\n\n" header = header.replace("**logout_control**",'(<a href="logout.py">log out</a>)') else: header = header.replace("**logout_control**",'') cpout += header if "current_doc" in theform: current_doc = theform["current_doc"] current_project = theform["current_project"] current_guidelines = get_guidelines_url(current_project) else: current_doc = "" current_project = "" current_guidelines = "" edit_bar = "edit_bar.html" edit_bar = readfile(templatedir+edit_bar) edit_bar = edit_bar.replace("**doc**",current_doc) edit_bar = edit_bar.replace("**project**",current_project) edit_bar = edit_bar.replace("**structure_disabled**",'') edit_bar = edit_bar.replace("**save_disabled**",'') edit_bar = edit_bar.replace("**reset_disabled**",'') edit_bar = edit_bar.replace("**segment_disabled**",'disabled="disabled"') edit_bar = edit_bar.replace("**submit_target**",'segment.py') edit_bar = edit_bar.replace("**action_type**",'seg_action') edit_bar = edit_bar.replace("**current_guidelines**",current_guidelines) edit_bar = edit_bar.replace("**serve_mode**",mode) edit_bar = edit_bar.replace("**open_disabled**",'') edit_bar = edit_bar.replace('id="nav_segment" class="nav_button"','id="nav_segment" class="nav_button nav_button_inset"') if admin == "0": edit_bar = edit_bar.replace("**admin_disabled**",'disabled="disabled"') else: edit_bar = edit_bar.replace("**admin_disabled**",'') cpout += edit_bar help = "help.html" help = readfile(templatedir+help) help_match = re.search(r'(<div.*?help_seg.*?</div>)',help,re.MULTILINE|re.DOTALL) help = help_match.group(1) cpout += help about = "about.html" about = readfile(templatedir+about) about = about.replace("**version**", _version.__version__) cpout += about if current_doc =="": cpout += '<p class="warn">No file found - please select a file to open</p>' return cpout cpout += '<input id="undo_log" type="hidden" value=""/>' cpout += '<input id="redo_log" type="hidden" value=""/>' cpout += '<input id="undo_state" type="hidden" value=""/>' cpout += '''<div class="canvas"> <div id="inner_canvas">''' if "reset" in theform or user=="demo": if len(theform["reset"]) > 1 or user=="demo": reset_rst_doc(current_doc,current_project,user) if "logging" in theform: if len(theform["logging"]) > 1: logging = theform["logging"] if len(logging) > 0: update_log(current_doc,current_project,user,logging,"segment",str(datetime.datetime.now())) if "seg_action" in theform: if len(theform["seg_action"]) > 1: action_log = theform["seg_action"] if len(action_log) > 0: actions = action_log.split(";") for action in actions: action_type = action.split(":")[0] action_params = action.split(":")[1] if action_type =="ins": insert_seg(int(action_params.replace("tok","")),current_doc,current_project,user) elif action_type =="del": merge_seg_forward(int(action_params.replace("tok","")),current_doc,current_project,user) segs={} rows = get_rst_doc(current_doc,current_project,user) if current_guidelines != "": cpout += '<script>enable_guidelines();</script>' cpout += '\t<script src="script/segment.js"></script>' cpout += '<h2>Edit segmentation</h2>' cpout += '\t<div id="control">' cpout += '\t<p>Document: <b>'+current_doc+'</b> (project: <i>'+current_project+'</i>)</p>' cpout += '\t<div id="segment_canvas">' for row in rows: if row[5] =="edu": segs[int(row[0])] = SEGMENT(row[0],row[6]) seg_counter=0 tok_counter=0 segs = collections.OrderedDict(sorted(segs.items())) first_seg = True for seg_id in segs: first_tok = True seg = segs[seg_id] seg_counter+=1 if first_seg: first_seg = False else: cpout += '<div class="tok_space" id="tok'+str(tok_counter)+'" style="display:none" onclick="act('+"'ins:"+'tok'+str(tok_counter)+"'"+')"> </div>' cpout += '\t\t\t<div id="segend_post_tok'+str(tok_counter)+'" class="seg_end" onclick="act('+"'del:"+'tok'+str(tok_counter)+"'"+')">||</div>' cpout += '\t\t</div>' cpout += '\t\t<div id="seg'+ str(seg_counter) +'" class="seg">' for token in seg.tokens: tok_counter+=1 if first_tok: first_tok = False else: cpout += '<div class="tok_space" id="tok'+str(tok_counter-1)+'" onclick="act('+"'ins:"+'tok'+str(tok_counter-1)+"'"+')"> </div>' cpout += '\t\t\t<div class="token" id="string_tok'+str(tok_counter)+'">' + token + '</div>' cpout += '\t\t</div>' cpout += '''\t</div></div> </body> </html> ''' if mode != "server": cpout = cpout.replace(".py","") return cpout
def doedituser(theform, userdir, thisscript, userconfig, action, newcookie): """Receives form submissions from the 'edit user' page.""" # parameters to get : # username, realname, email, adminlev, pass1, pass2 username = theform.getfirst('username') # the user we are editing loginname = theform.getfirst('loginname') # the new user name (won't usually change I guess) realname = theform.getfirst('realname') email = theform.getfirst('email') adminlev = theform.getfirst('adminlev') pass1 = theform.getfirst('pass1') pass2 = theform.getfirst('pass2') maxage = theform.getfirst('maxage') editable = theform.getfirst('editable') maxadminlev = min(int(userconfig['admin']), MAXADMINLEV) # check all the account values # this could be turned into a generic 'account checker' function if we wanted. email = validemail(email) if not email: edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'The Email Address Appears to Be Invalid.') if not loginname: edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'You Must Supply a Login Name.') for char in loginname.lower(): if not char in validchars: edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Login Name Contains Invalid Characters') if not realname: edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'You Must Supply a Real Name') if (pass1 or pass2) and not (pass1 and pass2): edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'To Change the Password - Enter it Twice') if pass1 != pass2: edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'The Two Passwords Are Different') if pass1 and len(pass1) < 5: edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Password Must Be at Least Five Characters') if not adminlev.isdigit(): edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'The Admin Level Must Be a Number') if int(adminlev) > maxadminlev: edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Admin Level is Higher than the Max (%s).' % maxadminlev) if not maxage.isdigit(): edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Cookie "max-age" Must Be a Number') if int(maxage) and int(maxage) < MINMAXAGE: edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Cookie "max-age" Must Be Greater Than %s' % MINMAXAGE) if editable: editable = 'Yes' else: editable = 'No' # let's just check if the username has changed thisuser = ConfigObj(userdir+username+'.ini') if loginname != username: pendinglist = ConfigObj(userdir + 'temp.ini').get('pending', []) if os.path.isfile(userdir+loginname+'.ini') or loginname in pendinglist or loginname.lower() in RESERVEDNAMES: edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Login Name Chosen Already Exists') thisuser.filename = userdir+loginname+'.ini' # change to new name os.remove(userdir+username+'.ini') # free up the old name if pass1: from dataenc import pass_enc thisuser['password'] = pass_enc(pass1, daynumber=True, timestamp=True) # thisuser['realname'] = realname thisuser['email'] = email thisuser['admin'] = adminlev thisuser['max-age'] = maxage thisuser['editable'] = editable thisuser.write() # edituser(theform, userdir, thisscript, userconfig, action, newcookie, '') edituser(theform, userdir, thisscript, userconfig, action, newcookie, 'Changes Made Successfully', True)
def confirm(theform, userdir, thisscript): """Confirm a login. Either from an invite or from a user who has registered.""" from modules.dataenc import pass_dec, pass_enc from login import encodestring fail = False try: theval, daynumber, timestamp = pass_dec(theform['id'].value) except: # FIXME: bare except.... newloginfail() tempstore = ConfigObj(userdir + 'temp.ini') if not tempstore.has_key(theval): newloginfail() uservals = tempstore[theval] del tempstore[theval] username = uservals['username'] if username in tempstore['pending']: tempstore['pending'].remove(username) tempstore.write() # newconfig = ConfigObj(userdir + 'default.ini') newpath = userdir + username + '.ini' if os.path.isfile(newpath): newloginfail() newconfig.filename = newpath # FIXME: should this be '' ? action = None for entry in uservals: if entry == 'action': action = uservals[entry] elif entry == 'password': password = uservals[entry] pwd_hash = pwd_context.hash(password, salt="") newconfig[entry] = pass_enc(pwd_hash, timestamp=True, daynumber=True) else: newconfig[entry] = uservals[entry] newconfig.write() # # next we need to create the cookie header to return it from Cookie import SimpleCookie thecookie = SimpleCookie() pwd_hash = pwd_context.hash(password, salt="") thecookie['userid'] = encodestring(newconfig['username'], pwd_hash) config = ConfigObj(userdir + 'config.ini') maxage = newconfig['max-age'] cookiepath = config['cookiepath'] if maxage and int( maxage ): # possible cause of error here if the maxage value in a users file isn't an integer !! thecookie['userid']['max-age'] = int(maxage) if cookiepath: thecookie['userid']['path'] = cookiepath if config['adminmail']: msg = 'A new user has created a login - "%s".\n\n' % thisscript for entry in newconfig: if entry != 'password': msg += entry + ' : ' + newconfig[entry] + '\n' # FIXME: should be mailme sendmailme(config['adminmail'], msg, config['email_subject'], config['adminmail'], html=False) return action, newconfig, thecookie.output()
def admin_main(user, admin, mode, **kwargs): scriptpath = os.path.dirname(os.path.realpath(__file__)) + os.sep userdir = scriptpath + "users" + os.sep config = ConfigObj(userdir + 'config.ini') exportdir = scriptpath + config['exportdir'].replace("/", os.sep) theform = kwargs cgitb.enable() import re def is_email(email): pattern = '[\.\w]{1,}[@]\w+[.]\w+' if re.match(pattern, email): return True else: return False config = ConfigObj(userdir + 'config.ini') templatedir = scriptpath + config['controltemplates'].replace("/", os.sep) template = "main_header.html" header = readfile(templatedir + template) header = header.replace("**page_title**", "Administration") header = header.replace("**user**", user) importdir = scriptpath + config['importdir'].replace("/", os.sep) def_relfile = scriptpath + config['default_rels'].replace("/", os.sep) cpout = "" if mode == "server": cpout += "Content-Type: text/html\n\n\n" header = header.replace("**logout_control**", '(<a href="logout.py">log out</a>)') else: header = header.replace("**logout_control**", '') cpout += header if "current_doc" in theform: current_doc = theform["current_doc"] current_project = theform["current_project"] else: current_doc = "" current_project = "" edit_bar = "edit_bar.html" edit_bar = readfile(templatedir + edit_bar) edit_bar = edit_bar.replace("**doc**", current_doc) edit_bar = edit_bar.replace("**project**", current_project) edit_bar = edit_bar.replace("**structure_disabled**", '') edit_bar = edit_bar.replace("**segment_disabled**", '') edit_bar = edit_bar.replace("**relations_disabled**", '') edit_bar = edit_bar.replace("**submit_target**", 'admin.py') edit_bar = edit_bar.replace("**action_type**", '') edit_bar = edit_bar.replace("**serve_mode**", mode) edit_bar = edit_bar.replace("**open_disabled**", '') edit_bar = edit_bar.replace("**reset_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**quickexp_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**screenshot_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**save_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**undo_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**redo_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace("**admin_disabled**", 'disabled="disabled"') edit_bar = edit_bar.replace( 'id="nav_admin" class="nav_button"', 'id="nav_admin" class="nav_button nav_button_inset"') if admin == "0": cpout += '<p class="warn">User ' + user + ' does not have administrator rights!</p></body></html>' return cpout else: edit_bar = edit_bar.replace("**admin_disabled**", '') cpout += edit_bar help = "help.html" help = readfile(templatedir + help) help_match = re.search(r'(<div id="help_admin".*?</div>)', help, re.MULTILINE | re.DOTALL) help = help_match.group(1) cpout += help about = "about.html" about = readfile(templatedir + about) about = about.replace("**version**", _version.__version__) cpout += about if "wipe" in theform: if theform["wipe"] == "wipe": setup_db() if "create_project" in theform: new_project = theform["create_project"] if len(new_project) > 0: create_project(new_project) if "doclist" in theform: current_doc = theform["doclist"] else: current_doc = "" cpout += ''' <div id="control"> <form id="admin_form" name="id_form" action="admin.py" method="post" enctype="multipart/form-data"> <input type="hidden" name="create_project" id="create_project" value=""/> <input type="hidden" name="sel_tab" id="sel_tab" value="project"/> <input type="hidden" name="delete" id="delete" value=""/> <input type="hidden" name="delete_user" id="delete_user" value=""/> <input type="hidden" name="export" id="export" value=""/> <input type="hidden" name="wipe" id="wipe" value=""/> <input type="hidden" name="switch_signals" id="switch_signals" value=""/> <input type="hidden" name="signals_file" id="signals_file" value=""/> <input type="hidden" name="switch_logging" id="switch_logging" value=""/> <input type="hidden" name="switch_span_buttons" id="switch_span_buttons" value=""/> <input type="hidden" name="switch_multinuc_buttons" id="switch_multinuc_buttons" value=""/> <input type="hidden" name="update_schema" id="update_schema" value=""/> <input type="hidden" name="imp_project" id="imp_project" value=""/> <input type="hidden" name="import_file_type" id="import_file_type" value=""/> <input type="hidden" name="do_tokenize" id="do_tokenize" value=""/> <input type="hidden" name="doclist" id="doclist" value="''' cpout += current_doc cpout += '''"/> <input type="hidden" name="userlist" id="userlist" value=""/> <input type="hidden" name="assign_doc" id="assign_doc" value=""/> <input type="hidden" name="assign_user" id="assign_user" value=""/> <input type="hidden" name="unassign_doc" id="unassign_doc" value=""/> <input type="hidden" name="unassign_user" id="unassign_user" value=""/> <input type="hidden" name="new_user_data" id="new_user_data" value=""/> <input type="hidden" name="edit_validation" id="edit_validation" value=""/> <input type="hidden" name="del_project" id="del_project" value=""/> <input type="hidden" name="guidelines_url" id="guidelines_url" value=""/> <input id="file" type="file" name="file" multiple="multiple"/> </form> <script src="./script/admin.js"></script> <script src="./script/jquery-1.11.3.min.js"></script> <script src="./script/jquery-ui.min.js"></script> ''' cpout += ''' <div class="tab_btn" id="project_btn" onclick="open_tab('project');">Projects</div><div id="project" class="tab"> <h2>Manage Projects</h2> <p>Create a new project named: <input id="new_project"/></p> <button onclick="admin('create_project')">Create Project</button> ''' #cpout += theform if "update_schema" in theform: if theform["update_schema"] == "update_schema": update_schema() if "del_project" in theform: projects_to_delete = theform["del_project"].split(";") if len(projects_to_delete) > 0: if isinstance(projects_to_delete, list): for project in projects_to_delete: delete_project(project) else: delete_project(projects_to_delete) all_project_list = get_all_projects() url_message = "" if "guidelines_url" in theform: if "::" in theform["guidelines_url"]: project_list, guideline_url = theform["guidelines_url"].split("::") if len(project_list) > 0: schema = get_schema() if schema < 2: update_schema() if isinstance(project_list, list): for project in project_list: set_guidelines_url(project, guideline_url) else: set_guidelines_url(project_list, guideline_url) url_message = '<p class="warn">Added the URL ' + guideline_url + " to the selected projects</p>" cpout += '<p>Existing projects:</p>' if all_project_list: cpout += '<select class="doclist" id="project_select" size="5" multiple="multiple">\n' for project in all_project_list: cpout += '\t<option value="' + project[0] + '">' + project[ 0] + "</option>" cpout += '</select>\n' else: cpout += '<p class="warn">No projects found!</p>' cpout += '''<p> <p>Add guidelines URL to selected project:</p><p><input id="guidelines_url_input"/></p> <button onclick="admin('guidelines_url')">Add URL</button> ''' + url_message cpout += ''' <p>Delete selected projects:</p> <button onclick="admin('delete_project')">Delete Selected</button></p> ''' # Handle validation settings validation_message = "" val_project = "" if "edit_validation" in theform: if "::" in theform["edit_validation"]: val_project, validations = theform["edit_validation"].split("::") schema = get_schema() if schema < 5: update_schema() set_project_validations(val_project, validations) validation_message = '<p class="warn">Updated validation settings for project ' + val_project cpout += '''<p>Change annotation warning settings for project:</p>''' if all_project_list: cpout += '''<select class="doclist" id="validate_project_select" onchange="toggle_validation_project();">\n''' if val_project == "": val_project = all_project_list[0][0] for project in all_project_list: if project[0] == val_project: sel_string = ' selected="selected"' else: sel_string = "" cpout += '\t<option value="' + project[ 0] + '"' + sel_string + '>' + project[0] + "</option>\n" cpout += '</select>\n' for project in all_project_list: vals = get_project_validations(project[0]) cpout += '\t<input id="validations_' + project[ 0] + '" type="hidden" value="' + vals + '">' else: cpout += "<p>No projects found with permissions for user: "******"</p>" validations = get_project_validations(val_project) validation_list = validations.split(";") checked = "" if "validate_empty" in validation_list: checked = " checked" cpout += '''<br><br><label class="switch"> <input type="checkbox" id="check_empty_span" onclick="admin('toggle_validations');"''' + checked + '''> <div class="slider round"></div> </label><div style="position: relative; top: -3px; left: 3px; display: inline-block">Warn on empty span <a class="tooltip" href=""> <i class="fa fa-question-circle"> </i> <span><img src="img/empty_span.png" height="100px"> <i>Highlights spans with single span child</i> </span> </a></div> ''' checked = "" if "validate_flat" in validation_list: checked = " checked" cpout += '''<br><label class="switch"> <input type="checkbox" id="check_flat_rst" onclick="admin('toggle_validations');"''' + checked + '''> <div class="slider round"></div> </label><div style="margin-top: 10px; position: relative; top: -3px; left: 3px; display: inline-block">Warn on multiple incoming flat RST relations <a class="tooltip" href=""> <i class="fa fa-question-circle"> </i> <span><img src="img/flat_rst.png" height="75px"> <i>Highlights spans with multiple incoming satellites</i> </span> </a></div> ''' checked = "" if "validate_mononuc" in validation_list: checked = " checked" cpout += '''<br><label class="switch"> <input type="checkbox" id="check_mononuc" onclick="admin('toggle_validations');"''' + checked + '''> <div class="slider round"></div> </label><div style="margin-top: 10px; position: relative; top: -3px; left: 3px; display: inline-block">Warn on multinucs with single child <a class="tooltip" href=""> <i class="fa fa-question-circle"> </i> <span><img src="img/mononuc.png" height="75px"> <i>Highlights multinucs with a single child</i> </span> </a></div> <script> selproj = document.getElementById("validate_project_select").value; validations = document.getElementById("validations_" + selproj).value; if (validations.indexOf("validate_flat")>0){ document.getElementById("check_flat_rst").checked = true; } if (validations.indexOf("validate_empty")>0){ document.getElementById("check_empty_span").checked = true; } if (validations.indexOf("validate_mononuc")>0){ document.getElementById("check_mononuc").checked = true; } </script>''' cpout += validation_message cpout += ''' </div>''' cpout += ''' <div class="tab_btn" id="import_btn" onclick="open_tab('import');">Import</div><div id="import" class="tab"> <h2>Import a Document</h2> <p>1. Upload .rs3 or plain text file(s): </p> <p>2. Choose file type: <select id="import_file_type_select" class="doclist"><option value="rs3">rs3</option><option value="plain">plain text (EDU per line)</option></select> </p> <p>3. Tokenize words automatically? <input type="checkbox" name="tokenize" id="tokenize"></p> <p>4. Import document(s) into this project: ''' if all_project_list: cpout += '<select class="doclist" id="imp_project_select">\n' if isinstance(all_project_list, list): for project in all_project_list: cpout += '\t<option value="' + project[0] + '">' + project[ 0] + "</option>" else: cpout += '\t<option value="' + all_project_list + '">' + all_project_list + "</option>" cpout += '</select>\n' else: cpout += "<p>No projects found with permissions for user: "******"</p>" cpout += ''' </p> <p><button onclick="admin('upload')">Upload</button></p> ''' fail = 0 if "file" in theform and "imp_project" in theform: fileitem = theform['file'] imp_project = theform["imp_project"] do_tokenize = theform["do_tokenize"] == "tokenize" if isinstance(fileitem, list): message = "" for filelist_item in fileitem: if filelist_item.filename and len( imp_project ) > 0: # Test if the file was uploaded and a project selection exists # strip leading path from file name to avoid directory traversal attacks fn = os.path.basename(filelist_item.filename) open(importdir + fn, 'wb').write(filelist_item.file.read()) message += 'The file "' + fn + '" was uploaded successfully<br/>' if theform['import_file_type'] == "rs3": fail = import_document(importdir + fn, imp_project, user, do_tokenize=do_tokenize) elif theform['import_file_type'] == "plain": if len(def_relfile) > 0: rel_hash = read_relfile(def_relfile) else: rel_hash = {} fail = import_plaintext(importdir + fn, imp_project, user, rel_hash, do_tokenize=do_tokenize) else: message = 'No file was uploaded' else: if fileitem.filename and len( imp_project ) > 0: # Test if the file was uploaded and a project selection exists # strip leading path from file name to avoid directory traversal attacks fn = os.path.basename(fileitem.filename) open(importdir + fn, 'wb').write(fileitem.file.read()) message = 'The file "' + fn + '" was uploaded successfully' if theform['import_file_type'] == "rs3": fail = import_document(importdir + fn, imp_project, user, do_tokenize=do_tokenize) elif theform['import_file_type'] == "plain": if len(def_relfile) > 0: rel_hash = read_relfile(def_relfile) else: rel_hash = {} fail = import_plaintext(importdir + fn, imp_project, user, rel_hash, do_tokenize=do_tokenize) else: message = 'No file was uploaded' if isinstance(fail, basestring): message = fail cpout += """ <p class="warn">%s</p> """ % (message, ) cpout += '</div><div class="tab_btn" id="docs_btn" onclick="open_tab(' + "'" + "docs" + "'" + ');">Documents</div><div id="docs" class="tab">' cpout += "<h2>Current Documents</h2>" if "delete" in theform: #execute delete documents before retrieving doclist doclist = theform["doclist"] if len(doclist) > 0 and theform["delete"] == "delete": docs_to_delete = doclist.split(";") if len(docs_to_delete) > 0: if isinstance(docs_to_delete, list): for doc in docs_to_delete: delete_document(doc.split("/")[1], doc.split("/")[0]) else: delete_document( docs_to_delete.split("/")[1], docs_to_delete.split("/")[0]) cpout += '<p class="warn">Deletion complete</p>' cpout += '<p>List of documents in the database:</p>' docs = get_all_docs_by_project() if not docs: cpout += "<p>No documents found with permissions for user name: " + user + "</p>" else: cpout += '<select name="doclist_select" id="doclist_select" class="doclist" size="15" multiple="multiple">\n' project_group = "" for doc in docs: if project_group != doc[1]: if project_group != "": cpout += '</optgroup>\n' project_group = doc[1] cpout += '<optgroup label="' + doc[1] + '">\n' cpout += '\t<option value="' + doc[1] + "/" + doc[0] + '">' + doc[ 0] + '</option>\n' cpout += '</optgroup>\n</select>\n' cpout += ''' <p>Export selected document(s) to export folder as .rs3 file(s):</p> <button onclick="admin('export');">Export</button> <p>Delete selected document(s):</p> <button onclick="admin('delete_doc');">Delete</button> ''' if "export" in theform: export_doc_list = theform["doclist"] if len(export_doc_list) > 0 and theform["export"] == "export": export_docs = export_doc_list.split(";") if isinstance(export_docs, list): for doc in export_docs: export_document( doc.split("/")[1], doc.split("/")[0], exportdir) else: export_document( export_docs.split("/")[1], export_docs.split("/")[0], exportdir) cpout += '<p class="warn">Export complete</p>' # Handle user add and delete before showing user list if "delete_user" in theform: if len(theform["delete_user"]) > 0: users_to_delete = theform["userlist"] users = users_to_delete.split(";") if isinstance(users, list): for user in users: try: os.remove(userdir + user) delete_docs_for_user(user) user_del_message = '<p class="warn">Deleted users from selection</p>' except OSError as e: if e.errno == errno.ENOENT: user_del_message = '<p class="warn">File to delete <i>' + user + '</i> not found!</p>' else: raise else: try: os.remove(userdir + users_to_delete) delete_docs_for_user(users_to_delete) user_del_message = 'Deleted users from selection' except OSError as e: if e.errno == errno.ENOENT: user_del_message = '<p class="warn">File to delete <i>' + users_to_delete + '</i> not found!</p>' else: raise # Handle user document assignment if "assign_user" in theform: if len(theform["assign_user"]) > 0: users_to_assign = theform["assign_user"] docs_to_assign = theform["assign_doc"] users = users_to_assign.split(";") if ";" in users_to_assign else [ users_to_assign ] docs = docs_to_assign.split(";") if ";" in docs_to_assign else [ docs_to_assign ] for user in users: for doc in docs: user = user.replace(".ini", "") copy_doc_to_user( doc.split("/")[1], doc.split("/")[0], user) if "unassign_user" in theform: if len(theform["unassign_user"]) > 0: user_to_unassign = theform["unassign_user"] doc_to_unassign = theform["unassign_doc"] delete_doc_user_version( doc_to_unassign.split("/")[1], doc_to_unassign.split("/")[0], user_to_unassign) if "new_user_data" in theform: if len(theform["new_user_data"]) > 0: user_data = theform["new_user_data"].split("/") username = user_data[0] realname = user_data[1] email = user_data[2] RESERVEDNAMES = { 'config': 1, 'default': 1, 'temp': 1, 'emails': 1, 'pending': 1, '_orig': 1 } if username in RESERVEDNAMES: user_create_message = '<p class="warn">User name cannot be: "config", "default", "temp", "emails", or "pending"</p>' else: if len(realname) < 1: user_create_message = '<p class="warn">The real name cannot be empty!</p>' else: if not is_email(email): user_create_message = '<p class="warn">Invalid e-mail address: <b>' + email + '</b></p>' else: password = user_data[3] if len(password) < 5: user_create_message = '<p class="warn">Password must be at least 5 characters long</p>' else: administrator = user_data[4] if str(administrator) != "3" and str( administrator) != "0": user_create_message = '<p class="warn">Invalid administrator setting value for new user</p>' else: createuser(userdir, realname, username, email, password, administrator) user_create_message = '<p class="warn">Created the user ' + username + '</p>' if mode == "server": cpout += '</div><div class="tab_btn" id="users_btn" onclick="open_tab(' + "'" + "users" + "'" + ');">Users</div><div id="users" class="tab">' else: cpout += '''</div><div class="tab_btn disabled_tab" id="users_btn" onclick="alert('User management disabled in local mode');">Users</div><div id="users" class="tab">''' cpout += '''<h2>User Management</h2> <table id="doc_assign"><tr><td><p>Users:</p> <select id="userlist_select" multiple="multiple" size="10" class="doclist"> ''' userfiles = [f for f in listdir(userdir) if isfile(join(userdir, f))] for userfile in sorted(userfiles): if userfile != "config.ini" and userfile != "default.ini" and userfile != "admin.ini" and userfile.endswith( ".ini"): userfile = userfile.replace(".ini", "") cpout += '<option value="' + userfile + '.ini">' + userfile + '</option>' cpout += ''' </select></td><td> <p>Documents to assign to:</p>''' docs = get_all_docs_by_project() if not docs: cpout += "<p class='warn'>No documents found with permissions for user name: " + user + "</p>" else: cpout += '<select name="doc_assign_select" id="doc_assign_select" class="doclist" size="10" multiple="multiple">\n' project_group = "" for doc in docs: if project_group != doc[1]: if project_group != "": cpout += '</optgroup>\n' project_group = doc[1] cpout += '<optgroup label="' + doc[1] + '">\n' cpout += '\t<option value="' + doc[1] + "/" + doc[0] + '">' + doc[ 0] + '</option>\n' cpout += '</optgroup>\n</select>\n' cpout += ''' </td></tr></table> <p>Delete selected user files: (annotations will not be deleted)</p> <button onclick="admin('delete_user')">Delete user(s)</button> ''' if "delete_user" in theform: if len(theform["delete_user"]) > 0: cpout += user_del_message cpout += ''' <p>Assign selected users to selected documents:</p> <button onclick="admin('assign_user')">Assign</button> <p>Delete assignments for user: (annotations will be deleted)</p> ''' assigned_users = get_assigned_users() if len(assigned_users) > 0: cpout += '<select id="assigned_user_sel" name="assigned_user_sel" class="doclist" onchange="update_assignments()">' first_user = assigned_users[0][0] for user in assigned_users: assigned_docs = get_assignments(user[0]) cpout += '<option value="' # + user[0] +":" for doc in assigned_docs: cpout += doc[1] + "/" + doc[0] + ";" cpout += '">' + user[0] + '</option>' cpout += '</select>\n' assigned_docs = get_assignments(first_user) cpout += '<select id="assigned_doc_sel" name="assigned_doc_sel" class="doclist">' for doc in assigned_docs: cpout += '<option value="' + doc[1] + "/" + doc[0] + '">' + doc[ 1] + "/" + doc[0] + '</option>' cpout += '</select>' cpout += '''<p><button onclick="admin('unassign_user')">Delete assignment</button></p>''' else: cpout += '<p class="warn">No user assignments found</p>' cpout += '''<p>Create new user:</p> <table class="gray_tab"> <tr><td>User name:</td><td><input name="username" id="username" type="text" value=""></td></tr> <tr><td>Real name:</td><td><input name="realname" id="realname" type="text" value=""></td></tr> <tr><td>Email address:</td><td><input name="email" id="email" type="text" value=""></td></tr> <tr><td>Password:</td><td><input name="pass" id="pass" type="password"> <tr><td>Administrator:</td><td><input type="checkbox" id="chk_admin" name="chk_admin"> <button onclick="admin('create_user')">Create user</button></td></tr> </td></tr> </table> ''' if "new_user_data" in theform: if len(theform["new_user_data"]) > 0: cpout += user_create_message ### database cpout += '</div><div class="tab_btn" id="database_btn" onclick="open_tab(' + "'" + "database" + "'" + ');">Database</div><div id="database" class="tab">' # signals cpout += '''<h2>Signals</h2> <p>Turn signal display and editing signals on/off.</p>''' try: signals_state = get_setting("signals") except IndexError: signals_state = "False" if "switch_signals" in theform: if theform["switch_signals"] == "switch_signals": if signals_state == "True": signals_state = "False" else: signals_state = "True" save_setting("signals", signals_state) if signals_state == "True": opposite_signals = "False" else: opposite_signals = "True" cpout += '''<button onclick="admin('switch_signals')">Turn ''' + ( 'on' if opposite_signals == "True" else 'off') + '''</button>''' if "signals_file" in theform and theform["signals_file"]: save_setting("signals_file", theform["signals_file"]) cpout += '''<div> <br> <span>Signal types: </span> <select name="signals_file_select" id="signals_file_select" class="doclist" onchange="admin('select_signals_file')">''' signals_files = [ fname for fname in os.listdir('signals') if fname.endswith('.json') ] signals_file = get_setting("signals_file") for fname in signals_files: selected_string = (' selected="selected"' if signals_file and signals_file == fname else '') cpout += '<option value="' + fname + '"' + selected_string + '>' + fname[: -5] + '</option>' cpout += '''</select> </div>''' # logging cpout += '''<h2>Logging</h2> <p>Turn detailed action logging on/off.</p>''' try: logging_state = get_setting("logging") except IndexError: logging_state = "off" if "switch_logging" in theform: if theform["switch_logging"] == "switch_logging": if logging_state == "on": logging_state = "off" else: logging_state = "on" save_setting("logging", logging_state) if logging_state == "on": opposite_logging = "off" else: opposite_logging = "on" cpout += '''<button onclick="admin('switch_logging')">Turn ''' + opposite_logging + '''</button>''' # spans/multinucs cpout += '''<h2>Disable spans or multinucs</h2> <p>Turn on/off add span and multinuc buttons (for non-RST annotation).</p>''' try: span_state = get_setting("use_span_buttons") multinuc_state = get_setting("use_multinuc_buttons") except IndexError: span_state = "True" multinuc_state = "True" if "switch_span_buttons" in theform: if int(get_schema()) < 3: update_schema() if theform["switch_span_buttons"] == "switch_span_buttons": if span_state == "True": span_state = "False" else: span_state = "True" save_setting("use_span_buttons", span_state) if span_state == "True": opposite_span = "Disable" else: opposite_span = "Enable" if "switch_multinuc_buttons" in theform: if int(get_schema()) < 3: update_schema() if theform["switch_multinuc_buttons"] == "switch_multinuc_buttons": if multinuc_state == "True": multinuc_state = "False" else: multinuc_state = "True" save_setting("use_multinuc_buttons", multinuc_state) if multinuc_state == "True": opposite_multinuc = "Disable" else: opposite_multinuc = "Enable" cpout += '''<button onclick="admin('switch_span_buttons')">''' + opposite_span + ''' span buttons</button><br/><br/>''' cpout += '''<button onclick="admin('switch_multinuc_buttons')">''' + opposite_multinuc + ''' multinuc buttons</button>''' cpout += '''<h2>Update schema</h2> <p>Update the schema without losing data between major schema upgrades.</p>''' cpout += '''<button onclick="admin('update_schema')">Update</button>''' cpout += '''<h2>Initialize the Database</h2> <p>Wipe and restore database structure.</p> <p class="warn">Warning:</p> <p>this will delete all imported documents and all edits from the database.</p> <button onclick="admin('wipe')">Init DB</button> ''' if "wipe" in theform: if theform["wipe"] == "wipe": cpout += '<p class="warn">Database has been re-initialized</p>' cpout += ''' </div> <script> open_tab(document.getElementById("sel_tab").value); </script>''' if "sel_tab" in theform: sel_tab = theform['sel_tab'] cpout += '<script>open_tab("' + sel_tab + '")</script>' cpout += ''' </div> </body> </html> ''' if mode != "server": cpout = cpout.replace(".py", "") return cpout
def structure_main(user, admin, mode, **kwargs): scriptpath = os.path.dirname(os.path.realpath(__file__)) + os.sep userdir = scriptpath + "users" + os.sep theform = kwargs UTF8Writer = codecs.getwriter('utf8') sys.stdout = UTF8Writer(sys.stdout) cgitb.enable() ###GRAPHICAL PARAMETERS### top_spacing = 20 layer_spacing = 60 config = ConfigObj(userdir + 'config.ini') templatedir = scriptpath + config['controltemplates'].replace("/",os.sep) template = "main_header.html" header = readfile(templatedir+template) header = header.replace("**page_title**","Structure editor") header = header.replace("**user**",user) cpout = "" if mode == "server": cpout += "Content-Type: text/html\n\n\n" header = header.replace("**logout_control**",'(<a href="logout.py">log out</a>)') else: header = header.replace("**logout_control**",'') cpout += header if "current_doc" in theform: current_doc = theform["current_doc"] current_project = theform["current_project"] current_guidelines = get_guidelines_url(current_project) else: current_doc = "" current_project = "" current_guidelines = "" edit_bar = "edit_bar.html" edit_bar = readfile(templatedir+edit_bar) edit_bar = edit_bar.replace("**doc**",current_doc) edit_bar = edit_bar.replace("**project**",current_project) edit_bar = edit_bar.replace("**structure_disabled**",'disabled="disabled"') edit_bar = edit_bar.replace("**segment_disabled**",'') edit_bar = edit_bar.replace("**relations_disabled**",'') edit_bar = edit_bar.replace("**current_guidelines**",current_guidelines) if mode == "server": edit_bar = edit_bar.replace("**submit_target**",'structure.py') else: edit_bar = edit_bar.replace("**submit_target**",'structure') edit_bar = edit_bar.replace("**action_type**",'action') edit_bar = edit_bar.replace("**serve_mode**",mode) edit_bar = edit_bar.replace("**open_disabled**",'') edit_bar = edit_bar.replace('id="nav_edit" class="nav_button"','id="nav_edit" class="nav_button nav_button_inset"') if admin == "3": edit_bar = edit_bar.replace("**admin_disabled**",'') else: edit_bar = edit_bar.replace("**admin_disabled**",'disabled="disabled"') cpout += edit_bar help = "help.html" help = readfile(templatedir+help) help_match = re.search(r'(<div id="help_edit".*?</div>)',help,re.MULTILINE|re.DOTALL) help = help_match.group(1) cpout += help.decode("utf-8") about = "about.html" about = readfile(templatedir+about) about = about.replace("**version**", _version.__version__) cpout += about if current_guidelines != "": cpout += '<script>enable_guidelines();</script>' cpout += '<script src="./script/structure.js"></script>' if current_doc =="": cpout += '<p class="warn">No file found - please select a file to open</p>' return cpout cpout += '''<div class="canvas">''' cpout += '\t<p>Document: <b>'+current_doc+'</b> (project: <i>'+current_project+'</i>)</p>' cpout += '''<div id="inner_canvas">''' rels = get_rst_rels(current_doc, current_project) def_multirel = get_def_rel("multinuc",current_doc, current_project) def_rstrel = get_def_rel("rst",current_doc, current_project) multi_options ="" rst_options ="" rel_kinds = {} for rel in rels: if rel[1]=="multinuc": multi_options += "<option value='"+rel[0]+"'>"+rel[0].replace("_m","")+'</option>' rel_kinds[rel[0]] = "multinuc" else: rst_options += "<option value='"+rel[0]+"'>"+rel[0].replace("_r","")+'</option>' rel_kinds[rel[0]] = "rst" multi_options += "<option value='"+def_rstrel+"'>(satellite...)</option>" if "action" in theform: if len(theform["action"]) > 1: action_log = theform["action"] if len(action_log) > 0: actions = action_log.split(";") for action in actions: action_type = action.split(":")[0] action_params = action.split(":")[1] params = action_params.split(",") if action_type == "up": update_parent(params[0],params[1],current_doc,current_project,user) elif action_type == "sp": insert_parent(params[0],"span","span",current_doc,current_project,user) elif action_type == "mn": insert_parent(params[0],def_multirel,"multinuc",current_doc,current_project,user) elif action_type == "rl": update_rel(params[0],params[1],current_doc,current_project,user) else: cpout += '<script>alert("the action was: " + theform["action"]);</script>' if "logging" in theform: if len(theform["logging"]) > 1: if get_setting("logging") == "on": logging = theform["logging"] if len(logging) > 0: update_log(current_doc,current_project,user,logging,"structure",str(datetime.datetime.now())) if "reset" in theform or user == "demo": if len(theform["reset"]) > 1 or user == "demo": reset_rst_doc(current_doc,current_project,user) nodes={} rows = get_rst_doc(current_doc,current_project,user) for row in rows: if row[7] in rel_kinds: relkind = rel_kinds[row[7]] else: relkind = "span" if row[5] == "edu": nodes[row[0]] = NODE(row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],relkind) else: nodes[row[0]] = NODE(row[0],0,0,row[3],row[4],row[5],row[6],row[7],relkind) for key in nodes: node = nodes[key] get_depth(node,node,nodes) for key in nodes: if nodes[key].kind == "edu": get_left_right(key, nodes,0,0,rel_kinds) anchors = {} pix_anchors = {} # Calculate anchor points for nodes # First get proportional position for anchor for key in sorted(nodes, key = lambda id: nodes[id].depth, reverse=True): node = nodes[key] if node.kind=="edu": anchors[node.id]= "0.5" if node.parent!="0": parent = nodes[node.parent] parent_wid = (parent.right- parent.left+1) * 100 - 4 child_wid = (node.right- node.left+1) * 100 - 4 if node.relname == "span": if node.id in anchors: anchors[parent.id] = str(((node.left - parent.left)*100)/parent_wid+float(anchors[node.id])*float(child_wid/parent_wid)) else: anchors[parent.id] = str(((node.left - parent.left)*100)/parent_wid+(0.5*child_wid)/parent_wid) elif node.relkind=="multinuc" and parent.kind =="multinuc": # For multinucs, the anchor is in the middle between leftmost and rightmost of the multinuc children # (not including other rst children) lr = get_multinuc_children_lr(node.parent,current_doc,current_project,user) lr_wid = (lr[0] + lr[1]) /2 lr_ids = get_multinuc_children_lr_ids(node.parent,lr[0],lr[1],current_doc,current_project,user) left_child = lr_ids[0] right_child = lr_ids[1] if left_child == right_child: anchors[parent.id] = "0.5" else: if left_child in anchors and right_child in anchors: #both leftmost and rightmost multinuc children have been found len_left = nodes[left_child].right-nodes[left_child].left+1 len_right = nodes[right_child].right-nodes[right_child].left+1 anchors[parent.id] = str(((float(anchors[left_child]) * len_left*100 + float(anchors[right_child]) * len_right * 100 + (nodes[right_child].left - parent.left) * 100)/2)/parent_wid) else: anchors[parent.id] = str((lr_wid - parent.left+1) / (parent.right - parent.left+1)) else: if not parent.id in anchors: anchors[parent.id] = "0.5" # Place anchor element to center on proportional position relative to parent for key in nodes: node = nodes[key] pix_anchors[node.id] = str(int(3+node.left * 100 -100 - 39 + float(anchors[node.id])*((node.right- node.left+1) * 100 - 4))) # Check that span and multinuc buttons should be used (if the interface is not used for RST, they may be disabled) if int(get_schema()) > 2: use_span_buttons = True if get_setting("use_span_buttons") == "True" else False use_multinuc_buttons = True if get_setting("use_multinuc_buttons") == "True" else False else: use_span_buttons = True use_multinuc_buttons = True for key in nodes: node = nodes[key] if node.kind != "edu": g_wid = str(int((node.right- node.left+1) *100 -4 )) cpout += '<div id="lg'+ node.id +'" class="group" style="left: ' +str(int(node.left*100 - 100))+ '; width: ' + g_wid + '; top:'+ str(int(top_spacing + layer_spacing+node.depth*layer_spacing)) +'px; z-index:1"><div id="wsk'+node.id+'" class="whisker" style="width:'+g_wid+';"></div></div>' cpout += '<div id="g'+ node.id +'" class="num_cont" style="position: absolute; left:' + pix_anchors[node.id] +'px; top:'+ str(int(4+ top_spacing + layer_spacing+node.depth*layer_spacing))+'px; z-index:'+str(int(200-(node.right-node.left)))+'"><table class="btn_tb"><tr><td rowspan="2"><button id="unlink_'+ node.id+'" title="unlink this node" class="minibtn" onclick="act('+"'up:"+node.id+",0'"+');">X</button></td><td rowspan="2"><span class="num_id">'+str(int(node.left))+"-"+str(int(node.right))+'</span></td>' if use_span_buttons: cpout += '<td><button id="aspan_'+ node.id+'" title="add span above" class="minibtn" onclick="act('+"'sp:"+node.id+"'"+');">T</button></td>' cpout += '</tr>' if use_multinuc_buttons: cpout += '<tr><td><button id="amulti_'+ node.id+'" title="add multinuc above" class="minibtn" onclick="act('+"'mn:"+node.id+"'"+');">' + "Λ".decode("utf-8") + '</button></td></tr>' cpout += '</table></div><br/>' elif node.kind=="edu": cpout += '<div id="edu'+str(node.id)+'" class="edu" title="'+str(node.id)+'" style="left:'+str(int(node.id)*100 - 100) +'; top:'+str(int(top_spacing +layer_spacing+node.depth*layer_spacing))+'; width: 96px">' cpout += '<div id="wsk'+node.id+'" class="whisker" style="width:96px;"></div><div class="edu_num_cont"><table class="btn_tb"><tr><td rowspan="2"><button id="unlink_'+ node.id+'" title="unlink this node" class="minibtn" onclick="act('+"'up:"+node.id+",0'"+');">X</button></td><td rowspan="2"><span class="num_id"> '+str(int(node.left))+' </span></td>' if use_span_buttons: cpout += '<td><button id="aspan_'+ node.id+'" title="add span above" class="minibtn" onclick="act('+"'sp:"+node.id+"'"+');">T</button></td>' cpout += '</tr>' if use_multinuc_buttons: cpout += '<tr><td><button id="amulti_'+ node.id+'" title="add multinuc above" class="minibtn" onclick="act('+"'mn:"+node.id+"'"+');">' + "Λ".decode("utf-8") + '</button></td></tr>' cpout += '</table></div>'+node.text+'</div>' max_right = get_max_right(current_doc,current_project,user) # Serialize data in hidden input for JavaScript cpout += '<input id="data" name="data" type="hidden" ' hidden_val="" for key in nodes: node = nodes[key] if node.relname: safe_relname = node.relname else: safe_relname = "none" if node.kind =="edu": hidden_val += "n" + node.id +",n" +node.parent+",e,"+ str(int(node.left)) + "," + safe_relname + "," + get_rel_type(node.relname,current_doc,current_project) + ";" elif node.kind =="span": hidden_val += "n" + node.id +",n" +node.parent+",s,0," + safe_relname + "," + get_rel_type(node.relname,current_doc,current_project) + ";" else: hidden_val += "n"+node.id +",n" +node.parent+",m,0," + safe_relname + "," + get_rel_type(node.relname,current_doc,current_project) + ";" hidden_val = hidden_val[0:len(hidden_val)-1] cpout += 'value="' + hidden_val + '"/>' cpout += '<input id="def_multi_rel" type="hidden" value="' + get_def_rel("multinuc",current_doc,current_project) +'"/>' cpout += '<input id="def_rst_rel" type="hidden" value="' + get_def_rel("rst",current_doc,current_project) +'"/>' cpout += '<input id="undo_log" type="hidden" value=""/>' cpout += '<input id="redo_log" type="hidden" value=""/>' cpout += '<input id="undo_state" type="hidden" value=""/>' cpout += '<input id="logging" type="hidden" value=""/>' cpout += '<input id="use_span_buttons" type="hidden" value="'+str(use_span_buttons)+'"/>' cpout += '<input id="use_multinuc_buttons" type="hidden" value="'+str(use_multinuc_buttons)+'"/>' cpout += ''' <script src="./script/jquery.jsPlumb-1.7.5-min.js"></script> <script> ''' cpout += 'function select_my_rel(options,my_rel){' cpout += 'var multi_options = "' + multi_options +'";' cpout += 'var rst_options = "' + rst_options +'";' cpout += 'if (options =="multi"){options = multi_options;} else {options=rst_options;}' cpout += ' return options.replace("<option value='+"'" +'"' + '+my_rel+'+'"' +"'"+'","<option selected='+"'"+'selected'+"'"+' value='+"'" +'"'+ '+my_rel+'+'"' +"'"+'");' cpout += ' }\n' cpout += '''function make_relchooser(id,option_type,rel){ return $("<select class='rst_rel' id='sel"+id.replace("n","")+"' onchange='crel(" + id.replace("n","") + ",this.options[this.selectedIndex].value);'>" + select_my_rel(option_type,rel) + "</select>"); }''' cpout += ''' jsPlumb.importDefaults({ PaintStyle : { lineWidth:2, strokeStyle: 'rgba(0,0,0,0.5)' }, Endpoints : [ [ "Dot", { radius:1 } ], [ "Dot", { radius:1 } ] ], EndpointStyles : [{ fillStyle:"#000000" }, { fillStyle:"#000000" }], Anchor:"Top", Connector : [ "Bezier", { curviness:50 } ] }) jsPlumb.ready(function() { jsPlumb.setContainer(document.getElementById("inner_canvas")); ''' cpout += "jsPlumb.setSuspendDrawing(true);" for key in nodes: node = nodes[key] if node.kind == "edu": node_id_str = "edu" + node.id else: node_id_str = "g" + node.id cpout += 'jsPlumb.makeSource("'+node_id_str+'", {anchor: "Top", filter: ".num_id", allowLoopback:false});' cpout += 'jsPlumb.makeTarget("'+node_id_str+'", {anchor: "Top", filter: ".num_id", allowLoopback:false});' # Connect nodes for key in nodes: node = nodes[key] if node.parent!="0": parent = nodes[node.parent] if node.kind == "edu": node_id_str = "edu" + node.id else: node_id_str = "g" + node.id if parent.kind == "edu": parent_id_str = "edu" + parent.id else: parent_id_str = "g" + parent.id if node.relname == "span": cpout += 'jsPlumb.connect({source:"'+node_id_str+'",target:"'+parent_id_str+ '", connector:"Straight", anchors: ["Top","Bottom"]});' elif parent.kind == "multinuc" and node.relkind=="multinuc": cpout += 'jsPlumb.connect({source:"'+node_id_str+'",target:"'+parent_id_str+ '", connector:"Straight", anchors: ["Top","Bottom"], overlays: [ ["Custom", {create:function(component) {return make_relchooser("'+node.id+'","multi","'+node.relname+'");},location:0.2,id:"customOverlay"}]]});' else: cpout += 'jsPlumb.connect({source:"'+node_id_str+'",target:"'+parent_id_str+'", overlays: [ ["Arrow" , { width:12, length:12, location:0.95 }],["Custom", {create:function(component) {return make_relchooser("'+node.id+'","rst","'+node.relname+'");},location:0.1,id:"customOverlay"}]]});' cpout += ''' jsPlumb.setSuspendDrawing(false,true); jsPlumb.bind("connection", function(info) { source = info.sourceId.replace(/edu|g/,"") target = info.targetId.replace(/edu|g/g,"") }); jsPlumb.bind("beforeDrop", function(info) { $(".minibtn").prop("disabled",true); ''' cpout += ''' var node_id = "n"+info.sourceId.replace(/edu|g|lg/,""); var new_parent_id = "n"+info.targetId.replace(/edu|g|lg/,""); nodes = parse_data(); new_parent = nodes[new_parent_id]; relname = nodes[node_id].relname; new_parent_kind = new_parent.kind; if (nodes[node_id].parent != "n0"){ old_parent_kind = nodes[nodes[node_id].parent].kind; } else { old_parent_kind ="none"; } if (info.sourceId != info.targetId){ if (!(is_ancestor(new_parent_id,node_id))){ jsPlumb.select({source:info.sourceId}).detach(); if (new_parent_kind == "multinuc"){ relname = get_multirel(new_parent_id,node_id,nodes); jsPlumb.connect({source:info.sourceId, target:info.targetId, connector:"Straight", anchors: ["Top","Bottom"], overlays: [ ["Custom", {create:function(component) {return make_relchooser(node_id,"multi",relname);},location:0.2,id:"customOverlay"}]]}); } else{ jsPlumb.connect({source:info.sourceId, target:info.targetId, overlays: [ ["Arrow" , { width:12, length:12, location:0.95 }],["Custom", {create:function(component) {return make_relchooser(node_id,"rst",relname);},location:0.1,id:"customOverlay"}]]}); } new_rel = document.getElementById("sel"+ node_id.replace("n","")).value; act('up:' + node_id.replace("n","") + ',' + new_parent_id.replace("n","")); update_rel(node_id,new_rel,nodes); recalculate_depth(parse_data()); } } $(".minibtn").prop("disabled",false); }); }); </script> </div> </div> <div id="anim_catch" class="anim_catch"> </div> </body> </html> ''' if mode != "server": cpout = cpout.replace(".py","") return cpout