diff --git a/src/postorius/doc/_build/html/objects.inv b/src/postorius/doc/_build/html/objects.inv
index 0fcc642..0c1d872 100644
--- a/src/postorius/doc/_build/html/objects.inv
+++ b/src/postorius/doc/_build/html/objects.inv
Binary files differ
diff --git a/src/postorius/doc/_build/html/py-modindex.html b/src/postorius/doc/_build/html/py-modindex.html
index 0d82010..aa9ec3a 100644
--- a/src/postorius/doc/_build/html/py-modindex.html
+++ b/src/postorius/doc/_build/html/py-modindex.html
@@ -53,22 +53,37 @@
Python Module Index
diff --git a/src/postorius/doc/_build/html/searchindex.js b/src/postorius/doc/_build/html/searchindex.js
index 6694033..acc72ee 100644
--- a/src/postorius/doc/_build/html/searchindex.js
+++ b/src/postorius/doc/_build/html/searchindex.js
@@ -1 +1 @@
-Search.setIndex({objects:{tests:{tests:[4,0,1,""]}},terms:{all:[0,4,2,3],code:[0,4,3],forget:4,prefil:4,test_list_set:0,four:[],ackownledg:[],runserv:2,dirnam:[],follow:[4,2],content:[1,4],decid:[],depend:[3,2],authoris:[],send:[],granci:3,under:3,introduc:[],merchant:3,sourc:[],everi:[],string:4,without:[4,3],far:[],none:4,offlin:[],util:[0,4,3],context_processor:0,mechan:4,veri:[],exact:4,mailmanwebgsoc2011:[],testobject:4,contenttyp:[],administr:4,level:[],did:4,button:4,list:[0,4,3],liza:4,"try":4,item:4,adjust:[],localhost:4,httpredirectobject:4,quick:[],setup:[4,2,3],dir:[],pleas:4,modelbackend:[],impli:3,fieldset_form:0,httpresponseredirect:4,cfg:[],seper:[],request:4,past:[],second:2,download:2,further:[],click:4,compat:[],index:4,what:[],name_of_permiss:[],appear:[],richard:3,sum:[],abl:[4,2],current:[0,4],delet:4,new_list1:4,postoriu:[0,1,2,3],franziska:[],"new":[1,3,4],net:[],method:[],abov:4,gener:[0,3],never:2,remeb:[],here:[],themself:[],ubuntu:[],path:2,along:3,modifi:[4,3],sinc:2,valu:4,search:[],mailinglist:[],vertifi:[],anymor:[],errorlog:[],step:[],jame:4,doctest:4,action:4,chang:2,mailman_media:[],mock:0,contactpag:[],via:[],appli:[],app:[4,3],sponser:[],foundat:3,releas:2,api:[0,3],sponsel:[],foord:0,instal:[1,2,3,4],myownunittest:0,middlewar:[],from:[0,4,2,3],describ:4,would:[],commun:[],doubl:4,two:[],perm:[],next:[],websit:[],few:[],call:3,typo:3,recommend:[],dict:[],type:4,web_host:4,create_mock_list:[],mailman_django:[],abspath:[],relat:4,ital:[],site:[4,2],warn:[4,2],trail:[],berlio:[],stick:[],autodoc:[],particular:3,dooo:[],hold:0,unpack:[],easiest:4,customlog:[],account:[3,2],join:[],alia:2,prepar:2,work:[],uniqu:[],dev:[3,2],itself:[],can:[0,4,2,3],purpos:3,def:[],control:3,defer:3,sqlite:2,prompt:2,login_requir:4,tar:[],process:3,sudo:2,accept:3,topic:[],want:[4,2],fieldset:0,create_mock_memb:[],create_mock_domain:[],nearli:[],alwai:2,cours:[],multipl:[],listmembersviewtest:0,anoth:4,faulti:[],georg:4,write:[],how:2,list_nam:[],reject:3,instead:4,config:[],nasti:[],css:[0,2],updat:4,product:2,resourc:4,farm:3,pypi:2,after:[4,2],"long":[],usabl:[],befor:[0,4],wrong:4,mai:[],end:[],data:4,"short":0,third:2,postfix:[],bind:[],bootstrap:[],credenti:[],django:[0,4,2,3],alias:3,environ:2,adverrtis:4,jain:3,enter:4,fallback:[],automaticli:[],egg:[],order:2,listnam:4,help:0,move:3,becaus:4,has_perm:[],"__future__":[],style:0,directli:4,fit:3,fix:3,browserid:3,unicode_liter:[],better:[],restart:[],onc:[],mail:[0,4,3],hidden:[],main:[],might:[],guarente:[],split:[],them:[0,2],good:[],"return":[],thei:4,python:[0,4,2,3],databs:[],handi:0,auth:0,unfortuneatli:[],"break":[],mention:4,front:4,retreiv:0,now:[4,2,3],introduct:[],term:3,benst:[],somewher:2,name:4,anyth:[],edit:[4,3],simpl:[],postorius_error:[],authent:0,separ:[4,3],easili:[],senarclen:3,each:4,debug:4,found:[4,2],went:4,mailman_test_bindir:[],everyth:4,domain:[0,4,3],fail:[4,3],replac:[],idea:[],procedur:[],realli:4,redistribut:3,meta:4,"static":[0,2],connect:[0,3],our:4,patch:[],todo:[],dependeci:[],out:3,variabl:0,shown:4,space:3,miss:3,robert:2,develop:[0,1,2,3,4],publish:3,api_us:4,profil:3,daniel:3,rest_serv:[],got:[],correct:2,common:[],earlier:[],insid:[],free:[4,3],effect:[],standalon:[],reason:[],base:0,mailmanweb:[],lists_of_domain:[],put:0,org:[3,2],restbackend:[],"40mail":4,launch:[],dev_setup:[],could:[],latest:2,membership:[0,4],keep:2,filter:[],thing:4,place:[],isn:[],root_urlconf:[],requireti:[],summari:4,first:[4,2],softwar:3,rang:[],render:[],prevent:[],feel:4,media_root:[],natti:[],restrict:4,date:2,instruct:[4,2],alreadi:4,done:4,messag:[4,3],authentif:4,owner:[0,4,3],stabl:4,installed_app:[],open:4,gpl:[],differ:2,rrze:[],my_own_test:0,script:0,benedict:3,hardcopi:[],licens:3,system:[],least:4,url_host:[],too:0,licenc:[],fullfil:[],"final":4,store:[],shell:[4,3],option:4,real_nam:4,copi:3,specifi:4,gsoc:[],"var":[],part:4,"__test__":0,exactli:[],priveledg:[],serv:2,test_some_method:[],kind:0,provid:[0,4,2,3],remov:[4,3],structur:[0,3],new_domain:[],project:[0,3,2],postorius_standalon:2,were:4,posit:4,minut:[],other:4,fqdn_listnam:4,pre:[],sai:[],runner:0,ani:3,postorius_access:[],packag:2,have:[0,4,2,3],tabl:2,need:[4,2],element:[],wsgiscriptalia:2,engin:2,inform:2,florian:[],destroi:[],self:[],note:[4,2],also:2,exampl:4,take:[],which:[4,2],combin:[],mock_memb:[],singl:[0,4],even:3,sure:[0,4,2],kati:4,allow:[4,2],shall:4,usernam:[],object:[0,4],asdasd:[],most:4,plan:[],letter:4,watt:4,alpha:3,"class":0,icon:[],collectstat:2,don:4,bzr:2,url:[0,4],doc:0,proud:[],cover:2,temporili:4,doe:4,deni:2,pars:4,usual:[],dot:[],wsgi:2,show:[4,3],text:4,contact_address:[],session:4,permiss:4,corner:[],fine:[],find:0,magicmock:[],redirect:4,absolut:[],onli:[4,2],eas:[],locat:2,launchpad:2,copyright:3,explain:4,configur:[],apach:[3,2],should:[0,4,2,3],theme:3,version:[3,2],suppos:[],templat:[0,3],folder:[0,2],local:[4,2],hope:3,media_url:[],hit:[],contribut:3,get:[0,4,2],familiar:0,"__file__":[],stop:4,obviou:[],absolute_import:[],csrf:3,subscript:4,requir:4,layout:0,mail_host:4,bar:[],template_dir:[],"public":3,reload:2,bad:[],mailmancli:3,restadmin:[],mm_membership:4,where:0,view:[0,4,3],wiki:[],conform:3,set:[0,4,2,3],special:[],sometest:[],see:[4,2,3],domain_admin:[],result:[],respons:4,testcas:[],wonder:[],awar:4,statu:4,mailman3a7:[],correctli:[],databas:[4,2],someth:4,test_list_memb:0,behind:[],quickest:2,between:4,"import":[0,4],awai:[],email:4,realnam:[],extens:[],correclti:[],advertis:4,subfold:[],thank:3,both:[],last:4,plugin:[],admin:2,howev:[],etc:2,instanc:4,context:3,delete_list:4,logout:[],login:[4,3],com:4,load:4,english:4,simpli:4,point:[],instanti:[],overview:4,unittest:[],address:[0,4],virtualhost:2,mock_list:[],header:[],featur:[],non:[],shortcut:0,linux:4,guid:[0,4,2],assum:2,backend:[],quit:[],trunk:[],mailman:[0,1,2,3,4],coupl:4,"0a7":[],been:4,compon:[],much:[],unsubscrib:[4,3],modif:[],addit:[],quickli:0,upcom:[],imag:[0,2],xxx:[],togeth:4,i18n:[],ngeorg:4,niederreit:2,those:4,"case":4,creativecommon:[],therefor:[],look:0,gnu:[3,2],plain:[],align:[],properti:[],lesser:3,"while":2,dashboard:4,publicli:2,error:3,everyon:[],authentication_backend:[],new_list:[],helper:3,almost:2,demo:[],metric:3,list_own:4,archiv:4,revis:[],"__init__":0,subscrib:[4,3],decor:4,let:4,welcom:[],author:0,receiv:3,media:[],make:[0,4,2],belong:4,same:4,member:[0,3],handl:[],html:0,gui:4,document:[0,3,2],mod_wsgi:[3,2],behav:0,acl:[],finish:4,http:[4,2,3],webserv:2,upon:[],mmclient:3,moment:[],http_host:4,initi:3,mani:3,implement:3,expand:[],appropri:[],moder:[0,3],scenario:[],framework:[],api_pass:4,well:[4,2],membership_set:[],pep8:3,client:[0,4,2],command:[0,3],thi:[0,4,2,3],choos:4,model:0,rout:0,left:[],summer:3,just:2,rest:4,indic:4,mailman3:4,webui:[],test_util:0,yet:[],languag:4,web:[1,2,3],display_nam:[],easi:[],project_path:[],had:[],list_summari:4,littl:0,apache2:[],add:[0,4,3],valid:[0,4],lawrenc:[],save:[],modul:[0,4],build:0,bin:[],applic:[0,2],obsolet:3,stein:3,unter:[],read:[],prefer:2,testlist:[],test:[0,4,3],know:2,gsoc_mailman:[],press:4,cooki:[],bit:0,password:[],tweak:[],authbackend:[],like:0,resid:[0,2],template_context_processor:[],success:[4,3],changelog:[1,3],restpass:[],server:[3,2],collect:2,href:[],setup_mm:4,either:4,page:4,www:3,right:[],acknowledg:[],creation:4,some:[0,4],back:[],proper:3,create_list:[],funcit:[],librari:0,distribut:3,basic:[0,3],buildout:2,djangoproject:[],confirm:4,avoid:[],woun:[],token:3,select:[],slash:[],necessari:2,foo:[],anna:3,refer:4,machin:4,core:[],who:[],run:[0,4,2],bold:[],symlink:[],host:[],repositori:[],post:4,bazaar:2,mm_new_domain:[],stage:[],src:0,about:[],central:[],usa:4,syncdb:2,mass_subscrib:4,rohan:3,side:[],permission_requir:[],srv:2,act:2,fals:4,discard:3,readi:2,processor:[],block:4,own:[0,4],addus:[],status_cod:4,pythonpath:2,xyz:[],within:4,someviewclass:[],easy_instal:2,warranti:3,creativ:[],empti:4,contrib:[],your:[0,2],manag:[0,4,2,3],choosen:4,span:4,log:4,wai:[4,2],"40exampl":4,execut:4,print:4,submit:4,custom:0,avail:[0,4,2],start:[0,4,2],reli:[],interfac:[3,2],includ:4,suit:4,superus:2,systers_django:[],"function":[0,4],head:[],form:[0,4,3],offer:4,descrip:[],requestfactori:[],link:4,translat:4,teardown_mm:4,branch:[3,2],line:[4,2],"true":4,info:3,pull:2,succe:4,made:4,render_mailman_them:[],bullet:[],possibl:2,whether:4,access:[4,3],displai:4,below:4,memebership:[],otherwis:[],more:3,postmast:[],extend_ajax:[],later:[],creat:[0,4,2],hardcod:[],dure:[4,3],doesn:[],listsettingsviewtest:0,exist:[],file:[0,3,2],home:[],pip:2,wackerbarth:3,check:[4,3],probabl:0,again:[],coder:2,successfulli:[],googl:3,titl:[],user:[0,4,2,3],when:[],detail:[3,2],gettext:4,"default":[],mizyrycki:3,mock_list2:[],futur:[],rememb:[],writabl:2,you:[0,4,2,3],servernam:[],nice:4,michael:0,why:4,prequir:[],consid:[],stai:[],outdat:4,sphinx:0,mock_domain:[],directori:[0,4,2],enjoi:4,bottom:[],descript:4,rule:[],mailman_them:3,mass:[4,3],came:[],time:[4,2],escap:4,inc:3},objtypes:{"0":"py:module"},titles:["Development","Postorius - The New Mailman Web UI","Installation","News / Changelog","Using the Django App - Developers Resource (outdated)"],objnames:{"0":["py","module","Python module"]},filenames:["development","index","setup","news","using"]})
\ No newline at end of file
+Search.setIndex({objects:{tests:{tests:[4,0,1,""]},"postorius.tests":{test_utils:[0,0,1,""]}},terms:{all:[0,4,2,3],code:[0,4,3],forget:4,prefil:4,test_list_set:0,four:[],ackownledg:[],runserv:2,dirnam:[],follow:[4,2],content:[1,4],decid:[],depend:[3,2],authoris:[],send:[],granci:3,under:3,sens:0,introduc:0,merchant:3,sourc:[],everi:[],string:4,without:[0,4,3],far:[],none:4,offlin:[],util:[0,4,3],context_processor:0,mechan:4,veri:0,exact:4,relev:0,mailmanwebgsoc2011:[],common:[],testobject:4,contenttyp:[],administr:4,level:[],did:4,button:4,list:[0,4,3],liza:4,"try":4,item:4,adjust:[],localhost:4,httpredirectobject:4,quick:[],setup:[4,2,3],dir:[],pleas:4,modelbackend:[],impli:3,fieldset_form:0,httpresponseredirect:4,cfg:[],seper:[],request:4,past:[],second:2,pass:0,download:2,further:[],click:4,compat:[],index:[0,4],what:[],name_of_permiss:[],appear:[],richard:3,sum:[],abl:[4,2],current:[0,4],delet:[0,4],new_list1:4,postoriu:[0,1,2,3],franziska:[],"new":[1,3,4],net:[],method:[],redirect:4,abov:4,gener:[0,3],never:2,remeb:[],here:[],themself:[],ubuntu:[],path:2,along:3,modifi:[4,3],sinc:[0,2],valu:4,search:[],mailinglist:[],vertifi:[],anymor:[],errorlog:[],step:[],jame:4,doctest:4,action:4,chang:[0,2],mailman_media:[],ourselv:0,mock:0,contactpag:[],via:[],appli:[],app:[4,3],sponser:[],prefer:2,releas:2,api:[0,3],sponsel:[],foord:0,instal:[1,2,3,4],myownunittest:0,middlewar:[],from:[0,4,2,3],describ:4,would:[],commun:[],doubl:4,two:0,perm:[],next:[],websit:[],few:[],call:[0,3],typo:3,recommend:[],dict:0,type:4,web_host:4,create_mock_list:0,mailman_django:[],abspath:[],relat:4,ital:[],site:[4,2],warn:[4,2],trail:[],berlio:[],stick:[],autodoc:[],particular:3,dooo:[],hold:0,unpack:[],easiest:4,customlog:[],account:[3,2],join:[],alia:2,prepar:2,work:0,uniqu:[],dev:[3,2],itself:0,can:[0,4,2,3],purpos:3,def:[],control:3,defer:3,sqlite:2,prompt:2,login_requir:[0,4],tar:[],process:3,sudo:2,accept:3,topic:[],want:[4,2],fieldset:0,create_mock_memb:0,create_mock_domain:0,nearli:[],alwai:[0,2],cours:[],multipl:[],listmembersviewtest:0,anoth:4,magigmock:0,faulti:[],georg:4,write:0,how:2,list_nam:0,reject:3,instead:[0,4],config:[],nasti:[],css:[0,2],updat:4,product:2,recogn:0,farm:3,pypi:2,after:[4,2],"long":[],usabl:[],befor:[0,4],wrong:4,date:2,end:[],data:4,"short":0,third:2,postfix:[],bind:[],bootstrap:[],credenti:[],django:[0,4,2,3],alias:3,environ:2,adverrtis:4,jain:3,enter:4,fallback:[],automaticli:[],egg:[],order:2,listnam:4,help:0,move:3,becaus:[0,4],has_perm:[],"__future__":[],style:0,directli:4,fit:3,fix:3,browserid:3,unicode_liter:[],better:[],restart:[],helper:3,media_root:[],mail:[0,4,3],hidden:[],main:[],might:[],guarente:[],split:[],them:[0,2],good:[],"return":0,thei:[0,4],python:[0,4,2,3],databs:[],handi:0,auth:0,unfortuneatli:[],"break":[],mention:4,front:4,resourc:4,now:[0,4,2,3],introduct:[],term:3,benst:[],somewher:2,name:[0,4],anyth:[],edit:[4,3],simpl:0,postorius_error:[],didn:0,authent:0,separ:[0,4,3],easili:[],senarclen:3,each:[0,4],debug:4,found:[4,2],went:4,mailman_test_bindir:[],everyth:[0,4],domain:[0,4,3],fail:[4,3],replac:[],idea:[],procedur:[],realli:4,redistribut:3,meta:4,"static":[0,2],connect:[0,3],our:4,patch:[],todo:[],dependeci:[],out:[0,3],variabl:0,shown:4,space:3,miss:3,robert:2,develop:[0,1,2,3,4],publish:3,api_us:4,profil:3,daniel:3,rest_serv:[],got:[],correct:[0,2],mailmancli:3,earlier:[],insid:[],free:[4,3],standard:0,standalon:[],reason:[],base:0,mailmanweb:[],dictionari:0,lists_of_domain:[],put:0,org:[0,3,2],restbackend:[],"40mail":4,launch:[],dev_setup:[],could:0,latest:2,membership:[0,4],keep:2,filter:[],thing:4,place:[],isn:[],root_urlconf:[],requireti:[],summari:4,first:[4,2],softwar:3,rang:[],render:[],prevent:[],feel:4,onc:[],number:0,natti:[],restrict:4,mai:[],instruct:[4,2],alreadi:4,done:4,messag:[0,4,3],authentif:4,owner:[0,4,3],stabl:4,installed_app:[],open:4,gpl:[],differ:[0,2],rrze:[],my_own_test:0,script:0,benedict:3,hardcopi:[],licens:3,system:[],least:4,downsid:0,url_host:0,too:0,licenc:[],fullfil:[],"final":4,store:[],shell:[4,3],option:4,real_nam:4,tool:0,copi:3,specifi:4,gsoc:[],"var":[],part:4,"__test__":0,exactli:[],priveledg:[],serv:2,test_some_method:[],kind:0,provid:[0,4,2,3],remov:[4,3],structur:[0,3],new_domain:[],project:[0,3,2],reus:0,postorius_standalon:2,were:4,posit:4,minut:[],other:[0,4],fqdn_listnam:[0,4],pre:[],sai:[],runner:0,ani:[0,3],postorius_access:[],packag:2,have:[0,4,2,3],tabl:2,need:[0,4,2],element:[],wsgiscriptalia:2,engin:2,inform:2,florian:[],destroi:[],self:[],note:[4,2],also:[0,2],exampl:[0,4],take:[],which:[0,4,2],combin:[],mock_memb:0,singl:[0,4],even:3,sure:[0,4,2],kati:4,allow:[4,2],shall:4,usernam:[],object:[0,4],asdasd:[],most:[0,4],plan:[],letter:4,watt:4,alpha:3,"class":0,icon:[],collectstat:2,don:[0,4],bzr:2,url:[0,4],doc:0,proud:[],cover:[0,2],temporili:4,doe:4,meanwhil:[],deni:2,pars:4,effect:[],usual:[],dot:0,wsgi:2,show:[4,3],text:4,contact_address:0,session:4,permiss:4,corner:[],fine:[],find:0,magicmock:0,involv:0,absolut:[],onli:[0,4,2],eas:[],locat:2,launchpad:2,copyright:3,explain:4,configur:[],apach:[3,2],behind:[],should:[0,4,2,3],theme:3,version:[3,2],suppos:[],templat:[0,3],folder:[0,2],local:[0,4,2],hope:3,media_url:[],hit:[],contribut:3,get:[0,4,2],familiar:0,"__file__":[],stop:4,obviou:0,absolute_import:[],csrf:3,subscript:4,requir:4,layout:0,retreiv:0,mail_host:[0,4],bar:[],template_dir:[],"public":3,reload:2,bad:[],integr:0,restadmin:[],mm_membership:4,where:0,view:[0,4,3],wiki:[],conform:3,set:[0,4,2,3],special:[],sometest:[],see:[4,2,3],domain_admin:[],result:0,respons:4,testcas:[],wonder:[],awar:4,statu:4,mailman3a7:[],correctli:[],databas:[0,4,2],someth:4,test_list_memb:0,state:0,quickest:2,between:4,"import":[0,4],awai:[],approach:0,email:4,realnam:[],unauthor:0,extens:[],spare:0,correclti:[],advertis:4,subfold:[],thank:3,both:[],protect:0,last:4,chain:0,plugin:[],admin:2,howev:[],etc:2,instanc:[0,4],context:3,delete_list:4,logout:[],login:[4,3],com:4,load:4,english:4,simpli:4,point:0,instanti:[],overview:4,unittest:[],address:[0,4],virtualhost:2,mock_list:0,header:[],featur:[],non:[],shortcut:0,linux:4,guid:[0,4,2],assum:2,backend:[],quit:[],trunk:[],mailman:[0,1,2,3,4],coupl:4,"0a7":[],is_superus:0,three:0,been:4,compon:[],much:0,unsubscrib:[4,3],modif:[],addit:[],quickli:0,upcom:[],imag:[0,2],xxx:[],togeth:4,i18n:[],ngeorg:4,niederreit:2,those:4,"case":4,creativecommon:[],therefor:[],look:0,gnu:[3,2],plain:[],align:[],properti:0,easier:0,lesser:3,"while":[0,2],dashboard:4,publicli:2,error:3,exist:[],everyon:[],authentication_backend:[],anonymousus:0,new_list:[],advantag:0,almost:2,demo:[],metric:3,list_own:4,worri:0,archiv:4,revis:[],"__init__":0,subscrib:[0,4,3],decor:[0,4],let:[0,4],welcom:[],author:0,receiv:3,media:[],make:[0,4,2],belong:4,same:4,member:[0,3],handl:[],html:0,gui:4,document:[0,3,2],mod_wsgi:[3,2],behav:0,acl:[],finish:4,http:[0,4,2,3],webserv:2,upon:[],mmclient:3,moment:[],http_host:4,initi:3,mani:3,suit:[0,4],stack:[],expand:[],appropri:[],moder:[0,3],foundat:3,scenario:[],framework:[],api_pass:4,whole:0,well:[4,2],membership_set:[],pep8:3,client:[0,4,2],command:[0,3],thi:[0,4,2,3],choos:4,model:0,rout:0,left:[],summer:3,just:[0,2],rest:[0,4],indic:4,mailman3:4,webui:[],test_util:0,yet:[],languag:4,web:[1,2,3],like:0,display_nam:0,easi:[],project_path:[],had:[],list_summari:4,littl:0,apache2:[],add:[0,4,3],valid:[0,4],lawrenc:[],discurag:0,save:[],modul:[0,4],build:0,bin:[],applic:[0,2],obsolet:3,stein:3,unter:[],read:[],big:0,testlist:0,test:[0,4,3],know:2,gsoc_mailman:[],press:4,cooki:[],bit:0,password:[],tweak:[],authbackend:[],apart:0,resid:[0,2],template_context_processor:[],success:[4,3],changelog:[1,3],restpass:[],server:[3,2],collect:2,href:[],setup_mm:4,either:4,page:[0,4],www:[0,3],right:[],acknowledg:[],creation:4,some:[0,4],back:[],proper:3,create_list:[],funcit:[],librari:0,distribut:3,basic:[0,3],buildout:2,djangoproject:[],confirm:4,avoid:[],woun:[],token:3,list_moderator_requir:0,select:[],slash:[],necessari:2,foo:[],anna:3,refer:4,machin:4,core:[],who:[],run:[0,4,2],bold:[],usag:[],symlink:[],host:[],repositori:[],post:4,bazaar:2,mm_new_domain:[],stage:[],src:0,about:0,central:[],usa:4,list_owner_requir:0,syncdb:2,mass_subscrib:4,rohan:3,side:[],permission_requir:[],srv:2,act:2,fals:4,discard:3,readi:2,processor:[],block:4,own:[0,4],addus:[],status_cod:4,pythonpath:2,xyz:[],within:4,someviewclass:[],easy_instal:2,warranti:3,creativ:[],empti:4,contrib:[],your:[0,2],manag:[0,4,2,3],choosen:4,span:4,log:4,wai:[0,4,2],"40exampl":4,execut:4,print:[0,4],submit:4,custom:0,avail:[0,4,2],start:[0,4,2],reli:[],interfac:[3,2],includ:4,create_mock_:0,superus:[0,2],systers_django:[],lambda:0,"function":[0,4],head:[],form:[0,4,3],offer:4,descrip:[],requestfactori:[],link:4,translat:4,teardown_mm:4,branch:[3,2],line:[4,2],"true":4,info:[0,3],pull:2,succe:4,made:4,render_mailman_them:[],bullet:[],possibl:2,whether:4,access:[4,3],displai:4,below:4,memebership:[],otherwis:[],more:3,postmast:0,extend_ajax:[],later:[],creat:[0,4,2],hardcod:[],dure:[4,3],doesn:[],listsettingsviewtest:0,implement:3,file:[0,3,2],home:[],pip:2,wackerbarth:3,check:[0,4,3],probabl:0,again:[],coder:2,successfulli:[],googl:3,titl:[],user:[0,4,2,3],when:0,detail:[3,2],gettext:4,"default":0,mizyrycki:3,mock_list2:[],role:0,futur:[],rememb:[],writabl:2,you:[0,4,2,3],user_passes_test:0,servernam:[],nice:4,michael:0,why:4,prequir:[],consid:[],stai:[],outdat:4,sphinx:0,faster:0,mock_domain:0,directori:[0,4,2],enjoi:4,bottom:[],descript:[0,4],rule:[],mailman_them:3,relvant:0,mass:[4,3],came:[],time:[4,2],escap:4,inc:3},objtypes:{"0":"py:module"},titles:["Development","Postorius - The New Mailman Web UI","Installation","News / Changelog","Using the Django App - Developers Resource (outdated)"],objnames:{"0":["py","module","Python module"]},filenames:["development","index","setup","news","using"]})
\ No newline at end of file
diff --git a/src/postorius/doc/development.rst b/src/postorius/doc/development.rst
index 3dc92b9..002310d 100644
--- a/src/postorius/doc/development.rst
+++ b/src/postorius/doc/development.rst
@@ -2,7 +2,6 @@
Development
===========
-
This is a short guide to help you get started with Postorius development.
@@ -35,16 +34,52 @@
views.py # View classes and functions for all views connected
# in urls.py
generic.py # Generic class-based views; Currently holds a
- # class for view based on a single mailing list
+ # class for views based on a single mailing list
+
+
+Writing View Code
+=================
+
+When the work on Postorius was started, the standard way to write view code in
+Django was to write a single function for each different view. Since then
+Django has introduced so-called 'class-based views' which make it much easier
+to reuse view functionality. While using simple functions is not discuraged, it
+makes sense to check if using a class-based approach could make sense.
+
+Check Postorius' ``views/views.py`` and ``views/generic.py`` for examples!
Authentication/Authorization
============================
+Three of Django's default User roles are relvant for Postorius:
+
+- Superuser: Can do everything.
+- AnonymousUser: Can view list index and info pages.
+- Authenticated users: Can view list index and info pages. Can (un)subscribe
+ from lists.
+
+Apart from these default roles, there are two others relevant in Postorius:
+
+- List owners: Can change list settings, moderate messages and delete their
+ lists.
+- List moderators: Can moderate messages.
+
+There are a number of decorators to protect views from unauthorized users.
+
+- ``@user_passes_test(lambda u: u.is_superuser)``
+- ``@login_required``
+- ``@list_owner_required``
+- ``@list_moderator_required``
+
+Check out views/views.py for examples!
+
Testing
=======
+Currently only some of the Postorius code is covered by a test. We should change that!
+
All test modules reside in the ``postorius/src/postorius/tests`` directory
and this is where you should put your own tests, too. To make the django test
runner find your tests, make sure to add them to the folder's ``__init__.py``:
@@ -67,24 +102,39 @@
Running the tests
-----------------
-To run the tests go to your project folder and run ``python manage.py test postorius`` from there.
+To run the tests go to your project folder and run ``python manage.py test
+postorius`` from there.
-Mocking
--------
+Testing mailman.client results
+------------------------------
-Postorius uses Michael Foord's ``mock`` library for mocking. There are some
-shortcuts you can use to quickly create mock objects that behave a little bit like
-objects retreived from mailman.client, like:
+Most of Postorius' code involves some results from calls to the mailman.client
+library. mailman.client is itself covered by tests, so Postorius' own tests
+don't need to check if mailman.client returns correct results. Instead we can
+just mock them! This has the big advantage that you can run the test suite
+without having to worry about the state of the local Mailman database. It also
+makes the tests run faster, because we spare ourselves the HTTP calls to the
+local Mailman REST API.
-- Domains
-- Mailing Lists
-- Users
-- Memberships
-- Addresses
+This approach has the obvious downside that the Postorius tests will not
+recognize any changes to the Mailman API. So at some point there should be some
+separate integration tests to test the whole chain. But let's not worry about
+that for now.
Mocking mailman.client objects
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+------------------------------
+
+Postorius uses Michael Foord's ``mock`` library for mocking. There are some
+shortcuts you can use to quickly create mock objects that behave a little bit
+like objects retreived from mailman.client, like:
+
+- create_mock_domain
+- create_mock_list
+- create_mock_member
+
+These ``create_mock_*`` functions are very simple tools that return MagigMock objects with the properties passed to them in a dictionary. They also set some defaults for properties that you didn't pass to its ``create_mock_*`` function. For instance, a mock list created with ``create_mock_list()`` will always have ``members``, ``moderators`` and ``owners`` properties.
+
.. automodule:: postorius.tests.test_utils
diff --git a/src/postorius/tests/utils.py b/src/postorius/tests/utils.py
index 7a157ef..7082f6c 100644
--- a/src/postorius/tests/utils.py
+++ b/src/postorius/tests/utils.py
@@ -48,6 +48,9 @@
:rtype: MagicMock
"""
mock_object = MagicMock(name='List')
+ mock_object.members = []
+ mock_object.moderators = []
+ mock_object.owners = []
# like in mock_domain, some defaults need to be added...
if properties is not None:
for key in properties:
|