This commit is contained in:
darthsandmann
2016-10-16 21:53:15 +02:00
parent 0d10f8b9dc
commit c9f3117da1
412 changed files with 137942 additions and 0 deletions

View File

@ -0,0 +1,3 @@
# coding: utf-8
# hier können Paket-Initialisierungen eingetragen werden

View File

@ -0,0 +1,25 @@
# coding: utf-8
import cherrypy
#----------------------------------------------------------
class Application_cl(object):
#----------------------------------------------------------
exposed = True # gilt für alle Methoden
#-------------------------------------------------------
def __init__(self):
#-------------------------------------------------------
pass
#-------------------------------------------------------
def default(self, *arguments, **kwargs):
#-------------------------------------------------------
msg_s = "unbekannte Anforderung: " + \
str(arguments) + \
' ' + \
str(kwargs)
raise cherrypy.HTTPError(404, msg_s)
# EOF

View File

@ -0,0 +1,340 @@
# coding: utf-8
import os
import os.path
import codecs
import json
import time
#----------------------------------------------------------
class Database_cl(object):
#----------------------------------------------------------
# alle Elemente werden zur Laufzeit des Servers zur Vereinfachung auch im
# Hauptspeicher abgelegt
# die nächste zu vergebende Id wird ebenfalls dauerhaft gespeichert
# zur Vereinfachung wird hier fest vorgegebenen, dass sich die Daten
# im Unterverzeichnis "data" befinden
#-------------------------------------------------------
def __init__(self):
#-------------------------------------------------------
self.data_o = {}
self.readData_p()
#-------------------------------------------------------
def createTopic_px(self, data_opl):
#-------------------------------------------------------
# Überprüfung der Daten müsste ergänzt werden!
id_ = self.nextTopicId_p()
self.data_o['topics'][str(id_)] = data_opl
file_o = codecs.open(os.path.join('data', 'topics.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['topics']))
file_o.close()
return id_
#-------------------------------------------------------
def createDiscussion_px(self, tId, data_opl):
#-------------------------------------------------------
# Überprüfung der Daten müsste ergänzt werden!
id_ = self.nextDiscussionId_p()
self.data_o['discussions'][id_] = data_opl
self.data_o['topics'][tId]['discs'].append(id_)
file_o = codecs.open(os.path.join('data', 'discussions.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['discussions']))
file_o.close()
file_o = codecs.open(os.path.join('data', 'topics.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['topics']))
file_o.close()
return id_
#-------------------------------------------------------
def createPost_px(self, dId, data_opl):
#-------------------------------------------------------
# Überprüfung der Daten müsste ergänzt werden!
id_ = self.nextPostId_p()
self.data_o['posts'][id_] = data_opl
self.data_o['discussions'][dId]['posts'].append(id_)
file_o = codecs.open(os.path.join('data', 'posts.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['posts']))
file_o.close()
file_o = codecs.open(os.path.join('data', 'discussions.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['discussions']))
file_o.close()
return id_
#-------------------------------------------------------
def readTopic_px(self, id_ = None):
#-------------------------------------------------------
# hier zur Vereinfachung:
# Aufruf ohne id: alle Einträge liefern
data_o = None
if id_ == None:
data_o = self.data_o['topics']
elif str(id_) == '0':
data_o = self.getDefaultTopic_px()
else:
if str(id_) in self.data_o['topics']:
data_o = self.data_o['topics'][str(id_)]
return data_o
#-------------------------------------------------------
def readDiscussion_px(self, tId, dId = None):
#-------------------------------------------------------
# hier zur Vereinfachung:
# Aufruf ohne id: alle Einträge liefern
data_o = None
if dId == None:
data_o = {}
discs = self.data_o['discussions']
indices = self.data_o['topics'][str(tId)]['discs']
for index in indices:
data_o[index] = discs[index]
elif str(dId) == '0':
data_o = self.getDefaultDiscussion_px()
else:
if str(dId) in self.data_o['discussions']:
data_o = self.data_o['discussions'][str(dId)]
return data_o
#-------------------------------------------------------
def readPost_px(self, dId, pId = None):
#-------------------------------------------------------
# hier zur Vereinfachung:
# Aufruf ohne id: alle Einträge liefern
data_o = None
if pId == None:
data_o = {}
posts = self.data_o['posts']
indices = self.data_o['discussions'][str(dId)]['posts']
for index in indices:
data_o[index] = posts[index]
elif str(pId) == '0':
data_o = self.getDefaultPost_px()
else:
if str(pId) in self.data_o['posts']:
data_o = self.data_o['posts'][str(pId)]
return data_o
#-------------------------------------------------------
def updateTopic_px(self, id_, data_opl):
#-------------------------------------------------------
# Überprüfung der Daten müsste ergänzt werden!
updated = False
if id_ in self.data_o['topics']:
for key in data_opl:
self.data_o['topics'][id_][key] = data_opl[key]
# Datei aktualisieren
file_o = codecs.open(os.path.join('data', 'topics.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['topics']))
file_o.close()
updated = True
return updated
#-------------------------------------------------------
def updateDiscussion_px(self, id_, data_opl):
#-------------------------------------------------------
# Überprüfung der Daten müsste ergänzt werden!
updated = False
if id_ in self.data_o['discussions']:
for key in data_opl:
self.data_o['discussions'][id_][key] = data_opl[key]
# Datei aktualisieren
file_o = codecs.open(os.path.join('data', 'discussions.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['discussions']))
file_o.close()
updated = True
return updated
#-------------------------------------------------------
def updatePost_px(self, id_, data_opl):
#-------------------------------------------------------
# Überprüfung der Daten müsste ergänzt werden!
updated = False
if id_ in self.data_o['posts']:
for key in data_opl:
self.data_o['posts'][id_][key] = data_opl[key]
# Datei aktualisieren
file_o = codecs.open(os.path.join('data', 'posts.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['posts']))
file_o.close()
updated = True
return updated
#-------------------------------------------------------
def deleteTopic_px(self, id_):
#-------------------------------------------------------
deleted = False
if id_ in self.data_o['topics']:
self.data_o['topics'][id_]['deleted'] = True
for key in self.data_o['topics'][id_]['discs']:
del self.data_o['discussions'][key]
self.data_o['topics'][id_]['discs'] = []
# Dateien aktualisieren
file_o = codecs.open(os.path.join('data', 'topics.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['topics']))
file_o.close()
file_o = codecs.open(os.path.join('data', 'discussions.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['discussions']))
file_o.close()
deleted = True
return deleted
#-------------------------------------------------------
def deleteDiscussion_px(self, id_):
#-------------------------------------------------------
deleted = False
if id_ in self.data_o['discussions']:
self.data_o['discussions'][id_]['deleted'] = True
for key in self.data_o['discussions'][id_]['posts']:
del self.data_o['posts'][key]
self.data_o['discussions'][id_]['posts'] = []
# Dateien aktualisieren
file_o = codecs.open(os.path.join('data', 'discussions.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['discussions']))
file_o.close()
file_o = codecs.open(os.path.join('data', 'posts.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['posts']))
file_o.close()
deleted = True
return deleted
#-------------------------------------------------------
def deletePost_px(self, id_):
#-------------------------------------------------------
deleted = False
if id_ in self.data_o['posts']:
self.data_o['posts'][id_]['deleted'] = True
# Datei aktualisieren
file_o = codecs.open(os.path.join('data', 'posts.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['posts']))
file_o.close()
deleted = True
return deleted
#-------------------------------------------------------
def getDefaultTopic_px(self):
#-------------------------------------------------------
return {
'name' : '',
'owner': '',
'deleted': False,
'discs': []
}
#-------------------------------------------------------
def getDefaultDiscussion_px(self):
#-------------------------------------------------------
return {
'name' : '',
'owner': '',
'deleted': False,
'posts': []
}
#-------------------------------------------------------
def getDefaultPost_px(self):
#-------------------------------------------------------
return {
'title' : '',
'text' : '',
'owner': '',
'time': time.localtime(),
'deleted': False,
}
#-------------------------------------------------------
def getUsers_px(self):
#-------------------------------------------------------
return self.data_o['users']
#-------------------------------------------------------
def saveUsers_px(self, data_o):
#-------------------------------------------------------
file_o = codecs.open(os.path.join('data', 'users.dat'), 'w', 'utf-8')
file_o.write(json.dumps(self.data_o['users']))
file_o.close()
#-------------------------------------------------------
def readData_p(self):
#-------------------------------------------------------
files_a = os.listdir('data')
for fileName_s in files_a:
if fileName_s.endswith('.dat'):
file_o = codecs.open(os.path.join('data', fileName_s), 'rU', 'utf-8')
content_s = file_o.read()
file_o.close()
id_s = fileName_s[:-4]
self.data_o[id_s] = json.loads(content_s)
#-------------------------------------------------------
def nextTopicId_p(self):
#-------------------------------------------------------
id_ = str(self.data_o['topics']['nextID'])
nextID = int(self.data_o['topics']['nextID'])
self.data_o['topics']['nextID'] = str(nextID + 1)
return id_
#-------------------------------------------------------
def nextDiscussionId_p(self):
#-------------------------------------------------------
id_ = str(self.data_o['discussions']['nextID'])
nextID = int(self.data_o['discussions']['nextID'])
self.data_o['discussions']['nextID'] = str(nextID + 1)
return id_
#-------------------------------------------------------
def nextPostId_p(self):
#-------------------------------------------------------
id_ = str(self.data_o['posts']['nextID'])
nextID = int(self.data_o['posts']['nextID'])
self.data_o['posts']['nextID'] = str(nextID + 1)
return id_
db_o = Database_cl()
# EOF

View File

@ -0,0 +1,120 @@
# coding: utf-8
import json
import cherrypy
from app import database
# Method-Dispatching!
# Übersicht Anforderungen / Methoden
# (beachte: / relativ zu /discussion, siehe Konfiguration Server!)
"""
Anforderung GET PUT POST DELETE
-----------------------------------------------------------------------------------------------------------------------------------------
/diskussion/{thema-id} alle disk. neue Diskussion - -
zum Thema zum Thema speichern
ausgeben diskussion-id zurück
/diskussion/{diskussion-id} eine - Diskussion mit ID Diskussion mit ID
Diskussion ändern löschen
anfordern
"""
#----------------------------------------------------------
class Discussion_cl(object):
#----------------------------------------------------------
exposed = True # gilt für alle Methoden
#-------------------------------------------------------
def __init__(self):
#-------------------------------------------------------
# spezielle Initialisierung können hier eingetragen werden
self.db_o = database.db_o
#-------------------------------------------------------
def GET(self, tId, dId = None):
#-------------------------------------------------------
retVal_o = {'data': None, 'id': None, 'tId': tId}
if dId == None:
# Anforderung der Liste
retVal_o['data'] = self.getList_p(tId)
else:
# Anforderung eines Dokuments
retVal_o['data'] = self.getDiscussion(tId, dId)
retVal_o['id'] = dId
if retVal_o['data'] == None:
cherrypy.response.status = 404
return json.dumps(retVal_o)
#-------------------------------------------------------
def POST(self, tId, id_, **data_opl):
#-------------------------------------------------------
# Sichern der Daten: jetzt wird keine vollständige Seite
# zurückgeliefert, sondern nur noch die Information, ob das
# Speichern erfolgreich war
retVal_o = {
'id': None
}
# data_opl: Dictionary mit den gelieferten key-value-Paaren
# hier müsste man prüfen, ob die Daten korrekt vorliegen!
id_ = str(id_)
data_o = self.db_o.data_o['discussions'][id_]
data_o['name'] = data_opl['name']
# Update-Operation
retVal_o['id'] = id_
if self.db_o.updateDiscussion_px(id_, data_o):
pass
else:
cherrypy.response.status = 404
return json.dumps(retVal_o)
#-------------------------------------------------------
def DELETE(self, id_):
#-------------------------------------------------------
# Eintrag löschen, nur noch Rückmeldung liefern
retVal_o = {
'id': id_
}
if self.db_o.deleteDiscussion_px(id_):
pass
else:
cherrypy.response.status = 404
return json.dumps(retVal_o)
#-------------------------------------------------------
def getList_p(self, tId):
#-------------------------------------------------------
data_o = self.db_o.readDiscussion_px(tId)
return data_o
getList_p.exposed = False;
#-------------------------------------------------------
def getDiscussion(self, tId, dId):
#-------------------------------------------------------
data_o = self.db_o.readDiscussion_px(tId, dId)
return data_o
getDiscussion.exposed = False;
# EOF

View File

@ -0,0 +1,54 @@
# coding: utf-8
import json
import os
import codecs
import cherrypy
from app import database
# Method-Dispatching!
# (beachte: / relativ zu /login, siehe Konfiguration Server!)
#----------------------------------------------------------
class Login_cl(object):
#----------------------------------------------------------
exposed = True # gilt für alle Methoden
#-------------------------------------------------------
def __init__(self):
#-------------------------------------------------------
self.db_o = database.Database_cl()
#**********************REST ANSATZ**********************
#-------------------------------------------------------
def GET(self, **data_o):
#-------------------------------------------------------
authenticated = False
users = self.db_o.getUsers_px();
if data_o['username'] in users and data_o['password'] == users[data_o['username']]:
authenticated = True
return json.dumps(authenticated)
#-------------------------------------------------------
def PUT(self, **data_o):
#-------------------------------------------------------
success = False
users = self.db_o.getUsers_px();
if data_o['username'] not in users:
users[data_o['username']] = data_o['password']
self.db_o.saveUsers_px(users);
success = True
return json.dumps(success)
#*******************************************************
# EOF

View File

@ -0,0 +1,46 @@
# coding: utf-8
import json
import os
import codecs
import cherrypy
import time
from app import database
#----------------------------------------------------------
class Time_cl(object):
#----------------------------------------------------------
exposed=True
#----------------------------------------------------------
def __init__(self):
#----------------------------------------------------------
t = time.localtime()
self.date_o = {
"day":"",
"month":"",
"year":"",
"hour":"",
"minute":""
}
self.date_o['day'] = t[2]
self.date_o['month']=t[1]
self.date_o['year']=t[0]
self.date_o['hour']=t[3]
self.date_o['minute']=t[4]
#----------------------------------------------------------
def getDateAsString_px(self):
#----------------------------------------------------------
return str(self.date_o['hour'])+':'+str(self.date_o['minute'])+' | '+str(self.date_o['day'])+'.'+str(self.date_o['month'])+'.'+str(self.date_o['year'] )
#----------------------------------------------------------
def getDate_px(self):
#----------------------------------------------------------
return self.date_o
# EOF

View File

@ -0,0 +1,150 @@
# coding: utf-8
import json
import datetime
import time
import cherrypy
from app import database
# Method-Dispatching!
# Übersicht Anforderungen / Methoden
# (beachte: / relativ zu /discussion, siehe Konfiguration Server!)
"""
Anforderung GET PUT POST DELETE
----------------------------------------------------------------
/beitrag/{diskussion-id} alle Beiträge neuen Beitrag - -
zur Diskussion zur Diskussion speichern
anfordern beitrag-id zurück
/beitrag/{beitrag-id} einen Beitrag - Beitrag mit ID Beitrag mit ID
anfordern ändern löschen
"""
#----------------------------------------------------------
class Post_cl(object):
#----------------------------------------------------------
exposed = True # gilt für alle Methoden
#-------------------------------------------------------
def __init__(self):
#-------------------------------------------------------
# spezielle Initialisierung können hier eingetragen werden
self.db_o = database.db_o
#-------------------------------------------------------
def GET(self, tId, dId, pId = None):
#-------------------------------------------------------
retVal_o = {'data': None, 'id': pId, 'tId':tId, 'dId': dId}
if dId == None:
# Anforderung der Liste
retVal_o['data'] = self.getList_p(dId)
else:
# Anforderung eines Dokuments
retVal_o['data'] = self.getPost(dId, pId)
if retVal_o['data'] == None:
cherrypy.response.status = 404
return json.dumps(retVal_o)
#-------------------------------------------------------
def PUT(self, tId, dId, **data_opl):
#-------------------------------------------------------
retVal_o = {
'id': None,
'dId': dId
}
# data_opl: Dictionary mit den gelieferten key-value-Paaren
if dId == 0 or dId == "0":
discData_o = self.db_o.getDefaultDiscussion_px()
discData_o['name'] = data_opl['title']
discData_o['owner'] = data_opl['owner']
dId = self.db_o.createDiscussion_px(tId, discData_o)
retVal_o['dId'] = dId
# hier müsste man prüfen, ob die Daten korrekt vorliegen!
data_o = self.db_o.getDefaultPost_px()
data_o['title'] = data_opl['title']
data_o['text'] = data_opl['text']
data_o['owner'] = data_opl['owner']
# Create-Operation
id_s = self.db_o.createPost_px(dId, data_o)
retVal_o['id'] = id_s
if retVal_o['id'] == None:
cherrypy.response.status = 409
return json.dumps(retVal_o)
#-------------------------------------------------------
def POST(self, tId, dId, id_, **data_opl):
#-------------------------------------------------------
# Sichern der Daten: jetzt wird keine vollständige Seite
# zurückgeliefert, sondern nur noch die Information, ob das
# Speichern erfolgreich war
retVal_o = {
'id': None,
'tId': tId,
'dId': dId
}
# data_opl: Dictionary mit den gelieferten key-value-Paaren
# hier müsste man prüfen, ob die Daten korrekt vorliegen!
id_ = str(id_)
data_o = self.db_o.data_o['posts'][id_]
data_o['text'] = data_opl['text']
data_o['title'] = data_opl['title']
# Update-Operation
retVal_o['id'] = id_
if self.db_o.updatePost_px(id_, data_o):
pass
else:
cherrypy.response.status = 404
return json.dumps(retVal_o)
#-------------------------------------------------------
def DELETE(self, id_):
#-------------------------------------------------------
# Eintrag löschen, nur noch Rückmeldung liefern
retVal_o = {
'id': id_
}
if self.db_o.deletePost_px(id_):
pass
else:
cherrypy.response.status = 404
return json.dumps(retVal_o)
#-------------------------------------------------------
def getList_p(self, tId):
#-------------------------------------------------------
data_o = self.db_o.readPost_px(tId)
return data_o
getList_p.exposed = False;
#-------------------------------------------------------
def getPost(self, tId, dId):
#-------------------------------------------------------
data_o = self.db_o.readPost_px(tId, dId)
return data_o
getPost.exposed = False;
# EOF

View File

@ -0,0 +1,52 @@
# coding: utf-8
import json
import os
import codecs
import cherrypy
# Method-Dispatching!
# Übersicht Anforderungen / Methoden
# (beachte: / relativ zu /template, siehe Konfiguration Server!)
"""
Anforderung GET PUT POST DELETE
----------------------------------------------------------------
/ Alle - - -
Templates
liefern
"""
#----------------------------------------------------------
class Template_cl(object):
#----------------------------------------------------------
exposed = True # gilt für alle Methoden
#-------------------------------------------------------
def __init__(self):
#-------------------------------------------------------
pass
#-------------------------------------------------------
def GET(self):
#-------------------------------------------------------
retVal_o = {
'templates': {}
}
files_a = os.listdir('templates')
for fileName_s in files_a:
file_o = codecs.open(os.path.join('templates', fileName_s), 'rU', 'utf-8')
content_s = file_o.read()
file_o.close()
retVal_o["templates"][fileName_s] = content_s
return json.dumps(retVal_o)
# EOF

View File

@ -0,0 +1,149 @@
# coding: utf-8
import json
import cherrypy
from app import database
# Method-Dispatching!
# Übersicht Anforderungen / Methoden
# (beachte: / relativ zu /topic, siehe Konfiguration Server!)
"""
Anforderung GET PUT POST DELETE
----------------------------------------------------------------
/ Liste - - -
Themen
liefern
/0 Thema Thema - -
mit id=0 anlegen
liefern
(Vorgabe-Werte)
/{id} Thema - Thema Thema
mit {id} ändern löschen
liefern
id > 0 !
"""
#----------------------------------------------------------
class Topic_cl(object):
#----------------------------------------------------------
exposed = True # gilt für alle Methoden
#-------------------------------------------------------
def __init__(self):
#-------------------------------------------------------
# spezielle Initialisierung können hier eingetragen werden
self.db_o = database.db_o
#-------------------------------------------------------
def GET(self, id_ = None):
#-------------------------------------------------------
retVal_o = {'data': None, 'id': None}
if id == None:
# Anforderung der Liste
retVal_o['data'] = self.getList_p()
else:
# Anforderung eines Dokuments
retVal_o['data'] = self.getTopic(id_)
retVal_o['id'] = id_
if retVal_o['data'] == None:
cherrypy.response.status = 404
return json.dumps(retVal_o)
#-------------------------------------------------------
def PUT(self, **data_opl):
#-------------------------------------------------------
retVal_o = {
'id': None
}
# data_opl: Dictionary mit den gelieferten key-value-Paaren
# hier müsste man prüfen, ob die Daten korrekt vorliegen!
print(data_opl)
data_o = {
'name' : data_opl['name'],
'owner': data_opl['owner'],
'deleted': False,
'discs': []
}
# Create-Operation
id_s = self.db_o.createTopic_px(data_o)
retVal_o['id'] = id_s
if retVal_o['id'] == None:
cherrypy.response.status = 409
return json.dumps(retVal_o)
#-------------------------------------------------------
def POST(self, id_, **data_opl):
#-------------------------------------------------------
# Sichern der Daten: jetzt wird keine vollständige Seite
# zurückgeliefert, sondern nur noch die Information, ob das
# Speichern erfolgreich war
retVal_o = {
'id': None
}
# data_opl: Dictionary mit den gelieferten key-value-Paaren
# hier müsste man prüfen, ob die Daten korrekt vorliegen!
id_ = str(id_)
data_o = self.db_o.data_o['topics'][id_]
data_o['name'] = data_opl['name']
# Update-Operation
retVal_o['id'] = id_
if self.db_o.updateTopic_px(id_, data_o):
pass
else:
cherrypy.response.status = 404
return json.dumps(retVal_o)
#-------------------------------------------------------
def DELETE(self, id_):
#-------------------------------------------------------
# Eintrag löschen, nur noch Rückmeldung liefern
retVal_o = {
'id': id_
}
if self.db_o.deleteTopic_px(id_):
pass
else:
cherrypy.response.status = 404
return json.dumps(retVal_o)
#-------------------------------------------------------
def getList_p(self):
#-------------------------------------------------------
data_o = self.db_o.readTopic_px()
return data_o
getList_p.exposed = False;
#-------------------------------------------------------
def getTopic(self, id_spl):
#-------------------------------------------------------
data_o = self.db_o.readTopic_px(id_spl)
return data_o
getTopic.exposed = False;
# EOF

View File

@ -0,0 +1,287 @@
/* main.css */
/* allgemeine Vorgaben */
body {
font-family: "Open Sans", sans-serif;
font-size: 12pt;
padding: 0;
margin: 0;
}
/* Basislayout */
.clSiteHeader {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 80px;
line-height: 80px;
margin: 0;
padding: 5px;
font-size: 12pt;
color: #efc9a0;
background-color: #40200d;
text-align: center;
}
.clLoggedInText {
position: absolute;
right: 50px;
height: 80px;
line-height: 48px;
margin: 0;
padding: 5px;
font-size: 12pt;
color: #efc9a0;
background-color: #40200d;
text-align: right;
font-weight: bold;
}
.clNav {
position: absolute;
left: 0;
width: 150px;
margin: 0;
padding: 10px;
}
.clNav a, a:hover, a:visited, a:active{
display: block;
text-decoration: none;
margin-bottom: 10px;
}
.clNav a:hover {
font-weight: bold;
}
.clContentOuter {
position: absolute;
top: 80px;
left: 0;
right: 0;
bottom: 0;
margin: 0;
padding: 0;
}
.clContent {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0;
padding: 5px;
background-color: #efc9a0;
}
/* Elemente im Content-Bereich */
.clContentHeader {
position: absolute;
top: 20px;
left: 0;
right: 0;
height: 30px;
line-height: 30px;
margin: 0;
padding: 5px;
font-size: 14pt;
text-align: center;
}
.clContentArea {
position: absolute;
top: 60px;
left: 0;
right: 0;
bottom: 40px;
margin: 0;
padding: 0px;
overflow-y: auto;
}
.clSelected{
color: white;
font-weight: bold;
}
.clButtonArea {
position: fixed;
left: 0;
right: 0;
bottom: 0;
height: 50px;
line-height: 50px;
margin: 0;
padding: 5px;
text-align: center;
background-color: #40200d;
}
/* Links und Submit-Schalter im Buttonbereich gestalten */
.clButtonArea button, a, input[type="submit"] {
margin: 0 5px;
padding: 3px 6px;
font-size: 10pt;
text-decoration: none;
border: 1px solid;
color: black;
background-color: #efc9a0;
}
.clButtonArea button:disabled {
color: graytext;
}
.clButtonArea button:enabled {
font-weight: bold;
}
/* Gestaltung von Tabellen */
#idTopicList, #idDiscussionList {
table-layout: fixed;
width: 66%;
border: 1px solid;
border-collapse: collapse;
margin:auto;
}
#idTopicList th, #idDiscussionList th {
text-align: left;
padding-left: 6px;
}
#idTopicList th, #idTopicList td, #idDiscussionList th, #idDiscussionList td{
padding: 3px;
border: 1px solid #000;
}
#idPostList {
width: 80%;
border: 1px solid;
border-collapse: collapse;
margin:auto;
padding: 0px;
}
.listheader th{
color:#efc9a0;
background-color:#40200d;
}
#idTopicList tr, #idDiscussionList tr{
background-color:#F4C484
}
#idTopicList tr:nth-of-type(even), #idDiscussionList tr:nth-of-type(even){
background-color:#E4A454;
}
/* Gestaltung von Formularen */
/* das Formular nochmals zusätzlich gestalten */
#idForm .clContentArea {
width: 500px;
margin-left: auto;
margin-right: auto;
}
.clFormRow {
position: relative; /* damit das Element in jedem Fall "positioniert" ist und damit als Bezugspunkt geeignet ist */
height: 25px;
margin-bottom: 10px;
}
.clFormRow label {
position: absolute;
top: 0;
left: 0;
width: 150px;
text-align: right;
}
.clFormRow input, .clFormRow textarea {
position: absolute;
top: 0;
left: 160px;
width: 250px;
margin: auto;
}
/********************************************************************STYLES für einzelne Posts der Postliste*/
#idPostList li {
border: 1px solid;
border-collapse: collapse;
padding: 0px;
margin: 0px;
list-style-type: none;
}
.clMetaDataContainer {
width: 15%;
height: 100%;
margin: 0px;
left: 0;
right: 80%;
color: #efc9a0;
background-color: #40200D;
}
.clPostContainer{
position: relative;
}
.clPostDataContainer{
position: absolute;
width: 85%;
left: 15%;
height: 100%;
top: 0;
}
#idPostList .clOwner {
margin: 0px;
}
#idPostList .clTime {
margin: 0px;
}
#idPostList .clTitle {
margin: 0;
position: relative;
width: 100%;
text-align: center;
color: #efc9a0;
background-color: #40200d;
}
#idPostList .clText {
margin: 0;
margin-left: 3px;
bottom: 0;
position: relative;
width: 100%;
}
/* EOF */

View File

@ -0,0 +1 @@
{"8": {"name": "Hallo", "deleted": false, "posts": ["10"], "owner": "Florian"}, "nextID": "11", "10": {"name": "test 2", "deleted": false, "posts": ["13", "14"], "owner": ""}, "9": {"name": "TestDiskussion", "deleted": false, "posts": ["11", "12"], "owner": "Florian"}}

View File

@ -0,0 +1 @@
{"13": {"deleted": false, "text": "", "time": [2016, 4, 26, 16, 37, 48, 1, 117, 1], "title": "test 2", "owner": ""}, "8": {"deleted": false, "text": "Hallo zusammen! Meiner Meinung nach ist das v\u00f6lliger Schwachsinn. Wie seht ihr das?", "title": "Ist ein Single-Page-Ansatz sinnvoll f\u00fcr ein Forum", "time": [2016, 1, 14, 14, 14, 39, 3, 14, 0], "owner": "Florian"}, "4": {"deleted": false, "text": "asdasdasdasd", "title": "asdasd", "time": [2016, 1, 14, 14, 10, 57, 3, 14, 0], "owner": "Test"}, "7": {"deleted": false, "text": "aaa", "title": "Hallo Freunde", "time": [2016, 1, 14, 14, 11, 30, 3, 14, 0], "owner": "Test"}, "6": {"deleted": false, "text": "asdasdAasdas", "title": "12123", "time": [2016, 1, 14, 14, 11, 21, 3, 14, 0], "owner": "Test"}, "9": {"deleted": false, "text": "Ich sehe das \u00e4hnlich, au\u00dferdem ist Javascript h\u00e4sslich", "title": "Re: Ist ein Single-Page-Ansatz sinnvoll f\u00fcr ein Forum", "time": [2016, 1, 14, 14, 16, 4, 3, 14, 0], "owner": "Kai"}, "14": {"deleted": false, "text": "test", "time": [2016, 4, 26, 16, 37, 59, 1, 117, 1], "title": "test3", "owner": ""}, "11": {"deleted": false, "text": "Test", "title": "TestBeitrag", "time": [2016, 1, 14, 15, 24, 38, 3, 14, 0], "owner": "Florian"}, "nextID": "15", "10": {"deleted": false, "text": "Freunde", "title": "Hallo", "time": [2016, 1, 14, 15, 19, 17, 3, 14, 0], "owner": "Florian"}, "12": {"deleted": true, "text": "noch ei test", "title": "Beitrag2", "time": [2016, 1, 14, 15, 25, 35, 3, 14, 0], "owner": "Florian"}}

View File

@ -0,0 +1 @@
{"1": {"name": "Ich bin das erste Test-Thema", "discs": [], "deleted": true, "owner": "Test"}, "5": {"name": "test", "deleted": false, "discs": [], "owner": "test"}, "4": {"name": "TestThema", "discs": ["9"], "deleted": false, "owner": "Florian"}, "2": {"name": "Und ich das zweite!", "discs": ["8", "10"], "deleted": false, "owner": "Test"}, "nextID": "6", "3": {"name": "Web-Engineering", "discs": [], "deleted": true, "owner": "Florian"}}

View File

@ -0,0 +1 @@
{"Kai": "fucku", "Test": "test", "test": "test", "Florian": "123", "": ""}

View File

@ -0,0 +1,129 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="generator" content="pandoc">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title></title>
<style type="text/css">code{white-space: pre;}</style>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<h1 id="dokumentation-praktikum-3">Dokumentation Praktikum 3</h1>
<h2 id="einleitende-daten">Einleitende Daten</h2>
<p>Gruppe: D Team: Florian Heinze Kai Rese</p>
<p>Gültigkeitsdatum:<br />
13.01.16</p>
<h2 id="allgemeine-beschreibung-der-lösung">1.Allgemeine Beschreibung der Lösung</h2>
<p>Die Aufgabe wird als Single-Page-Applikation umgesetzt. Serverseitig werden die Daten hierbei in einzelnen .dat Dateien gespeichert, deren Inhalt nach Json-Standard eingetragen ist.<br />
Clientseitig werden html, CSS, Javascript, die Javascript-Bibliothek Jquery und Module vom Professor verwendet.</p>
<p>Es können Themen mit einem Titel erstellt, bearbeitet und gelöscht werden.<br />
Zu den Themen können Diskussionen erstellt, bearbeitet und gelöscht werden.<br />
Zu den Diskussionen können Beiträge erstellt, bearbeitet und gelöscht werden.</p>
<p>Daten können jedoch nur erstellt, bearbeitet oder gelöscht werden, wenn man sich eingeloggt hat. Weiterhin kann man nur seine eigenen Daten bearbeiten/löschen.</p>
<h2 id="implementierung-des-serverseitig">2.Implementierung des Serverseitig</h2>
<h3 id="rest-interface">2.1 REST-Interface</h3>
<p>Folgende URL werden mittels REST-Prinzip entgegengenommen:<br />
/topic /discussion<br />
/post<br />
/login</p>
<h3 id="module">2.2 Module</h3>
<h4 id="server.py">server.py:</h4>
<p>Startet und konfiguriert den Server und das REST-Interface</p>
<h4 id="topic.py">topic.py:</h4>
<p>Dieses Modul soll alle Anfragen annehmen, die sich direkt auf Themen beziehen.</p>
<p>Enthält die Klasse: &quot;Topic_cl&quot;.</p>
<p>Topic_cl enthält folgende Methoden:<br />
GET: Wenn keine ID übergeben wird, wird die Themenliste zurückgegeben, anderenfalls wird das Thema zurückgegeben. Bei der ID &quot;0&quot; wird ein &quot;Default-Topic&quot; zurückgegeben.</p>
<p>PUT: Erzeugt aus den übergegeben Werten ein Thema.</p>
<p>POST: Editiert das Thema mit der übergebenen ID mit den übergebenen Werten.</p>
<p>DELETE: Das Thema mit der übergebenen ID wird gelöscht.</p>
<h4 id="discussion.py">discussion.py:</h4>
<p>Dieses Modul soll alle Anfragen annehmen, die sich direkt auf Diskussionen beziehen.</p>
<p>Enthält die Klasse: &quot;Discussion_cl&quot;.</p>
<p>Diskussion_cl enthält folgende Methoden:<br />
GET: Wenn keine Diskussions-ID übergeben wird, wird die Diskussionsliste der übergebenen Themen-ID zurückgegeben, anderenfalls wird die Diskussion zurückgegeben. Bei der ID &quot;0&quot; wird eine &quot;Default-Discussion&quot; zurückgegeben.</p>
<p>PUT: Erzeugt aus den übergegeben Werten eine Diskussion.</p>
<p>POST: Editiert die Diskussion mit der übergebenen ID mit den übergebenen Werten.</p>
<p>DELETE: Die Diskussion mit der übergebenen ID wird gelöscht.</p>
<h4 id="post.py">post.py:</h4>
<p>Dieses Modul soll alle Anfragen annehmen, die sich direkt auf Beiträge beziehen.</p>
<p>Enthält die Klasse: &quot;Post_cl&quot;.</p>
<p>Diskussion_cl enthält folgende Methoden:<br />
GET: Wenn keine Beitrags-ID übergeben wird, wird die Beitragsliste der übergebenen Diskussions-ID zurückgegeben, anderenfalls wird der Beitrag zurückgegeben. Bei der ID &quot;0&quot; wird ein &quot;Default-Beitrag&quot; zurückgegeben.</p>
<p>PUT: Erzeugt aus den übergegeben Werten einen Beitrag.</p>
<p>POST: Editiert den Beitrag mit der übergebenen ID mit den übergebenen Werten.</p>
<p>DELETE: Der Beitrag mit der übergebenen ID wird gelöscht.</p>
<h4 id="database.py">database.py:</h4>
<p>Dieses Modul verwaltet die Daten der anderen Module, die zurückgegeben, verändert oder gespeichert werden sollen.</p>
<p>Enthält die Klasse: &quot;Database_cl&quot;.</p>
<p>Database_cl enthält folgende Methoden: createTopic_px(self, data_opl): Speichert ein neues Thema mit dem Werten von &quot;data_opl&quot; in die topics.dat.</p>
<p>createDiscussion_px(self, tID, data_opl): Speichert eine neue Diskussion mit den Werten von &quot;data_opl&quot; in die discussions.dat und seine ID in das übergebene Thema.</p>
<p>createPost_px(self, dID, data_opl): Speichert einen neuen Beitrag mit den Werten von &quot;data_opl&quot; in die posts.dat und seine ID in die übergebene Diskussion.</p>
<p>readTopic_px(self, id_ = None):Bei id_ == None wird die Themenliste, bei &quot;0&quot; ein &quot;Default-Thema&quot; und bei einem Wert != 0 das entsprechende Thema zurückgegeben.</p>
<p>readDiscussion_px(self, tId, dId = None): Bei dId == None wird die Diskussionsliste, bei &quot;0&quot; eine &quot;Default-Diskussion&quot; und bei einem Wert != 0 die entsprechende Diskussion zurückgegeben.</p>
<p>readPost_px(self, dID, pID = None): Bei dId == None wird die Beitragsliste, bei &quot;0&quot; ein &quot;Default-Beitrag&quot; und bei einem Wert != 0 der entsprechende Beitrag zurückgegeben.</p>
<p>updateTopic_px(self, id_, data_opl): Überprüft ob ein Thema mit der übergebenen ID existiert, wenn ja wird das editierte Thema abgespeichert.</p>
<p>updateDiscussion_px(self, id_, data_opl): Überprüft ob eine Diskussion mit der übergebenen ID existiert, wenn ja wird die editierte Diskussion abgespeichert.</p>
<p>updatePost_px(self, id_, data_opl): Überprüft ob ein Beitrag mit der übergebenen ID existiert, wenn ja wird der editerte Beitrag abgespeichert.</p>
<p>deleteTopic_px(self, id_): Überprüft ob ein Thema mit der übergebenen ID existiert, wenn ja wird es gelöscht.</p>
<p>deleleteDiscussion_px(self, id_): Überprüft ob eine Diskussion mit der übergebenen ID existiert, wenn ja wird sie gelöscht.</p>
<p>deletePost_px(self, id_): Überprüft ob ein Beitrag mit der übergebenen ID existiert, wenn ja wird er gelöscht.</p>
<p>getDefaultTopic_px(self): Gibt ein &quot;Default-Thema&quot; zurück.</p>
<p>getDefaultDiscussion_px(self): Gibt eine &quot;Default-Diskussion&quot; zurück.</p>
<p>getDefaultPost_px(self): Gibt einen &quot;Default-Beitrag&quot; zurück.</p>
<p>getUsers_px(self): Gibt die Benutzer zurück.</p>
<p>saveUsers_px(self, data_o): Speichert den übergebenen Benutzer in die &quot;users.dat&quot;.</p>
<h4 id="login.py">login.py</h4>
<p>Dieses Modul verwaltet den Login.</p>
<p>Enthält die Klasse: &quot;Login_cl&quot;.</p>
<p>Login_cl enhält folgende Methoden: GET: Prüft, ob der übergebene Benutzer vorhanden ist. Wenn ja, wird zurückgegebenen, dass dieser autorisiert ist.</p>
<p>Put: Prüft, ob der übergebene Benutzername schon existiert, falls nicht wird dieser neu angelegt und gespeichert.</p>
<h4 id="template.py">template.py</h4>
<p>Dieses Modul lädt alle Templates</p>
<p>Enthält die Klasse: &quot;Template_cl&quot;.</p>
<p>Template_cl enthält folgende Methoden: GET: Gibt alle Templates zurück.</p>
<h4 id="application.py">application.py:</h4>
<p>Dieses Modul gibt bei unbekannter Anforderung eine Fehlermeldung zurück.</p>
<h4 id="zusammenwirken">Zusammenwirken</h4>
<p>Die Module topic.py, discussion.py, login.py und post.py verwalten alle ihre Daten mithilfe der database.py. Diese löscht, speichert, erstellt oder verändert Daten und übergibt diese dann an die entsprechenden Module zur weiteren Verarbeitung.</p>
<h3 id="datenhaltung">2.3 Datenhaltung</h3>
<h4 id="topics.dat">topics.dat</h4>
<p>Die Themen werden wie folgt in der &quot;topics.dat&quot; gespeichert: &quot;name&quot;: [string] &quot;owner&quot;: [string] &quot;discs&quot;: [Keys der dazugehörigen Diskussionen] &quot;deleted&quot;: [boolean]</p>
<p>Zusätzlich wird die ID für das nächste Thema gespeichert.</p>
<h4 id="discussions.dat">discussions.dat</h4>
<p>Die Diskussionen werden wie folgt in der &quot;discussions.dat&quot; gespeichert: &quot;name&quot;: [string] &quot;owner&quot;: [string] &quot;posts&quot;: [Keys der dazugehörigen Beiträge] &quot;deleted&quot;: [boolean]</p>
<p>Zusätzlich wird die ID für die nächste Diskussion gespeichert.</p>
<h4 id="posts.dat">posts.dat</h4>
<p>Die Beiträge werden wie folgt in der &quot;posts.dat&quot; gespeichert: &quot;text&quot;: [string] &quot;owner&quot;: [string] &quot;title&quot;: [string] &quot;time&quot;: [Zeitangaben] &quot;deleted&quot;: [boolean]</p>
<p>Zusätzlich wird die ID für den nächsten Beitrag gespeichert.</p>
<h4 id="users.dat">users.dat</h4>
<p>Die Themen werden wie folgt in der &quot;users.dat&quot; gespeichert: &quot;[Benutzername]&quot;: [Passwort]</p>
<h3 id="implementierung-des-clients">3. Implementierung des Clients</h3>
<h4 id="klassen">3.1 Klassen</h4>
<p>FORUM.User_cl: Stellt die Funktionen für den Benutzer zur Verfügung.</p>
<p>FORUM.Application_cl: Erzeugt die Instanzen der einzelnen Klassen und verwaltet das Anzeigen der Inhalte auf Basis der Nachrichten des Eventservices.</p>
<p>FORUM.TopicListView_cl: Verwaltet die Anzeige der Themenliste und die in diesem Teil enstehenden Events.</p>
<p>FORUM.TopicDetailView_cl: Verwaltet die Anzeige des Themenformulars und die in diesem Teil enstehenden Events.</p>
<p>FORUM.DiscussionListView_cl: Verwaltet die Anzeige der Diskussionsliste und die in diesem Teil enstehenden Events.</p>
<p>FORUM.DiscussionDetailView_cl: Verwaltet die Anzeige des Diskussionsformulars und die in diesem Teil enstehenden Events.</p>
<p>FORUM.PostListView_cl: Verwaltet die Anzeige der Beitragsliste und die in diesem Teil enstehenden Events.</p>
<p>FORUM.PostDetailView_cl: Verwaltet die Anzeige des Beitragsformulars und die in diesem Teil enstehenden Events.</p>
<p>FORUM.LoginView_cl: Verwaltet die Anzeige des Loginformulars und die in diesem Teil enstehenden Events.</p>
<h4 id="eventservice">3.2 Eventservice:</h4>
<p>Der Eventservice wird benutzt um zu signalisieren, dass eine Seite angezeigt werden soll,<br />
diese werden in Forum.js abgehandelt und dann zu den entsprechenden Skripten weitergeschickt.</p>
<h4 id="templateverarbeitung">3.3 Templateverarbeitung:</h4>
<p>Die drei Templates die verwendet werden sind:<br />
list_topic.tpl<br />
list_discussion.tpl<br />
list_post.tpl<br />
Diese Templates bauen die Tabellen zusammen, die jeweils die Listen aus Themen, Beiträgen und Diskussionen anzeigen.</p>
<h2 id="prüfung-markup-und-stilregeln">Prüfung Markup und Stilregeln:</h2>
<p>Ergebnis Test des Markups des HTML Codes der Index Datei mit Beispielementen in allen drei Kategorien: &quot;Document checking completed. No errors or warnings to show.&quot;</p>
<p>Ergebnis des Tests der CSS-Stilregeln:<br />
&quot;Congratulations! No Error Found.&quot; </p>
</body>
</html>

View File

@ -0,0 +1,19 @@
<div id="idDiscussionEditContent" class="clContent">
<form id="idDiscussionDetail" class="clContent" action="#"">
<h2 id="idDiscussionDetailContentHeader" class="clContentHeader">
Diskussion Ändern
</h2>
<div id="idDiscussionFormContentArea" class="clContentArea">
<div class="clFormRow">
<label for="dEditName">Name <span class="clRequired"></span></label>
<input type="text" value="" id="dEditName" name="name" />
</div>
</div>
<div class="clButtonArea">
<button data-action="back" class="clButton">Zurück</button>
<button data-action="save" class="clButton">Speichern</button>
</div>
</form>
</div>

View File

@ -0,0 +1,23 @@
<div id="idPostEditContent" class="clContent">
<form id="idPostDetail" class="clContent" action="#"">
<h2 id="idPostDetailContentHeader" class="clContentHeader">
Neuer Beitrag
</h2>
<div id="idPostFormContentArea" class="clContentArea">
<div class="clFormRow">
<label for="pEditTitle">Titel <span class="clRequired"></span></label>
<input type="text" value="" id="pEditTitle" name="title"/>
</div>
<div class="clFormRow">
<label for="pEditText">Text <span class="clRequired"></span></label>
<textarea id="pEditText" name="text" />
</div>
</div>
<div class="clButtonArea">
<button data-action="back" class="clButton">Zurück</button>
<button data-action="save" class="clButton">Speichern</button>
</div>
</form>
</div>

View File

@ -0,0 +1,19 @@
<div id="idTopicEditContent" class="clContent">
<form id="idTopicDetail" class="clContent" action="#"">
<h2 id="idTopicDetailContentHeader" class="clContentHeader">
Neues Thema
</h2>
<div id="idTopicFormContentArea" class="clContentArea">
<div class="clFormRow">
<label for="tEditName">Name <span class="clRequired"></span></label>
<input type="text" value="" id="tEditName" name="name" />
</div>
</div>
<div class="clButtonArea">
<button data-action="back" class="clButton">Zurück</button>
<button data-action="save" class="clButton">Speichern</button>
</div>
</form>
</div>

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<title>
Web Engineering 2015/2016 Forum Gruppe D
</title>
<meta charset="UTF-8" />
<style type="text/css">
@import url("/css/main.css");
</style>
<script type="text/javascript" src="/js/jquery-2.2.0.min.js"></script>
<script type="text/javascript" src="/js/inheritance.js"></script>
<script type="text/javascript" src="/js/es.js"></script>
<script type="text/javascript" src="/js/te.js"></script>
<script type="text/javascript" src="/js/tm.js"></script>
<!-- hier zur Verdeutlichung der Strukturierung Aufteilung in mehrere js-Quellen -->
<!-- wird man im produktiven Einsatz zu einer js-Quelle zusammenfassen -->
<script type="text/javascript" src="/js/forum.js"></script>
<script type="text/javascript" src="/js/list_topic.js"></script>
<script type="text/javascript" src="/js/edit_topic.js"></script>
<script type="text/javascript" src="/js/list_discussion.js"></script>
<script type="text/javascript" src="/js/edit_discussion.js"></script>
<script type="text/javascript" src="/js/list_post.js"></script>
<script type="text/javascript" src="/js/edit_post.js"></script>
<script type="text/javascript" src="/js/post.js"></script>
<script type="text/javascript" src="/js/nav.js"></script>
<script type="text/javascript" src="/js/login.js"></script>
</head>
<body>
<header>
<h1 id="idSiteHeader" class="clSiteHeader">
Web Engineering 2015/2016 Forum Gruppe D
</h1>
</header>
<section class="clLoggedInText">
<p id="idNotLoggedIn">Sie sind nicht eingeloggt!</p>
<p id="idLoggedIn">Sie sind eingeloggt als: </p>
</section>
<section id="idContentOuter" class="clContentOuter">
</section>
<nav id="idNav" class="clNav">
<a href="#" data-action="topicList">Home</a>
<a href="#" data-action="login" class="clLinkToLogin">Login</a>
<a href="#" data-action="logout" class="clLinkToLogout">Logout</a>
</nav>
</body>
</html>

View File

@ -0,0 +1,16 @@
<div id="idDiscussionListContent" class="clContent">
<h2 id="idContentHeader" class="clContentHeader">Diskussionen</h2>
<div id="idContentArea" class="clContentArea">
<table id="idDiscussionList"><tbody>
<tr class="listheader"><th>Diskussion</th><th>Besitzer</th></tr>
</tbody></table>
<div class="clButtonArea">
<button data-action="back" class="clButton">Zurück</button>
<button data-action="addDiscussion" class="clButton">Hinzufügen</button>
<button data-action="discussion" class="clButton">Ansehen</button>
<button data-action="editDiscussion" class="clButton">Bearbeiten</button>
<button data-action="deleteDiscussion" class="clButton">Löschen</button>
</div>
</div>
</div>

View File

@ -0,0 +1,15 @@
<div id="idPostListContent" class="clContent">
<h2 id="idContentHeader" class="clContentHeader">Beiträge</h2>
<div id="idContentArea" class="clContentArea">
<ul id="idPostList">
</ul>
<div class="clButtonArea">
<button data-action="back" class="clButton">Zurück</button>
<button data-action="addPost" class="clButton">Hinzufügen</button>
<button data-action="post" class="clButton">Ansehen</button>
<button data-action="editPost" class="clButton">Bearbeiten</button>
<button data-action="deletePost" class="clButton">Löschen</button>
</div>
</div>
</div>

View File

@ -0,0 +1,15 @@
<div id="idTopicListContent" class="clContent">
<h2 id="idContentHeader" class="clContentHeader">Themen</h2>
<div id="idContentArea" class="clContentArea">
<table id="idTopicList"><tbody>
<tr class="listheader"><th>Thema</th><th>Besitzer</th></tr>
</tbody></table>
<div class="clButtonArea">
<button data-action="addTopic" class="clButton">Hinzufügen</button>
<button data-action="discussionList" class="clButton">Diskussionen</button>
<button data-action="editTopic" class="clButton">Bearbeiten</button>
<button data-action="deleteTopic" class="clButton">Löschen</button>
</div>
</div>
</div>

View File

@ -0,0 +1,26 @@
<div id="idLoginContent" class="clContent">
<h2 id="idContentHeader" class="clContentHeader">Einloggen</h2>
<div id="idContentArea" class="clContentArea">
<form id="idLoginForm" class="clContent" action="#">
<div id="idLogin" class="clLogin">
<div class="clFormRow">
<label for="username">Benutzername</label>
<input type="text" value="" id="username" name="username" />
</div>
<div class="clFormRow">
<label for="password">Passwort</label>
<input type="password" value="" id="password" name="password" />
</div>
</div>
<div id="idButtonArea" class="clButtonArea">
<button data-action="back" class="clButton">Zurück</button>
<button data-action="login" class="clButton">Login</button>
<button data-action="register" class="clButton">Registrieren</button>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,13 @@
<div id="idPostContent" class="clContent">
<h2 id="idContentHeader" class="clContentHeader">Beitrag</h2>
<div id="idContentArea" class="clContentArea">
<ul id="idPost">
</ul>
<div class="clButtonArea">
<button data-action="back" class="clButton">Zurück</button>
<button data-action="editPost" class="clButton">Bearbeiten</button>
<button data-action="deletePost" class="clButton">Löschen</button>
</div>
</div>
</div>

View File

@ -0,0 +1,116 @@
// ----------------------------------------------
// edit_discussion.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.DiscussionDetailView_cl = Class.create({
// ----------------------------------------------
initialize: function () {
// Basis-Markup des Formulars anfordern
var that = this;
$.get('/html/edit_discussion.html', function (data_spl) {
$("#idContentOuter").append(data_spl);
$("#idDiscussionEditContent").hide();
that.initHandler_p();
that.storeFormContent_p();
});
},
canClose_px: function () {
// Prüfen, ob Formularinhalt verändert wurde
return (this.isModified_p() ? confirm("Es gibt nicht gespeicherte Änderungen - verwerfen?") : true);
},
close_px: function () {
$("#idDiscussionEditContent").hide();
},
render_px: function (ids) {
tId_ = ids[0];
id_ = ids[1];
$.ajax({
dataType: "json",
url: '/discussion/' + tId_ + '/' + (id_ == null?'0':id_),
type: 'GET'
})
.done($.proxy(this.doRender_p, this))
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[Form] Fehler bei Anforderung: " + textStatus_spl);
});
},
doRender_p: function (data_opl) {
// in das Formular übertragen
var data_o = data_opl['data'];
$('#dEditName').val(data_o['name']);
this.id_s = data_opl['id'];
this.tId = data_opl['tId'];
this.storeFormContent_p();
$("#idDiscussionEditContent").show();
},
initHandler_p: function () {
// Ereignisverarbeitung für das Formular einrichten
$("#idDiscussionEditContent").on("click", "button", $.proxy(this.onClickButtons_p, this));
},
onClickButtons_p: function (event_opl) {
var do_b = false;
var path_s;
var action_s = $(event_opl.target).attr("data-action");
switch (action_s) {
case "back":
// Weiterleiten
FORUM.es_o.publish_px('ContentChanged', ['discussionList', this.tId]);
break;
case "save":
// Formularinhalt prüfen
if (this.isModified_p()) {
if (this.checkContent_p()) {
// kein klassisches submit, es wird auch keine neue Anzeige vorgenommen
var data_s = $("#idDiscussionDetail").serialize();
data_s += "&owner=" + FORUM.user_o.name_s;
$.ajax({
context: this,
dataType: "json",
data: data_s,
url: '/discussion/' + this.tId + '/' + (this.id_s != 0?'/' + this.id_s:''),
type: (this.id_s != 0?'POST':'PUT')
})
.done(function (data_opl) {
// aktuellen Formularinhalt speichern
// (das Formular wird ja nicht mehr neu geladen!)
this.storeFormContent_p();
alert("Speichern ausgeführt!");
FORUM.es_o.publish_px('ContentChanged', ['discussionList', this.tId]);
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert("Fehler bei Anforderung: " + textStatus_spl);
});
} else {
alert("Bitte prüfen Sie die Eingaben in den Formularfeldern!")
}
}
else {
FORUM.es_o.publish_px('ContentChanged', ['discussionList', null]);
}
break;
}
// Weiterleitung und Standardbearbeitung unterbinden
event_opl.stopPropagation();
event_opl.preventDefault();
},
isModified_p: function () {
// Prüfen, ob Formularinhalt verändert wurde
return (this.FormContentOrg_s != $("#idDiscussionDetail").serialize());
},
checkContent_p: function () {
// hier nur zur Demonstration Prüfung des Typs gegen eine Werteliste
// (das realisiert man besser mit einer Liste)
var status_b = true;
return status_b;
},
storeFormContent_p: function () {
this.FormContentOrg_s = $("#idDiscussionDetail").serialize();
}
});
// EOF

View File

@ -0,0 +1,128 @@
// ----------------------------------------------
// edit_Topic.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.PostDetailView_cl = Class.create({
// ----------------------------------------------
initialize: function () {
// Basis-Markup des Formulars anfordern
var that = this;
$.get('/html/edit_post.html', function (data_spl) {
$("#idContentOuter").append(data_spl);
$("#idPostEditContent").hide();
that.initHandler_p();
that.storeFormContent_p();
});
},
canClose_px: function () {
// Prüfen, ob Formularinhalt verändert wurde
return (this.isModified_p() ? confirm("Es gibt nicht gespeicherte Änderungen - verwerfen?") : true);
},
close_px: function () {
$("#idPostEditContent").hide();
},
render_px: function (ids) {
tId_ = ids[0];
dId_ = ids[1];
id_ = ids[2];
$.ajax({
dataType: "json",
url: '/post/' + tId_ + '/' + dId_ + '/' + (id_ == null?'0':id_),
type: 'GET'
})
.done($.proxy(this.doRender_p, this))
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[Form] Fehler bei Anforderung: " + textStatus_spl);
});
},
doRender_p: function (data_opl) {
// in das Formular übertragen
var data_o = data_opl['data'];
$('#pEditTitle').val(data_o['title']);
$('#pEditText').val(data_o['text']);
this.id_s = data_opl['id'];
this.tId = data_opl['tId'];
this.dId = data_opl['dId'];
if (parseInt(this.dId) == 0)
$("#idPostDetailContentHeader").text("Neue Diskussion");
else if (parseInt(this.id_s) != 0)
$("#idPostDetailContentHeader").text("Beitrag ändern");
this.storeFormContent_p();
$("#idPostEditContent").show();
},
initHandler_p: function () {
// Ereignisverarbeitung für das Formular einrichten
$("#idPostEditContent").on("click", "button", $.proxy(this.onClickButtons_p, this));
},
onClickButtons_p: function (event_opl) {
var do_b = false;
var path_s;
var action_s = $(event_opl.target).attr("data-action");
switch (action_s) {
case "back":
// Weiterleiten
if (this.dId != 0)
FORUM.es_o.publish_px('ContentChanged', ['discussion', this.tId, this.dId]);
else
FORUM.es_o.publish_px('ContentChanged', ['discussionList', this.tId]);
break;
case "save":
// Formularinhalt prüfen
if (this.isModified_p()) {
if (this.checkContent_p()) {
// kein klassisches submit, es wird auch keine neue Anzeige vorgenommen
var data_s = $("#idPostDetail").serialize();
data_s += "&owner=" + FORUM.user_o.name_s;
$.ajax({
context: this,
dataType: "json",
data: data_s,
url: '/post/' + this.tId + '/' + this.dId + (this.id_s != 0?'/' + this.id_s:''),
type: (this.id_s != 0?'POST':'PUT')
})
.done(function (data_opl) {
// aktuellen Formularinhalt speichern
// (das Formular wird ja nicht mehr neu geladen!)
this.storeFormContent_p();
this.dId = data_opl['dId'];
alert("Speichern ausgeführt!");
FORUM.es_o.publish_px('ContentChanged', ['discussion', this.tId, this.dId]);
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert("Fehler bei Anforderung: " + textStatus_spl);
});
} else {
alert("Bitte prüfen Sie die Eingaben in den Formularfeldern!")
}
}
else {
FORUM.es_o.publish_px('ContentChanged', ['discussion', this.tId, this.dId]);
}
break;
}
// Weiterleitung und Standardbearbeitung unterbinden
event_opl.stopPropagation();
event_opl.preventDefault();
},
isModified_p: function () {
// Prüfen, ob Formularinhalt verändert wurde
return (this.FormContentOrg_s != $("#idPostDetail").serialize());
},
checkContent_p: function () {
// hier nur zur Demonstration Prüfung des Typs gegen eine Werteliste
// (das realisiert man besser mit einer Liste)
var status_b = true;
return status_b;
},
storeFormContent_p: function () {
this.FormContentOrg_s = $("#idPostDetail").serialize();
}
});
// EOF

View File

@ -0,0 +1,117 @@
// ----------------------------------------------
// edit_Topic.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.TopicDetailView_cl = Class.create({
// ----------------------------------------------
initialize: function () {
// Basis-Markup des Formulars anfordern
var that = this;
$.get('/html/edit_topic.html', function (data_spl) {
$("#idContentOuter").append(data_spl);
$("#idTopicEditContent").hide();
that.initHandler_p();
that.storeFormContent_p();
});
},
canClose_px: function () {
// Prüfen, ob Formularinhalt verändert wurde
return (this.isModified_p() ? confirm("Es gibt nicht gespeicherte Änderungen - verwerfen?") : true);
},
close_px: function () {
$("#idTopicEditContent").hide();
},
render_px: function (id_) {
this.id_ = id_;
$.ajax({
dataType: "json",
url: '/topic/' + (id_ == null?'0':id_),
type: 'GET'
})
.done($.proxy(this.doRender_p, this))
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[Form] Fehler bei Anforderung: " + textStatus_spl);
});
},
doRender_p: function (data_opl) {
// in das Formular übertragen
var data_o = data_opl['data'];
$('#tEditName').val(data_o['name']);
this.id_s = data_opl['id'];
if (this.id_s != 0)
$("#idTopicDetailContentHeader").text("Thema ändern");
this.storeFormContent_p();
$("#idTopicEditContent").show();
},
initHandler_p: function () {
// Ereignisverarbeitung für das Formular einrichten
$("#idTopicEditContent").on("click", "button", $.proxy(this.onClickButtons_p, this));
},
onClickButtons_p: function (event_opl) {
var do_b = false;
var path_s;
var action_s = $(event_opl.target).attr("data-action");
switch (action_s) {
case "back":
// Weiterleiten
FORUM.es_o.publish_px('ContentChanged', ['topicList', null]);
break;
case "save":
// Formularinhalt prüfen
if (this.isModified_p()) {
if (this.checkContent_p()) {
// kein klassisches submit, es wird auch keine neue Anzeige vorgenommen
var data_s = $("#idTopicDetail").serialize();
data_s += "&owner=" + FORUM.user_o.name_s;
$.ajax({
context: this,
dataType: "json",
data: data_s,
url: '/topic' + (this.id_s != 0?'/' + this.id_s:''),
type: (this.id_s != 0?'POST':'PUT')
})
.done(function (data_opl) {
// aktuellen Formularinhalt speichern
// (das Formular wird ja nicht mehr neu geladen!)
this.storeFormContent_p();
alert("Speichern ausgeführt!");
FORUM.es_o.publish_px('ContentChanged', ['topicList', null]);
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert("Fehler bei Anforderung: " + textStatus_spl);
});
} else {
alert("Bitte prüfen Sie die Eingaben in den Formularfeldern!")
}
}
else {
FORUM.es_o.publish_px('ContentChanged', ['topicList', null]);
}
break;
}
// Weiterleitung und Standardbearbeitung unterbinden
event_opl.stopPropagation();
event_opl.preventDefault();
},
isModified_p: function () {
// Prüfen, ob Formularinhalt verändert wurde
return (this.FormContentOrg_s != $("#idTopicDetail").serialize());
},
checkContent_p: function () {
// hier nur zur Demonstration Prüfung des Typs gegen eine Werteliste
// (das realisiert man besser mit einer Liste)
var status_b = true;
return status_b;
},
storeFormContent_p: function () {
this.FormContentOrg_s = $("#idTopicDetail").serialize();
}
});
// EOF

View File

@ -0,0 +1,91 @@
//------------------------------------------------------------------------------
// Event-Service: asynchroner Nachrichtenaustausch
//------------------------------------------------------------------------------
// depends-on:
// jquery
// inheritance
// underscore
//------------------------------------------------------------------------------
function defer_p (notifier_ppl, entry_opl, message_spl, data_opl) {
return setTimeout(function() {
return notifier_ppl.apply(entry_opl, [entry_opl, message_spl, data_opl]);
}, 1);
}
function each(object_opl, iterator, context) {
for (var key in object_opl) {
iterator.call(context, object_opl[key], key);
}
}
function findAll(object_opl, iterator, context) {
var results = [];
each(object_opl, function(value, index) {
if (iterator.call(context, value, index))
results.push(value);
});
return results;
}
function compact(object_opl) {
return findAll(object_opl, function(value) {
return value != null;
});
}
//------------------------------------------------------------------------------
var EventService_cl = Class.create({
//------------------------------------------------------------------------------
initialize: function () {
this.Data_o = null;
this.Subscriber_o = {};
this.Method_o = null;
},
subscribe_px: function (Subscriber_opl, Message_spl) {
if (Message_spl in this.Subscriber_o) {
// Message bekannt, Liste der Subscriber untersuchen
if (this.Subscriber_o[Message_spl].indexOf(Subscriber_opl) == -1) {
this.Subscriber_o[Message_spl].push(Subscriber_opl);
}
} else {
// Message noch nicht vorhanden, neu eintragen
this.Subscriber_o[Message_spl] = [Subscriber_opl];
}
},
unSubscribe_px: function (Subscriber_opl, Message_spl) {
if (Message_spl in this.Subscriber_o) {
// Message bekannt, Liste der Subscriber untersuchen
var Entry_a = this.Subscriber_o[Message_spl];
var index_i = Entry_a.indexOf(Subscriber_opl);
if (index_i >= 0) {
// Eintrag entfernen
Entry_a[index_i] = null;
Entry_a = compact(Entry_a); // compact liefert Kopie!
if (Entry_a.length == 0) {
// keine Subscriber mehr, kann entfernt werden
delete this.Subscriber_o[Message_spl];
}
}
} else {
// Message nicht vorhanden, falsche Anforderung
}
},
publish_px: function (Message_spl, Data_opl) {
console.info('es - publish ' + Message_spl);
each(this.Subscriber_o, function (value_apl, key_spl) {
// geliefert wird jeweils ein Wert, hier ein Array, und der Key
if (key_spl == Message_spl) {
// an alle Subscriber weitergeben
each(value_apl, function (entry_opl, index_ipl) {
// geliefert wird hier das Element und der Index
//_.defer(entry_opl.notify_px, entry_opl, Message_spl, Data_opl);
defer_p(entry_opl.notify_px, entry_opl, Message_spl, Data_opl);
}, this
);
}
}, this
)
}
});
// EOF

View File

@ -0,0 +1,178 @@
// ----------------------------------------------
// forum.js
// ----------------------------------------------
// Verwendung von jquery, inheritance, Single-Page / Ajax, Event-Service
// REST-Interface
// ----------------------------------------------
var FORUM = {};
//********************************************** USER-object
FORUM.User_cl = Class.create({
initialize: function () {
this.name_s = '';
this.password = '';
this.loggedin = false;
$('.clLoggedInText #idLoggedIn').hide();
},
logout: function () {
this.id_s = "";
this.name_s = "niemand";
this.loggedin = false;
this.items = [];
$('#idNav .clLinkToLogin').show();
$('#idNav .clLinkToSignup').show();
$('#idNav .clLinkToLogout').hide();
$('.clLoggedInText #idNotLoggedIn').show();
$('.clLoggedInText #idLoggedIn').hide();
$('.clLoggedInText #idLoggedIn').text('Sie sind eingeloggt als: ');
},
login: function (data_o) {
this.name_s = data_o['username'];
this.password = data_o['password'];
this.loggedin = true;
$('#idNav .clLinkToLogin').hide();
$('#idNav .clLinkToSignup').hide();
$('#idNav .clLinkToLogout').show();
$('.clLoggedInText #idNotLoggedIn').hide();
$('.clLoggedInText #idLoggedIn').show();
$('.clLoggedInText #idLoggedIn').text($('.clLoggedInText #idLoggedIn').text() + data_o['username']);
},
register: function () {
this.id_s = "";
this.name_s = "niemand";
this.loggedin = false;
$('#idNav .clLinkToLogin').show();
$('#idNav .clLinkToSignup').show();
$('#idNav .clLinkToLogout').hide();
}
});
//**********************************************
// ----------------------------------------------
FORUM.Application_cl = Class.create({
// ----------------------------------------------
initialize: function () {
this.content_o = null; // das jeweils aktuelle Objekt im Contentbereich
this.nav_o = new FORUM.Nav_cl();
//this.discussionView_o = new FORUM.PostlistView_cl();
this.topicListView_o = new FORUM.TopicListView_cl();
this.topicDetailView_o = new FORUM.TopicDetailView_cl();
this.discussionListView_o = new FORUM.DiscussionListView_cl();
this.discussionDetailView_o = new FORUM.DiscussionDetailView_cl();
this.postListView_o = new FORUM.PostListView_cl();
this.postDetailView_o = new FORUM.PostDetailView_cl();
this.postView_o = new FORUM.PostView_cl();
//this.discussionlistView_o = new FORUM.DiscussionlistView_cl();
//this.postView_o = new FORUM.PostView_cl();
this.loginView_o = new FORUM.LoginView_cl();
// Registrierungen
FORUM.es_o.subscribe_px(this, 'ContentChanged');
},
notify_px: function (self_opl, message_spl, data_apl) {
switch (message_spl) {
case 'ContentChanged':
switch (data_apl[0]) {
case 'init':
FORUM.tm_o = new TELIB.TemplateManager_cl();
break;
case 'showRegister':
self_opl.setContent_p(self_opl.registerView_o, data_apl[1]);
break;
case 'showLogin':
self_opl.setContent_p(self_opl.loginView_o, data_apl[1]);
break;
case 'logout':
FORUM.user_o.logout();
self_opl.setContent_p(self_opl.topicListView_o, data_apl[1]);
break;
case 'login':
self_opl.setContent_p(self_opl.loginView_o, data_apl[1]);
break;
case 'register':
FORUM.user_o.register();
self_opl.setContent_p(self_opl.topicListView_o, data_apl[1]);
break;
case 'topicList':
self_opl.setContent_p(self_opl.topicListView_o, data_apl[1]);
break;
case 'topicEdit':
self_opl.setContent_p(self_opl.topicDetailView_o, data_apl[1]);
break;
case 'discussionList':
self_opl.setContent_p(self_opl.discussionListView_o, data_apl[1]);
break;
case 'discussionEdit':
self_opl.setContent_p(self_opl.discussionDetailView_o, [data_apl[1], data_apl[2]]);
break;
case 'discussion':
self_opl.setContent_p(self_opl.postListView_o, [data_apl[1], data_apl[2]]);
break;
case 'postEdit':
self_opl.setContent_p(self_opl.postDetailView_o, [data_apl[1], data_apl[2], data_apl[3]]);
break;
case 'post':
self_opl.setContent_p(self_opl.postView_o, [data_apl[1], data_apl[2], data_apl[3]]);
break;
default:
console.warn('[Application_cl] unbekannte app-Notification: ' + data_apl[0]);
break;
}
break;
default:
console.warn('[Application_cl] unbekannte Notification: ' + message_spl);
break;
}
},
setContent_p: function (newContent_opl, data_opl) {
if (this.content_o != null) {
if (this.content_o.canClose_px()) {
this.content_o.close_px();
this.content_o = newContent_opl;
this.content_o.render_px(data_opl);
}
} else {
this.content_o = newContent_opl;
this.content_o.render_px(data_opl);
}
}
});
// ----------------------------------------------
$(document).ready(function () {
// ----------------------------------------------
// wird ausgeführt, wenn das Dokument vollständig geladen wurde
FORUM.es_o = new EventService_cl();
FORUM.app_o = new FORUM.Application_cl();
FORUM.user_o = new FORUM.User_cl();
FORUM.es_o.publish_px('ContentChanged', ['init', null]);
});
// EOF

View File

@ -0,0 +1,122 @@
/*
Class, version 2.7
Copyright (c) 2006, 2007, 2008, Alex Arnell <alex@twologic.com>
Licensed under the new BSD License. See end of file for full license terms.
*/
var Class = (function() {
var __extending = {};
return {
extend: function(parent, def) {
if (arguments.length == 1) { def = parent; parent = null; }
var func = function() {
if (arguments[0] == __extending) { return; }
this.initialize.apply(this, arguments);
};
if (typeof(parent) == 'function') {
func.prototype = new parent( __extending);
}
var mixins = [];
if (def && def.include) {
if (def.include.reverse) {
// methods defined in later mixins should override prior
mixins = mixins.concat(def.include.reverse());
} else {
mixins.push(def.include);
}
delete def.include; // clean syntax sugar
}
if (def) Class.inherit(func.prototype, def);
for (var i = 0; (mixin = mixins[i]); i++) {
Class.mixin(func.prototype, mixin);
}
return func;
},
mixin: function (dest, src, clobber) {
clobber = clobber || false;
if (typeof(src) != 'undefined' && src !== null) {
for (var prop in src) {
if (clobber || (!dest[prop] && typeof(src[prop]) == 'function')) {
dest[prop] = src[prop];
}
}
}
return dest;
},
inherit: function(dest, src, fname) {
if (arguments.length == 3) {
var ancestor = dest[fname], descendent = src[fname], method = descendent;
descendent = function() {
var ref = this.parent; this.parent = ancestor;
var result = method.apply(this, arguments);
ref ? this.parent = ref : delete this.parent;
return result;
};
// mask the underlying method
descendent.valueOf = function() { return method; };
descendent.toString = function() { return method.toString(); };
dest[fname] = descendent;
} else {
for (var prop in src) {
if (dest[prop] && typeof(src[prop]) == 'function') {
Class.inherit(dest, src, prop);
} else {
dest[prop] = src[prop];
}
}
}
return dest;
},
singleton: function() {
var args = arguments;
if (args.length == 2 && args[0].getInstance) {
var klass = args[0].getInstance(__extending);
// we're extending a singleton swap it out for it's class
if (klass) { args[0] = klass; }
}
return (function(args){
// store instance and class in private variables
var instance = false;
var klass = Class.extend.apply(args.callee, args);
return {
getInstance: function () {
if (arguments[0] == __extending) return klass;
if (instance) return instance;
return (instance = new klass());
}
};
})(args);
}
};
})();
// finally remap Class.create for backward compatability with prototype
Class.create = function() {
return Class.extend.apply(this, arguments);
};
/*
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of typicalnoise.com nor the names of its contributors may be
used to endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,196 @@
// ----------------------------------------------
// list_discussion.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.DiscussionListView_cl = Class.create({
// ----------------------------------------------
onClickButtons_p: function (event_opl) {
var action_s = $(event_opl.target).attr("data-action");
switch (action_s) {
case 'addDiscussion':
FORUM.es_o.publish_px('ContentChanged', ['postEdit', this.tId, 0, 0]);
break;
case 'discussion':
if (this.rowId_s != "") {
FORUM.es_o.publish_px('ContentChanged', ['discussion', this.tId, this.rowId_s]);
this.updateButtons_p();
} else {
alert("Wählen Sie bitte einen Eintrag in der Tabelle aus!");
}
break;
case 'deleteDiscussion':
if (this.rowId_s != "") {
if (confirm("Soll der Datensatz gelöscht werden?")) {
var path_s = "/discussion/" + this.rowId_s;
$.ajax({
context: this,
// dataType: "json",
url: path_s,
type: 'DELETE'
})
.done(function (data_opl) {
$('#d' + this.rowId_s + ' .clName').text('>>Gelöscht durch ' + $('#d' + this.rowId_s + ' .clOwner').text() + '<<');
$('.clSelected').removeClass('clSelected');
FORUM.es_o.publish_px('ContentChanged', ['discussionList', this.tId]);
this.initList_p();
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[ThemenListe] Fehler bei Anforderung: " + textStatus_spl);
});
}
} else {
alert("Wählen Sie bitte einen Eintrag in der Tabelle aus!");
}
break;
case 'editDiscussion':
if (this.rowId_s != "") {
FORUM.es_o.publish_px('ContentChanged', ['discussionEdit', this.tId, this.rowId_s]);
}
break;
case 'back':
FORUM.es_o.publish_px('ContentChanged', ['topicList', null]);
break;
}
event_opl.stopPropagation();
event_opl.preventDefault();
},
getContentFromEditForm_p: function () {
return content_o = {
'title': $('#idDiscussionFormEdit #edittitle_s').text(),
'username': FORUM.user_o.name_s,
'userid': FORUM.user_o.id_s
}
},
storeContentToEditForm_p: function () {
content_s = $('#idDiscussionList .clSelected #idDiscussionTitle').text();
$('#idDiscussionDetailContentHeader #edittitle_s').text(content_s);
},
onClickList_p: function (event_opl) {
this.initList_p();
if ($(event_opl.target).parent().attr('id') != 'deleted') {
this.rowId_s = $(event_opl.target).parent().attr('id').substring(1);
$("#d" + this.rowId_s).addClass("clSelected");
this.updateButtons_p();
}
},
initialize: function () {
var that = this;
$.get('/html/list_discussion.html', function (data_spl) {
$("#idContentOuter").append(data_spl);
$("#idDiscussionListContent").hide();
that.initHandler_p();
that.initList_p();
});
},
render_px: function (data_opl) {
$.ajax({
dataType: "json",
url: '/discussion/' + data_opl,
type: 'GET'
})
.done($.proxy(this.doRender_p, this))
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[Liste] Fehler bei Anforderung: " + textStatus_spl);
});
},
doRender_p: function (data_opl) {
var unsorted = {};
var sorted = [];
for (key in data_opl['data']) {
var lowest = parseInt(data_opl['data'][key]['posts'][0])
for (lower in data_opl['data'][key]['posts']) {
if (parseInt(data_opl['data'][key]['posts'][lower]) < lowest) {
lowest = parseInt(data_opl['data'][key]['posts'][lower]);
}
}
unsorted[lowest.toString()] = key;
sorted.push(lowest);
}
sorted.sort(function (a, b) { return a - b; });
for (key in sorted) {
sorted[key] = unsorted[sorted[parseInt(key)]];
}
data_opl['indices'] = sorted;
var rows_s = FORUM.tm_o.execute_px('list_discussion.tpl', data_opl);
this.tId = data_opl['tId'];
this.initList_p();
$("#idDiscussionList tbody tr[class!='listheader']").remove();
$("#idDiscussionList tbody").append(rows_s);
$("#idDiscussionListContent").show();
console.log("[ListView_cl] doRender");
this.updateButtons_p();
},
initList_p: function () {
$(".clSelected").removeClass("clSelected");
this.rowId_s = '';
this.updateButtons_p();
},
initHandler_p: function () {
$("#idDiscussionList").on("click", "td", $.proxy(this.onClickList_p, this));
$("#idDiscussionListContent").on("click", "button", $.proxy(this.onClickButtons_p, this));
},
enableButtons_p: function () {
$("#idDiscussionListContent .clButtonArea button[data-action=back]").prop("disabled", false);
if (FORUM.user_o.loggedin) {
this.enableAddButton_p();
}
if (this.rowId_s != '') {
this.enableViewButton_p();
}
if (this.rowId_s != '' && FORUM.user_o.loggedin && $("#d" + this.rowId_s + " .clOwner").text() == FORUM.user_o.name_s) {
this.enableEditButtons_p();
}
},
enableViewButton_p: function () {
$("#idDiscussionListContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "discussion") {
$(this).prop("disabled", false);
}
});
},
enableAddButton_p: function () {
$("#idDiscussionListContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "addDiscussion") {
$(this).prop("disabled", false);
}
});
},
enableEditButtons_p: function () {
$("#idDiscussionListContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "deleteDiscussion" || $(this).attr("data-action") == "editDiscussion") {
$(this).prop("disabled", false);
}
});
},
updateButtons_p: function () {
$("#idDiscussionListContent .clButtonArea button").each(function () {
$(this).prop("disabled", true);
});
this.enableButtons_p();
},
canClose_px: function () {
return true;
},
close_px: function () {
this.updateButtons_p();
$('.clSelected').removeClass("clSelected");
$("#idDiscussionListContent").hide();
}
});
// EOF

View File

@ -0,0 +1,191 @@
// ----------------------------------------------
// list_post.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.PostListView_cl = Class.create({
// ----------------------------------------------
onClickButtons_p: function (event_opl) {
var action_s = $(event_opl.target).attr("data-action");
switch (action_s) {
case 'addPost':
FORUM.es_o.publish_px('ContentChanged', ['postEdit', this.tId, this.dId, 0]);
break;
case 'post':
if (this.rowId_s != "") {
FORUM.es_o.publish_px('ContentChanged', ['post', this.tId, this.dId, this.rowId_s]);
this.updateButtons_p();
} else {
alert("Wählen Sie bitte einen Eintrag in der Tabelle aus!");
}
break;
case 'deletePost':
if (this.rowId_s != "") {
if (confirm("Soll der Datensatz gelöscht werden?")) {
var path_s = "/post/" + this.rowId_s;
$.ajax({
context: this,
// dataType: "json",
url: path_s,
type: 'DELETE'
})
.done(function (data_opl) {
$('#p' + this.rowId_s + ' .clName').text('>>Gelöscht durch ' + $('#p' + this.rowId_s + ' .clOwner').text() + '<<');
$('.clSelected').removeClass('clSelected');
FORUM.es_o.publish_px('ContentChanged', ['discussion', this.tId, this.dId]);
this.initList_p();
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[ThemenListe] Fehler bei Anforderung: " + textStatus_spl);
});
}
} else {
alert("Wählen Sie bitte einen Eintrag in der Liste aus!");
}
break;
case 'editPost':
if (this.rowId_s != "") {
FORUM.es_o.publish_px('ContentChanged', ['postEdit', this.tId, this.dId, this.rowId_s]);
}
break;
case 'back':
FORUM.es_o.publish_px('ContentChanged', ['discussionList', this.tId]);
break;
}
event_opl.stopPropagation();
event_opl.preventDefault();
},
getContentFromEditForm_p: function () {
return content_o = {
'title': $('#idPostFormEdit #edittitle_s').text(),
'username': FORUM.user_o.name_s,
'userid': FORUM.user_o.id_s
}
},
storeContentToEditForm_p: function () {
content_s = $('#p' + this.rowId_s + ' #idPostTitle').text();
$('#idPostDetailContentHeader #edittitle_s').text(content_s);
},
onClickList_p: function (event_opl) {
this.initList_p();
if ($(event_opl.target).parent().parent().parent().attr('id') != 'deleted') {
this.rowId_s = $(event_opl.target).parent().parent().parent().attr('id').substring(1);
$("#p" + this.rowId_s).addClass("clSelected");
this.updateButtons_p();
}
},
initialize: function () {
var that = this;
$.get('/html/list_post.html', function (data_spl) {
$("#idContentOuter").append(data_spl);
$("#idPostListContent").hide();
that.initHandler_p();
that.initList_p();
});
},
render_px: function (data_opl) {
$("#pEditTitle").val('');
$("#pEditText").val('');
$.ajax({
dataType: "json",
url: '/post/' + data_opl[0] + '/' + data_opl[1],
type: 'GET'
})
.done($.proxy(this.doRender_p, this))
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[Liste] Fehler bei Anforderung: " + textStatus_spl);
});
},
doRender_p: function (data_opl) {
var sorted = [];
for (key in data_opl['data']) {
if (data_opl['data'][key] != 'nextID') {
sorted.push(key);
}
}
sorted.sort(function (a, b) { return a - b; });
data_opl['indices'] = sorted;
var rows_s = FORUM.tm_o.execute_px('list_post.tpl', data_opl);
this.tId = data_opl['tId'];
this.dId = data_opl['dId'];
this.initList_p();
$("#idPostList li").remove();
$("#idPostList").append(rows_s);
$("#idPostList li:last-of-type").addClass("newest");
$("#idPostListContent").show();
console.log("[PostListView_cl] doRender");
this.updateButtons_p();
},
initList_p: function () {
$(".clSelected").removeClass("clSelected");
this.rowId_s = '';
this.updateButtons_p();
},
initHandler_p: function () {
$("#idPostList").on("click", ".clPostContainer", $.proxy(this.onClickList_p, this));
$("#idPostListContent").on("click", "button", $.proxy(this.onClickButtons_p, this));
},
enableButtons_p: function () {
$("#idPostListContent .clButtonArea button[data-action=back]").prop("disabled", false);
if (FORUM.user_o.loggedin) {
this.enableAddButton_p();
}
if (this.rowId_s != '') {
this.enableViewButton_p();
}
if (this.rowId_s != '' && FORUM.user_o.loggedin && $("#p" + this.rowId_s + " .clOwner").text() == FORUM.user_o.name_s && $("#p" + this.rowId_s).attr('class') == "newest clSelected") {
this.enableEditButtons_p();
}
},
enableViewButton_p: function () {
$("#idPostListContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "post") {
$(this).prop("disabled", false);
}
});
},
enableAddButton_p: function () {
$("#idPostListContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "addPost") {
$(this).prop("disabled", false);
}
});
},
enableEditButtons_p: function () {
$("#idPostListContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "deletePost" || $(this).attr("data-action") == "editPost") {
$(this).prop("disabled", false);
}
});
},
updateButtons_p: function () {
$("#idPostListContent .clButtonArea button").each(function () {
$(this).prop("disabled", true);
});
this.enableButtons_p();
},
canClose_px: function () {
return true;
},
close_px: function () {
this.updateButtons_p();
$('.clSelected').removeClass("clSelected");
$("#idPostList li").remove();
$("#idPostListContent").hide();
}
});
// EOF

View File

@ -0,0 +1,190 @@
// ----------------------------------------------
// list_topic.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.TopicListView_cl = Class.create({
// ----------------------------------------------
onClickButtons_p: function (event_opl) {
var action_s = $(event_opl.target).attr("data-action");
switch (action_s) {
case 'addTopic':
FORUM.es_o.publish_px('ContentChanged', ['topicEdit', 0]);
break;
case 'discussionList':
if (this.rowId_s != "") {
FORUM.es_o.publish_px('ContentChanged', ['discussionList', this.rowId_s]);
this.updateButtons_p();
} else {
alert("Wählen Sie bitte einen Eintrag in der Tabelle aus!");
}
break;
case 'deleteTopic':
if (this.rowId_s != "") {
if (confirm("Soll der Datensatz gelöscht werden?")) {
var path_s = "/topic/" + this.rowId_s;
$.ajax({
context: this,
// dataType: "json",
url: path_s,
type: 'DELETE'
})
.done(function (data_opl) {
$('#t' + this.rowId_s + ' .clName').text('>>Gelöscht durch ' + $('#t' + this.rowId_s + ' .clOwner').text() + '<<');
$('.clSelected').removeClass('clSelected');
FORUM.es_o.publish_px('ContentChanged', ['topicList', null]);
this.initList_p();
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[ThemenListe] Fehler bei Anforderung: " + textStatus_spl);
});
}
} else {
alert("Wählen Sie bitte einen Eintrag in der Tabelle aus!");
}
break;
case 'editTopic':
if (this.rowId_s != "") {
FORUM.es_o.publish_px('ContentChanged', ['topicEdit', this.rowId_s]);
}
break;
}
event_opl.stopPropagation();
event_opl.preventDefault();
},
getContentFromEditForm_p: function () {
return content_o = {
'title': $('#idTopicFormEdit #edittitle_s').text(),
'username': FORUM.user_o.name_s,
'userid': FORUM.user_o.id_s
}
},
storeContentToEditForm_p: function () {
content_s = $('#idTopicList .clSelected #idTopicTitle').text();
$('#idTopicDetailContentHeader #edittitle_s').text(content_s);
},
onClickList_p: function (event_opl) {
this.initList_p();
if ($(event_opl.target).parent().attr('id') != 'deleted') {
this.rowId_s = $(event_opl.target).parent().attr('id').substring(1);
$("#t" + this.rowId_s).addClass("clSelected");
this.updateButtons_p();
}
},
initialize: function () {
var that = this;
$.get('/html/list_topic.html', function (data_spl) {
$("#idContentOuter").append(data_spl);
$("#idTopicListContent").hide();
that.initHandler_p();
that.initList_p();
});
},
render_px: function (data_opl) {
$.ajax({
dataType: "json",
url: '/topic',
type: 'GET'
})
.done($.proxy(this.doRender_p, this))
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[Liste] Fehler bei Anforderung: " + textStatus_spl);
});
},
doRender_p: function (data_opl) {
var unsorted = [];
var sorted = [];
for (key in data_opl['data']) {
if (data_opl['data'][key] != 'nextID') {
unsorted.push(data_opl['data'][key]['name']);
}
}
unsorted.sort();
for (key in unsorted) {
for (key2 in data_opl['data']) {
if (key2 != 'nextID' && unsorted[key] == data_opl['data'][key2]['name']) {
sorted.push(key2);
}
}
}
data_opl['indices'] = sorted;
var rows_s = FORUM.tm_o.execute_px('list_topic.tpl', data_opl);
this.initList_p();
$("#idTopicList tbody tr[class!='listheader']").remove();
$("#idTopicList tbody").append(rows_s);
$("#idTopicListContent").show();
console.log("[TopicListView_cl] doRender");
this.updateButtons_p();
},
initList_p: function () {
$(".clSelected").removeClass("clSelected");
this.rowId_s = '';
this.updateButtons_p();
},
initHandler_p: function () {
$("#idTopicList").on("click", "td", $.proxy(this.onClickList_p, this));
$("#idTopicListContent .clButtonArea").on("click", "button", $.proxy(this.onClickButtons_p, this));
},
enableButtons_p: function () {
if (FORUM.user_o.loggedin) {
this.enableAddButton_p();
}
if (this.rowId_s != '') {
this.enableViewButton_p();
}
if (this.rowId_s != '' && FORUM.user_o.loggedin && $("#t" + this.rowId_s + " .clOwner").text() == FORUM.user_o.name_s) {
this.enableEditButtons_p();
}
},
enableViewButton_p: function () {
$("#idTopicListContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "discussionList") {
$(this).prop("disabled", false);
}
});
},
enableAddButton_p: function () {
$("#idTopicListContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "addTopic") {
$(this).prop("disabled", false);
}
});
},
enableEditButtons_p: function () {
$("#idTopicListContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "deleteTopic" || $(this).attr("data-action") == "editTopic") {
$(this).prop("disabled", false);
}
});
},
updateButtons_p: function () {
$("#idTopicListContent .clButtonArea button").each(function () {
$(this).prop("disabled", true);
});
this.enableButtons_p();
},
canClose_px: function () {
return true;
},
close_px: function () {
this.updateButtons_p();
$('.clSelected').removeClass("clSelected");
$("#idTopicListContent").hide();
}
});
// EOF

View File

@ -0,0 +1,135 @@
// ----------------------------------------------
// login.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.LoginView_cl = Class.create({
// ----------------------------------------------
initialize: function () {
var that = this;
$.get('/html/login.html', function (data_spl) {
$("#idContentOuter").append(data_spl);
$("#idLoginContent").hide();
that.initHandler_p();
});
FORUM.es_o.subscribe_px(this, 'loginform');
},
notify_px: function (self_opl, message_spl, data_apl) {
switch (message_spl) {
case 'loginform':
switch (data_apl[0]) {
case 'refresh':
self_opl.render_px(null);
break;
default:
console.warning('[LoginView_cl] unbekannte list-Notification: ' + data_apl[0]);
break;
}
break;
default:
console.warning('[LoginView_cl] unbekannte Notification: ' + message_spl);
break;
}
},
canClose_px: function () {
return true;
},
close_px: function () {
$("#idLoginContent").hide();
},
render_px: function (data_opl) {
this.doRender_p();
},
doRender_p: function () {
$("#idLoginContent").show();
console.log("[LoginView_cl] doRender");
},
initHandler_p: function () {
$("#idLoginContent #idButtonArea").on("click", "button", $.proxy(this.onClickButtons_p, this));
},
onClickButtons_p: function (event_opl) {
var action_s = $(event_opl.target).attr("data-action");
data_o = {
"username": $("#idLoginContent #username").val(),
"password": $("#idLoginContent #password").val()
}
switch (action_s) {
case 'login':
$.ajax({
data: data_o,
dataType: "json",
url: '/login/',
type: 'GET'
})
.done(function (data_opl) {
if (data_opl) {
FORUM.user_o.login(data_o);
alert("Sie sind eingeloggt als \"" + data_o['username'] + "\"")
FORUM.es_o.publish_px('ContentChanged', ['topicList', null]);
}
else {
alert("Die Login-Daten sind nicht korrekt!");
}
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert( "Fehler bei Anforderung: " + textStatus_spl );
});
break;
case 'register':
$.ajax({
data: data_o,
dataType: "json",
url: '/login/',
type: 'PUT'
})
.done(function (data_opl) {
if (data_opl) {
FORUM.user_o.login(data_o);
alert("Sie sind registriert als \"" + data_o['username'] + "\"")
FORUM.es_o.publish_px('ContentChanged', ['topicList', null]);
}
else {
alert("Der Benutzername ist leider schon vergeben!");
}
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert("Fehler bei Anforderung: " + textStatus_spl);
});
break;
case 'back':
FORUM.es_o.publish_px('ContentChanged', ['topicList', null]);
break;
}
event_opl.stopPropagation();
event_opl.preventDefault();
},
enableButtons_p: function () {
$("#idLoginContent #idButtonArea button").each(function () {
if ($(this).attr("data-action") != "add") {
$(this).prop("disabled", false);
}
});
},
disableButtons_p: function () {
$("#idLoginContent #idButtonArea button").each(function () {
if ($(this).attr("data-action") != "add") {
$(this).prop("disabled", true);
}
});
}
});
// EOF

View File

@ -0,0 +1,29 @@
// ----------------------------------------------
// litnav.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.Nav_cl = Class.create({
// ----------------------------------------------
initialize: function () {
this.render_px();
this.initHandler_p();
},
render_px: function (data_opl) {
$('#idNav .clLinkToLogout').hide();
},
initHandler_p: function () {
$("#idNav").on("click", "a", function (event_opl) {
var action_s = $(event_opl.target).attr('data-action');
FORUM.es_o.publish_px('ContentChanged', [action_s, null]);
event_opl.stopPropagation();
event_opl.preventDefault();
});
}
});
// EOF

View File

@ -0,0 +1,124 @@
// ----------------------------------------------
// list_post.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.PostView_cl = Class.create({
// ----------------------------------------------
onClickButtons_p: function (event_opl) {
var action_s = $(event_opl.target).attr("data-action");
switch (action_s) {
case 'deletePost':
if (confirm("Soll der Datensatz gelöscht werden?")) {
var path_s = "/post/" + this.id_s;
$.ajax({
context: this,
// dataType: "json",
url: path_s,
type: 'DELETE'
})
.done(function (data_opl) {
FORUM.es_o.publish_px('ContentChanged', ['discussion', this.tId, this.dId]);
this.initList_p();
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[ThemenListe] Fehler bei Anforderung: " + textStatus_spl);
});
}
break;
case 'editPost':
FORUM.es_o.publish_px('ContentChanged', ['postEdit', this.tId, this.dId, this.id_s]);
break;
case 'back':
FORUM.es_o.publish_px('ContentChanged', ['discussion', this.tId, this.dId]);
break;
}
event_opl.stopPropagation();
event_opl.preventDefault();
},
storeContentToEditForm_p: function () {
content_s = $('#p' + this.rowId_s + ' #idPostTitle').text();
$('#idPostContentHeader #edittitle_s').text(content_s);
},
initialize: function () {
var that = this;
$.get('/html/post.html', function (data_spl) {
$("#idContentOuter").append(data_spl);
$("#idPostContent").hide();
that.initHandler_p();
that.initList_p();
});
},
render_px: function (data_opl) {
$("#pEditTitle").val('');
$("#pEditText").val('');
$.ajax({
dataType: "json",
url: '/post/' + data_opl[0] + '/' + data_opl[1] + '/' + data_opl[2],
type: 'GET'
})
.done($.proxy(this.doRender_p, this))
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[Liste] Fehler bei Anforderung: " + textStatus_spl);
});
},
doRender_p: function (data_opl) {
this.initList_p();
this.tId = data_opl['tId'];
this.dId = data_opl['dId'];
this.id_s = data_opl['id'];
data_o = {};
data_o[this.id_s] = data_opl['data'];
data_opl['data'] = data_o
data_opl['indices'] = [this.id_s];
var rows_s = FORUM.tm_o.execute_px('list_post.tpl', data_opl);
$("#idPost li").remove();
$("#idPost").append(rows_s);
$("#idPostContent").show();
console.log("[PostView_cl] doRender");
this.updateButtons_p();
},
initList_p: function () {
$(".clSelected").removeClass("clSelected");
this.id_s = '';
},
initHandler_p: function () {
$("#idPostContent").on("click", "button", $.proxy(this.onClickButtons_p, this));
},
enableButtons_p: function () {
$("#idPostContent .clButtonArea button[data-action=back]").prop("disabled", false);
if (FORUM.user_o.loggedin && $("#p" + this.id_s + " .clOwner").text() == FORUM.user_o.name_s) {
this.enableEditButtons_p();
}
},
enableEditButtons_p: function () {
$("#idPostContent .clButtonArea button").each(function () {
if ($(this).attr("data-action") == "deletePost" || $(this).attr("data-action") == "editPost") {
$(this).prop("disabled", false);
}
});
},
updateButtons_p: function () {
$("#idPostContent .clButtonArea button").each(function () {
$(this).prop("disabled", true);
});
this.enableButtons_p();
},
canClose_px: function () {
return true;
},
close_px: function () {
this.updateButtons_p();
$("#idPost li").remove();
$("#idPostContent").hide();
}
});
// EOF

View File

@ -0,0 +1,111 @@
// ----------------------------------------------
// register.js
// ----------------------------------------------
// ----------------------------------------------
FORUM.RegisterView_cl = Class.create({
// ----------------------------------------------
initialize: function () {
var that = this;
$.get('/html/register.html', function (data_spl) {
$("#idContentOuter").append(data_spl);
$("#idRegisterContent").hide();
that.initHandler_p();
});
FORUM.es_o.subscribe_px(this, 'register');
},
notify_px: function (self_opl, message_spl, data_apl) {
switch (message_spl) {
case 'register':
switch (data_apl[0]) {
case 'refresh':
self_opl.render_px(null);
break;
default:
console.warning('[RegisterView_cl] unbekannte list-Notification: ' + data_apl[0]);
break;
}
break;
default:
console.warning('[RegisterView_cl] unbekannte Notification: ' + message_spl);
break;
}
},
canClose_px: function () {
return true;
},
close_px: function () {
$("#idRegisterContent").hide();
},
render_px: function (data_opl) {
$.ajax({
dataType: "json",
url: '/register',
type: 'GET'
})
.done($.proxy(this.doRender_p, this))
.fail(function (jqXHR_opl, textStatus_spl) {
alert("[Register] Fehler bei Anforderung: " + textStatus_spl);
});
},
doRender_p: function (data_opl) {
$("#idRegisterContent").show();
console.log("[RegisterView_cl] doRender");
},
initHandler_p: function () {
$("#idRegisterContent #idButtonArea").on("click", "button", $.proxy(this.onClickButtons_p, this));
},
onClickButtons_p: function (event_opl) {
var action_s = $(event_opl.target).attr("data-action");
switch (action_s) {
case 'registerme':
var data_s = $("#idForm").serialize();
alert(data_s);
$.ajax({
context: this,
data: data_s,
url: '/register',
type: 'PUT'
})
.done(function (data_opl) {
alert("Speichern ausgeführt!");
})
.fail(function (jqXHR_opl, textStatus_spl) {
alert( "Fehler bei Anforderung: " + textStatus_spl );
});
FORUM.es_o.publish_px('app', [action_s, null]);
break;
}
event_opl.stopPropagation();
event_opl.preventDefault();
},
enableButtons_p: function () {
$("#idRegisterContent #idButtonArea button").each(function () {
if ($(this).attr("data-action") != "add") {
$(this).prop("disabled", false);
}
});
},
disableButtons_p: function () {
$("#idRegisterContent #idButtonArea button").each(function () {
if ($(this).attr("data-action") != "add") {
$(this).prop("disabled", true);
}
});
}
});
// EOF

View File

@ -0,0 +1,308 @@
//------------------------------------------------------------------------------
// Template-Engine
//------------------------------------------------------------------------------
// depends-on:
// inheritance
//------------------------------------------------------------------------------
// String-Methoden ergänzen
APPCO = {};
APPCO.apply = function(o, c, defaults){
// no "this" reference for friendly out of scope calls
if (defaults) {
APPCO.apply(o, defaults);
}
if (o && c && typeof c == 'object') {
for (var p in c) {
o[p] = c[p];
}
}
return o;
};
// quick and dirty! Manche Autoren lehnen solche Erweiterungen ab
APPCO.apply(String.prototype, {
include: function (pattern) {
return this.indexOf(pattern) > -1;
},
startsWith: function (pattern) {
return this.lastIndexOf(pattern, 0) === 0;
},
endsWith: function (pattern) {
var d = this.length - pattern.length;
return d >= 0 && this.indexOf(pattern, d) === d;
}
});
APPCO.apply(String, {
interpret: function(value) {
return value == null ? '' : String(value);
}
});
// Namensraum
var TELIB = {};
TELIB.Generator_cl = Class.create({
initialize: function () {
this.reset_px();
},
reset_px: function () {
this.code_a = ['var result_a = [];\n'];
},
write_px: function (text_spl) {
// Escape-Zeichen bei Strings ergänzen
var text_s = text_spl.replace(/"/g, '\\"');
text_s = text_s.replace(/'/g, "\\'");
this.code_a.push('result_a.push("' + text_s + '");\n');
},
code_px: function (code_spl) {
if (code_spl.startsWith('if')) {
this.code_a.push('if (' + code_spl.substr(2) + ') {\n');
} else if (code_spl.startsWith('else')) {
this.code_a.push('} else {\n');
} else if (code_spl.startsWith('endif')) {
this.code_a.push('}\n');
} else if (code_spl.startsWith('for')) {
this.code_a.push('for (' + code_spl.substr(3) + ') {\n');
} else if (code_spl.startsWith('endfor')) {
this.code_a.push('}\n');
} else {
this.code_a.push(code_spl + '\n');
}
},
substitute_px: function (subst_spl) {
this.code_a.push('result_a.push(' + String.interpret(subst_spl) + ');\n');
},
generate_px: function () {
var result_s = this.code_a.join('') + ' return result_a.join("");';
var f_o = new Function ('context', result_s);
return f_o;
}
});
TELIB.TemplateCompiler_cl = Class.create({
initialize: function () {
this.gen_o = new TELIB.Generator_cl();
this.reset_px();
},
reset_px: function () {
this.gen_o.reset_px();
this.preservePreWS_b = false;
this.preservePostWS_b = false;
},
setPreWS_px: function (on_bpl) {
if ((on_bpl == undefined) || (on_bpl == false)) {
this.preservePreWS_b = false;
} else {
this.preservePreWS_b = true;
}
},
setPostWS_px: function (on_bpl) {
if ((on_bpl == undefined) || (on_bpl == false)) {
this.preservePostWS_b = false;
} else {
this.preservePostWS_b = true;
}
},
compile_px: function (source_spl) {
var state_i = 0;
var pos_i = 0;
var len_i = source_spl.length;
var text_s = '';
var code_s = '';
var subst_s = '';
this.gen_o.reset_px();
var doubletest_f = function (char_spl) {
var status_b = false;
if ((pos_i + 1) < len_i) {
if ((source_spl[pos_i] == char_spl) && (source_spl[pos_i+1] == char_spl)) {
status_b = true;
}
}
return status_b;
}
while (pos_i < len_i) {
switch (state_i) {
case 0:
// outside
if (source_spl[pos_i] == '@') { // ECMA 5!
if (doubletest_f('@') == false) {
state_i = 2;
code_s = '';
} else {
// als normales Zeichen verarbeiten, ein Zeichen überlesen
state_i = 1;
text_s = '@';
pos_i++;
}
} else if (source_spl[pos_i] == '#') {
if (doubletest_f('#') == false) {
state_i = 3;
subst_s = '';
} else {
// als normales Zeichen verarbeiten, ein Zeichen überlesen
state_i = 1;
text_s = '#';
pos_i++;
}
} else if ((source_spl[pos_i] == ' ') || (source_spl[pos_i] == '\t')) {
state_i = 4;
text_s = '';
pos_i--; // Zeichen erneut verarbeiten
} else {
state_i = 1;
text_s = '';
pos_i--; // Zeichen erneut verarbeiten
}
break;
case 1:
// inText
if (source_spl[pos_i] == '@') {
if (doubletest_f('@') == false) {
// Textende, neuer Code
state_i = 0;
this.gen_o.write_px(text_s);
pos_i--; // Zeichen erneut verarbeiten
} else {
// als normales Zeichen verarbeiten, ein Zeichen überlesen
text_s += '@';
pos_i++;
}
} else if (source_spl[pos_i] == '#') {
if (doubletest_f('#') == false) {
// Textende, neue Substitution
state_i = 0;
this.gen_o.write_px(text_s);
pos_i--; // Zeichen erneut verarbeiten
// Textende, neuer Code
} else {
// als normales Zeichen verarbeiten, ein Zeichen überlesen
text_s += '#';
pos_i++;
}
} else if ((source_spl[pos_i] == ' ') || (source_spl[pos_i] == '\t')) {
// Textende
state_i = 0;
this.gen_o.write_px(text_s);
pos_i--; // Zeichen erneut verarbeiten
} else {
// sammeln
if ((source_spl[pos_i] != '\n') && (source_spl[pos_i] != '\r')) {
text_s += source_spl[pos_i];
} else if (source_spl[pos_i] == '\n') {
text_s += '\\n';
} else {
text_s += '\\r';
}
}
break;
case 2:
// inCode
if (source_spl[pos_i] == '@') {
if (doubletest_f('@') == false) {
// zu Ende, erzeugen
this.gen_o.code_px(code_s);
state_i = 5; //0
} else {
// als normales Zeichen verarbeiten, ein Zeichen überlesen
code_s += '@';
pos_i++;
}
} else {
// sammeln
code_s += source_spl[pos_i];
}
break;
case 3:
// inSubst
// Verdopplung # hier nicht zulässig!
if (source_spl[pos_i] == '#') {
// zu Ende, erzeugen
this.gen_o.substitute_px(subst_s);
state_i = 0;
} else {
// sammeln
subst_s += source_spl[pos_i];
}
break;
case 4:
// pre-code-whitespace
switch (source_spl[pos_i]) {
case ' ':
case '\t':
// sammeln
text_s += source_spl[pos_i];
break;
default:
state_i = 0;
if (source_spl[pos_i] != '@') {
this.gen_o.write_px(text_s);
} else {
if (doubletest_f('@') == false) {
// Whitespace vor Code-Beginn erkannt
if (this.preservePreWS_b == true) {
// trotzdem ausgeben
this.gen_o.write_px(text_s);
}
} // ansonsten wie normales Zeichen behandeln
}
pos_i--; // Zeichen erneut verarbeiten
}
break;
case 5:
// post-code-whitespace
switch (source_spl[pos_i]) {
case '\n':
text_s += '\\n';
state_i = 0;
break;
case '\r':
text_s += '\\r';
state_i = 0;
break;
case ' ':
case '\t':
// ggf. sammeln
text_s += source_spl[pos_i];
break;
default:
// Whitespace nach Code erkannt
if (this.preservePostWS_b == true) {
// trotzdem ausgeben
this.gen_o.write_px(text_s);
}
state_i = 0;
pos_i--; // Zeichen erneut verarbeiten
}
break;
}
pos_i++;
}
// welcher Zustand liegt abschließend vor?
if (state_i == 1) {
this.gen_o.write_px(text_s);
} else if (state_i == 2) {
this.gen_o.code_px(code_s);
} else if (state_i == 3) {
this.gen_o.substitute_px(subst_s);
} else if (state_i == 4) {
if (this.preservePreWS_b == true) {
this.gen_o.write_px(text_s);
}
} else if (state_i == 5) {
if (this.preservePostWS_b == true) {
this.gen_o.write_px(text_s);
}
}
return this.gen_o.generate_px();
}
});
// EOF

View File

@ -0,0 +1,61 @@
//------------------------------------------------------------------------------
// Template-Manager
// - Laden und Bereitstellen von Template-Quellen oder anderen Textquellen
//------------------------------------------------------------------------------
// depends-on:
// jquery
// inheritance
//------------------------------------------------------------------------------
// Namensraum TELIB verwenden
TELIB.TemplateManager_cl = Class.create({
initialize: function () {
this.templates_o = {};
this.compiled_o = {};
this.teCompiler_o = new TELIB.TemplateCompiler_cl();
// Templates als Ressource anfordern und speichern
var path_s = "/template/";
$.ajax({
dataType: "json",
url: path_s,
type: 'GET',
context: this
})
.done(function (data_opl) {
this.templates_o = data_opl['templates'];
// Benachrichtigung senden
//+++ Bezeichnung Namensraum korrigieren
FORUM.es_o.publish_px('ContentChanged', ['topicList', null]);
})
.fail(function(jqXHR_opl, textStatus_spl) {
alert( "[TELIB.tm] Fehler bei Anforderung: " + textStatus_spl );
});
},
get_px: function (name_spl) {
if (name_spl in this.templates_o) {
return this.templates_o[name_spl];
} else {
return null;
}
},
execute_px: function (name_spl, data_opl) {
var compiled_o = null;
if (name_spl in this.compiled_o) {
compiled_o = this.compiled_o[name_spl];
} else {
// Übersetzen und ausführen
if (name_spl in this.templates_o) {
this.teCompiler_o.reset_px();
compiled_o = this.teCompiler_o.compile_px(this.templates_o[name_spl]);
this.compiled_o[name_spl] = compiled_o;
}
}
if (compiled_o != null) {
return compiled_o(data_opl);
} else {
return null;
}
}
});
// EOF

View File

@ -0,0 +1,19 @@
[global]
tools.log_headers.on: True
tools.sessions.on: False
tools.encode.on: True
tools.encode.encoding:"utf-8"
server.socket_port: 8080
server.socket_timeout:60
server.thread_pool: 10
server.environment: "production"
log.screen: True
[/]
tools.staticdir.root: cherrypy.Application.currentDir_s
tools.staticdir.on = True
tools.staticdir.dir = '.'
tools.staticdir.index = 'html/index.html'

View File

@ -0,0 +1,66 @@
# coding:utf-8
import os.path
import cherrypy
from app import topic, discussion, post, login, template
#----------------------------------------------------------
def main():
#----------------------------------------------------------
# aktuelles Verzeichnis ermitteln, damit es in der Konfigurationsdatei als
# Bezugspunkt verwendet werden kann
try: # aktuelles Verzeichnis als absoluter Pfad
currentDir_s = os.path.dirname(os.path.abspath(__file__))
except:
currentDir_s = os.path.dirname(os.path.abspath(sys.executable))
cherrypy.Application.currentDir_s = currentDir_s
configFileName_s = 'server.conf' # im aktuellen Verzeichnis
if os.path.exists(configFileName_s) == False:
# Datei gibt es nicht
configFileName_s = None
# autoreload und timeout_Monitor hier abschalten
# für cherrypy-Versionen >= "3.1.0" !
cherrypy.engine.autoreload.unsubscribe()
cherrypy.engine.timeout_monitor.unsubscribe()
# Standardverhalten, Berücksichtigung der Konfigurationsangaben im configFile
cherrypy.tree.mount(
None, '/', configFileName_s
)
# Method-Dispatcher für die "Applikation" "topic" vereinbaren
cherrypy.tree.mount(
topic.Topic_cl(), '/topic', {'/': {'request.dispatch': cherrypy.dispatch.MethodDispatcher()}}
)
# Method-Dispatcher für die "Applikation" "discussion" vereinbaren
cherrypy.tree.mount(
discussion.Discussion_cl(), '/discussion', {'/': {'request.dispatch': cherrypy.dispatch.MethodDispatcher()}}
)
# Method-Dispatcher für die "Applikation" "discussion" vereinbaren
cherrypy.tree.mount(
post.Post_cl(), '/post', {'/': {'request.dispatch': cherrypy.dispatch.MethodDispatcher()}}
)
# Method-Dispatcher für die "Applikation" "login" vereinbaren
cherrypy.tree.mount(
login.Login_cl(), '/login', {'/': {'request.dispatch': cherrypy.dispatch.MethodDispatcher()}}
)
# Method-Dispatcher für die "Applikation" "template" vereinbaren
cherrypy.tree.mount(
template.Template_cl(), '/template', {'/': {'request.dispatch': cherrypy.dispatch.MethodDispatcher()}}
)
cherrypy.engine.start()
cherrypy.engine.block()
#----------------------------------------------------------
if __name__ == '__main__':
#----------------------------------------------------------
main()

View File

@ -0,0 +1,2 @@
server.py
pause

View File

@ -0,0 +1,16 @@
@var rows_o = context['data'];@
@var order = context['indices'];@
@for var key in order@
@if rows_o[order[key]]["deleted"]@
<tr id="deleted">
<td class="clName">&gt;&gt;Gel&ouml;scht durch #rows_o[order[key]]["owner"]#&lt;&lt;</td>
@else@
<tr id="d#order[key]#">
<td class="clName">#rows_o[order[key]]["name"]#</td>
@endif@
<td class="clOwner">#rows_o[order[key]]["owner"]#</td>
</tr>
@endfor@

View File

@ -0,0 +1,27 @@
@var rows_o = context['data'];@
@var order = context['indices'];@
@for var key in order@
@if rows_o[order[key]]["deleted"]@
<li id="deleted">
@else@
<li id="p#order[key]#">
@endif@
<div class="clPostContainer">
<div class="clMetaDataContainer">
<div class="clOwner">#rows_o[order[key]]["owner"]#</div>
<div class="clTime">#rows_o[order[key]]["time"][2]#.#rows_o[order[key]]["time"][1]#.#rows_o[order[key]]["time"][0]#<br>
#rows_o[order[key]]["time"][3]#:#rows_o[order[key]]["time"][4]#</div>
</div>
<div class="clPostDataContainer">
@if rows_o[order[key]]["deleted"]@
<div class="clTitle">&gt;&gt;Gel&ouml;scht&lt;&lt;</div>
<div class="clText">&gt;&gt;Gel&ouml;scht&lt;&lt;</div>
@else@
<div class="clTitle">#rows_o[order[key]]["title"]#</div>
<div class="clText">#rows_o[order[key]]["text"]#</div>
@endif@
</div>
</div>
</li>
@endfor@

View File

@ -0,0 +1,16 @@
@var rows_o = context['data'];@
@var order = context['indices'];@
@for var key in order@
@if rows_o[order[key]]["deleted"]@
<tr id="deleted">
<td class="clName">&gt;&gt;Gel&ouml;scht durch #rows_o[order[key]]["owner"]#&lt;&lt;</td>
@else@
<tr id="t#order[key]#">
<td class="clName">#rows_o[order[key]]["name"]#</td>
@endif@
<td class="clOwner">#rows_o[order[key]]["owner"]#</td>
</tr>
@endfor@