summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkmccarth@redhat.com <kmccarth@redhat.com>2007-08-12 04:53:18 -0700
committerkmccarth@redhat.com <kmccarth@redhat.com>2007-08-12 04:53:18 -0700
commitcdaee95230322744be0a254b57832c5a35be4ca2 (patch)
treeb01adeb19e8f873c9cb1602cb024b06c56ed5d33
parent9a01f353cff7bdba3fdaa2aca2d29abf3c80c021 (diff)
downloadfreeipa-cdaee95230322744be0a254b57832c5a35be4ca2.tar.gz
freeipa-cdaee95230322744be0a254b57832c5a35be4ca2.tar.xz
freeipa-cdaee95230322744be0a254b57832c5a35be4ca2.zip
Addiing initial turbogears web gui.
Contains simple user add, list, and view pages.
-rw-r--r--ipa-server/ipa-gui/README.txt4
-rw-r--r--ipa-server/ipa-gui/dev.cfg66
-rw-r--r--ipa-server/ipa-gui/ipa_gui.egg-info/PKG-INFO15
-rw-r--r--ipa-server/ipa-gui/ipa_gui.egg-info/SOURCES.txt21
-rw-r--r--ipa-server/ipa-gui/ipa_gui.egg-info/dependency_links.txt1
-rw-r--r--ipa-server/ipa-gui/ipa_gui.egg-info/not-zip-safe1
-rw-r--r--ipa-server/ipa-gui/ipa_gui.egg-info/paster_plugins.txt2
-rw-r--r--ipa-server/ipa-gui/ipa_gui.egg-info/requires.txt1
-rw-r--r--ipa-server/ipa-gui/ipa_gui.egg-info/sqlobject.txt2
-rw-r--r--ipa-server/ipa-gui/ipa_gui.egg-info/top_level.txt1
-rw-r--r--ipa-server/ipa-gui/ipagui/config/app.cfg51
-rw-r--r--ipa-server/ipa-gui/ipagui/config/log.cfg29
-rw-r--r--ipa-server/ipa-gui/ipagui/controllers.py168
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/user.py67
-rw-r--r--ipa-server/ipa-gui/ipagui/json.py10
-rw-r--r--ipa-server/ipa-gui/ipagui/model.py9
-rw-r--r--ipa-server/ipa-gui/ipagui/release.py14
-rw-r--r--ipa-server/ipa-gui/ipagui/static/css/style.css146
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/groupindex.kid18
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/grouplayout.kid22
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/login.kid112
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/master.kid48
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/resindex.kid18
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/reslayout.kid22
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/useredit.kid13
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/userform.kid125
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/userlayout.kid22
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/userlist.kid26
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/usernew.kid13
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/usershow.kid50
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/welcome.kid18
-rw-r--r--ipa-server/ipa-gui/ipagui/tests/test_controllers.py32
-rw-r--r--ipa-server/ipa-gui/ipagui/tests/test_model.py22
-rw-r--r--ipa-server/ipa-gui/sample-prod.cfg84
-rw-r--r--ipa-server/ipa-gui/setup.py62
-rw-r--r--ipa-server/ipa-gui/start-ipagui.py25
-rw-r--r--ipa-server/ipa-gui/test.cfg4
37 files changed, 1344 insertions, 0 deletions
diff --git a/ipa-server/ipa-gui/README.txt b/ipa-server/ipa-gui/README.txt
new file mode 100644
index 000000000..876b880b1
--- /dev/null
+++ b/ipa-server/ipa-gui/README.txt
@@ -0,0 +1,4 @@
+ipa-gui
+
+This is a TurboGears (http://www.turbogears.org) project. It can be
+started by running the start-ipagui.py script. \ No newline at end of file
diff --git a/ipa-server/ipa-gui/dev.cfg b/ipa-server/ipa-gui/dev.cfg
new file mode 100644
index 000000000..7bb0fd8c4
--- /dev/null
+++ b/ipa-server/ipa-gui/dev.cfg
@@ -0,0 +1,66 @@
+[global]
+# This is where all of your settings go for your development environment
+# Settings that are the same for both development and production
+# (such as template engine, encodings, etc.) all go in
+# ipagui/config/app.cfg
+
+# DATABASE
+
+# pick the form for your database
+# sqlobject.dburi="postgres://username@hostname/databasename"
+# sqlobject.dburi="mysql://username:password@hostname:port/databasename"
+# sqlobject.dburi="sqlite:///file_name_and_path"
+
+# If you have sqlite, here's a simple default to get you started
+# in development
+sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite"
+
+
+# if you are using a database or table type without transactions
+# (MySQL default, for example), you should turn off transactions
+# by prepending notrans_ on the uri
+# sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename"
+
+# for Windows users, sqlite URIs look like:
+# sqlobject.dburi="sqlite:///drive_letter:/path/to/file"
+
+# SERVER
+
+# Some server parameters that you may want to tweak
+# server.socket_port=8080
+
+# Enable the debug output at the end on pages.
+# log_debug_info_filter.on = False
+
+server.environment="development"
+autoreload.package="ipagui"
+
+# Auto-Reload after code modification
+# autoreload.on = True
+
+# Set to True if you'd like to abort execution if a controller gets an
+# unexpected parameter. False by default
+tg.strict_parameters = True
+
+# LOGGING
+# Logging configuration generally follows the style of the standard
+# Python logging module configuration. Note that when specifying
+# log format messages, you need to use *() for formatting variables.
+# Deployment independent log configuration is in ipagui/config/log.cfg
+[logging]
+
+[[loggers]]
+[[[ipagui]]]
+level='DEBUG'
+qualname='ipagui'
+handlers=['debug_out']
+
+[[[allinfo]]]
+level='INFO'
+handlers=['debug_out']
+
+[[[access]]]
+level='INFO'
+qualname='turbogears.access'
+handlers=['access_out']
+propagate=0
diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/PKG-INFO b/ipa-server/ipa-gui/ipa_gui.egg-info/PKG-INFO
new file mode 100644
index 000000000..544ba9f98
--- /dev/null
+++ b/ipa-server/ipa-gui/ipa_gui.egg-info/PKG-INFO
@@ -0,0 +1,15 @@
+Metadata-Version: 1.0
+Name: ipa-gui
+Version: 1.0
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 3 - Alpha
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Framework :: TurboGears
diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/SOURCES.txt b/ipa-server/ipa-gui/ipa_gui.egg-info/SOURCES.txt
new file mode 100644
index 000000000..49b7ce44d
--- /dev/null
+++ b/ipa-server/ipa-gui/ipa_gui.egg-info/SOURCES.txt
@@ -0,0 +1,21 @@
+README.txt
+setup.py
+start-ipagui.py
+ipa_gui.egg-info/PKG-INFO
+ipa_gui.egg-info/SOURCES.txt
+ipa_gui.egg-info/dependency_links.txt
+ipa_gui.egg-info/not-zip-safe
+ipa_gui.egg-info/paster_plugins.txt
+ipa_gui.egg-info/requires.txt
+ipa_gui.egg-info/sqlobject.txt
+ipa_gui.egg-info/top_level.txt
+ipagui/__init__.py
+ipagui/controllers.py
+ipagui/json.py
+ipagui/model.py
+ipagui/release.py
+ipagui/config/__init__.py
+ipagui/templates/__init__.py
+ipagui/tests/__init__.py
+ipagui/tests/test_controllers.py
+ipagui/tests/test_model.py
diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/dependency_links.txt b/ipa-server/ipa-gui/ipa_gui.egg-info/dependency_links.txt
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/ipa-server/ipa-gui/ipa_gui.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/not-zip-safe b/ipa-server/ipa-gui/ipa_gui.egg-info/not-zip-safe
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/ipa-server/ipa-gui/ipa_gui.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/paster_plugins.txt b/ipa-server/ipa-gui/ipa_gui.egg-info/paster_plugins.txt
new file mode 100644
index 000000000..14fec70ae
--- /dev/null
+++ b/ipa-server/ipa-gui/ipa_gui.egg-info/paster_plugins.txt
@@ -0,0 +1,2 @@
+TurboGears
+PasteScript
diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/requires.txt b/ipa-server/ipa-gui/ipa_gui.egg-info/requires.txt
new file mode 100644
index 000000000..aecc30960
--- /dev/null
+++ b/ipa-server/ipa-gui/ipa_gui.egg-info/requires.txt
@@ -0,0 +1 @@
+TurboGears >= 1.0.2.2 \ No newline at end of file
diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/sqlobject.txt b/ipa-server/ipa-gui/ipa_gui.egg-info/sqlobject.txt
new file mode 100644
index 000000000..cc84b8d00
--- /dev/null
+++ b/ipa-server/ipa-gui/ipa_gui.egg-info/sqlobject.txt
@@ -0,0 +1,2 @@
+db_module=ipagui.model
+history_dir=$base/ipagui/sqlobject-history
diff --git a/ipa-server/ipa-gui/ipa_gui.egg-info/top_level.txt b/ipa-server/ipa-gui/ipa_gui.egg-info/top_level.txt
new file mode 100644
index 000000000..2ba21519d
--- /dev/null
+++ b/ipa-server/ipa-gui/ipa_gui.egg-info/top_level.txt
@@ -0,0 +1 @@
+ipagui
diff --git a/ipa-server/ipa-gui/ipagui/config/app.cfg b/ipa-server/ipa-gui/ipagui/config/app.cfg
new file mode 100644
index 000000000..38d896f27
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/config/app.cfg
@@ -0,0 +1,51 @@
+[global]
+# The settings in this file should not vary depending on the deployment
+# environment. dev.cfg and prod.cfg are the locations for
+# the different deployment settings. Settings in this file will
+# be overridden by settings in those other files.
+
+# The commented out values below are the defaults
+
+# VIEW
+
+# which view (template engine) to use if one is not specified in the
+# template name
+# tg.defaultview = "kid"
+
+# The following kid settings determine the settings used by the kid serializer.
+
+# One of (html|html-strict|xhtml|xhtml-strict|xml|json)
+# kid.outputformat="html"
+
+# kid.encoding="utf-8"
+
+# The sitetemplate is used for overall styling of a site that
+# includes multiple TurboGears applications
+# tg.sitetemplate="<packagename.templates.templatename>"
+
+# Allow every exposed function to be called as json,
+# tg.allow_json = False
+
+# List of Widgets to include on every page.
+# for exemple ['turbogears.mochikit']
+# tg.include_widgets = []
+
+# Set to True if the scheduler should be started
+# tg.scheduler = False
+
+# Set session or cookie
+# session_filter.on = True
+
+
+# compress the data sends to the web browser
+# [/]
+# gzip_filter.on = True
+# gzip_filter.mime_types = ["application/x-javascript", "text/javascript", "text/html", "text/css", "text/plain"]
+
+[/static]
+static_filter.on = True
+static_filter.dir = "%(top_level_dir)s/static"
+
+[/favicon.ico]
+static_filter.on = True
+static_filter.file = "%(top_level_dir)s/static/images/favicon.ico"
diff --git a/ipa-server/ipa-gui/ipagui/config/log.cfg b/ipa-server/ipa-gui/ipagui/config/log.cfg
new file mode 100644
index 000000000..ce776f850
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/config/log.cfg
@@ -0,0 +1,29 @@
+# LOGGING
+# Logging is often deployment specific, but some handlers and
+# formatters can be defined here.
+
+[logging]
+[[formatters]]
+[[[message_only]]]
+format='*(message)s'
+
+[[[full_content]]]
+format='*(asctime)s *(name)s *(levelname)s *(message)s'
+
+[[handlers]]
+[[[debug_out]]]
+class='StreamHandler'
+level='DEBUG'
+args='(sys.stdout,)'
+formatter='full_content'
+
+[[[access_out]]]
+class='StreamHandler'
+level='INFO'
+args='(sys.stdout,)'
+formatter='message_only'
+
+[[[error_out]]]
+class='StreamHandler'
+level='ERROR'
+args='(sys.stdout,)'
diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py
new file mode 100644
index 000000000..361a5cbdf
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/controllers.py
@@ -0,0 +1,168 @@
+import cherrypy
+import turbogears
+from turbogears import controllers, expose, flash
+from turbogears import validators, validate
+from turbogears import widgets, paginate
+from turbogears import error_handler
+# from model import *
+# import logging
+# log = logging.getLogger("ipagui.controllers")
+# import ipa.rpcclient
+import ipa.config
+import ipa.ipaclient
+import ipa.user
+import xmlrpclib
+import forms.user
+
+ipa.config.init_config()
+user_form = forms.user.UserFormWidget()
+
+client = ipa.ipaclient.IPAClient(True)
+client.set_principal("test@FREEIPA.ORG")
+
+def restrict_post():
+ if cherrypy.request.method != "POST":
+ turbogears.flash("This method only accepts posts")
+ raise turbogears.redirect("/")
+
+def user_to_hash(user):
+ return {
+ 'uid' : user.getValue('uid'),
+ 'givenName' : user.getValue('givenName'),
+ 'sn' : user.getValue('sn'),
+ 'mail' : user.getValue('mail'),
+ 'telephoneNumber': user.getValue('telephoneNumber'),
+ 'uidNumber': user.getValue('uidNumber'),
+ 'gidNumber': user.getValue('gidNumber'),
+ }
+
+class Root(controllers.RootController):
+
+ @expose(template="ipagui.templates.welcome")
+ def index(self):
+ return dict()
+
+
+ ########
+ # User #
+ ########
+
+ @expose("ipagui.templates.usernew")
+ def usernew(self, tg_errors=None):
+ """Displays the new user form"""
+ if tg_errors:
+ turbogears.flash("There was a problem with the form!")
+
+ return dict(form=user_form)
+
+ @expose()
+ def usercreate(self, **kw):
+ """Creates a new user"""
+ restrict_post()
+ if kw.get('submit') == 'Cancel':
+ turbogears.flash("Add user cancelled")
+ raise turbogears.redirect('/userlist')
+
+ tg_errors, kw = self.uservalidate(**kw)
+ if tg_errors:
+ return dict(form=user_form, tg_template='ipagui.templates.usernew')
+
+ try:
+ # rv = ipa.rpcclient.add_user(kw)
+ newuser = ipa.user.User(None)
+ newuser.setValue('uid', kw['uid'])
+ newuser.setValue('givenName', kw['givenName'])
+ newuser.setValue('sn', kw['sn'])
+ newuser.setValue('mail', kw['mail'])
+ newuser.setValue('telephoneNumber', kw['telephoneNumber'])
+ newuser2 = {
+ 'uid' : kw['uid'],
+ 'givenName' : kw['givenName'],
+ 'sn' : kw['sn'],
+ 'mail' : kw['mail'],
+ 'telephoneNumber': kw['telephoneNumber']
+ }
+ rv = client.add_user(newuser2)
+ turbogears.flash("%s added!" % kw['uid'])
+ raise turbogears.redirect('/usershow', uid=kw['uid'])
+ except xmlrpclib.Fault, f:
+ turbogears.flash("User add failed: " + str(f.faultString))
+ return dict(form=user_form, tg_template='ipagui.templates.usernew')
+
+
+ @expose("ipagui.templates.useredit")
+ def useredit(self, uid, tg_errors=None):
+ """Displays the edit user form"""
+ if tg_errors:
+ turbogears.flash("There was a problem with the form!")
+
+ # user = ipa.rpcclient.get_user(uid)
+ user = client.get_user(uid)
+ return dict(form=user_form, user=user_to_hash(user))
+
+ @expose()
+ def userupdate(self, **kw):
+ """Updates an existing user"""
+ restrict_post()
+ if kw.get('submit') == 'Cancel':
+ turbogears.flash("Edit user cancelled")
+ raise turbogears.redirect('/usershow', uid=kw.get('uid'))
+
+ tg_errors, kw = self.uservalidate(**kw)
+ if tg_errors:
+ return dict(form=user_form, user={}, tg_template='ipagui.templates.useredit')
+
+ try:
+ # rv = ipa.rpcclient.add_user(kw)
+ turbogears.flash("%s updated!" % kw['uid'])
+ raise turbogears.redirect('/usershow', uid=kw['uid'])
+ except xmlrpclib.Fault, f:
+ turbogears.flash("User add failed: " + str(f.faultString))
+ return dict(form=user_form, user={}, tg_template='ipagui.templates.useredit')
+
+
+ @expose("ipagui.templates.userlist")
+ @paginate('users', limit=3, allow_limit_override=True)
+ def userlist(self):
+ """Retrieve a list of all users and display them in one huge list"""
+ # users = ipa.rpcclient.get_all_users()
+ users = client.get_all_users()
+ return dict(users=users)
+
+
+ @expose("ipagui.templates.usershow")
+ def usershow(self, uid):
+ """Retrieve a single user for display"""
+ try:
+ # user = ipa.rpcclient.get_user(uid)
+ user = client.get_user(uid)
+ return dict(user=user_to_hash(user))
+ except xmlrpclib.Fault, f:
+ turbogears.flash("User show failed: " + str(f.faultString))
+ raise turbogears.redirect("/")
+
+ @validate(form=user_form)
+ def uservalidate(self, tg_errors=None, **kw):
+ return tg_errors, kw
+
+ @expose()
+ def userindex(self):
+ raise turbogears.redirect("/userlist")
+
+
+ #########
+ # Group #
+ #########
+
+ @expose("ipagui.templates.groupindex")
+ def groupindex(self, tg_errors=None):
+ return dict()
+
+
+ ############
+ # Resource #
+ ############
+
+ @expose("ipagui.templates.resindex")
+ def resindex(self, tg_errors=None):
+ return dict()
diff --git a/ipa-server/ipa-gui/ipagui/forms/user.py b/ipa-server/ipa-gui/ipagui/forms/user.py
new file mode 100644
index 000000000..3cb4aed49
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/forms/user.py
@@ -0,0 +1,67 @@
+import turbogears
+from turbogears import validators, widgets
+
+class UserFields():
+ uid = widgets.TextField(name="uid", label="Login:")
+ userPassword = widgets.TextField(name="userPassword", label="Password:")
+ uidNumber = widgets.TextField(name="uidNumber", label="UID:")
+ gidNumber = widgets.TextField(name="gidNumber", label="GID:")
+ givenName = widgets.TextField(name="givenName", label="First name:")
+ sn = widgets.TextField(name="sn", label="Last name:")
+ mail = widgets.TextField(name="mail", label="E-mail address:")
+ telephoneNumber = widgets.TextField(name="telephoneNumber", label="Phone:")
+
+ uid.validator = validators.PlainText(not_empty=True)
+ userPassword.validator = validators.String(not_empty=True)
+ givenName.validator = validators.String(not_empty=True)
+ sn.validator = validators.String(not_empty=True)
+ mail.validator = validators.Email(not_empty=True)
+ # validators.PhoneNumber may be a bit too picky, requiring an area code
+ telephoneNumber.validator = validators.PlainText(not_empty=True)
+
+
+class UserFormWidget(widgets.Form):
+ params = ['user']
+# fields = [UserFields.uid, UserFields.userPassword, UserFields.givenName,
+# UserFields.sn, UserFields.mail]
+ fields = [UserFields.uid, UserFields.givenName,
+ UserFields.uidNumber, UserFields.gidNumber,
+ UserFields.sn, UserFields.mail]
+
+ def __init__(self, *args, **kw):
+ super(UserFormWidget,self).__init__(*args, **kw)
+ (self.template_c, self.template) = widgets.meta.load_kid_template("ipagui.templates.userform")
+ self.user = UserFields
+
+ def update_params(self, params):
+ super(UserFormWidget,self).update_params(params)
+ params['has_foo'] = self.has_foo
+
+ def has_foo(self):
+ return False
+
+# TODO - add dynamic field retrieval:
+# myfields=[]
+# schema = ipa.rpcclient.get_add_schema ()
+#
+# # FIXME: What if schema is None or an error is thrown?
+#
+# for s in schema:
+# required=False
+#
+# if (s['type'] == "text"):
+# field = widgets.TextField(name=s['name'],label=s['label'])
+# elif (s['type'] == "password"):
+# field = widgets.PasswordField(name=s['name'],label=s['label'])
+#
+# if (s['required'] == "true"):
+# required=True
+#
+# if (s['validator'] == "text"):
+# field.validator=validators.PlainText(not_empty=required)
+# elif (s['validator'] == "email"):
+# field.validator=validators.Email(not_empty=required)
+# elif (s['validator'] == "string"):
+# field.validator=validators.String(not_empty=required)
+#
+# myfields.append(field)
diff --git a/ipa-server/ipa-gui/ipagui/json.py b/ipa-server/ipa-gui/ipagui/json.py
new file mode 100644
index 000000000..66d5cfb65
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/json.py
@@ -0,0 +1,10 @@
+# A JSON-based API(view) for your app.
+# Most rules would look like:
+# @jsonify.when("isinstance(obj, YourClass)")
+# def jsonify_yourclass(obj):
+# return [obj.val1, obj.val2]
+# @jsonify can convert your objects to following types:
+# lists, dicts, numbers and strings
+
+from turbojson.jsonify import jsonify
+
diff --git a/ipa-server/ipa-gui/ipagui/model.py b/ipa-server/ipa-gui/ipagui/model.py
new file mode 100644
index 000000000..244cfff70
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/model.py
@@ -0,0 +1,9 @@
+from turbogears.database import PackageHub
+from sqlobject import *
+
+hub = PackageHub('ipagui')
+__connection__ = hub
+
+# class YourDataClass(SQLObject):
+# pass
+
diff --git a/ipa-server/ipa-gui/ipagui/release.py b/ipa-server/ipa-gui/ipagui/release.py
new file mode 100644
index 000000000..7be40c007
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/release.py
@@ -0,0 +1,14 @@
+# Release information about ipa-gui
+
+version = "1.0"
+
+# description = "Your plan to rule the world"
+# long_description = "More description about your plan"
+# author = "Your Name Here"
+# email = "YourEmail@YourDomain"
+# copyright = "Vintage 2006 - a good year indeed"
+
+# if it's open source, you might want to specify these
+# url = "http://yourcool.site/"
+# download_url = "http://yourcool.site/download"
+# license = "MIT"
diff --git a/ipa-server/ipa-gui/ipagui/static/css/style.css b/ipa-server/ipa-gui/ipagui/static/css/style.css
new file mode 100644
index 000000000..9cd7fe094
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/static/css/style.css
@@ -0,0 +1,146 @@
+/*
+ * Quick mash-up of CSS for the TG quick start page.
+ */
+
+html, body {
+ color: #000;
+ background:#fff;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ min-width: 750px;
+}
+
+#page {
+ background:#ccc; /* should be same as #sidebar */
+ margin:0 auto;
+ width:100%;
+}
+
+
+#header {
+ background:#fff;
+}
+
+#header h1 {
+ padding:5px;
+ margin:0;
+}
+
+
+#nav {
+ background:#cc0000;
+ color:#fff;
+ padding:5px;
+}
+
+#nav ul {
+ margin:0;
+ padding:0;
+ list-style:none;
+}
+
+#nav li {
+ display:inline;
+}
+
+#nav a:visited {
+ color:#fff;
+}
+#nav a:link {
+ color:#fff;
+}
+
+
+#main_content {
+ background:#fff;
+ float:right;
+ width:85%;
+ border-left: 1px solid #000;
+ padding-left: 15px;
+ padding-bottom: 15px;
+/* color: black;
+ font-size: 127%;
+ background-color: white;
+ margin: 0 auto 0 auto;
+ padding: 10px;
+ float: left; */
+}
+
+
+#sidebar {
+ background:#ccc; /* should be same as #page */
+ float:left;
+ width:10%;
+ /* border: 1px solid #aaa;
+ background-color: #eee;
+ margin: 0.5em;
+ padding: 1em;
+ float: left;
+ font-size: 88%; */
+}
+
+#sidebar h2 {
+ margin-top: 0;
+}
+
+#sidebar ul {
+ margin-left: 1.5em;
+ padding-left: 0;
+}
+
+
+#footer {
+ background:#fff;
+ clear:both;
+ border-top: 1px solid #000;
+ /* color: #999;
+ background-color: white;
+ padding: 10px;
+ font-size: 80%;
+ text-align: center;
+ margin: 0 auto 1em auto; */
+}
+
+
+.formsection {
+ color: #888888;
+ width: 90%;
+ font-weight: bold;
+ border-bottom: 1px solid;
+ margin: 20px 0px 20px 0px;
+}
+
+.formtable {
+ width: 90%;
+}
+
+.formtable th {
+ width: 15%;
+ text-align: right;
+}
+
+#status_block {
+ margin: 0 auto 0.5em auto;
+ padding: 15px 10px 15px 55px;
+ background: #cec URL('../images/ok.png') left center no-repeat;
+ border: 1px solid #9c9;
+ width: 450px;
+ font-size: 120%;
+ font-weight: bolder;
+}
+
+.notice {
+ margin: 0.5em auto 0.5em auto;
+ padding: 15px 10px 15px 55px;
+ width: 450px;
+ background: #eef URL('../images/info.png') left center no-repeat;
+ border: 1px solid #cce;
+}
+
+.fielderror {
+ color: red;
+ font-weight: bold;
+}
diff --git a/ipa-server/ipa-gui/ipagui/templates/groupindex.kid b/ipa-server/ipa-gui/ipagui/templates/groupindex.kid
new file mode 100644
index 000000000..0bb70ac9a
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/groupindex.kid
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'grouplayout.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>Group Listing</title>
+</head>
+<body>
+ Groups go here.
+ <br />
+ <br />
+ <br />
+ <br />
+ <br />
+ <br />
+ <br />
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/grouplayout.kid b/ipa-server/ipa-gui/ipagui/templates/grouplayout.kid
new file mode 100644
index 000000000..af05a8082
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/grouplayout.kid
@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'master.kid'">
+<head>
+</head>
+
+<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()">
+ <div id="main_content">
+ <div id="status_block" class="flash" py:if="value_of('tg_flash', None)" py:content="tg_flash"></div>
+
+ <div py:replace="[item.text]+item[:]"></div>
+ </div>
+
+ <div id="sidebar">
+ <h2>Tools</h2>
+ <a href="${tg.url('/groupindex')}">Add Group</a><br/>
+ <a href="${tg.url('/groupindex')}">Find Group</a><br/>
+ <a href="${tg.url('/groupindex')}">List Groups</a><br/>
+ </div>
+</body>
+
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/login.kid b/ipa-server/ipa-gui/ipagui/templates/login.kid
new file mode 100644
index 000000000..a819cfc7a
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/login.kid
@@ -0,0 +1,112 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://purl.org/kid/ns#">
+
+<head>
+ <meta content="text/html; charset=UTF-8"
+ http-equiv="content-type" py:replace="''"/>
+ <title>Login</title>
+ <style type="text/css">
+ #loginBox
+ {
+ width: 30%;
+ margin: auto;
+ margin-top: 10%;
+ padding-left: 10%;
+ padding-right: 10%;
+ padding-top: 5%;
+ padding-bottom: 5%;
+ font-family: verdana;
+ font-size: 10px;
+ background-color: #eee;
+ border: 2px solid #ccc;
+ }
+
+ #loginBox h1
+ {
+ font-size: 42px;
+ font-family: "Trebuchet MS";
+ margin: 0;
+ color: #ddd;
+ }
+
+ #loginBox p
+ {
+ position: relative;
+ top: -1.5em;
+ padding-left: 4em;
+ font-size: 12px;
+ margin: 0;
+ color: #666;
+ }
+
+ #loginBox table
+ {
+ table-layout: fixed;
+ border-spacing: 0;
+ width: 100%;
+ }
+
+ #loginBox td.label
+ {
+ width: 33%;
+ text-align: right;
+ }
+
+ #loginBox td.field
+ {
+ width: 66%;
+ }
+
+ #loginBox td.field input
+ {
+ width: 100%;
+ }
+
+ #loginBox td.buttons
+ {
+ text-align: right;
+ }
+
+ </style>
+</head>
+
+<body>
+ <div id="loginBox">
+ <h1>Login</h1>
+ <p>${message}</p>
+ <form action="${previous_url}" method="POST">
+ <table>
+ <tr>
+ <td class="label">
+ <label for="user_name">User Name:</label>
+ </td>
+ <td class="field">
+ <input type="text" id="user_name" name="user_name"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="label">
+ <label for="password">Password:</label>
+ </td>
+ <td class="field">
+ <input type="password" id="password" name="password"/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2" class="buttons">
+ <input type="submit" name="login" value="Login"/>
+ </td>
+ </tr>
+ </table>
+
+ <input py:if="forward_url" type="hidden" name="forward_url"
+ value="${forward_url}"/>
+
+ <input py:for="name,value in original_parameters.items()"
+ type="hidden" name="${name}" value="${value}"/>
+ </form>
+ </div>
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/master.kid b/ipa-server/ipa-gui/ipagui/templates/master.kid
new file mode 100644
index 000000000..464560d26
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/master.kid
@@ -0,0 +1,48 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<?python import sitetemplate ?>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="sitetemplate">
+
+<head py:match="item.tag=='{http://www.w3.org/1999/xhtml}head'" py:attrs="item.items()">
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+ <title py:replace="''">Your title goes here</title>
+ <meta py:replace="item[:]"/>
+ <style type="text/css" media="screen">
+ @import "${tg.url('/static/css/style.css')}";
+ </style>
+</head>
+
+<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()">
+ <div py:if="tg.config('identity.on') and not defined('logging_in')" id="pageLogin">
+ <span py:if="tg.identity.anonymous">
+ <a href="${tg.url('/login')}">Login</a>
+ </span>
+ <span py:if="not tg.identity.anonymous">
+ Welcome ${tg.identity.user.display_name}.
+ <a href="${tg.url('/logout')}">Logout</a>
+ </span>
+ </div>
+
+ <div id="page">
+ <div id="header">
+ <h1>Free IPA</h1>
+ </div>
+
+ <div id="nav">
+ <ul>
+ <li><a href="${tg.url('/userindex')}">Users</a></li>
+ <li><a href="${tg.url('/groupindex')}">Groups</a></li>
+ <li><a href="${tg.url('/resindex')}">Resources</a></li>
+ </ul>
+ </div>
+
+ <div py:replace="[item.text]+item[:]"></div>
+
+
+ <div id="footer">
+ This is the footer
+ </div>
+ </div>
+
+</body>
+
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/resindex.kid b/ipa-server/ipa-gui/ipagui/templates/resindex.kid
new file mode 100644
index 000000000..5cd06f7eb
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/resindex.kid
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'reslayout.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>Resource Listing</title>
+</head>
+<body>
+ Resources go here.
+ <br />
+ <br />
+ <br />
+ <br />
+ <br />
+ <br />
+ <br />
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/reslayout.kid b/ipa-server/ipa-gui/ipagui/templates/reslayout.kid
new file mode 100644
index 000000000..5b9b35c12
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/reslayout.kid
@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'master.kid'">
+<head>
+</head>
+
+<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()">
+ <div id="main_content">
+ <div id="status_block" class="flash" py:if="value_of('tg_flash', None)" py:content="tg_flash"></div>
+
+ <div py:replace="[item.text]+item[:]"></div>
+ </div>
+
+ <div id="sidebar">
+ <h2>Tools</h2>
+ <a href="${tg.url('/resindex')}">Add Resource</a><br/>
+ <a href="${tg.url('/resindex')}">Find Resource</a><br/>
+ <a href="${tg.url('/resindex')}">List Resources</a><br/>
+ </div>
+</body>
+
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/useredit.kid b/ipa-server/ipa-gui/ipagui/templates/useredit.kid
new file mode 100644
index 000000000..781ff7ff1
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/useredit.kid
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'userlayout.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>Edit a Person</title>
+</head>
+<body>
+ <h2>Edit User</h2>
+
+ ${form.display(action="userupdate", value=user)}
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/userform.kid b/ipa-server/ipa-gui/ipagui/templates/userform.kid
new file mode 100644
index 000000000..dd852865a
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/userform.kid
@@ -0,0 +1,125 @@
+<div xmlns:py="http://purl.org/kid/ns#"
+ class="simpleroster">
+ <form action="${action}" name="${name}" method="${method}" class="tableform">
+
+ <div class="formsection">Account Details</div>
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <th>
+ <label class="fieldlabel" for="${user.uid.field_id}"
+ py:content="user.uid.label" />
+ </th>
+ <td>
+ <span py:replace="user.uid.display(value_for(user.uid))" />
+ <span py:if="tg.errors.get('uid')" class="fielderror"
+ py:content="tg.errors.get('uid')" />
+ </td>
+ </tr>
+
+<!-- <tr>
+ <th>
+ <label class="fieldlabel" for="${user.userPassword.field_id}"
+ py:content="user.userPassword.label" />
+ </th>
+ <td>
+ <span py:replace="user.userPassword.display(value_for(user.userPassword))" />
+ <span py:if="tg.errors.get('userPassword')" class="fielderror"
+ py:content="tg.errors.get('userPassword')" />
+ </td>
+ </tr> -->
+
+ <tr>
+ <th>
+ <label class="fieldlabel" for="${user.uidNumber.field_id}"
+ py:content="user.uidNumber.label" />
+ </th>
+ <td>
+ <span py:replace="user.uidNumber.display(value_for(user.uidNumber))" />
+ <span py:if="tg.errors.get('uidNumber')" class="fielderror"
+ py:content="tg.errors.get('uidNumber')" />
+ </td>
+ </tr>
+
+ <tr>
+ <th>
+ <label class="fieldlabel" for="${user.gidNumber.field_id}"
+ py:content="user.gidNumber.label" />
+ </th>
+ <td>
+ <span py:replace="user.gidNumber.display(value_for(user.gidNumber))" />
+ <span py:if="tg.errors.get('gidNumber')" class="fielderror"
+ py:content="tg.errors.get('gidNumber')" />
+ </td>
+ </tr>
+ </table>
+
+ <div class="formsection">Identity Details</div>
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <th>
+ <label class="fieldlabel" for="${user.givenName.field_id}"
+ py:content="user.givenName.label" />
+ </th>
+ <td>
+ <span py:replace="user.givenName.display(value_for(user.givenName))" />
+ <span py:if="tg.errors.get('givenName')" class="fielderror"
+ py:content="tg.errors.get('givenName')" />
+
+ </td>
+ </tr>
+
+ <tr>
+ <th>
+ <label class="fieldlabel" for="${user.sn.field_id}"
+ py:content="user.sn.label" />
+ </th>
+ <td>
+ <span py:replace="user.sn.display(value_for(user.sn))" />
+ <span py:if="tg.errors.get('sn')" class="fielderror"
+ py:content="tg.errors.get('sn')" />
+ </td>
+ </tr>
+ </table>
+
+ <div class="formsection">Contact Details</div>
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <th>
+ <label class="fieldlabel" for="${user.mail.field_id}"
+ py:content="user.mail.label" />
+ </th>
+ <td>
+ <span py:replace="user.mail.display(value_for(user.mail))" />
+ <span py:if="tg.errors.get('mail')" class="fielderror"
+ py:content="tg.errors.get('mail')" />
+ </td>
+ </tr>
+ <tr>
+ <th>
+ <label class="fieldlabel" for="${user.telephoneNumber.field_id}"
+ py:content="user.telephoneNumber.label" />
+ </th>
+ <td>
+ <span py:replace="user.telephoneNumber.display(value_for(user.telephoneNumber))" />
+ <span py:if="tg.errors.get('telephoneNumber')" class="fielderror"
+ py:content="tg.errors.get('telephoneNumber')" />
+ </td>
+ </tr>
+ </table>
+
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <th>
+ <br />
+ <input type="submit" class="submitbutton" name="submit" value="Submit"/>
+ </th>
+ <td>
+ <br />
+ <input type="submit" class="submitbutton" name="submit" value="Cancel" />
+ </td>
+ <td></td>
+ </tr>
+ </table>
+
+ </form>
+</div>
diff --git a/ipa-server/ipa-gui/ipagui/templates/userlayout.kid b/ipa-server/ipa-gui/ipagui/templates/userlayout.kid
new file mode 100644
index 000000000..40ddcca39
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/userlayout.kid
@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'master.kid'">
+<head>
+</head>
+
+<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()">
+ <div id="main_content">
+ <div id="status_block" class="flash" py:if="value_of('tg_flash', None)" py:content="tg_flash"></div>
+
+ <div py:replace="[item.text]+item[:]"></div>
+ </div>
+
+ <div id="sidebar">
+ <h2>Tools</h2>
+ <a href="${tg.url('/usernew')}">Add Person</a><br/>
+ <a href="${tg.url('/userindex')}">Find People</a><br/>
+ <a href="${tg.url('/userlist')}">List People</a><br/>
+ </div>
+</body>
+
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/userlist.kid b/ipa-server/ipa-gui/ipagui/templates/userlist.kid
new file mode 100644
index 000000000..7b5667fb5
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/userlist.kid
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'userlayout.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>User Listing</title>
+</head>
+<body>
+ <fieldset>
+ <legend>People List</legend>
+ <div>
+ Page:
+ <span py:for="page in tg.paginate.pages">
+ <a py:if="page != tg.paginate.current_page"
+ href="${tg.paginate.get_href(page)}">${page}</a>
+ <b py:if="page == tg.paginate.current_page">${page}</b>
+ </span>
+ <p/>
+ <span py:for="user in users">
+ <a href="${tg.url('/usershow',uid=user.uid)}">${user.cn}</a>
+ <br/>
+ </span>
+ </div>
+ </fieldset>
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/usernew.kid b/ipa-server/ipa-gui/ipagui/templates/usernew.kid
new file mode 100644
index 000000000..84b6029ae
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/usernew.kid
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'userlayout.kid'">
+<head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+ <title>Add a Person</title>
+</head>
+<body>
+ <h2>Add New User</h2>
+
+ ${form.display(action="usercreate")}
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/usershow.kid b/ipa-server/ipa-gui/ipagui/templates/usershow.kid
new file mode 100644
index 000000000..7fea76dbd
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/usershow.kid
@@ -0,0 +1,50 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'userlayout.kid'">
+<head>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+ <title>View a Person</title>
+</head>
+<body>
+ <h2>View User</h2>
+
+ <div class="formsection">Account Details</div>
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <th>User ID:</th>
+ <td>${user.get("uid")}</td>
+ </tr>
+ <tr>
+ <th>UID:</th>
+ <td>${user.get("uidNumber")}</td>
+ </tr>
+ <tr>
+ <th>GID:</th>
+ <td>${user.get("gidNumber")}</td>
+ </tr>
+ </table>
+
+ <div class="formsection">Identity Details</div>
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <th>Full Name:</th>
+ <td>${user.get("givenName")} ${user.get("sn")}</td>
+ </tr>
+ </table>
+
+ <div class="formsection">Contact Details</div>
+ <table class="formtable" cellpadding="2" cellspacing="0" border="0">
+ <tr>
+ <th>Email:</th>
+ <td>${user.get("mail")}</td>
+ </tr>
+ <tr>
+ <th>Telephone:</th>
+ <td>${user.get("telephoneNumber")}</td>
+ </tr>
+ </table>
+
+ <a href="${tg.url('/useredit', uid=user.get('uid'))}">edit</a>
+
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/templates/welcome.kid b/ipa-server/ipa-gui/ipagui/templates/welcome.kid
new file mode 100644
index 000000000..dc08c0b32
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/templates/welcome.kid
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'master.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>Welcome</title>
+</head>
+<body>
+ <div id="sidebar">
+ <h2>Tools</h2>
+ </div>
+ <div id="main_content">
+ <div id="status_block" class="flash" py:if="value_of('tg_flash', None)" py:content="tg_flash"></div>
+ <h1>Welcome to Free IPA</h1>
+ </div>
+
+</body>
+</html>
diff --git a/ipa-server/ipa-gui/ipagui/tests/test_controllers.py b/ipa-server/ipa-gui/ipagui/tests/test_controllers.py
new file mode 100644
index 000000000..b9b4f5c5c
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/tests/test_controllers.py
@@ -0,0 +1,32 @@
+import unittest
+import turbogears
+from turbogears import testutil
+from ipagui.controllers import Root
+import cherrypy
+
+cherrypy.root = Root()
+
+class TestPages(unittest.TestCase):
+
+ def setUp(self):
+ turbogears.startup.startTurboGears()
+
+ def tearDown(self):
+ """Tests for apps using identity need to stop CP/TG after each test to
+ stop the VisitManager thread.
+ See http://trac.turbogears.org/turbogears/ticket/1217 for details.
+ """
+ turbogears.startup.stopTurboGears()
+
+ def test_method(self):
+ "the index method should return a string called now"
+ import types
+ result = testutil.call(cherrypy.root.index)
+ assert type(result["now"]) == types.StringType
+
+ def test_indextitle(self):
+ "The indexpage should have the right title"
+ testutil.createRequest("/")
+ response = cherrypy.response.body[0].lower()
+ assert "<title>welcome to turbogears</title>" in response
+
diff --git a/ipa-server/ipa-gui/ipagui/tests/test_model.py b/ipa-server/ipa-gui/ipagui/tests/test_model.py
new file mode 100644
index 000000000..bd0db4c70
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/tests/test_model.py
@@ -0,0 +1,22 @@
+# If your project uses a database, you can set up database tests
+# similar to what you see below. Be sure to set the db_uri to
+# an appropriate uri for your testing database. sqlite is a good
+# choice for testing, because you can use an in-memory database
+# which is very fast.
+
+from turbogears import testutil, database
+# from ipagui.model import YourDataClass, User
+
+# database.set_db_uri("sqlite:///:memory:")
+
+# class TestUser(testutil.DBTest):
+# def get_model(self):
+# return User
+# def test_creation(self):
+# "Object creation should set the name"
+# obj = User(user_name = "creosote",
+# email_address = "spam@python.not",
+# display_name = "Mr Creosote",
+# password = "Wafer-thin Mint")
+# assert obj.display_name == "Mr Creosote"
+
diff --git a/ipa-server/ipa-gui/sample-prod.cfg b/ipa-server/ipa-gui/sample-prod.cfg
new file mode 100644
index 000000000..ffa684cc1
--- /dev/null
+++ b/ipa-server/ipa-gui/sample-prod.cfg
@@ -0,0 +1,84 @@
+[global]
+# This is where all of your settings go for your production environment.
+# You'll copy this file over to your production server and provide it
+# as a command-line option to your start script.
+# Settings that are the same for both development and production
+# (such as template engine, encodings, etc.) all go in
+# ipagui/config/app.cfg
+
+# DATABASE
+
+# pick the form for your database
+# sqlobject.dburi="postgres://username@hostname/databasename"
+# sqlobject.dburi="mysql://username:password@hostname:port/databasename"
+# sqlobject.dburi="sqlite:///file_name_and_path"
+
+# If you have sqlite, here's a simple default to get you started
+# in development
+sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite"
+
+
+# if you are using a database or table type without transactions
+# (MySQL default, for example), you should turn off transactions
+# by prepending notrans_ on the uri
+# sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename"
+
+# for Windows users, sqlite URIs look like:
+# sqlobject.dburi="sqlite:///drive_letter:/path/to/file"
+
+
+# SERVER
+
+server.environment="production"
+
+# Sets the number of threads the server uses
+# server.thread_pool = 1
+
+# if this is part of a larger site, you can set the path
+# to the TurboGears instance here
+# server.webpath=""
+
+# Set to True if you are deploying your App behind a proxy
+# e.g. Apache using mod_proxy
+# base_url_filter.on = False
+
+# Set to True if your proxy adds the x_forwarded_host header
+# base_url_filter.use_x_forwarded_host = True
+
+# If your proxy does not add the x_forwarded_host header, set
+# the following to the *public* host url.
+# (Note: This will be overridden by the use_x_forwarded_host option
+# if it is set to True and the proxy adds the header correctly.
+# base_url_filter.base_url = "http://www.example.com"
+
+# Set to True if you'd like to abort execution if a controller gets an
+# unexpected parameter. False by default
+# tg.strict_parameters = False
+
+# LOGGING
+# Logging configuration generally follows the style of the standard
+# Python logging module configuration. Note that when specifying
+# log format messages, you need to use *() for formatting variables.
+# Deployment independent log configuration is in ipagui/config/log.cfg
+[logging]
+
+[[handlers]]
+
+[[[access_out]]]
+# set the filename as the first argument below
+args="('server.log',)"
+class='FileHandler'
+level='INFO'
+formatter='message_only'
+
+[[loggers]]
+[[[ipagui]]]
+level='ERROR'
+qualname='ipagui'
+handlers=['error_out']
+
+[[[access]]]
+level='INFO'
+qualname='turbogears.access'
+handlers=['access_out']
+propagate=0
diff --git a/ipa-server/ipa-gui/setup.py b/ipa-server/ipa-gui/setup.py
new file mode 100644
index 000000000..371325f6a
--- /dev/null
+++ b/ipa-server/ipa-gui/setup.py
@@ -0,0 +1,62 @@
+from setuptools import setup, find_packages
+from turbogears.finddata import find_package_data
+
+import os
+execfile(os.path.join("ipagui", "release.py"))
+
+setup(
+ name="ipa-gui",
+ version=version,
+
+ # uncomment the following lines if you fill them out in release.py
+ #description=description,
+ #author=author,
+ #author_email=email,
+ #url=url,
+ #download_url=download_url,
+ #license=license,
+
+ install_requires = [
+ "TurboGears >= 1.0.2.2",
+ ],
+ scripts = ["start-ipagui.py"],
+ zip_safe=False,
+ packages=find_packages(),
+ package_data = find_package_data(where='ipagui',
+ package='ipagui'),
+ keywords = [
+ # Use keywords if you'll be adding your package to the
+ # Python Cheeseshop
+
+ # if this has widgets, uncomment the next line
+ # 'turbogears.widgets',
+
+ # if this has a tg-admin command, uncomment the next line
+ # 'turbogears.command',
+
+ # if this has identity providers, uncomment the next line
+ # 'turbogears.identity.provider',
+
+ # If this is a template plugin, uncomment the next line
+ # 'python.templating.engines',
+
+ # If this is a full application, uncomment the next line
+ # 'turbogears.app',
+ ],
+ classifiers = [
+ 'Development Status :: 3 - Alpha',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ 'Framework :: TurboGears',
+ # if this is an application that you'll distribute through
+ # the Cheeseshop, uncomment the next line
+ # 'Framework :: TurboGears :: Applications',
+
+ # if this is a package that includes widgets that you'll distribute
+ # through the Cheeseshop, uncomment the next line
+ # 'Framework :: TurboGears :: Widgets',
+ ],
+ test_suite = 'nose.collector',
+ )
+
diff --git a/ipa-server/ipa-gui/start-ipagui.py b/ipa-server/ipa-gui/start-ipagui.py
new file mode 100644
index 000000000..2165e110b
--- /dev/null
+++ b/ipa-server/ipa-gui/start-ipagui.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+import pkg_resources
+pkg_resources.require("TurboGears")
+
+from turbogears import update_config, start_server
+import cherrypy
+cherrypy.lowercase_api = True
+from os.path import *
+import sys
+
+# first look on the command line for a desired config file,
+# if it's not on the command line, then
+# look for setup.py in this directory. If it's not there, this script is
+# probably installed
+if len(sys.argv) > 1:
+ update_config(configfile=sys.argv[1],
+ modulename="ipagui.config")
+elif exists(join(dirname(__file__), "setup.py")):
+ update_config(configfile="dev.cfg",modulename="ipagui.config")
+else:
+ update_config(configfile="prod.cfg",modulename="ipagui.config")
+
+from ipagui.controllers import Root
+
+start_server(Root())
diff --git a/ipa-server/ipa-gui/test.cfg b/ipa-server/ipa-gui/test.cfg
new file mode 100644
index 000000000..df909c99d
--- /dev/null
+++ b/ipa-server/ipa-gui/test.cfg
@@ -0,0 +1,4 @@
+# You can place test-specific configuration options here (like test db uri, etc)
+
+sqlobject.dburi = "sqlite:///:memory:"
+