summaryrefslogtreecommitdiffstats
path: root/mediawiki
diff options
context:
space:
mode:
Diffstat (limited to 'mediawiki')
-rw-r--r--mediawiki/PHPSerialize.py149
-rw-r--r--mediawiki/PHPUnserialize.py187
-rw-r--r--mediawiki/README106
-rw-r--r--mediawiki/UserRegister.alias.php7
-rw-r--r--mediawiki/UserRegister.body.php14
-rw-r--r--mediawiki/UserRegister.i18n.php6
-rw-r--r--mediawiki/UserRegister.php24
-rw-r--r--mediawiki/WsgiInjectableSpecialPage.php80
-rw-r--r--mediawiki/__init__.py0
-rw-r--r--mediawiki/api.py138
-rw-r--r--mediawiki/auth.py59
-rw-r--r--mediawiki/forms.py164
-rw-r--r--mediawiki/junk.py2
-rw-r--r--mediawiki/middleware.py57
-rw-r--r--mediawiki/models.py312
-rw-r--r--mediawiki/templatetags/__init__.py0
-rw-r--r--mediawiki/templatetags/mediawikitags.py62
-rw-r--r--mediawiki/views.py192
18 files changed, 1559 insertions, 0 deletions
diff --git a/mediawiki/PHPSerialize.py b/mediawiki/PHPSerialize.py
new file mode 100644
index 00000000..d25b71bd
--- /dev/null
+++ b/mediawiki/PHPSerialize.py
@@ -0,0 +1,149 @@
+import types, string
+
+"""
+Serialize class for the PHP serialization format.
+
+@version v0.4 BETA
+@author Scott Hurring; scott at hurring dot com
+@copyright Copyright (c) 2005 Scott Hurring
+@license http://opensource.org/licenses/gpl-license.php GNU Public License
+$Id: PHPSerialize.py,v 1.1 2006/01/08 21:53:19 shurring Exp $
+
+Most recent version can be found at:
+http://hurring.com/code/python/phpserialize/
+
+Usage:
+# Create an instance of the serialize engine
+s = PHPSerialize()
+# serialize some python data into a string
+serialized_string = s.serialize(string)
+# encode a session list (php's session_encode)
+serialized_string = s.session_encode(list)
+
+See README.txt for more information.
+"""
+
+class PHPSerialize(object):
+ """
+ Class to serialize data using the PHP Serialize format.
+
+ Usage:
+ serialized_string = PHPSerialize().serialize(data)
+ serialized_string = PHPSerialize().session_encode(list)
+ """
+
+ def __init__(self):
+ pass
+
+ def session_encode(self, session):
+ """Thanks to Ken Restivo for suggesting the addition
+ of session_encode
+ """
+ out = ""
+ for (k,v) in session.items():
+ out = out + "%s|%s" % (k, self.serialize(v))
+ return out
+
+ def serialize(self, data):
+ return self.serialize_value(data)
+
+ def is_int(self, data):
+ """
+ Determine if a string var looks like an integer
+ TODO: Make this do what PHP does, instead of a hack
+ """
+ try:
+ int(data)
+ return True
+ except:
+ return False
+
+ def serialize_key(self, data):
+ """
+ Serialize a key, which follows different rules than when
+ serializing values. Many thanks to Todd DeLuca for pointing
+ out that keys are serialized differently than values!
+
+ From http://us2.php.net/manual/en/language.types.array.php
+ A key may be either an integer or a string.
+ If a key is the standard representation of an integer, it will be
+ interpreted as such (i.e. "8" will be interpreted as int 8,
+ while "08" will be interpreted as "08").
+ Floats in key are truncated to integer.
+ """
+ # Integer, Long, Float, Boolean => integer
+ if type(data) is types.IntType or type(data) is types.LongType \
+ or type(data) is types.FloatType or type(data) is types.BooleanType:
+ return "i:%s;" % int(data)
+
+ # String => string or String => int (if string looks like int)
+ elif type(data) is types.StringType:
+ if self.is_int(data):
+ return "i:%s;" % int(data)
+ else:
+ return "s:%i:\"%s\";" % (len(data), data);
+
+ # None / NULL => empty string
+ elif type(data) is types.NoneType:
+ return "s:0:\"\";"
+
+ # I dont know how to serialize this
+ else:
+ raise Exception("Unknown / Unhandled key type (%s)!" % type(data))
+
+
+ def serialize_value(self, data):
+ """
+ Serialize a value.
+ """
+
+ # Integer => integer
+ if type(data) is types.IntType:
+ return "i:%s;" % data
+
+ # Float, Long => double
+ elif type(data) is types.FloatType or type(data) is types.LongType:
+ return "d:%s;" % data
+
+ # String => string or String => int (if string looks like int)
+ # Thanks to Todd DeLuca for noticing that PHP strings that
+ # look like integers are serialized as ints by PHP
+ elif type(data) is types.StringType:
+ if self.is_int(data):
+ return "i:%s;" % int(data)
+ else:
+ return "s:%i:\"%s\";" % (len(data), data);
+
+ # None / NULL
+ elif type(data) is types.NoneType:
+ return "N;";
+
+ # Tuple and List => array
+ # The 'a' array type is the only kind of list supported by PHP.
+ # array keys are automagically numbered up from 0
+ elif type(data) is types.ListType or type(data) is types.TupleType:
+ i = 0
+ out = []
+ # All arrays must have keys
+ for k in data:
+ out.append(self.serialize_key(i))
+ out.append(self.serialize_value(k))
+ i += 1
+ return "a:%i:{%s}" % (len(data), "".join(out))
+
+ # Dict => array
+ # Dict is the Python analogy of a PHP array
+ elif type(data) is types.DictType:
+ out = []
+ for k in data:
+ out.append(self.serialize_key(k))
+ out.append(self.serialize_value(data[k]))
+ return "a:%i:{%s}" % (len(data), "".join(out))
+
+ # Boolean => bool
+ elif type(data) is types.BooleanType:
+ return "b:%i;" % (data == 1)
+
+ # I dont know how to serialize this
+ else:
+ raise Exception("Unknown / Unhandled data type (%s)!" % type(data))
diff --git a/mediawiki/PHPUnserialize.py b/mediawiki/PHPUnserialize.py
new file mode 100644
index 00000000..b59c869c
--- /dev/null
+++ b/mediawiki/PHPUnserialize.py
@@ -0,0 +1,187 @@
+import types, string, re
+
+"""
+Unserialize class for the PHP serialization format.
+
+@version v0.4 BETA
+@author Scott Hurring; scott at hurring dot com
+@copyright Copyright (c) 2005 Scott Hurring
+@license http://opensource.org/licenses/gpl-license.php GNU Public License
+$Id: PHPUnserialize.py,v 1.1 2006/01/08 21:53:19 shurring Exp $
+
+Most recent version can be found at:
+http://hurring.com/code/python/phpserialize/
+
+Usage:
+# Create an instance of the unserialize engine
+u = PHPUnserialize()
+# unserialize some string into python data
+data = u.unserialize(serialized_string)
+
+Please see README.txt for more information.
+"""
+
+class PHPUnserialize(object):
+ """
+ Class to unserialize something from the PHP Serialize format.
+
+ Usage:
+ u = PHPUnserialize()
+ data = u.unserialize(serialized_string)
+ """
+
+ def __init__(self):
+ pass
+
+ def session_decode(self, data):
+ """Thanks to Ken Restivo for suggesting the addition
+ of session_encode
+ """
+ session = {}
+ while len(data) > 0:
+ m = re.match('^(\w+)\|', data)
+ if m:
+ key = m.group(1)
+ offset = len(key)+1
+ (dtype, dataoffset, value) = self._unserialize(data, offset)
+ offset = offset + dataoffset
+ data = data[offset:]
+ session[key] = value
+ else:
+ # No more stuff to decode
+ return session
+
+ return session
+
+ def unserialize(self, data):
+ return self._unserialize(data, 0)[2]
+
+ def _unserialize(self, data, offset=0):
+ """
+ Find the next token and unserialize it.
+ Recurse on array.
+
+ offset = raw offset from start of data
+
+ return (type, offset, value)
+ """
+
+ buf = []
+ dtype = string.lower(data[offset:offset+1])
+
+ #print "# dtype =", dtype
+
+ # 't:' = 2 chars
+ dataoffset = offset + 2
+ typeconvert = lambda x : x
+ chars = datalength = 0
+
+ # int => Integer
+ if dtype == 'i':
+ typeconvert = lambda x : int(x)
+ (chars, readdata) = self.read_until(data, dataoffset, ';')
+ # +1 for end semicolon
+ dataoffset += chars + 1
+
+ # bool => Boolean
+ elif dtype == 'b':
+ typeconvert = lambda x : (int(x) == 1)
+ (chars, readdata) = self.read_until(data, dataoffset, ';')
+ # +1 for end semicolon
+ dataoffset += chars + 1
+
+ # double => Floating Point
+ elif dtype == 'd':
+ typeconvert = lambda x : float(x)
+ (chars, readdata) = self.read_until(data, dataoffset, ';')
+ # +1 for end semicolon
+ dataoffset += chars + 1
+
+ # n => None
+ elif dtype == 'n':
+ readdata = None
+
+ # s => String
+ elif dtype == 's':
+ (chars, stringlength) = self.read_until(data, dataoffset, ':')
+ # +2 for colons around length field
+ dataoffset += chars + 2
+
+ # +1 for start quote
+ (chars, readdata) = self.read_chars(data, dataoffset+1, int(stringlength))
+ # +2 for endquote semicolon
+ dataoffset += chars + 2
+
+ if chars != int(stringlength) != int(readdata):
+ raise Exception("String length mismatch")
+
+ # array => Dict
+ # If you originally serialized a Tuple or List, it will
+ # be unserialized as a Dict. PHP doesn't have tuples or lists,
+ # only arrays - so everything has to get converted into an array
+ # when serializing and the original type of the array is lost
+ elif dtype == 'a':
+ readdata = {}
+
+ # How many keys does this list have?
+ (chars, keys) = self.read_until(data, dataoffset, ':')
+ # +2 for colons around length field
+ dataoffset += chars + 2
+
+ # Loop through and fetch this number of key/value pairs
+ for i in range(0, int(keys)):
+ # Read the key
+ (ktype, kchars, key) = self._unserialize(data, dataoffset)
+ dataoffset += kchars
+ #print "Key(%i) = (%s, %i, %s) %i" % (i, ktype, kchars, key, dataoffset)
+
+ # Read value of the key
+ (vtype, vchars, value) = self._unserialize(data, dataoffset)
+ dataoffset += vchars
+ #print "Value(%i) = (%s, %i, %s) %i" % (i, vtype, vchars, value, dataoffset)
+
+ # Set the list element
+ readdata[key] = value
+
+ # +1 for end semicolon
+ dataoffset += 1
+ #chars = int(dataoffset) - start
+
+ # I don't know how to unserialize this
+ else:
+ raise Exception("Unknown / Unhandled data type (%s)!" % dtype)
+
+
+ return (dtype, dataoffset-offset, typeconvert(readdata))
+
+ def read_until(self, data, offset, stopchar):
+ """
+ Read from data[offset] until you encounter some char 'stopchar'.
+ """
+ buf = []
+ char = data[offset:offset+1]
+ i = 2
+ while char != stopchar:
+ # Consumed all the characters and havent found ';'
+ if i+offset > len(data):
+ raise Exception("Invalid")
+ buf.append(char)
+ char = data[offset+(i-1):offset+i]
+ i += 1
+
+ # (chars_read, data)
+ return (len(buf), "".join(buf))
+
+ def read_chars(self, data, offset, length):
+ """
+ Read 'length' number of chars from data[offset].
+ """
+ buf = []
+ # Account for the starting quote char
+ #offset += 1
+ for i in range(0, length):
+ char = data[offset+(i-1):offset+i]
+ buf.append(char)
+
+ # (chars_read, data)
+ return (len(buf), "".join(buf))
diff --git a/mediawiki/README b/mediawiki/README
new file mode 100644
index 00000000..b664a8a7
--- /dev/null
+++ b/mediawiki/README
@@ -0,0 +1,106 @@
+This is a rough example of integrated mediawiki authentication
+originally written to work on a customized MW (some tables are different from standard)
+so to adapt this to your case you'll most likely need to tweak this
+
+Also keep in mind that probably a better solution would be to create a single signon site.
+
+Author: evgeny.fadeev@gmail.com (Evgeny)
+
+==Minimal directions==
+
+1) Add the following to your settings_local.py (with relevant modifications):
+
+USE_EXTERNAL_LEGACY_LOGIN = True #enable external legacy login
+EXTERNAL_LEGACY_LOGIN_AUTHENTICATION_BACKEND = 'mediawiki.auth.IncludeVirtualAuthenticationBackend'
+EXTERNAL_LEGACY_LOGIN_AUTHENTICATION_MIDDLEWARE = 'mediawiki.middleware.IncludeVirtualAuthenticationMiddleware'
+EXTERNAL_LEGACY_LOGIN_MODULE = 'mediawiki' #current module
+EXTERNAL_LEGACY_LOGIN_HOST = 'yoursite.org' #wiki domain
+EXTERNAL_LEGACY_LOGIN_PORT = 80 #port, probably 80
+EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'My Wiki' #html allowed
+MEDIAWIKI_URL="http://yoursite.org/wiki/index.php"
+MEDIAWIKI_SALT_PASSWORD=True #or False - depending on your LocalSettings.php
+MEDIAWIKI_INDEX_PHP_URL='/wiki/index.php'
+MEDIAWIKI_COOKIE_DOMAIN='.yoursite.org' #for cross subdomain login
+MEDIAWIKI_SESSION_COOKIE_NAME = '' # probably '<dbname>_<dbtblprefix>_session'
+MEDIAWIKI_PHP_SESSION_PREFIX='sess_' #depends on your setup
+MEDIAWIKI_PHP_SESSION_PATH='/var/lib/php/session'
+SESSION_COOKIE_DOMAIN = '.yoursite.org' #use this notation for cross-subdomain login
+
+2) Configure apache to access forum "backend" via the wiki domain.
+Example configuration is in the end of the doc.
+
+3) Install two wiki extensions WsgiInjectableSpecialPage, UserRegister -
+the usual MW way. You might want to disable traditional wiki registration.
+
+4) grep files for 'yourwiki' and change that to your own taste - there are some
+hardcoded urls
+
+5) Templates are in templates/mediawiki - you probably will want to customize them,
+form js media: templates/content/js/mediawiki-login.js
+form css media: templates/content/style/mediawiki-login.css
+
+==Requirements==
+wiki and forum must live in the same mysql database for registration to work,
+however login will work even if this is not the case
+
+you must own both wiki and forum or there must be good trust relationship
+between the owners - because password is shared
+
+==Notes on how external login currently works.==
+password and login are entered in the login form.
+these are checked against mw api
+
+password is saved in the auth_user table (the django way)
+so if you at some point set USE_EXTERNAL_LEGACY_LOGIN = False
+wiki passwords and logins will still work on the forum
+
+login action is partially synchronized btw wiki and forum (from forum to wiki,
+but not the opposite way yet)
+
+when users first register - either on wiki or forum they are logged in on both
+
+on registration they receive a greeting email - you will want to customize messages
+
+technically, on the wiki registration form is injected via apache SSI
+- using include virtual call
+
+there is a possibility for cross-site scripting attack if wiki session is stolen
+
+==Apache setup example for the wiki==
+This assumes that wiki and forum facing the user are on different subdomains.
+Also this setup is just an example - you may do better :).
+Forum setup in apache is described in main osqa INSTALL document - that's extra.
+
+<VirtualHost your.ip:port>
+ ServerAdmin admin@yourwiki.org
+ DocumentRoot /path/to/wiki/root #dir containing wiki directory
+ ServerName yourwiki.org
+ AddOutputFilter INCLUDES .php
+ Alias /backend/content/ /path/to/forum/templates/content/
+ AliasMatch (content\/style\/[^/]*\.css) /path/to/forum/templates/$1
+ AliasMatch (content\/.*) /path/to/forum/templates/$1
+ <Directory /path/to/forum/templates/content>
+ Order deny,allow
+ Allow from all
+ </Directory>
+ WSGIDaemonProcess my-forum-wiki-side #use daemon mode so to avoid potential timezone messups
+ WSGIProcessGroup my-forum-wiki-side
+ WSGIScriptAlias /backend /path/to/forum/cnprog.wsgi
+ CustomLog /var/log/httpd/yourwiki/access_log common
+ ErrorLog /var/log/httpd/yourwiki/error_log
+ LogLevel debug
+ DirectoryIndex index.php index.html
+</VirtualHost>
+<Directory /path/to/wiki/root>
+ Options Includes
+ <IfModule sapi_apache2.c>
+ php_admin_flag engine on
+ php_admin_flag safe_mode off
+ </IfModule>
+ <IfModule mod_php5.c>
+ php_admin_flag engine on
+ php_admin_flag safe_mode off
+ php_admin_value open_basedir "/path/to/wiki/root:.:/tmp" #tmp used for sessions
+ </IfModule>
+</Directory>
+
diff --git a/mediawiki/UserRegister.alias.php b/mediawiki/UserRegister.alias.php
new file mode 100644
index 00000000..0d5c1523
--- /dev/null
+++ b/mediawiki/UserRegister.alias.php
@@ -0,0 +1,7 @@
+<?php
+$messages = array();
+
+/* *** English *** */
+$messages['en'] = array(
+ 'User Register' => array('User Register'),
+);
diff --git a/mediawiki/UserRegister.body.php b/mediawiki/UserRegister.body.php
new file mode 100644
index 00000000..f8c953a9
--- /dev/null
+++ b/mediawiki/UserRegister.body.php
@@ -0,0 +1,14 @@
+<?php
+class UserRegister extends WsgiInjectableSpecialPage {
+ function __construct() {
+ parent::__construct( 'UserRegister',
+ '/backend/account/nmr-wiki/signup/',
+ array(0=>'/backend/content/style/mediawiki-login.css'),
+ array(
+ 0=>'/backend/content/js/jquery-1.2.6.min.js',
+ 1=>'/backend/content/js/mediawiki-login.js'
+ )
+ );
+ }
+}
+
diff --git a/mediawiki/UserRegister.i18n.php b/mediawiki/UserRegister.i18n.php
new file mode 100644
index 00000000..2f8d41d0
--- /dev/null
+++ b/mediawiki/UserRegister.i18n.php
@@ -0,0 +1,6 @@
+<?php
+$messages = array();
+
+$messages['en'] = array(
+ 'userregister' => 'Join the Wiki',
+);
diff --git a/mediawiki/UserRegister.php b/mediawiki/UserRegister.php
new file mode 100644
index 00000000..cff0e69d
--- /dev/null
+++ b/mediawiki/UserRegister.php
@@ -0,0 +1,24 @@
+<?php
+# Alert the user that this is not a valid entry point to MediaWiki if they try to access the special pages file directly.
+if (!defined('MEDIAWIKI')) {
+ echo <<<EOT
+Not a valid entry point.
+EOT;
+ exit( 1 );
+}
+
+$wgExtensionCredits['specialpage'][] = array(
+ 'name' => 'User Registration',
+ 'author' => 'Evgeny Fadeev',
+ 'url' => 'none',
+ 'description' => 'Creates new user account for the Wiki and Q&A forum',
+ 'descriptionmsg' => 'people-page-desc',
+ 'version' => '0.0.0',
+);
+
+$dir = dirname(__FILE__) . '/';
+
+$wgAutoloadClasses['UserRegister'] = $dir . 'UserRegister.body.php'; # Tell MediaWiki to load the extension body.
+$wgExtensionMessagesFiles['UserRegister'] = $dir . 'UserRegister.i18n.php';
+$wgExtensionAliasesFiles['UserRegister'] = $dir . 'UserRegister.alias.php';
+$wgSpecialPages['UserRegister'] = 'UserRegister'; # Let MediaWiki know about your new special page.
diff --git a/mediawiki/WsgiInjectableSpecialPage.php b/mediawiki/WsgiInjectableSpecialPage.php
new file mode 100644
index 00000000..d23f22e8
--- /dev/null
+++ b/mediawiki/WsgiInjectableSpecialPage.php
@@ -0,0 +1,80 @@
+<?php
+
+class WsgiInjectableSpecialPage extends SpecialPage {
+ var $default_wsgi_command;
+ function __construct($page_name,$default_wsgi_command,$css='',$scripts='',$wsgi_prefix=''){
+ parent::__construct($page_name);
+ wfLoadExtensionMessages($page_name);
+ $this->default_wsgi_command = $default_wsgi_command;
+ $this->css = $css;
+ $this->scripts = $scripts;
+ $this->wsgi_prefix = $wsgi_prefix;
+ }
+ function execute($par){
+ global $wgWsgiScriptPath, $wgRequest, $wgOut, $wgHeader;
+ $wgWsgiScriptPath = '';
+ if ($this->wsgi_prefix != ''){
+ $wsgi_call = $this->wsgi_prefix;
+ }
+ else {
+ $wsgi_call = $wgWsgiScriptPath;
+ }
+
+ $this->setHeaders();
+
+ if ($this->css != ''){
+ if (is_array($this->css)){
+ foreach($this->css as $css){
+ $wgHeader->addCSS($css);
+ }
+ }
+ else{
+ $wgHeader->addCSS($this->css);
+ }
+ }
+ if ($this->scripts != ''){
+ if (is_array($this->scripts)){
+ foreach($this->scripts as $script){
+ $wgHeader->addScript($script);
+ }
+ }
+ else{
+ $wgHeader->addScript($this->css);
+ }
+ }
+
+ #add command
+ if (!is_null($wgRequest->getVal('command'))){
+ $wsgi_call .= $wgRequest->getVal('command');
+ }
+ else {
+ #why is this not working?
+ $wsgi_call .= $this->default_wsgi_command;
+ }
+ #add session key
+ $session_name = ini_get('session.name');#session_name();
+ $session = '';
+ if (array_key_exists($session_name, $_COOKIE)){
+ $session = $_COOKIE[$session_name];
+ }
+ $wsgi_call .= "?session=${session}";
+
+ #add posted request variables
+ if ($wgRequest->wasPosted()){
+ $data = $wgRequest->data;
+ foreach ($data as $key => $value){
+ if ($key != 'title'){
+ $wsgi_call .= "&${key}=${value}";
+ }
+ }
+ $wsgi_call .= '&was_posted=true';
+ }
+ else {
+ $wsgi_call .= '&was_posted=false';
+ }
+
+ #print the include statement called as GET request
+ $wgOut->addHTML("<!--#include virtual=\"${wsgi_call}\"-->");
+ #$wgOut->addHTML("<!-- ${wsgi_call} -->"); #print this only for debugging
+ }
+}
diff --git a/mediawiki/__init__.py b/mediawiki/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/mediawiki/__init__.py
diff --git a/mediawiki/api.py b/mediawiki/api.py
new file mode 100644
index 00000000..912de041
--- /dev/null
+++ b/mediawiki/api.py
@@ -0,0 +1,138 @@
+#this file contains stub functions that can be extended to support
+#connect legacy login with external site
+from django.conf import settings
+from django_authopenid.models import ExternalLoginData
+import httplib
+import urllib
+import Cookie
+import cookielib
+from django import forms
+import xml.dom.minidom as xml
+import logging
+from models import User as MWUser
+
+def login(request,user):
+ """performs the additional external login operation
+ """
+ pass
+
+def set_login_cookies(response,user):
+ #should be unique value by design
+ try:
+ eld = ExternalLoginData.objects.get(user=user)
+
+ data = eld.external_session_data
+ dom = xml.parseString(data)
+ login_response = dom.getElementsByTagName('login')[0]
+ userid = login_response.getAttribute('lguserid')
+ username = login_response.getAttribute('lgusername')
+ token = login_response.getAttribute('lgtoken')
+ prefix = login_response.getAttribute('cookieprefix').decode('utf-8')
+ sessionid = login_response.getAttribute('sessionid')
+
+ c = {}
+ c[prefix + 'UserName'] = username
+ c[prefix + 'UserID'] = userid
+ c[prefix + 'Token'] = token
+ c[prefix + '_session'] = sessionid
+
+ logging.debug('have cookies ' + str(c))
+
+ #custom code that copies cookies from external site
+ #not sure how to set paths and domain of cookies here
+ domain = settings.MEDIAWIKI_COOKIE_DOMAIN
+ for key in c:
+ if c[key]:
+ response.set_cookie(str(key),\
+ value=str(c[key]),\
+ domain=domain)
+ for c in response.cookies.values():
+ logging.debug(c.output())
+ except ExternalLoginData.DoesNotExist:
+ #this must be an OpenID login
+ pass
+
+#function to perform external logout, if needed
+def logout(request):
+ pass
+
+#should raise User.DoesNotExist or pass
+def clean_username(username):
+ return username
+
+def check_password(username,password):
+ """connects to external site and submits username/password pair
+ return True or False depending on correctness of login
+ saves remote unique id and remote session data in table ExternalLoginData
+ may raise forms.ValidationError
+ """
+ host = settings.EXTERNAL_LEGACY_LOGIN_HOST
+ port = settings.EXTERNAL_LEGACY_LOGIN_PORT
+ ext_site = httplib.HTTPConnection(host,port)
+
+ print 'connected to %s:%s' % (str(host),str(port))
+
+ #custom code. this one does authentication through
+ #MediaWiki API
+ params = urllib.urlencode({'action':'login','format':'xml',
+ 'lgname':username,'lgpassword':password})
+ headers = {"Content-type": "application/x-www-form-urlencoded",
+ 'User-Agent':"User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7",
+ "Accept": "text/xml"}
+ ext_site.request("POST","/wiki/api.php",params,headers)
+ response = ext_site.getresponse()
+ if response.status != 200:
+ raise forms.ValidationError('error ' + response.status + ' ' + response.reason)
+ data = response.read().strip()
+ ext_site.close()
+
+ print data
+
+ dom = xml.parseString(data)
+ login = dom.getElementsByTagName('login')[0]
+ result = login.getAttribute('result')
+
+ if result == 'Success':
+ username = login.getAttribute('lgusername')
+ try:
+ eld = ExternalLoginData.objects.get(external_username=username)
+ except ExternalLoginData.DoesNotExist:
+ eld = ExternalLoginData()
+ eld.external_username = username
+ eld.external_session_data = data
+ eld.save()
+ return True
+ else:
+ error = login.getAttribute('details')
+ raise forms.ValidationError(error)
+ return False
+
+def createuser(username,email,password):
+ pass
+
+#retrieve email address
+def get_email(username,password):
+ try:
+ u = MWUser.objects.get(user_name=username)
+ return u.user_email
+ except MWUser.DoesNotExist:
+ return ''
+
+#try to get full name from mediawiki
+def get_screen_name(username,password):
+ try:
+ u = MWUser.objects.get(user_name=username)
+ full_name = u' '.join((u.user_first_name, u.user_last_name)).strip()
+ if full_name != u'':
+ return full_name
+ else:
+ return username
+ except MWUser.DoesNotExist:
+ return username
+
+def connect_local_user_to_external_user(user, login, password):
+ try:
+ u = MWUser.objects.get(user_name=login)
+ user.mediawiki_user = u
+ except MWUser.DoesNotExist:
+ pass
diff --git a/mediawiki/auth.py b/mediawiki/auth.py
new file mode 100644
index 00000000..ee367dc1
--- /dev/null
+++ b/mediawiki/auth.py
@@ -0,0 +1,59 @@
+from mediawiki.models import User as MWUser
+from django.contrib.auth.models import User
+from django_authopenid.models import ExternalLoginData
+from django.conf import settings
+import logging
+from PHPUnserialize import PHPUnserialize
+import os
+
+class php(object):
+ @staticmethod
+ def get_session_data(session):
+ prefix = settings.MEDIAWIKI_PHP_SESSION_PREFIX
+ path = settings.MEDIAWIKI_PHP_SESSION_PATH
+ file = os.path.join(path,prefix + session)
+ #file may not exist
+ data = open(file).read()
+ u = PHPUnserialize()
+ return u.session_decode(data)
+
+class IncludeVirtualAuthenticationBackend(object):
+ def authenticate(self,token=None):
+ logging.debug('authenticating session %s' % token)
+ try:
+ php_session = php.get_session_data(token)
+ #todo: report technical errors to root
+ except:
+ #Fail condition 1. Session data cannot be retrieved
+ logging.debug('session %s cannot be retrieved' % str(token))
+ return None
+ try:
+ name = php_session['wsUserName']
+ id = php_session['wsUserID']
+ except:
+ #Fail condition 2. Data misses keys
+ logging.debug('missing data in session table')
+ return None
+ try:
+ logging.debug('trying to find user %s id=%s in the MW database' % (name,id))
+ wu = MWUser.objects.get(user_name=name,user_id=id)
+ except MWUser.DoesNotExist:
+ #Fail condition 3. User does not match session data
+ logging.debug('could not find wiki user who owns session')
+ return None
+ try:
+ logging.debug('trying to get external login data for mw user %s' % name)
+ eld = ExternalLoginData.objects.get(external_username=name)
+ #update session data and save?
+ return eld.user #may be none!
+ except ExternalLoginData.DoesNotExist:
+ #Fail condition 4. no external login data - user never logged in through django
+ #using the wiki login and password
+ logging.debug('no association found for MW user %s with django user' % name)
+ return None
+
+ def get_user(self, user_id):
+ try:
+ return User.objects.get(pk=user_id)
+ except User.DoesNotExist:
+ return None
diff --git a/mediawiki/forms.py b/mediawiki/forms.py
new file mode 100644
index 00000000..bac39b5a
--- /dev/null
+++ b/mediawiki/forms.py
@@ -0,0 +1,164 @@
+from utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm
+from django import forms
+from django.forms import ValidationError
+from models import User as MWUser
+from models import TITLE_CHOICES
+from django.contrib.auth.models import User
+from django.utils.translation import ugettext as _
+from django.utils.safestring import mark_safe
+from django.contrib.formtools.wizard import FormWizard
+from forum.forms import EditUserEmailFeedsForm, SimpleEmailSubscribeForm
+from django.forms import ValidationError
+from recaptcha_django import ReCaptchaField
+from utils.forms import StrippedNonEmptyCharField
+from forum.templatetags import extra_tags as forum_extra_tags
+
+#make better translations in your language django.po
+EMAIL_FEED_CHOICES = (
+ ('y',_('okay, let\'s try!')),
+ ('n',_('no OSQA community email please, thanks'))
+)
+
+wiki_account_taken_msg = _('Wiki site already has this account, if it is yours perhaps you can '
+ 'just try to log in with it?<br/>'
+ 'Otherwise, please pick another login name.')
+
+class RegisterForm(SetPasswordForm, SimpleEmailSubscribeForm):
+ login_name = UserNameField(label=_('Login name'), \
+ db_model=MWUser, \
+ db_field='user_name', \
+ error_messages={ \
+ 'required':_('Please enter login name above, it is required for the Wiki site'), \
+ 'taken': mark_safe(wiki_account_taken_msg) \
+ }
+ )
+ next = NextUrlField()
+ email = UserEmailField()
+ screen_name = UserNameField(label=mark_safe(_('Please type your nickname below')), \
+ skip_clean=True, \
+ required=False)
+ first_name = StrippedNonEmptyCharField(max_length=255,label=mark_safe(_('First name')),
+ error_messages={'required':_('First name is required')}
+ )
+ last_name = StrippedNonEmptyCharField(max_length=255,label=_('Last name'),
+ error_messages={'required':_('Last name is required')}
+ )
+ #cannot be just "title" because there would be a conflict with "title" field used for MW!!!
+ user_title = forms.ChoiceField(choices=TITLE_CHOICES, label=_('Title (optional)'))
+ use_separate_screen_name = forms.BooleanField(
+ label=mark_safe(_('I prefer (or have to) to use a separate forum screen name')),
+ required=False,
+ )
+ #subscribe = forms.ChoiceField(widget=forms.widgets.RadioSelect, \
+ # error_messages={'required':_('please choose one of the options above')},
+ # choices= EMAIL_FEED_CHOICES)
+ recaptcha = ReCaptchaField()
+
+ class Media:
+ css={'all':(forum_extra_tags.href('/content/style/mediawiki-login.css'),),}
+ js=(forum_extra_tags.href('/content/js/mediawiki-login.js'),)
+
+ def add_screen_name_error(self, err):
+ if 'screen_name' in self.cleaned_data:
+ del self.cleaned_data['screen_name']
+ error_list = self._errors.get('screen_name',forms.util.ErrorList([]))
+ if isinstance(err, forms.util.ErrorList):
+ error_list.extend(err)
+ else:
+ error_list.append(err)
+ self._errors['screen_name'] = error_list
+
+ def clean(self):
+ #this method cleans screen_name and use_separate_screen_name
+ screen_name = self.cleaned_data.get('screen_name', '')
+
+ if 'use_separate_screen_name' in self.cleaned_data \
+ and self.cleaned_data['use_separate_screen_name']:
+ if screen_name == '':
+ msg = _('please enter an alternative screen name or uncheck the box above')
+ self.add_screen_name_error(msg)
+ else:
+ try:
+ screen_name = self.fields['screen_name'].clean(screen_name)
+ self.final_clean_screen_name(screen_name)
+ except ValidationError, e:
+ self.add_screen_name_error(e)
+ else:
+ if screen_name != '':
+ self.add_screen_name_error(_('sorry, to use alternative screen name, please confirm it by checking the box above'))
+ else:
+ #build screen name from first and last names
+ first = self.cleaned_data.get('first_name',None)
+ last = self.cleaned_data.get('last_name',None)
+ if first and last:
+ screen_name = u'%s %s' % (first,last)
+ self.final_clean_screen_name(screen_name)
+ return self.cleaned_data
+
+ def final_clean_screen_name(self,name):
+ try:
+ u = User.objects.get(username=name)
+ msg = _('Screen name <strong>%(real_name)s</strong> is somehow already taken on the forum.<br/>'
+ 'Unfortunately you will have to pick a separate screen name, but of course '
+ 'there is no need to change the first name and last name entries.<br/>'
+ 'Please send us your feedback if you feel there might be a mistake. '
+ 'Sorry for the inconvenience.')\
+ % {'real_name':name}
+ self.add_screen_name_error(mark_safe(msg))
+ except:
+ self.cleaned_data['screen_name'] = name
+
+ #overridden validation for UserNameField
+ def clean_login_name(self):
+ try:
+ MWUser.objects.get(user_name=self.cleaned_data['login_name'])
+ del self.cleaned_data['login_name']
+ raise ValidationError(_('sorry this login name is already taken, please try another'))
+ except:
+ return self.cleaned_data['login_name']
+
+class RegisterFormWizard(FormWizard):
+ def done(self, request, form_list):
+ data = form_list[0].cleaned_data
+ login_name = data['login_name']
+ password = data['password']
+ first_name = data['first_name']
+ last_name = data['last_name']
+ screen_name = data['screen_name']
+ email = data['email']
+ subscribe = data['subscribe']
+ next = data['next']
+
+ #register mediawiki user
+ mwu = MWUser(
+ user_name=login_name,
+ user_password=password,
+ user_first_name = first_name,
+ user_last_name = last_name,
+ user_email = email
+ )
+ mwu.save()
+
+ #register local user
+ User.objects.create_user(screen_name, email, password)
+ u = authenticate(username=screen_name, password=password)
+ u.mediawiki_user = mwu
+ u.save()
+
+ #save email feed settings
+ EFF = EditUserEmailFeedsForm
+ if subscribe == 'y':
+ email_settings_form = EFF()
+ else:
+ email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
+ email_settings_form.save(u)
+
+ #create welcome message
+ u.message_set.create(message=_('Welcome to Q&A forum!'))
+ return HttpResponseRedirect(next)
+
+ def get_template(self, step):
+ if step == 0:
+ return 'mediawiki/mediawiki_signup.html'
+ elif step == 1:
+ return 'notarobot.html'
diff --git a/mediawiki/junk.py b/mediawiki/junk.py
new file mode 100644
index 00000000..e67d492a
--- /dev/null
+++ b/mediawiki/junk.py
@@ -0,0 +1,2 @@
+def junk():
+ pass
diff --git a/mediawiki/middleware.py b/mediawiki/middleware.py
new file mode 100644
index 00000000..a46f486a
--- /dev/null
+++ b/mediawiki/middleware.py
@@ -0,0 +1,57 @@
+from django.contrib import auth
+from django.core.exceptions import ImproperlyConfigured
+from django.conf import settings
+import logging
+import traceback
+import sys
+
+class IncludeVirtualAuthenticationMiddleware(object):
+ def process_request(self,request):
+ """in this type of authentication the mw session token is passed via
+ "session" request parameter and authentication happens on every
+ request
+ """
+ logging.debug('trying include virtual milldeware')
+ if not hasattr(request,'user'):
+ raise ImproperlyConfigured(
+ "The include virtual mediawiki authentication middleware requires the"
+ " authentication middleware to be installed. Edit your"
+ " MIDDLEWARE_CLASSES setting to insert"
+ " 'django.contrib.auth.middleware.AuthenticationMiddleware'"
+ " before the IncludeVirtualAuthenticationMiddleware class."
+ )
+
+ session = None
+ request.is_include_virtual = False
+ if request.is_ajax():
+ logging.debug('have ajax request')
+ cookie_name = settings.MEDIAWIKI_SESSION_COOKIE_NAME
+ if cookie_name in request.COOKIES:
+ session = request.COOKIES[cookie_name]
+ logging.debug('ajax call has session %s' % session)
+ else:
+ logging.debug('dont have cookie')
+ else:
+ if request.REQUEST.has_key('session'):
+ session = request.REQUEST['session']
+ request.is_include_virtual = True
+ logging.debug('I am virtual')
+ if request.REQUEST.get('was_posted','false') == 'true':
+ data = request.GET.copy()
+ data['recaptcha_ip_field'] = request.META['REMOTE_ADDR']
+ request.GET = data
+ logging.debug('REQUEST is now %s' % str(request.GET))
+ user = auth.authenticate(token=session) #authenticate every time
+ if user:
+ request.user = user
+ auth.login(request,user)
+ #else I probably need to forbid access
+ #raise ImproperlyConfigured(
+ # "The include virtual mediawiki authentication middleware requires the"
+ # "'session' request parameter set in the including document"
+ #)
+
+ def process_exception(self,request,exception):
+ exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
+ logging.debug('\n'.join(traceback.format_tb(exceptionTraceback)))
+ logging.debug('have exception %s %s' % (exceptionType,exceptionValue))
diff --git a/mediawiki/models.py b/mediawiki/models.py
new file mode 100644
index 00000000..e37aec32
--- /dev/null
+++ b/mediawiki/models.py
@@ -0,0 +1,312 @@
+# This is an auto-generated Django model module.
+# You'll have to do the following manually to clean this up:
+# * Rearrange models' order
+# * Make sure each model has one field with primary_key=True
+# Feel free to rename the models, but don't rename db_table values or field names.
+#
+# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'
+# into your database.
+
+table_prefix = u'nmrwiki'
+from django.db import models
+import re
+from django.conf import settings
+import logging
+from django.contrib.auth.models import User as DjangoUser
+from django.utils.translation import ugettext as _
+import hashlib
+import time
+import random
+
+MW_TS = '%Y%m%d%H%M%S'
+
+TITLE_CHOICES = (
+ ('none',_('----')),
+ ('prof',_('Prof.')),
+ ('dr',_('Dr.')),
+)
+
+class User(models.Model):
+ user_id = models.IntegerField(primary_key=True,db_column='user_id')
+ user_name = models.CharField(max_length=765)
+ user_real_name = models.CharField(max_length=765)
+ user_password = models.TextField()
+ user_newpassword = models.TextField()
+ user_newpass_time = models.CharField(max_length=14, blank=True)
+ user_email = models.TextField()
+ user_options = models.TextField()
+ user_touched = models.CharField(max_length=14)
+ user_token = models.CharField(max_length=32)
+ user_email_authenticated = models.CharField(max_length=14, blank=True)
+ user_email_token = models.CharField(max_length=32, blank=True)
+ user_email_token_expires = models.CharField(max_length=14, blank=True)
+ user_registration = models.CharField(max_length=14, blank=True)
+ user_editcount = models.IntegerField(null=True, blank=True)
+ user_last_name = models.CharField(max_length=765, blank=True)
+ user_first_name = models.CharField(max_length=765, blank=True)
+ user_reason_to_join = models.CharField(max_length=765, blank=True)
+ user_title = models.CharField(max_length=16, blank=True, choices=TITLE_CHOICES)
+ class Meta:
+ db_table = table_prefix + u'user'
+ managed = False
+
+ def set_default_options(self):
+ default_options = {
+ 'quickbar':1,
+ 'underline':2,
+ 'cols':80,
+ 'rows':25,
+ 'searchlimit':20,
+ 'contextlines':5,
+ 'contextchars':50,
+ 'skin':'false',
+ 'math':1,
+ 'rcdays':7,
+ 'rclimit':50,
+ 'wllimit':250,
+ 'highlightbroken':1,
+ 'stubthreshold':0,
+ 'previewontop':1,
+ 'editsection':1,
+ 'editsectiononrightclick':0,
+ 'showtoc':1,
+ 'showtoolbar':1,
+ 'date':'default',
+ 'imagesize':2,
+ 'thumbsize':2,
+ 'rememberpassword':0,
+ 'enotifwatchlistpages':0,
+ 'enotifusertalkpages':1,
+ 'enotifminoredits':0,
+ 'enotifrevealaddr':0,
+ 'shownumberswatching':1,
+ 'fancysig':0,
+ 'externaleditor':0,
+ 'externaldiff':0,
+ 'showjumplinks':1,
+ 'numberheadings':0,
+ 'uselivepreview':0,
+ 'watchlistdays':3.0,
+ 'usenewrc':1,
+ }
+ self.user_options = '\n'.join(
+ map(lambda opt: '%s=%s' % (opt[0], str(opt[1])),
+ default_options.items())
+ )
+
+ def set_password_and_token(self,password):
+ p = hashlib.md5(password).hexdigest()
+ if hasattr(settings,'MEDIAWIKI_SALT_PASSWORD') and settings.MEDIAWIKI_SALT_PASSWORD == True:
+ p = hashlib.md5('%d-%s' % (self.user_id, p)).hexdigest()
+ self.user_password = p
+ self.user_token = hashlib.md5(p + str(time.time())).hexdigest()
+
+ def get_name(self):
+ if self.user_real_name:
+ if re.search(r'\S',self.user_real_name):
+ return self.user_real_name
+ return self.user_name + ' (nickname)'
+
+ def get_html(self):
+ return '<a href="%s">%s</a>' % (self.get_absolute_url(),self.get_name())
+
+ def get_absolute_url(self):
+ url = settings.MEDIAWIKI_URL + '?title=User:' + self.user_name
+ return url
+
+class UserProfile(models.Model):
+ nup_user_id = models.ForeignKey(User,primary_key=True,db_column='nup_user_id')
+ nup_about = models.CharField(max_length=765, blank=True)
+ nup_position_title = models.CharField(max_length=765, blank=True)
+ nup_position_type = models.CharField(max_length=765, blank=True)
+ nup_employer_division = models.CharField(max_length=765, blank=True)
+ nup_employer_company = models.CharField(max_length=765, blank=True)
+ nup_employer_type = models.CharField(max_length=765, blank=True)
+ nup_employment_status = models.CharField(max_length=45, blank=True)
+ nup_profession = models.CharField(max_length=765, blank=True)
+ nup_city = models.CharField(max_length=765, blank=True)
+ nup_state = models.CharField(max_length=765, blank=True)
+ nup_country = models.CharField(max_length=765, blank=True)
+ nup_lattitude = models.FloatField(null=True, blank=True)
+ nup_longitude = models.FloatField(null=True, blank=True)
+ nup_hiring = models.IntegerField(null=True, blank=True)
+ nup_hunting = models.IntegerField(null=True, blank=True)
+ nup_education = models.TextField(blank=True)
+ nup_websites = models.TextField(blank=True)
+ nup_interests = models.TextField(blank=True)
+ nup_job_ad = models.TextField(blank=True)
+ nup_job_ad_title = models.CharField(max_length=765, blank=True)
+ nup_job_ad_active = models.IntegerField(null=True, blank=True)
+ nup_expertise = models.TextField(blank=True)
+ nup_is_approved = models.BooleanField()
+ class Meta:
+ db_table = table_prefix + u'new_user_profile'
+ managed = False
+
+class RecentChanges(models.Model):
+ rc_id = models.AutoField(primary_key=True, db_column='rc_id')
+ rc_timestamp = models.CharField(max_length=14)
+ rc_cur_time = models.CharField(max_length=14)
+ rc_user = models.ForeignKey(User, db_column='rc_user')
+ rc_user_text = models.CharField(max_length=765)
+ rc_namespace = models.IntegerField()
+ rc_title = models.CharField(max_length=765)
+ rc_comment = models.CharField(max_length=765)
+ rc_minor = models.IntegerField()
+ rc_bot = models.IntegerField()
+ rc_new = models.IntegerField()
+ rc_cur_id = models.IntegerField()
+ rc_this_oldid = models.IntegerField()
+ rc_last_oldid = models.IntegerField()
+ rc_type = models.IntegerField()
+ rc_moved_to_ns = models.IntegerField()
+ rc_moved_to_title = models.CharField(max_length=765)
+ rc_patrolled = models.IntegerField()
+ rc_ip = models.CharField(max_length=40)
+ rc_old_len = models.IntegerField(null=True, blank=True)
+ rc_new_len = models.IntegerField(null=True, blank=True)
+ rc_deleted = models.IntegerField()
+ rc_logid = models.ForeignKey('Logging', db_column='rc_logid')
+ rc_log_type = models.CharField(max_length=255, blank=True)
+ rc_log_action = models.CharField(max_length=255, blank=True)
+ rc_params = models.TextField(blank=True)
+ class Meta:
+ db_table = table_prefix + u'recentchanges'
+ managed = False
+
+class Logging(models.Model):
+ log_id = models.AutoField(primary_key=True)
+ log_type = models.CharField(max_length=10)
+ log_action = models.CharField(max_length=10)
+ log_timestamp = models.CharField(max_length=14)
+ log_user = models.ForeignKey(User,db_column='log_user')
+ log_namespace = models.IntegerField()
+ log_title = models.CharField(max_length=765)
+ log_comment = models.CharField(max_length=765)
+ log_params = models.TextField()
+ log_deleted = models.IntegerField()
+ class Meta:
+ db_table = table_prefix + u'logging'
+ managed = False
+
+ def show_in_recent_changes(self, ip=None, rc_minor=False):
+ #to call this method self object must already exist in DB
+ if self.log_type == 'newusers' and self.log_action=='create':
+ rc = RecentChanges(
+ rc_ip=ip,
+ rc_minor=int(rc_minor),
+ rc_deleted=0,
+ rc_bot=0,
+ rc_new=0,
+ rc_moved_to_title='',
+ rc_moved_to_ns=0,
+ rc_this_oldid=0,
+ rc_last_oldid=0,
+ rc_patrolled=1,
+ rc_old_len=None,
+ rc_new_len=None,
+ rc_logid=self,
+ rc_user=self.log_user,
+ rc_user_text=self.log_user.user_name,
+ rc_log_type=self.log_type,
+ rc_log_action=self.log_action,
+ rc_timestamp = self.log_timestamp,
+ rc_cur_time = self.log_timestamp,
+ rc_title='Log/newusers',
+ rc_namespace=-1, #-1 special, 2 is User namespace
+ rc_params=self.log_params,
+ rc_comment=_('Welcome new user!'),
+ rc_type=3,#MW RCLOG constant from Defines.php
+ rc_cur_id=0,
+ )
+ rc.save()
+ else:
+ raise NotImplementedError()
+
+
+class Page(models.Model):
+ page_id = models.AutoField(primary_key=True)
+ page_namespace = models.IntegerField(unique=True)
+ page_title = models.CharField(max_length=765)
+ page_restrictions = models.TextField()
+ page_counter = models.IntegerField()
+ page_is_redirect = models.IntegerField()
+ page_is_new = models.IntegerField()
+ page_random = models.FloatField()
+ page_touched = models.CharField(max_length=14)
+ page_latest = models.IntegerField()
+ page_len = models.IntegerField()
+ class Meta:
+ db_table = table_prefix + u'page'
+ managed = False
+ def save(self):
+ raise Exception('WikiUser table is read-only in this application')
+
+class PageLinks(models.Model):
+ pl_from = models.ForeignKey(Page)
+ pl_namespace = models.IntegerField()
+ pl_title = models.CharField(max_length=765)
+ class Meta:
+ db_table = table_prefix + u'pagelinks'
+ managed = False
+ def save(self):
+ raise Exception('WikiUser table is read-only in this application')
+
+class Revision(models.Model):
+ rev_id = models.IntegerField(unique=True)
+ rev_page = models.IntegerField()
+ rev_text_id = models.IntegerField()
+ rev_comment = models.TextField()
+ rev_user = models.IntegerField()
+ rev_user_text = models.CharField(max_length=765)
+ rev_timestamp = models.CharField(max_length=14)
+ rev_minor_edit = models.IntegerField()
+ rev_deleted = models.IntegerField()
+ rev_len = models.IntegerField(null=True, blank=True)
+ rev_parent_id = models.IntegerField(null=True, blank=True)
+ class Meta:
+ db_table = table_prefix + u'revision'
+ managed = False
+
+class Text(models.Model):
+ old_id = models.IntegerField(primary_key=True)
+ old_text = models.TextField()
+ old_flags = models.TextField()
+ class Meta:
+ db_table = table_prefix + u'text'
+ managed = False
+
+#nmrwiki_stats table may be of interest
+
+class UserGroups(models.Model):
+ ug_user = models.ForeignKey(User,primary_key=True)
+ ug_group = models.CharField(max_length=16)
+ class Meta:
+ db_table = table_prefix + u'user_groups'
+ managed = False
+
+def user_get_absolute_url(user):
+ return user.mediawiki_user.get_absolute_url()
+
+def user_get_html(user):
+ return user.mediawiki_user.get_html()
+
+def user_has_valid_email(user):
+ if user.mediawiki_user.user_email_authenticated:
+ return True
+ else:
+ return False
+
+def user_get_description_for_admin(user):
+ out = user.get_html() + ' (%s)' % user.username
+ if user.has_valid_email():
+ out += ' - has valid email'
+ else:
+ out += ' - <em>no email!</em>'
+ return out
+
+DjangoUser.add_to_class('mediawiki_user',models.ForeignKey(User, null=True))
+DjangoUser.add_to_class('get_wiki_profile_url',user_get_absolute_url)
+DjangoUser.add_to_class('get_wiki_profile_url_html',user_get_html)
+DjangoUser.add_to_class('get_description_for_admin',user_get_description_for_admin)
+DjangoUser.add_to_class('has_valid_wiki_email',user_has_valid_email)
diff --git a/mediawiki/templatetags/__init__.py b/mediawiki/templatetags/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/mediawiki/templatetags/__init__.py
diff --git a/mediawiki/templatetags/mediawikitags.py b/mediawiki/templatetags/mediawikitags.py
new file mode 100644
index 00000000..b21789f4
--- /dev/null
+++ b/mediawiki/templatetags/mediawikitags.py
@@ -0,0 +1,62 @@
+from django import template
+from django.template.defaultfilters import stringfilter
+from django.conf import settings
+import logging
+
+register = template.Library()
+
+#template tags
+class MWPluginFormActionNode(template.Node):
+ def __init__(self, wiki_page, form_action):
+ self.form_action = ''.join(form_action[1:-1])
+ self.wiki_page = ''.join(wiki_page[1:-1])
+ def render(self, context):
+ out = ('<input type="hidden" name="title" value="%s"/>' \
+ + '<input type="hidden" name="command" value="%s"/>') \
+ % (self.wiki_page, self.form_action)
+ return out
+
+def curry_up_to_two_argument_tag(TagNodeClass):
+ def do_the_action_func(parser,token):
+ args = token.split_contents()
+ if len(args) > 3:
+ tagname = token.contents.split()[0]
+ raise template.TemplateSyntaxError, \
+ '%s tag requires two arguments or less' % tagname
+ if len(args) > 1:
+ argument1 = ''.join(args[1][1:-1])
+ else:
+ argument1 = None
+ if len(args) == 3:
+ argument2 = ''.join(args[2][1:-1])
+ else:
+ argument2 = None
+ return TagNodeClass(argument1, argument2)
+ return do_the_action_func
+
+def do_mw_plugin_form_action(parser,token):
+ args = token.split_contents()
+ if len(args) != 3:
+ tagname = token.contents.split()[0]
+ raise template.TemplateSyntaxError, \
+ '%s tag requires two arguments' % tagname
+ return MWPluginFormActionNode(args[1],args[2])
+
+class MediaWikiPluginUrlNode(template.Node):
+ """will return either wiki url, a particular page url
+ or a page with command argument to be interpreted by the plugin
+ """
+ def __init__(self,wiki_page=None,url=None):
+ self.url = url
+ self.wiki_page = wiki_page
+ def render(self,context):
+ title_token = '?title=%s' % self.wiki_page
+ cmd_token = '&command=%s' % self.url
+ if self.wiki_page == None:
+ return settings.MEDIAWIKI_URL
+ if self.url == None:
+ return settings.MEDIAWIKI_URL + title_token
+ return settings.MEDIAWIKI_URL + title_token + cmd_token
+
+register.tag('mw_plugin_form_action',do_mw_plugin_form_action)
+register.tag('mw_plugin_url',curry_up_to_two_argument_tag(MediaWikiPluginUrlNode))
diff --git a/mediawiki/views.py b/mediawiki/views.py
new file mode 100644
index 00000000..012d6f42
--- /dev/null
+++ b/mediawiki/views.py
@@ -0,0 +1,192 @@
+#this file contains stub functions that can be extended to support
+#connect legacy login with external site
+#from django import forms
+import time
+from models import User as MWUser
+from models import Logging
+from models import MW_TS
+import api
+from django.shortcuts import render_to_response
+from django.utils.translation import ugettext as _
+from django.template import RequestContext
+from django.http import HttpResponseRedirect
+from forms import RegisterForm
+from forum.forms import SimpleEmailSubscribeForm
+from forum.models import Question
+from django.contrib.auth.models import User
+from django.contrib.auth import authenticate, login
+from django.http import HttpResponseRedirect
+from django.db import transaction
+from django_authopenid.models import ExternalLoginData
+from django_authopenid.views import not_authenticated
+from django.template import loader
+from django.core.mail import send_mail
+from django.conf import settings
+from django.utils.safestring import mark_safe
+import hashlib
+import random
+
+#not a view, but uses request and templates
+def send_welcome_email(request, wiki_user, django_user):
+ random.seed()
+ confirmation_token = '%032x' % random.getrandbits(128)
+ wiki_user.user_email_token = hashlib.md5(confirmation_token).hexdigest()
+ wiki_user.user_email_token_expires = time.strftime(MW_TS,(time.gmtime(time.time() + 7*24*60*60)))
+ wiki_user.save()
+
+ link = 'http://' + settings.EXTERNAL_LEGACY_LOGIN_HOST \
+ + settings.MEDIAWIKI_INDEX_PHP_URL \
+ + '?title=Special:Confirmemail/' \
+ + confirmation_token
+
+ pw_link = 'http://' + settings.EXTERNAL_LEGACY_LOGIN_HOST \
+ + settings.MEDIAWIKI_INDEX_PHP_URL \
+ + '?title=Password_recovery'
+
+ if wiki_user.user_title == 'prof':
+ template_name = 'mediawiki/welcome_professor_email.txt'
+ else:
+ template_name = 'mediawiki/welcome_email.txt'
+ t = loader.get_template(template_name)
+
+ data = {
+ 'email_confirmation_url':mark_safe(link),
+ 'admin_email':settings.DEFAULT_FROM_EMAIL,
+ 'first_name':wiki_user.user_first_name,
+ 'last_name':wiki_user.user_last_name,
+ 'login_name':wiki_user.user_name,
+ 'title':wiki_user.user_title,
+ 'user_email':wiki_user.user_email,
+ 'forum_screen_name':django_user.username,
+ 'password_recovery_url':mark_safe(pw_link),
+ }
+ body = t.render(RequestContext(request,data))
+ if wiki_user.user_title in ('prof','dr'):
+ subject = _('%(title)s %(last_name)s, welcome to the OSQA online community!') \
+ % {'title':wiki_user.get_user_title_display(),'last_name':wiki_user.user_last_name }
+ else:
+ subject = _('%(first_name)s, welcome to the OSQA online community!') \
+ % {'first_name':wiki_user.user_first_name}
+ from_email = settings.DEFAULT_FROM_EMAIL
+ send_mail(subject,body,from_email,[wiki_user.user_email])
+
+@transaction.commit_manually
+def signup(request):
+ #this view works through forum and mediawiki (using apache include virtual injection)
+ if request.is_include_virtual and request.REQUEST.get('was_posted','false')=='true':
+ POST_INCLUDE_VIRTUAL = True
+ POST_DATA = request.GET
+ else:
+ POST_INCLUDE_VIRTUAL = False
+ if request.method == 'POST':
+ POST_DATA = request.POST
+ else:
+ POST_DATA = None
+
+ if POST_DATA:
+ form = RegisterForm(POST_DATA)
+ if form.is_valid():
+ data = form.cleaned_data
+ login_name = data['login_name']
+ password = data['password']
+ first_name = data['first_name']
+ last_name = data['last_name']
+ screen_name = data['screen_name']
+ user_title = data['user_title']
+ email = data['email']
+ next = data['next']
+
+ #register mediawiki user
+ user_real_name = u'%s %s' % (first_name,last_name)
+ mwu = MWUser(
+ user_name=login_name,
+ user_first_name = first_name,
+ user_last_name = last_name,
+ user_title = user_title,
+ user_email = email,
+ user_real_name=user_real_name
+ )
+ mwu.set_default_options()
+ mwu.save()
+ #password may need user id so reload it
+ mwu = MWUser.objects.get(user_name = login_name)
+ mwu.set_password_and_token(password)
+ mwu.save()
+
+ #create log message
+ mwu_creation_log = Logging(
+ log_type='newusers',
+ log_action='create',
+ log_timestamp=time.strftime(MW_TS),
+ log_params=str(mwu.user_id),
+ log_namespace=2,
+ log_user=mwu,
+ log_deleted=0,
+ )
+ mwu_creation_log.save()
+ mwu_creation_log.show_in_recent_changes(ip=request.META['REMOTE_ADDR'])
+ print 'creation log saved'
+
+ #register local user
+ User.objects.create_user(screen_name, email, password)
+ u = authenticate(username=screen_name, password=password)
+ login(request,u)
+ u.mediawiki_user = mwu
+ u.save()
+
+ #save email feed settings
+ subscribe = SimpleEmailSubscribeForm(POST_DATA)
+ if subscribe.is_valid():
+ subscribe.save(user=u)
+
+ #save external login data
+ eld = ExternalLoginData(external_username=login_name, user=u)
+ eld.save()
+
+ transaction.commit()#commit so that user becomes visible on the wiki side
+
+ #check password through API and load MW HTTP header session data
+ api.check_password(login_name,password)
+
+ print 'wiki login worked'
+
+ #create welcome message on the forum
+ u.message_set.create(message=_('Welcome to the OSQA community!'))
+ print 'about to send confirmation email'
+ send_welcome_email(request, mwu, u)
+
+ if POST_INCLUDE_VIRTUAL:
+ questions = Question.objects.exclude(deleted=True, closed=True, answer_accepted=True)
+ questions = questions.order_by('-last_activity_at')[:5]
+ response = render_to_response('mediawiki/thanks_for_joining.html', \
+ {
+ 'wiki_user':mwu,
+ 'user':u,
+ 'questions':questions,
+ },
+ context_instance = RequestContext(request))
+ api.set_login_cookies(response, u)
+ #call session middleware now to get the django login cookies
+ from django.contrib.sessions.middleware import SessionMiddleware
+ sm = SessionMiddleware()
+ response = sm.process_response(request,response)
+ cookies = response.cookies
+ for c in cookies.values():
+ response.write(c.js_output())
+ else:
+ response = HttpResponseRedirect(next)
+ api.set_login_cookies(response, u)
+
+ #set cookies so that user is logged in in the wiki too
+ transaction.commit()
+ return response
+ else:
+ form = RegisterForm()
+
+ transaction.commit()
+ if request.is_include_virtual:
+ template_name = 'mediawiki/mediawiki_signup_content.html'
+ else:
+ template_name = 'mediawiki/mediawiki_signup.html'
+ return render_to_response(template_name,{'form':form},\
+ context_instance=RequestContext(request))