changeset 112:7e1da1c3afae

Merge branch 'master' of ssh://git.sv.gnu.org/srv/git/savane-cleanup/framework
author Jonathan Gonzalez V <zeus@gnu.org>
date Tue, 28 Jul 2009 14:07:00 -0400
parents 8aa5c05316ed (current diff) e470dbd1bc0f (diff)
children 3408dc47acdd
files src/savane/main/__init__.py src/savane/main/urls.py src/savane/my/admin.py src/savane/my/fixtures/README src/savane/my/fixtures/developmentstatus.yaml src/savane/my/fixtures/license.yaml src/savane/my/models.py
diffstat 20 files changed, 1182 insertions(+), 510 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/MIGRATION.txt
@@ -0,0 +1,232 @@
+- users:
+  - clean-up duplicate users
+  - cf. migrate_old_savane.sql
+  - fill-in is_staff and is_superuser
+- groups:
+  - SQL
+  - licenses: content/hashes.txt -> svmain_license
+    + there's a special 'none' ('N/A') value
+    group field type: slug (e.g. 'gpl', 'gplv3orlater')
+  - development_status: content/hashes.txt -> svmain_develomentstatus (ids)
+    group field type: id (e.g. '4')
+    I think we can ignore the hashes.txt comment about id=7 for 'N/A'
+- group configurations (group_type):
+  - 
+
+Users
+=====
+
+To remove duplicate users *FOR TESTING*:
+(TODO: need to remove only the duplicate, not both)
+DELETE FROM user WHERE user_name IN ('adiebald','alkana','andersgh','andrenix','animalfarm','antigerme','baravalle','Bassslinger','blitzkerk','bolek11','brawil1','brijesh_gadhiya','caribe','caterkiller2001','cce','chawil','conan','cux221','dave_san','ddavies','doudou61','dungeon','elektroland','emil5','ericclaassen','espahr','fcattoen','gauntlet','Gerardoco','Gonzalvez','GOOS','grubthosch','hans-peter','heikorah','helen','jaguaryou','jezdikm','JonGretar','kodion','kpc82','kumud','lamborghini0102','markm','mbaruchel','mdopheide','moocha','Netsnipe','osia','otello','pepex82','phgbest007','planetstar','plijnzaad','plomo00','Progenator','pthom','puyo','qemm','quaxter','rafaelrt','rdunphy','realshitok','roeles','rubenperez','scorbeau','seb_cante','SeeksTheMoon','shezAbrat','shiau','shyguy','siman','sjh0320','sorenoid','Steve_Stanfield','surfnix54','suydam','Sweethrt758','taroo','tdp','The_WABBIT','thresher','tklein','tontonraoul','tony-rs','treker','tt3','vdemart','vinod','vka3','wbrown','Webb','wertzu','willou','wutzkem');
+
+Warnings during import:
+
+TODO: I don't really understand why three are illegal UTF-8 strings,
+this was taken care of during the savane-cleanup UTF-8 upgrade,
+cf. savane/update/clean-up/09-utf-8.php
+
+| Warning | 1265 | Data truncated for column 'first_name' at row 370                                          | 
+| Warning | 1366 | Incorrect string value: '\xC2\x8Eeljk...' for column 'first_name' at row 1080              | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 1207                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 1297                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 1701                                         | 
+| Warning | 1366 | Incorrect string value: '\xC5\x82' for column 'first_name' at row 2117                     | 
+| Warning | 1366 | Incorrect string value: '\xC2\x82\xC2\x95\xC2\x9A' for column 'first_name' at row 2656     | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 3182                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 3310                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 3430                                         | 
+| Warning | 1366 | Incorrect string value: '\xC2\x9Aa Bo...' for column 'first_name' at row 3798              | 
+| Warning | 1366 | Incorrect string value: '\xC2\x9Aan F...' for column 'first_name' at row 4404              | 
+| Warning | 1366 | Incorrect string value: '\xC5\x9Eent\xC3...' for column 'first_name' at row 4823           | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 5045                                         | 
+| Warning | 1366 | Incorrect string value: '\xC5\x84ski' for column 'first_name' at row 5613                  | 
+| Warning | 1366 | Incorrect string value: '\xC5\x82 Mu\xC5...' for column 'first_name' at row 5793           | 
+| Warning | 1366 | Incorrect string value: '\xE0\xA6\xB6\xE0\xA7\x81...' for column 'first_name' at row 6116  | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 6604                                         | 
+| Warning | 1366 | Incorrect string value: '\xC2\x9Aa Du...' for column 'first_name' at row 7648              | 
+| Warning | 1366 | Incorrect string value: '\xC2\x8A\xC2\xB1?G...' for column 'first_name' at row 7695        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 7855                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 8105                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 8422                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 9033                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 9045                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 9488                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 9863                                         | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 10394                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 10760                                        | 
+| Warning | 1366 | Incorrect string value: '\xC2\x8Aegan' for column 'first_name' at row 11949                | 
+| Warning | 1366 | Incorrect string value: '\xC2\x91P\xC2\x8Ee' for column 'first_name' at row 11987          | 
+| Warning | 1366 | Incorrect string value: '\xD0\x90\xD0\xBB\xD0\xB5...' for column 'first_name' at row 13137 | 
+| Warning | 1366 | Incorrect string value: '\xC2\x9Cnik' for column 'first_name' at row 13151                 | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 13674                                        | 
+| Warning | 1366 | Incorrect string value: '\xC4\x83' for column 'first_name' at row 13997                    | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 14114                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 14200                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 14287                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 14579                                        | 
+| Warning | 1366 | Incorrect string value: '\xC2\x9A' for column 'first_name' at row 14794                    | 
+| Warning | 1366 | Incorrect string value: '\xC2\x93@?G\xC2...' for column 'first_name' at row 15408          | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 15819                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 16444                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 16587                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 16798                                        | 
+| Warning | 1366 | Incorrect string value: '\xC2\x9Ain' for column 'first_name' at row 16956                  | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 17909                                        | 
+| Warning | 1366 | Incorrect string value: '\xD0\x94\xD0\xB0\xD0\xBD...' for column 'first_name' at row 18031 | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 18064                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 18134                                        | 
+| Warning | 1366 | Incorrect string value: '\xC2\x9Eek' for column 'first_name' at row 19360                  | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 19720                                        | 
+| Warning | 1366 | Incorrect string value: '\xC5\x88uk' for column 'first_name' at row 19744                  | 
+| Warning | 1366 | Incorrect string value: '\xC5\x99enek...' for column 'first_name' at row 19749             | 
+| Warning | 1366 | Incorrect string value: '\xC4\x9Fda\xC5\x9F...' for column 'first_name' at row 19780       | 
+| Warning | 1366 | Incorrect string value: '\xE5\x86\xAF\xE6\x98\x8E...' for column 'first_name' at row 20033 | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 21228                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 21553                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 21762                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 21797                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 22367                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 22464                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 23013                                        | 
+| Warning | 1265 | Data truncated for column 'first_name' at row 24967                                        | 
+
+----
+
+| Warning | 1366 | Incorrect string value: '\xC2\x9198\x0D\x0A...' for column 'people_resume' at row 1197          | 
+| Warning | 1366 | Incorrect string value: '\xC2\x95 Des...' for column 'people_resume' at row 4592                | 
+| Warning | 1366 | Incorrect string value: '\xC2\x95 C, ...' for column 'people_resume' at row 5390                | 
+| Warning | 1366 | Incorrect string value: '\xC2\x92s de...' for column 'people_resume' at row 9639                | 
+| Warning | 1366 | Incorrect string value: '\xC2\x96 200...' for column 'people_resume' at row 10729               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x96 Phi...' for column 'people_resume' at row 11448               | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1366 | Incorrect string value: '\xC2\x95\x09Str...' for column 'people_resume' at row 15267            | 
+| Warning | 1366 | Incorrect string value: '\xC2\x91A\xC2\x92 ...' for column 'people_resume' at row 17491         | 
+| Warning | 1366 | Incorrect string value: '\xC2\x95 Dat...' for column 'people_resume' at row 18044               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x92Intr...' for column 'people_resume' at row 19923               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x91Netw...' for column 'people_resume' at row 21149               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x96 Apr...' for column 'people_resume' at row 23126               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x95\x09Chi...' for column 'people_resume' at row 23274            | 
+| Warning | 1366 | Incorrect string value: '\xC2\x95 (43...' for column 'people_resume' at row 25042               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x92t ha...' for column 'people_resume' at row 25253               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x95 COL...' for column 'people_resume' at row 25484               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x92s ma...' for column 'people_resume' at row 26162               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x92 exp...' for column 'people_resume' at row 27182               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x96 Pre...' for column 'people_resume' at row 28369               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x96 Lee...' for column 'people_resume' at row 28853               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x96 91-...' for column 'people_resume' at row 29208               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x92s We...' for column 'people_resume' at row 31392               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x96a di...' for column 'people_resume' at row 31490               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x9E/?~W...' for column 'gpg_key' at row 31866                     | 
+| Warning | 1366 | Incorrect string value: '\xC2\x96 01 ...' for column 'people_resume' at row 32776               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x95\x09Pre...' for column 'people_resume' at row 33378            | 
+| Warning | 1366 | Incorrect string value: '\xC2\x80STER...' for column 'authorized_keys' at row 33546             | 
+| Warning | 1366 | Incorrect string value: '\xC2\x93seei...' for column 'people_resume' at row 34256               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x96 pre...' for column 'people_resume' at row 34387               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x92m a ...' for column 'people_resume' at row 34810               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x92s Co...' for column 'people_resume' at row 35162               | 
+| Warning | 1366 | Incorrect string value: '\xC2\x92s\x0D\x0A ...' for column 'people_resume' at row 35163         | 
+| Warning | 1366 | Incorrect string value: '\xEF\x83\x92 Mi...' for column 'people_resume' at row 38675            | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1366 | Incorrect string value: '\xD1\x85\xD0\xBC' for column 'people_resume' at row 39772              | 
+| Warning | 1366 | Incorrect string value: '\xEF\x82\xA7\x09B....' for column 'people_resume' at row 39894         | 
+| Warning | 1366 | Incorrect string value: '\xC4\xB1n\xC4\xB1f...' for column 'people_resume' at row 40734         | 
+| Warning | 1366 | Incorrect string value: '\xE5\xBD\xAD\xE7\x95\x85...' for column 'authorized_keys' at row 41167 | 
+| Warning | 1366 | Incorrect string value: '\xEF\x82\xA7\x09Hi...' for column 'people_resume' at row 41332         | 
+| Warning | 1366 | Incorrect string value: '\xEF\xBB\xBF\x0D\x0AI...' for column 'people_resume' at row 41438      | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+| Warning | 1366 | Incorrect string value: '\xC5\x84sk U...' for column 'people_resume' at row 45091               | 
+| Warning | 1366 | Incorrect string value: '\xEF\x83\x98\x09Pl...' for column 'people_resume' at row 48021         | 
+| Warning | 1048 | Column 'email_hide' cannot be null                                                              | 
+
+
+----
+
+Groups:
++---------+------+-------------------------------------------------------------------------------+
+| Level   | Code | Message                                                                       |
++---------+------+-------------------------------------------------------------------------------+
+| Warning | 1048 | Column 'short_description' cannot be null                                     | 
+| Warning | 1048 | Column 'long_description' cannot be null                                      | 
+| Warning | 1048 | Column 'license_other' cannot be null                                         | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1048 | Column 'registered_gpg_keys' cannot be null                                   | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_homepage' at row 1                | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_mail' at row 1                    | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_patch' at row 1                   | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_task' at row 1                    | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_forum' at row 1                   | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_cvs' at row 1                     | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_news' at row 1                    | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_support' at row 1                 | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_bugs' at row 1                    | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_extralink_documentation' at row 1 | 
+| Warning | 1048 | Column 'long_description' cannot be null                                      | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 2               | 
+| Warning | 1048 | Column 'long_description' cannot be null                                      | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 3               | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_homepage' at row 3                | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_extralink_documentation' at row 3 | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 4               | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_homepage' at row 4                | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_extralink_documentation' at row 4 | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 5               | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_homepage' at row 5                | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_extralink_documentation' at row 5 | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 6               | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_homepage' at row 6                | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_extralink_documentation' at row 6 | 
+| Warning | 1048 | Column 'long_description' cannot be null                                      | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 7               | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 8               | 
+| Warning | 1048 | Column 'long_description' cannot be null                                      | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 9               | 
+| Warning | 1048 | Column 'registered_gpg_keys' cannot be null                                   | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_homepage' at row 9                | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_extralink_documentation' at row 9 | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 10              | 
+| Warning | 1048 | Column 'long_description' cannot be null                                      | 
+| Warning | 1048 | Column 'required_software' cannot be null                                     | 
+| Warning | 1048 | Column 'other_comments' cannot be null                                        | 
+| Warning | 1264 | Out of range value adjusted for column 'register_time' at row 11              | 
+| Warning | 1048 | Column 'registered_gpg_keys' cannot be null                                   | 
+| Warning | 1366 | Incorrect integer value: '' for column 'use_homepage' at row 11               | 
++---------+------+-------------------------------------------------------------------------------+
--- a/migrate_old_savane.sql
+++ b/migrate_old_savane.sql
@@ -9,16 +9,264 @@
       CONCAT('md5$$', user_pw), now(), FROM_UNIXTIME(add_date), status='A',
       0, 0
     FROM savane_old.user
-    WHERE user_id != 100;
+    WHERE savane_old.user.user_id != 100;
 
 -- Import all extended information except for the 'None' user (#100)
-INSERT INTO my_extendeduser
+-- (X or 0) means 'if V==NULL then 0 else V'
+INSERT INTO svmain_extendeduser
     (user_ptr_id, status, spamscore, authorized_keys,
      authorized_keys_count, people_view_skills, people_resume,
      timezone, theme, email_hide, gpg_key, gpg_key_count)
   SELECT user_id, status, spamscore, authorized_keys,
       authorized_keys_count, people_view_skills,
       people_resume, timezone, theme,
-      email_hide, gpg_key, gpg_key_count
+      (email_hide or 0), gpg_key, gpg_key_count
     FROM savane_old.user
-    WHERE user_id != 100;
+    WHERE savane_old.user.user_id != 100;
+
+-- Import group configurations
+-- type_id -> id
+INSERT INTO svmain_groupconfiguration
+    (id, name, description, base_host,
+     mailing_list_address, mailing_list_virtual_host, mailing_list_format,
+     can_use_homepage, can_use_download, can_use_cvs, can_use_arch, can_use_svn, can_use_git, can_use_hg,
+     can_use_bzr, can_use_license, can_use_devel_status, can_use_mailing_list,
+     can_use_patch, can_use_task, can_use_news, can_use_support, can_use_bug,
+     is_menu_configurable_homepage,
+     is_menu_configurable_download,
+     is_menu_configurable_support,
+     is_menu_configurable_mail,
+     is_menu_configurable_cvs,
+     is_menu_configurable_cvs_viewcvs,
+     is_menu_configurable_cvs_viewcvs_homepage,
+     is_menu_configurable_arch,
+     is_menu_configurable_arch_viewcvs,
+     is_menu_configurable_svn,
+     is_menu_configurable_svn_viewcvs,
+     is_menu_configurable_git,
+     is_menu_configurable_git_viewcvs,
+     is_menu_configurable_hg,
+     is_menu_configurable_hg_viewcvs,
+     is_menu_configurable_bzr,
+     is_menu_configurable_bzr_viewcvs,
+     is_menu_configurable_bugs,
+     is_menu_configurable_task,
+     is_menu_configurable_patch,
+     is_menu_configurable_extralink_documentation,
+     is_configurable_download_dir,
+     homepage_scm,
+     dir_type_cvs,
+     dir_type_arch,
+     dir_type_svn,
+     dir_type_git,
+     dir_type_hg,
+     dir_type_bzr,
+     dir_type_homepage,
+     dir_type_download,
+     dir_homepage,
+     dir_cvs,
+     dir_arch,
+     dir_svn,
+     dir_git,
+     dir_hg,
+     dir_bzr,
+     dir_download,
+     url_homepage,
+     url_cvs_viewcvs_homepage,
+     url_cvs_viewcvs,
+     url_arch_viewcvs,
+     url_svn_viewcvs,
+     url_git_viewcvs,
+     url_hg_viewcvs,
+     url_bzr_viewcvs,
+     url_download,
+     url_mailing_list_listinfo,
+     url_mailing_list_subscribe,
+     url_mailing_list_unsubscribe,
+     url_mailing_list_archives,
+     url_mailing_list_archives_private,
+     url_mailing_list_admin,
+     url_extralink_documentation)
+  SELECT type_id, name, description, base_host,
+      mailing_list_address, mailing_list_virtual_host, mailing_list_format,
+      can_use_homepage, can_use_download, can_use_cvs, can_use_arch, can_use_svn, can_use_git, can_use_hg,
+      can_use_bzr, can_use_license, can_use_devel_status, can_use_mailing_list,
+      can_use_patch, can_use_task, can_use_news, can_use_support, can_use_bug,
+      is_menu_configurable_homepage,
+      is_menu_configurable_download,
+      is_menu_configurable_support,
+      is_menu_configurable_mail,
+      is_menu_configurable_cvs,
+      is_menu_configurable_cvs_viewcvs,
+      is_menu_configurable_cvs_viewcvs_homepage,
+      is_menu_configurable_arch,
+      is_menu_configurable_arch_viewcvs,
+      is_menu_configurable_svn,
+      is_menu_configurable_svn_viewcvs,
+      is_menu_configurable_git,
+      is_menu_configurable_git_viewcvs,
+      is_menu_configurable_hg,
+      is_menu_configurable_hg_viewcvs,
+      is_menu_configurable_bzr,
+      is_menu_configurable_bzr_viewcvs,
+      is_menu_configurable_bugs,
+      is_menu_configurable_task,
+      is_menu_configurable_patch,
+      is_menu_configurable_extralink_documentation,
+      is_configurable_download_dir,
+      homepage_scm,
+      dir_type_cvs,
+      dir_type_arch,
+      dir_type_svn,
+      dir_type_git,
+      dir_type_hg,
+      dir_type_bzr,
+      dir_type_homepage,
+      dir_type_download,
+      dir_homepage,
+      dir_cvs,
+      dir_arch,
+      dir_svn,
+      dir_git,
+      dir_hg,
+      dir_bzr,
+      dir_download,
+      url_homepage,
+      url_cvs_viewcvs_homepage,
+      url_cvs_viewcvs,
+      url_arch_viewcvs,
+      url_svn_viewcvs,
+      url_git_viewcvs,
+      url_hg_viewcvs,
+      url_bzr_viewcvs,
+      url_download,
+      url_mailing_list_listinfo,
+      url_mailing_list_subscribe,
+      url_mailing_list_unsubscribe,
+      url_mailing_list_archives,
+      url_mailing_list_archives_private,
+      url_mailing_list_admin,
+      url_extralink_documentation
+    FROM savane_old.group_type;
+
+
+-- Import groups
+-- id <- group_id
+-- name <- unix_group_name
+INSERT INTO auth_group
+    (id, name)
+  SELECT group_id, unix_group_name
+    FROM savane_old.groups
+    WHERE savane_old.groups.group_id != 100;
+
+-- Import license/devel_status
+-- ./manage.py loaddata savane/svmain/fixtures/license.yaml
+-- ./manage.py loaddata savane/svmain/fixtures/developmentstatus.yaml
+
+-- Import groups
+-- group_ptr_id <- group_id
+-- full_name <- group_name
+-- license_id <- license+1 (django counts from 1, not from 0)
+-- devel_status_id <- devel_status+1 (django counts from 1, not from 0)
+-- type_id <- type
+-- register_time <- FROM_UNIXTIME(register_time)
+--
+-- Using LEFT JOIN so that if the license isn't known, the project is
+-- not ignored (with license_id=NULL).
+INSERT INTO svmain_extendedgroup
+    (group_ptr_id, full_name, license_id, devel_status_id, type_id,
+     register_time,
+     is_public,
+     status,
+     short_description,
+     long_description,
+     license_other,
+     register_purpose,
+     required_software,
+     other_comments,
+     registered_gpg_keys,
+     use_homepage,
+     use_mail,
+     use_patch,
+     use_task,
+     use_cvs,
+     use_arch,
+     use_svn,
+     use_git,
+     use_hg,
+     use_bzr,
+     use_news,
+     use_support,
+     use_download,
+     use_bugs,
+     use_extralink_documentation,
+     url_homepage,
+     url_download,
+     url_support,
+     url_mail,
+     url_cvs,
+     url_cvs_viewcvs,
+     url_cvs_viewcvs_homepage,
+     url_arch,
+     url_arch_viewcvs,
+     url_svn,
+     url_svn_viewcvs,
+     url_git,
+     url_git_viewcvs,
+     url_hg,
+     url_hg_viewcvs,
+     url_bzr,
+     url_bzr_viewcvs,
+     url_bugs,
+     url_task,
+     url_patch,
+     url_extralink_documentation)
+  SELECT group_id, group_name, svmain_license.id, devel_status+1, type,
+      FROM_UNIXTIME(register_time),
+      is_public,
+      status,
+      short_description,
+      long_description,
+      license_other,
+      register_purpose,
+      required_software,
+      other_comments,
+      registered_gpg_keys,
+      use_homepage,
+      use_mail,
+      use_patch,
+      use_task,
+      use_cvs,
+      use_arch,
+      use_svn,
+      use_git,
+      use_hg,
+      use_bzr,
+      use_news,
+      use_support,
+      use_download,
+      use_bugs,
+      use_extralink_documentation,
+      url_homepage,
+      url_download,
+      url_support,
+      url_mail,
+      url_cvs,
+      url_cvs_viewcvs,
+      url_cvs_viewcvs_homepage,
+      url_arch,
+      url_arch_viewcvs,
+      url_svn,
+      url_svn_viewcvs,
+      url_git,
+      url_git_viewcvs,
+      url_hg,
+      url_hg_viewcvs,
+      url_bzr,
+      url_bzr_viewcvs,
+      url_bugs,
+      url_task,
+      url_patch,
+      url_extralink_documentation
+    FROM savane_old.groups LEFT JOIN savane.svmain_license ON savane_old.groups.license = savane.svmain_license.slug
+    WHERE savane_old.groups.group_id != 100;
deleted file mode 100644
deleted file mode 100644
--- a/src/savane/main/urls.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# URL dispatching for presentation pages
-# Copyright (C) 2009  Sylvain Beucler
-# Copyright (C) 2009  Jonathan Gonzalez V.
-#
-# This file is part of Savane.
-# 
-# Savane is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-# 
-# Savane is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-# 
-# You should have received a copy of the GNU Affero General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-from django.conf.urls.defaults import *
-
-urlpatterns = patterns ('',
-  url(r'^$', 'django.views.generic.simple.direct_to_template',
-      { 'template' : 'index.html',
-        'extra_context' : { 'has_left_menu': False } },
-      name='homepage'),
-  url(r'^contact$', 'django.views.generic.simple.direct_to_template',
-      { 'template' : 'contact.html' },
-      name='contact'),
-)
deleted file mode 100644
--- a/src/savane/my/admin.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from django.contrib import admin
-from models import User
-
-admin.site.register(User)
deleted file mode 100644
--- a/src/savane/my/fixtures/README
+++ /dev/null
@@ -1,2 +0,0 @@
-The fixtures subdirectory is used by the Django framework for test
-suites.
deleted file mode 100644
--- a/src/savane/my/fixtures/developmentstatus.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-- fields:
-    name: '0 - Undefined'
-  model: my.developmentstatus
-  pk: 1
-- fields:
-    name: '1 - Planning'
-  model: my.developmentstatus
-  pk: 2
-- fields:
-    name: '2 - Pre-Alpha'
-  model: my.developmentstatus
-  pk: 3
-- fields:
-    name: '3 - Alpha'
-  model: my.developmentstatus
-  pk: 4
-- fields:
-    name: '4 - Beta'
-  model: my.developmentstatus
-  pk: 5
-- fields:
-    name: '5 - Production/Stable'
-  model: my.developmentstatus
-  pk: 6
-- fields:
-    name: '6 - Mature'
-  model: my.developmentstatus
-  pk: 7
-- fields:
-    name: 'N/A'
-  model: my.developmentstatus
-  pk: 8
-- fields:
-    name: '? - Orphaned/Unmaintained'
-  model: my.developmentstatus
-  pk: 9
deleted file mode 100644
--- a/src/savane/my/fixtures/license.yaml
+++ /dev/null
@@ -1,88 +0,0 @@
-- fields:
-    slug: 'gpl'
-    name: 'GNU General Public License V2 or later'
-    url: 'http://www.gnu.org/copyleft/gpl.html'
-  model: my.license
-  pk: 1
-- fields:
-    slug: 'lgpl'
-    name: 'GNU Lesser General Public License'
-    url: 'http://www.gnu.org/copyleft/lesser.html'
-  model: my.license
-  pk: 2
-- fields:
-    slug: 'fdl'
-    name: 'GNU Free Documentation License'
-    url: 'http://www.gnu.org/copyleft/fdl.html'
-  model: my.license
-  pk: 3
-- fields:
-    slug: 'mbsd'
-    name: 'Modified BSD License'
-    url: 'http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5'
-  model: my.license
-  pk: 4
-- fields:
-    slug: 'x11'
-    name: 'X11 license'
-    url: 'http://www.x.org/terms.htm'
-  model: my.license
-  pk: 5
-- fields:
-    slug: 'cryptix'
-    name: 'Cryptix General License'
-    url: 'http://www.cryptix.org/docs/license.html'
-  model: my.license
-  pk: 6
-- fields:
-    slug: 'zlib'
-    name: 'The license of ZLib'
-    url: 'ftp://ftp.freesoftware.com/pub/infozip/zlib/zlib_license.html'
-  model: my.license
-  pk: 7
-- fields:
-    slug: 'imatrix'
-    name: 'The license of the iMatix Standard Function Library'
-  model: my.license
-  pk: 8
-- fields:
-    slug: 'w3c'
-    name: 'The W3C Software Notice and License'
-    url: 'http://www.w3.org/Consortium/Legal/copyright-software.html'
-  model: my.license
-  pk: 9
-- fields:
-    slug: 'berkeley'
-    name: 'The Berkeley Database License'
-    url: 'http://www.sleepycat.com/license.net'
-  model: my.license
-  pk: 10
-- fields:
-    slug: 'python16'
-    name: 'The License of Python 1.6a2 and earlier versions'
-    url: 'http://www.python.org/doc/Copyright.html'
-  model: my.license
-  pk: 11
-- fields:
-    slug: 'cartistic'
-    name: 'The Clarified Artistic License'
-    url: 'http://www.statistica.unimib.it/utenti/dellavedova/software/artistic2.html'
-  model: my.license
-  pk: 12
-- fields:
-    slug: 'expat'
-    name: 'Expat License (sometime refered to as MIT License)'
-    url: 'http://www.gnu.org/licenses/license-list.html#Expat'
-  model: my.license
-  pk: 13
-- fields:
-    slug: 'affero'
-    name: 'Affero General Public License V1 or later'
-    url: 'http://www.affero.org/oagpl.html'
-  model: my.license
-  pk: 14
-- fields:
-    slug: 'website'
-    name: 'WebSite Only'
-  model: my.license
-  pk: 15
deleted file mode 100644
--- a/src/savane/my/models.py
+++ /dev/null
@@ -1,333 +0,0 @@
-# User extra attributes
-# Copyright (C) 2009  Sylvain Beucler
-# Copyright (C) 2009  Jonathan Gonzalez V.
-#
-# This file is part of Savane.
-# 
-# Savane is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-# 
-# Savane is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-# 
-# You should have received a copy of the GNU Affero General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-from django.db import models
-from django.contrib.auth import models as auth_models
-
-# TODO: these models probably don't belong to the 'my' application
-
-class ExtendedUser(auth_models.User):
-    """Django base User class + extra Savane fields"""
-
-    # Migrated to 'firstname' in auth.User
-    #realname = models.CharField(max_length=96)
-
-    # Old Savane can be Active/Deleted/Pending/Suspended/SQuaD
-    status_CHOICES = (
-        ('A', 'Active'),
-        ('D', 'Deleted'),
-        ('P', 'Pending'),
-        ('S', 'Suspended'),
-        #('SQD', 'Squad'), # TODO: implement squads more cleanly
-        )
-    status = models.CharField(max_length=3)
-
-    # Used by trackers only but it could be used more widely
-    spamscore = models.IntegerField(null=True, blank=True)
-    # Previously used for e-mail changes and password recovery, Django
-    # does it different with a auth.tokens
-    #confirm_hash = models.CharField(max_length=96, blank=True, null=True)
-
-    # Keys
-    authorized_keys = models.TextField(blank=True, null=True)
-    authorized_keys_count = models.IntegerField(null=True, blank=True)
-    gpg_key = models.TextField(blank=True, null=True)
-    gpg_key_count = models.IntegerField(null=True, blank=True)
-
-    # Personal info
-    people_resume = models.TextField()
-
-    # Preferences - /!\ some are also in the user_preferences table
-    people_view_skills = models.IntegerField(null=True)
-    timezone = models.CharField(max_length=192, blank=True, null=True)
-    theme = models.CharField(max_length=45, blank=True, null=True)
-    email_hide = models.CharField(max_length=9, blank=True, null=True)
-
-
-    # Inherit specialized models.Manager with convenience functions
-    objects = auth_models.UserManager()
-
-class License(models.Model):
-    """
-    Main license used by a project
-
-    TODO: support several licenses per project (mixed-licensed code,
-    documentation, ...)
-    """
-    slug = models.CharField(max_length=32)
-    name = models.CharField(max_length=255)
-    url = models.CharField(max_length=255)
-
-class DevelopmentStatus(models.Model):
-    """Describe the development status of a project"""
-    name = models.CharField(max_length=255)
-
-class GroupConfiguration(models.Model):
-    """Group configuration and main category (previously group_type)"""
-    name = models.CharField(max_length=255)
-    # Text added to each project page
-    description = models.TextField(blank=True)
-
-    #admin_email_adress = models.CharField(max_length=128, null=True) # unused
-
-    # Redirect to this host when visiting project page
-    base_host = models.CharField(max_length=128, null=True)
-    # Mailing lists are hosted there
-    mailing_list_host = models.CharField(max_length=255, null=True)
-
-    # Permissions
-    can_use_homepage     = models.BooleanField(default=True)
-    can_use_download     = models.BooleanField(default=True)
-    can_use_cvs          = models.BooleanField(default=True)
-    can_use_arch         = models.BooleanField(default=False)
-    can_use_svn          = models.BooleanField(default=False)
-    can_use_git          = models.BooleanField(default=False)
-    can_use_hg           = models.BooleanField(default=False)
-    can_use_bzr          = models.BooleanField(default=False)
-    can_use_license      = models.BooleanField(default=True)
-    can_use_devel_status = models.BooleanField(default=True)
-    can_use_forum        = models.BooleanField(default=False)
-    can_use_mailing_list = models.BooleanField(default=True)
-    can_use_patch        = models.BooleanField(default=False)
-    can_use_task         = models.BooleanField(default=True)
-    can_use_news         = models.BooleanField(default=True)
-    can_use_support      = models.BooleanField(default=True)
-    can_use_bug          = models.BooleanField(default=True)
-    is_menu_configurable_homepage                = models.BooleanField(default=False)
-    is_menu_configurable_download                = models.BooleanField(default=False)
-    is_menu_configurable_forum                   = models.BooleanField(default=False)
-    is_menu_configurable_support                 = models.BooleanField(default=False)
-    is_menu_configurable_mail                    = models.BooleanField(default=False)
-    is_menu_configurable_cvs                     = models.BooleanField(default=False)
-    is_menu_configurable_cvs_viewcvs             = models.BooleanField(default=False)
-    is_menu_configurable_cvs_viewcvs_homepage    = models.BooleanField(default=False)
-    is_menu_configurable_arch                    = models.BooleanField(default=False)
-    is_menu_configurable_arch_viewcvs            = models.BooleanField(default=False)
-    is_menu_configurable_svn                     = models.BooleanField(default=False)
-    is_menu_configurable_svn_viewcvs             = models.BooleanField(default=False)
-    is_menu_configurable_git                     = models.BooleanField(default=False)
-    is_menu_configurable_git_viewcvs             = models.BooleanField(default=False)
-    is_menu_configurable_hg                      = models.BooleanField(default=False)
-    is_menu_configurable_hg_viewcvs              = models.BooleanField(default=False)
-    is_menu_configurable_bzr                     = models.BooleanField(default=False)
-    is_menu_configurable_bzr_viewcvs             = models.BooleanField(default=False)
-    is_menu_configurable_bugs                    = models.BooleanField(default=False)
-    is_menu_configurable_task                    = models.BooleanField(default=False)
-    is_menu_configurable_patch                   = models.BooleanField(default=False)
-    is_menu_configurable_extralink_documentation = models.BooleanField(default=False)
-    is_configurable_download_dir                 = models.BooleanField(default=False)
-
-    # Directory creation config
-    SCM_CHOICES = (
-        ('cvs', 'CVS'),
-        ('svn' , 'Subversion'),
-        ('arch' , 'GNU Arch'),
-        ('git' , 'Git'),
-        ('hg' , 'Mercurial'),
-        ('bzr' , 'Bazaar'),
-        )
-    homepage_scm = models.CharField(max_length=4, choices=SCM_CHOICES, default='cvs')
-    DIR_TYPE_CHOICES = (
-        ('basicdirectory', 'Basic directory'),
-        ('basiccvs', 'Basic CVS directory'),
-        ('basicsvn', 'Basic Subversion directory'),
-        ('basicgit', 'Basic Git directory'),
-        ('basichg', 'Basic Mercurial directory'),
-        ('basicbzr', 'Basic Bazaar directory'),
-        ('cvsattic', 'CVS Attic/Gna!'),
-        ('svnattic', 'Subversion Attic/Gna!'),
-        ('svnatticwebsite', 'Subversion Subdirectory Attic/Gna!'),
-        ('savannah-gnu', 'CVS Savannah GNU'),
-        ('savannah-nongnu', 'CVS Savannah non-GNU'),
-        )
-    dir_type_cvs      = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basiccvs')
-    dir_type_arch     = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicdirectory')
-    dir_type_svn      = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicsvn')
-    dir_type_git      = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicgit')
-    dir_type_hg       = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basichg')
-    dir_type_bzr      = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicbzr')
-    dir_type_homepage = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicdirectory')
-    dir_type_download = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicdirectory')
-    dir_homepage = models.CharField(max_length=255, default='/'),
-    dir_cvs      = models.CharField(max_length=255, default='/')
-    dir_arch     = models.CharField(max_length=255, default='/')
-    dir_svn      = models.CharField(max_length=255, default='/')
-    dir_git      = models.CharField(max_length=255, default='/')
-    dir_hg       = models.CharField(max_length=255, default='/')
-    dir_bzr      = models.CharField(max_length=255, default='/')
-    dir_download = models.CharField(max_length=255, default='/')
-
-    # Default URLs
-    url_homepage             = models.CharField(max_length=255, default='http://'),
-    url_download             = models.CharField(max_length=255, default='http://')
-    url_cvs_viewcvs          = models.CharField(max_length=255, default='http://')
-    url_arch_viewcvs         = models.CharField(max_length=255, default='http://') 
-    url_svn_viewcvs          = models.CharField(max_length=255, default='http://')
-    url_git_viewcvs          = models.CharField(max_length=255, default='http://')
-    url_hg_viewcvs           = models.CharField(max_length=255, default='http://')
-    url_bzr_viewcvs          = models.CharField(max_length=255, default='http://')
-    url_cvs_viewcvs_homepage = models.CharField(max_length=255, default='http://')
-    url_mailing_list_listinfo         = models.CharField(max_length=255, default='http://'),
-    url_mailing_list_subscribe        = models.CharField(max_length=255, default='http://')
-    url_mailing_list_unsubscribe      = models.CharField(max_length=255, default='http://')
-    url_mailing_list_archives         = models.CharField(max_length=255, default='http://')
-    url_mailing_list_archives_private = models.CharField(max_length=255, default='http://')
-    url_mailing_list_admin            = models.CharField(max_length=255, default='http://')
-    url_extralink_documentation = models.CharField(max_length=255, blank=True)
-
-    # Unused
-    #license_array = models.TextField()
-
-    devel_status_array = models.ForeignKey(DevelopmentStatus),
-
-    mailing_list_address = models.CharField(max_length=255, default='@'),
-    mailing_list_virtual_host = models.CharField(max_length=255, default=''),
-    mailing_list_format = models.CharField(max_length=255, default='%NAME'),
-
-    # TODO: split forum and news config
-    #forum_flags     = IntegerField(default='2')
-    #news_flags      = IntegerField(default='3')
-    #forum_rflags    = IntegerField(default='2')
-    #news_rflags     = IntegerField(default='2')
-
-    # TODO: split tracker config
-    #bugs_flags      = IntegerField(default='2')
-    #task_flags      = IntegerField(default='2')
-    #patch_flags     = IntegerField(default='2')
-    #cookbook_flags  = IntegerField(default='2')
-    #support_flags   = IntegerField(default='2')
-    #bugs_rflags     = IntegerField(default='2')
-    #task_rflags     = IntegerField(default='5')
-    #patch_rflags    = IntegerField(default='2')
-    #cookbook_rflags = IntegerField(default='5')
-    #support_rflags  = IntegerField(default='2')
-
-
-class ExtendedGroup(auth_models.Group):
-    """Django base Group class + extra Savane fields"""
-    
-    type = models.ForeignKey(GroupConfiguration)
-    name = models.CharField(max_length=30, blank=True)
-    is_public = models.BooleanField(default=False)
-    status_CHOICES = (
-        ('A', 'Active'),
-        ('P', 'Pending'),
-        ('D', 'Deleted'),
-        ('M', 'Maintenance (accessible only to superuser)'),
-        ('I', 'Incomplete (failure during registration)'),
-        )
-    status = models.CharField(max_length=1, default='A')
-    short_description = models.CharField(max_length=255, blank=True)
-    long_description = models.TextField()
-    license = models.ForeignKey(License, null=True)
-    license_other = models.TextField()
-
-    devel_status = models.ForeignKey(DevelopmentStatus),
-
-    # Registration-specific
-    register_purpose = models.TextField()
-    required_software = models.TextField()
-    other_comments = models.TextField()
-
-    register_time = models.DateTimeField()
-    #rand_hash text,
-    
-    registered_gpg_keys = models.TextField()
-
-    # Project "Features"
-    use_homepage                = models.BooleanField(default=False)
-    use_mail                    = models.BooleanField(default=False)
-    use_patch                   = models.BooleanField(default=False)
-    use_task                    = models.BooleanField(default=False)
-    use_forum                   = models.BooleanField(default=False)
-    use_cvs                     = models.BooleanField(default=False)
-    use_arch                    = models.BooleanField(default=False)
-    use_svn                     = models.BooleanField(default=False)
-    use_git                     = models.BooleanField(default=False)
-    use_hg                      = models.BooleanField(default=False)
-    use_bzr                     = models.BooleanField(default=False)
-    use_news                    = models.BooleanField(default=False)
-    use_support                 = models.BooleanField(default=False)
-    use_download                = models.BooleanField(default=False)
-    use_bugs                    = models.BooleanField(default=False)
-    use_extralink_documentation = models.BooleanField(default=False)
-
-    # 'null' means 'use default'
-    url_homepage                = models.CharField(max_length=255, null=True)
-    url_download                = models.CharField(max_length=255, null=True)
-    url_forum                   = models.CharField(max_length=255, null=True)
-    url_support                 = models.CharField(max_length=255, null=True)
-    url_mail                    = models.CharField(max_length=255, null=True)
-    url_cvs                     = models.CharField(max_length=255, null=True)
-    url_cvs_viewcvs             = models.CharField(max_length=255, null=True)
-    url_cvs_viewcvs_homepage    = models.CharField(max_length=255, null=True)
-    url_arch                    = models.CharField(max_length=255, null=True)
-    url_arch_viewcvs            = models.CharField(max_length=255, null=True)
-    url_svn                     = models.CharField(max_length=255, null=True)
-    url_svn_viewcvs             = models.CharField(max_length=255, null=True)
-    url_git                     = models.CharField(max_length=255, null=True)
-    url_git_viewcvs             = models.CharField(max_length=255, null=True)
-    url_hg                      = models.CharField(max_length=255, null=True)
-    url_hg_viewcvs              = models.CharField(max_length=255, null=True)
-    url_bzr                     = models.CharField(max_length=255, null=True)
-    url_bzr_viewcvs             = models.CharField(max_length=255, null=True)
-    url_bugs                    = models.CharField(max_length=255, null=True)
-    url_task                    = models.CharField(max_length=255, null=True)
-    url_patch                   = models.CharField(max_length=255, null=True)
-    url_extralink_documentation = models.CharField(max_length=255, null=True)
-
-    # Admin override (unused)
-    #dir_cvs = models.CharField(max_length=255, null=True)
-    #dir_arch = models.CharField(max_length=255, null=True)
-    #dir_svn = models.CharField(max_length=255, null=True)
-    #dir_git = models.CharField(max_length=255, null=True)
-    #dir_hg = models.CharField(max_length=255, null=True)
-    #dir_bzr = models.CharField(max_length=255, null=True)
-    #dir_homepage = models.CharField(max_length=255, null=True)
-    #dir_download = models.CharField(max_length=255, null=True)
-
-    # TODO: split trackers configuration
-    #bugs_preamble = models.TextField()
-    #task_preamble = models.TextField()
-    #patch_preamble = models.TextField()
-    #support_preamble = models.TextField()
-    #cookbook_preamble = models.TextField()
-
-    #new_bugs_address text NOT NULL
-    #new_patch_address text NOT NULL
-    #new_support_address text NOT NULL
-    #new_task_address text NOT NULL
-    #new_news_address text NOT NULL
-    #new_cookbook_address text NOT NULL
-
-    #bugs_glnotif int(11) NOT NULL default '1'
-    #support_glnotif int(11) NOT NULL default '1'
-    #task_glnotif int(11) NOT NULL default '1'
-    #patch_glnotif int(11) NOT NULL default '1'
-    #cookbook_glnotif int(11) NOT NULL default '1'
-    #send_all_bugs int(11) NOT NULL default '0'
-    #send_all_patch int(11) NOT NULL default '0'
-    #send_all_support int(11) NOT NULL default '0'
-    #send_all_task int(11) NOT NULL default '0'
-    #send_all_cookbook int(11) NOT NULL default '0'
-    #bugs_private_exclude_address text
-    #task_private_exclude_address text
-    #support_private_exclude_address text
-    #patch_private_exclude_address text
-    #cookbook_private_exclude_address text
--- a/src/savane/my/urls.py
+++ b/src/savane/my/urls.py
@@ -22,7 +22,7 @@
 from django.views.generic.simple import direct_to_template
 from django.views.generic.list_detail import object_list
 import views
-import models as my_models
+import savane.svmain.models as svmain_models
 from decorator import decorator
 
 @decorator
@@ -52,6 +52,6 @@
   url('^conf/ssh_gpg$', views.sv_ssh_gpg),
   url('^conf/ssh_gpg$', views.sv_ssh_gpg),
   url(r'^groups/$', object_list__only_mine,
-      { 'queryset' : my_models.ExtendedGroup.objects.all() },
+      { 'queryset' : svmain_models.ExtendedGroup.objects.all() },
       name='savane.my.generic.group_list'),
 )
--- a/src/savane/my/views.py
+++ b/src/savane/my/views.py
@@ -23,7 +23,7 @@
 from django.contrib.auth import authenticate, login, logout
 from django.contrib.auth.decorators import login_required
 from django import forms
-from models import ExtendedUser
+from savane.svmain.models import ExtendedUser
 
 @login_required()
 def sv_conf( request ):
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/src/savane/svmain/admin.py
@@ -0,0 +1,96 @@
+from django.contrib import admin
+from django.contrib.auth import admin as auth_admin
+from django.utils.translation import ugettext, ugettext_lazy as _
+import models as svmain_models
+
+class LicenseAdmin(admin.ModelAdmin):
+    list_display  = ['slug', 'pk', 'name', 'url']
+    search_fields = ['name']
+
+class DevelopmentStatusAdmin(admin.ModelAdmin):
+    list_display  = ['name', 'pk']
+    search_fields = ['name']
+
+class ExtendedUserAdmin(admin.ModelAdmin):
+    # Copy/pasted from django.contrib.auth.admin; inheritance fails
+    # when you attempt to display extended fields..
+    fieldsets = (
+        (None, {'fields': ('username', 'password')}),
+        (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
+        (_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
+        (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
+        (_('Groups'), {'fields': ('groups',)}),
+        (_('Savane'),
+         {'fields': ('status', 'spamscore',
+                     'authorized_keys', 'authorized_keys_count',
+                     'gpg_key', 'gpg_key_count',
+                     'people_view_skills', 'email_hide', 'timezone', 'theme',)}),
+        )
+    list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
+    list_filter = ('is_staff', 'is_superuser')
+    search_fields = ('username', 'first_name', 'last_name', 'email')
+    ordering = ('username',)
+    filter_horizontal = ('user_permissions',)
+
+class GroupConfigurationAdmin(admin.ModelAdmin):
+    fieldsets = (
+        (_('General Settings'), {'fields': ('name', 'base_host', 'description')}),
+        (_('Project WWW Homepage'),
+         {'fields': ('can_use_homepage', 'homepage_scm', 'dir_type_homepage',
+                     'dir_homepage', 'url_homepage', 'url_cvs_viewcvs_homepage')}),
+        (_('Source Code Manager: CVS'),
+         {'fields': ('can_use_cvs', 'dir_type_cvs',
+                     'dir_cvs', 'url_cvs_viewcvs')}),
+        (_('Source Code Manager: GNU Arch'),
+         {'fields': ('can_use_arch', 'dir_type_arch',
+                     'dir_arch', 'url_arch_viewcvs')}),
+        (_('Source Code Manager: Subversion'),
+         {'fields': ('can_use_svn', 'dir_type_svn',
+                     'dir_svn', 'url_svn_viewcvs')}),
+        (_('Source Code Manager: Git'),
+         {'fields': ('can_use_git', 'dir_type_git',
+                     'dir_git', 'url_git_viewcvs')}),
+        (_('Source Code Manager: Mercurial'),
+         {'fields': ('can_use_hg', 'dir_type_hg',
+                     'dir_hg', 'url_hg_viewcvs')}),
+        (_('Source Code Manager: Bazaar'),
+         {'fields': ('can_use_bzr', 'dir_type_bzr',
+                     'dir_bzr', 'url_bzr_viewcvs')}),
+        (_('Download area'),
+         {'fields': ('can_use_download', 'dir_type_download',
+                     'dir_download', 'url_download')}),
+        (_('Licenses'), {'fields': ('can_use_license',)}),
+        (_('Development Status'), {'fields': ('can_use_devel_status',)}),
+        (_('Mailing List'),
+         {'fields': ('can_use_mailing_list', 'mailing_list_virtual_host',
+                     'mailing_list_address', 'mailing_list_format',
+                     'url_mailing_list_listinfo', 'url_mailing_list_subscribe',
+                     'url_mailing_list_unsubscribe', 'url_mailing_list_archives',
+                     'url_mailing_list_archives_private', 'url_mailing_list_admin')}),
+        # TODO: finish
+        (_('News Manager'), {'fields': ('can_use_news',)}),
+        (_('Project Menu Settings'),
+         {'fields': ('is_menu_configurable_homepage',
+                     'is_menu_configurable_extralink_documentation',
+                     'is_menu_configurable_download',
+                     'is_configurable_download_dir',
+                     'is_menu_configurable_support',
+                     # ...
+                     )}),
+        
+        )
+
+class ExtendedGroupAdmin(admin.ModelAdmin):
+    # Copy/pasted from django.contrib.auth.admin; inheritance fails
+    # when you attempt to display extended fields..
+    search_fields = ('name',)
+    ordering = ('name',)
+    filter_horizontal = ('permissions',)
+    list_display  = ('name', 'pk', 'full_name', 'type', 'license',)
+    list_filter = ('type', 'license', 'devel_status',)
+
+admin.site.register(svmain_models.ExtendedUser, ExtendedUserAdmin)
+admin.site.register(svmain_models.ExtendedGroup, ExtendedGroupAdmin)
+admin.site.register(svmain_models.GroupConfiguration, GroupConfigurationAdmin)
+admin.site.register(svmain_models.License, LicenseAdmin)
+admin.site.register(svmain_models.DevelopmentStatus, DevelopmentStatusAdmin)
new file mode 100644
--- /dev/null
+++ b/src/savane/svmain/fixtures/README
@@ -0,0 +1,2 @@
+The fixtures subdirectory is used by the Django framework for test
+suites.
new file mode 100644
--- /dev/null
+++ b/src/savane/svmain/fixtures/developmentstatus.yaml
@@ -0,0 +1,36 @@
+- fields:
+    name: '0 - Undefined'
+  model: svmain.developmentstatus
+  pk: 1
+- fields:
+    name: '1 - Planning'
+  model: svmain.developmentstatus
+  pk: 2
+- fields:
+    name: '2 - Pre-Alpha'
+  model: svmain.developmentstatus
+  pk: 3
+- fields:
+    name: '3 - Alpha'
+  model: svmain.developmentstatus
+  pk: 4
+- fields:
+    name: '4 - Beta'
+  model: svmain.developmentstatus
+  pk: 5
+- fields:
+    name: '5 - Production/Stable'
+  model: svmain.developmentstatus
+  pk: 6
+- fields:
+    name: '6 - Mature'
+  model: svmain.developmentstatus
+  pk: 7
+- fields:
+    name: 'N/A'
+  model: svmain.developmentstatus
+  pk: 8
+- fields:
+    name: '? - Orphaned/Unmaintained'
+  model: svmain.developmentstatus
+  pk: 9
new file mode 100644
--- /dev/null
+++ b/src/savane/svmain/fixtures/license.yaml
@@ -0,0 +1,134 @@
+- fields:
+    slug: 'none'
+    name: 'N/A'
+  model: svmain.license
+  pk: 1
+- fields:
+    slug: 'gpl'
+    name: 'GNU General Public License V2 or later'
+    url: 'http://www.gnu.org/copyleft/gpl.html'
+  model: svmain.license
+  pk: 2
+- fields:
+    slug: 'lgpl'
+    name: 'GNU Lesser General Public License'
+    url: 'http://www.gnu.org/copyleft/lesser.html'
+  model: svmain.license
+  pk: 3
+- fields:
+    slug: 'fdl'
+    name: 'GNU Free Documentation License'
+    url: 'http://www.gnu.org/copyleft/fdl.html'
+  model: svmain.license
+  pk: 4
+- fields:
+    slug: 'mbsd'
+    name: 'Modified BSD License'
+    url: 'http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5'
+  model: svmain.license
+  pk: 5
+- fields:
+    slug: 'x11'
+    name: 'X11 license'
+    url: 'http://www.x.org/terms.htm'
+  model: svmain.license
+  pk: 6
+- fields:
+    slug: 'cryptix'
+    name: 'Cryptix General License'
+    url: 'http://www.cryptix.org/docs/license.html'
+  model: svmain.license
+  pk: 7
+- fields:
+    slug: 'zlib'
+    name: 'The license of ZLib'
+    url: 'ftp://ftp.freesoftware.com/pub/infozip/zlib/zlib_license.html'
+  model: svmain.license
+  pk: 8
+- fields:
+    slug: 'imatrix'
+    name: 'The license of the iMatix Standard Function Library'
+  model: svmain.license
+  pk: 9
+- fields:
+    slug: 'w3c'
+    name: 'The W3C Software Notice and License'
+    url: 'http://www.w3.org/Consortium/Legal/copyright-software.html'
+  model: svmain.license
+  pk: 10
+- fields:
+    slug: 'berkeley'
+    name: 'The Berkeley Database License'
+    url: 'http://www.sleepycat.com/license.net'
+  model: svmain.license
+  pk: 11
+- fields:
+    slug: 'python16'
+    name: 'The License of Python 1.6a2 and earlier versions'
+    url: 'http://www.python.org/doc/Copyright.html'
+  model: svmain.license
+  pk: 12
+- fields:
+    slug: 'cartistic'
+    name: 'The Clarified Artistic License'
+    url: 'http://www.statistica.unimib.it/utenti/dellavedova/software/artistic2.html'
+  model: svmain.license
+  pk: 13
+- fields:
+    slug: 'expat'
+    name: 'Expat License (sometime refered to as MIT License)'
+    url: 'http://www.gnu.org/licenses/license-list.html#Expat'
+  model: svmain.license
+  pk: 14
+- fields:
+    slug: 'affero'
+    name: 'Affero General Public License V1 or later'
+    url: 'http://www.affero.org/oagpl.html'
+  model: svmain.license
+  pk: 15
+- fields:
+    slug: 'website'
+    name: 'WebSite Only'
+  model: svmain.license
+  pk: 16
+- fields:
+    slug: 'python2'
+    name: 'The License of Python 2.0.1, 2.1.1, and newer versions'
+    url: 'http://www.python.org/2.0.1/license.html'
+  model: svmain.license
+  pk: 17
+- fields:
+    slug: 'perl'
+    name: 'The license of Perl (disjunction of the Artistic License and the GNU GPL)'
+    url: 'http://www.gnu.org/philosophy/license-list.html#PerlLicense'
+  model: svmain.license
+  pk: 18
+- fields:
+    slug: 'classpath'
+    name: 'GNU General Public License v2 or later with GNU Classpath special exception'
+    url: 'http://www.gnu.org/software/classpath/license.html'
+  model: svmain.license
+  pk: 19
+- fields:
+    slug: 'public domain'
+    name: 'Public domain'
+  model: svmain.license
+  pk: 20
+- fields:
+    slug: 'dual-gpl'
+    name: 'GNU General Public License v2 or later (+ dual licensing)'
+    url: 'http://www.gnu.org/licenses/gpl.html'
+  model: svmain.license
+  pk: 21
+- fields:
+    slug: 'gplv3orlater'
+    name: 'GNU General Public License v3 or later'
+    url: 'http://www.gnu.org/licenses/gpl.html'
+  model: svmain.license
+  pk: 22
+- fields:
+    slug: 'agpl'
+    name: 'GNU Affero General Public License v3 or later'
+    url: 'http://www.gnu.org/licenses/agpl-3.0.html'
+  model: svmain.license
+  pk: 23
new file mode 100644
--- /dev/null
+++ b/src/savane/svmain/models.py
@@ -0,0 +1,387 @@
+# User/group extra attributes
+# Copyright (C) 2002-2006 Mathieu Roy <yeupou--gnu.org>
+# Copyright (C) 2007, 2008, 2009  Sylvain Beucler
+# Copyright (C) 2008  Aleix Conchillo Flaque
+# Copyright (C) 2009  Jonathan Gonzalez V.
+#
+# This file is part of Savane.
+# 
+# Savane is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+# 
+# Savane is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from django.db import models
+from django.contrib.auth import models as auth_models
+
+class ExtendedUser(auth_models.User):
+    """Django base User class + extra Savane fields"""
+
+    # Migrated to 'firstname' in auth.User
+    #realname = models.CharField(max_length=96)
+
+    # Old Savane can be Active/Deleted/Pending/Suspended/SQuaD
+    status_CHOICES = (
+        ('A', 'Active'),
+        ('D', 'Deleted'),
+        ('P', 'Pending'),
+        ('S', 'Suspended'),
+        #('SQD', 'Squad'), # TODO: implement squads more cleanly
+        )
+    status = models.CharField(max_length=3, choices=status_CHOICES)
+
+    # Used by trackers only but it could be used more widely
+    spamscore = models.IntegerField(null=True, blank=True)
+    # Previously used for e-mail changes and password recovery, Django
+    # does it different with a auth.tokens
+    #confirm_hash = models.CharField(max_length=96, blank=True, null=True)
+
+    # Keys
+    authorized_keys = models.TextField(blank=True)
+    authorized_keys_count = models.IntegerField(null=True, blank=True)
+    gpg_key = models.TextField(blank=True)
+    gpg_key_count = models.IntegerField(null=True, blank=True)
+
+    # Personal info
+    people_resume = models.TextField()
+
+    # Preferences - /!\ some are also in the user_preferences table
+    people_view_skills = models.BooleanField(default=False)
+    email_hide = models.BooleanField(default=False)
+    timezone = models.CharField(max_length=192, blank=True)
+    theme = models.CharField(max_length=45, blank=True)
+
+
+    # Inherit specialized models.Manager with convenience functions
+    objects = auth_models.UserManager()
+
+class License(models.Model):
+    """
+    Main license used by a project
+
+    TODO: support several licenses per project (mixed-licensed code,
+    documentation, ...)
+    """
+    slug = models.CharField(max_length=32)
+    name = models.CharField(max_length=255)
+    url = models.CharField(max_length=255, blank=True)
+
+    def __unicode__(self):
+        return self.slug + ": " + self.name
+
+    class Meta:
+        ordering = ['slug']
+
+class DevelopmentStatus(models.Model):
+    """Describe the development status of a project"""
+    name = models.CharField(max_length=255)
+
+    def __unicode__(self):
+        return self.name
+
+    class Meta:
+        ordering = ['name']
+        verbose_name_plural='Development statuses'
+
+class GroupConfiguration(models.Model):
+    """Group configuration and main category (previously group_type)"""
+    name = models.CharField(max_length=255)
+    description = models.TextField(blank=True,
+      help_text='Will be added on each project main page')
+
+    #admin_email_adress = models.CharField(max_length=128, null=True) # unused
+
+    # Redirect to this host when visiting project page
+    base_host = models.CharField(max_length=128, blank=True)
+
+    # Mailing lists
+    mailing_list_address = models.CharField(max_length=255, default='@',
+      help_text='would be %LIST@gnu.org for GNU projects at sv.gnu.org')
+    mailing_list_virtual_host = models.CharField(max_length=255, blank=True,
+      help_text='would be lists.gnu.org or lists.nongnu.org at sv.gnu.org [BACKEND SPECIFIC]')
+    mailing_list_format = models.CharField(max_length=255, default='%NAME',
+      help_text='With this, you can force projects to follow a specific policy'
+        + ' for the name of the %LIST. Here you should use the special wildcard'
+        + ' %NAME, which is the part the of the mailing list name that the'
+        + ' project admin can define (would be %PROJECT-%NAME for non-GNU'
+        + ' projects at sv.gnu.org). Do no add any @hostname here!'
+        + ' You can specify multiple formats separated by a "," comma.')
+    #mailing_list_host = models.CharField(max_length=255, help_text='DEPRECATED')
+
+    # Permissions
+    can_use_homepage     = models.BooleanField(default=True)
+    can_use_download     = models.BooleanField(default=True,
+      help_text='This is useful if you provide directly download areas (created'
+        + ' by the backend) or if you want to allow projects to configure the'
+        + ' related menu entry (see below).')
+    can_use_cvs          = models.BooleanField(default=False)
+    can_use_arch         = models.BooleanField(default=False)
+    can_use_svn          = models.BooleanField(default=False)
+    can_use_git          = models.BooleanField(default=False)
+    can_use_hg           = models.BooleanField(default=False)
+    can_use_bzr          = models.BooleanField(default=False)
+    can_use_license      = models.BooleanField(default=True,
+      help_text='This is useful if you want project to select a license'
+        + ' on submission.')
+    can_use_devel_status = models.BooleanField(default=True,
+      help_text='This is useful if you want project to be able to defines their'
+        + ' development status that will be shown on their main page. This is'
+        + ' purely a matter of cosmetics. This option is mainly here just to'
+        + ' remove this content in case it is useless (it does not makes sense'
+        + ' for organizational projects).')
+    can_use_mailing_list = models.BooleanField(default=True,
+      help_text='This is one of the main issue tracker of Savane.'
+        + ' Projects are supposed to use it as primary interface with end user.')
+    can_use_support      = models.BooleanField(default=True)
+    can_use_bug          = models.BooleanField(default=True)
+    can_use_task         = models.BooleanField(default=True)
+    can_use_patch        = models.BooleanField(default=False)
+    can_use_news         = models.BooleanField(default=True)
+    is_menu_configurable_homepage                = models.BooleanField(default=False,
+      help_text='the homepage link can be modified')
+    is_menu_configurable_download                = models.BooleanField(default=False)
+    is_menu_configurable_support                 = models.BooleanField(default=False)
+    is_menu_configurable_mail                    = models.BooleanField(default=False)
+    is_menu_configurable_cvs                     = models.BooleanField(default=False)
+    is_menu_configurable_cvs_viewcvs             = models.BooleanField(default=False)
+    is_menu_configurable_cvs_viewcvs_homepage    = models.BooleanField(default=False)
+    is_menu_configurable_arch                    = models.BooleanField(default=False)
+    is_menu_configurable_arch_viewcvs            = models.BooleanField(default=False)
+    is_menu_configurable_svn                     = models.BooleanField(default=False)
+    is_menu_configurable_svn_viewcvs             = models.BooleanField(default=False)
+    is_menu_configurable_git                     = models.BooleanField(default=False)
+    is_menu_configurable_git_viewcvs             = models.BooleanField(default=False)
+    is_menu_configurable_hg                      = models.BooleanField(default=False)
+    is_menu_configurable_hg_viewcvs              = models.BooleanField(default=False)
+    is_menu_configurable_bzr                     = models.BooleanField(default=False)
+    is_menu_configurable_bzr_viewcvs             = models.BooleanField(default=False)
+    is_menu_configurable_bugs                    = models.BooleanField(default=False)
+    is_menu_configurable_task                    = models.BooleanField(default=False)
+    is_menu_configurable_patch                   = models.BooleanField(default=False)
+    is_menu_configurable_extralink_documentation = models.BooleanField(default=False)
+    is_configurable_download_dir = models.BooleanField(default=False,
+      help_text="the download _directory_ can be modified -- beware, if the"
+        + " backend is running and creating download dir, it can be used"
+        + " maliciously. don't activate this feature unless you truly know"
+        + "what you're doing")
+
+    # Directory creation config
+    SCM_CHOICES = (
+        ('cvs', 'CVS'),
+        ('svn' , 'Subversion'),
+        ('arch' , 'GNU Arch'),
+        ('git' , 'Git'),
+        ('hg' , 'Mercurial'),
+        ('bzr' , 'Bazaar'),
+        )
+    homepage_scm = models.CharField(max_length=4, choices=SCM_CHOICES, default='cvs')
+    DIR_TYPE_CHOICES = (
+        ('basicdirectory', 'Basic directory'),
+        ('basiccvs', 'Basic CVS directory'),
+        ('basicsvn', 'Basic Subversion directory'),
+        ('basicgit', 'Basic Git directory'),
+        ('basichg', 'Basic Mercurial directory'),
+        ('basicbzr', 'Basic Bazaar directory'),
+        ('cvsattic', 'CVS Attic/Gna!'),
+        ('svnattic', 'Subversion Attic/Gna!'),
+        ('svnatticwebsite', 'Subversion Subdirectory Attic/Gna!'),
+        ('savannah-gnu', 'CVS Savannah GNU'),
+        ('savannah-nongnu', 'CVS Savannah non-GNU'),
+        )
+    dir_type_cvs      = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basiccvs')
+    dir_type_arch     = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicdirectory')
+    dir_type_svn      = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicsvn')
+    dir_type_git      = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicgit')
+    dir_type_hg       = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basichg')
+    dir_type_bzr      = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicbzr')
+    dir_type_homepage = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicdirectory')
+    dir_type_download = models.CharField(max_length=15, choices=DIR_TYPE_CHOICES, default='basicdirectory')
+    dir_homepage = models.CharField(max_length=255, default='/')
+    dir_cvs      = models.CharField(max_length=255, default='/')
+    dir_arch     = models.CharField(max_length=255, default='/')
+    dir_svn      = models.CharField(max_length=255, default='/')
+    dir_git      = models.CharField(max_length=255, default='/')
+    dir_hg       = models.CharField(max_length=255, default='/')
+    dir_bzr      = models.CharField(max_length=255, default='/')
+    dir_download = models.CharField(max_length=255, default='/')
+
+    # Default URLs
+    url_homepage             = models.CharField(max_length=255, default='http://')
+    url_cvs_viewcvs_homepage = models.CharField(max_length=255, default='http://')
+    url_cvs_viewcvs          = models.CharField(max_length=255, default='http://')
+    url_arch_viewcvs         = models.CharField(max_length=255, default='http://') 
+    url_svn_viewcvs          = models.CharField(max_length=255, default='http://')
+    url_git_viewcvs          = models.CharField(max_length=255, default='http://')
+    url_hg_viewcvs           = models.CharField(max_length=255, default='http://')
+    url_bzr_viewcvs          = models.CharField(max_length=255, default='http://')
+    url_download             = models.CharField(max_length=255, default='http://')
+    url_mailing_list_listinfo         = models.CharField(max_length=255, default='http://')
+    url_mailing_list_subscribe        = models.CharField(max_length=255, default='http://')
+    url_mailing_list_unsubscribe      = models.CharField(max_length=255, default='http://')
+    url_mailing_list_archives         = models.CharField(max_length=255, default='http://')
+    url_mailing_list_archives_private = models.CharField(max_length=255, default='http://')
+    url_mailing_list_admin            = models.CharField(max_length=255, default='http://')
+    url_extralink_documentation = models.CharField(max_length=255, blank=True)
+
+    # Deprecated
+    # "Forum is a deprecated feature of Savane. We do not recommend
+    #  using it and we do not maintain this code any longer."
+    #can_use_forum = models.BooleanField(default=False)
+    #is_menu_configurable_forum = models.BooleanField(default=False)
+    #forum_flags = IntegerField(default='2')
+    #forum_rflags = IntegerField(default='2')
+
+    # Unused
+    #license_array = models.TextField()
+    #devel_status_array = models.TextField()
+
+    # TODO: split forum and news config
+    #news_flags      = IntegerField(default='3')
+    #news_rflags     = IntegerField(default='2')
+
+    # TODO: split tracker config
+    #bugs_flags      = IntegerField(default='2')
+    #task_flags      = IntegerField(default='2')
+    #patch_flags     = IntegerField(default='2')
+    #cookbook_flags  = IntegerField(default='2')
+    #support_flags   = IntegerField(default='2')
+    #bugs_rflags     = IntegerField(default='2')
+    #task_rflags     = IntegerField(default='5')
+    #patch_rflags    = IntegerField(default='2')
+    #cookbook_rflags = IntegerField(default='5')
+    #support_rflags  = IntegerField(default='2')
+
+    def __unicode__(self):
+        return self.name
+
+
+class ExtendedGroup(auth_models.Group):
+    """Django base Group class + extra Savane fields"""
+    
+    type = models.ForeignKey(GroupConfiguration)
+    full_name = models.CharField(max_length=30, blank=True,
+      help_text="Full project name (not Unix system name)")
+    is_public = models.BooleanField(default=False)
+    status_CHOICES = (
+        ('A', 'Active'),
+        ('P', 'Pending'),
+        ('D', 'Deleted'),
+        ('M', 'Maintenance (accessible only to superuser)'),
+        ('I', 'Incomplete (failure during registration)'),
+        )
+    status = models.CharField(max_length=1, choices=status_CHOICES, default='A')
+    short_description = models.CharField(max_length=255, blank=True)
+    long_description = models.TextField(blank=True)
+    license = models.ForeignKey(License, blank=True, null=True)
+    license_other = models.TextField(blank=True)
+
+    devel_status = models.ForeignKey(DevelopmentStatus)
+
+    # Registration-specific
+    register_purpose = models.TextField(blank=True)
+    required_software = models.TextField(blank=True)
+    other_comments = models.TextField(blank=True)
+
+    register_time = models.DateTimeField()
+    #rand_hash text,
+    
+    registered_gpg_keys = models.TextField(blank=True)
+
+    # Project "Features"
+    use_homepage                = models.BooleanField(default=False)
+    use_mail                    = models.BooleanField(default=False)
+    use_patch                   = models.BooleanField(default=False)
+    use_task                    = models.BooleanField(default=False)
+    use_cvs                     = models.BooleanField(default=False)
+    use_arch                    = models.BooleanField(default=False)
+    use_svn                     = models.BooleanField(default=False)
+    use_git                     = models.BooleanField(default=False)
+    use_hg                      = models.BooleanField(default=False)
+    use_bzr                     = models.BooleanField(default=False)
+    use_news                    = models.BooleanField(default=False)
+    use_support                 = models.BooleanField(default=False)
+    use_download                = models.BooleanField(default=False)
+    use_bugs                    = models.BooleanField(default=False)
+    use_extralink_documentation = models.BooleanField(default=False)
+
+    # blank means 'use default'
+    url_homepage                = models.CharField(max_length=255, blank=True)
+    url_download                = models.CharField(max_length=255, blank=True)
+    url_support                 = models.CharField(max_length=255, blank=True)
+    url_mail                    = models.CharField(max_length=255, blank=True)
+    url_cvs                     = models.CharField(max_length=255, blank=True)
+    url_cvs_viewcvs             = models.CharField(max_length=255, blank=True)
+    url_cvs_viewcvs_homepage    = models.CharField(max_length=255, blank=True)
+    url_arch                    = models.CharField(max_length=255, blank=True)
+    url_arch_viewcvs            = models.CharField(max_length=255, blank=True)
+    url_svn                     = models.CharField(max_length=255, blank=True)
+    url_svn_viewcvs             = models.CharField(max_length=255, blank=True)
+    url_git                     = models.CharField(max_length=255, blank=True)
+    url_git_viewcvs             = models.CharField(max_length=255, blank=True)
+    url_hg                      = models.CharField(max_length=255, blank=True)
+    url_hg_viewcvs              = models.CharField(max_length=255, blank=True)
+    url_bzr                     = models.CharField(max_length=255, blank=True)
+    url_bzr_viewcvs             = models.CharField(max_length=255, blank=True)
+    url_bugs                    = models.CharField(max_length=255, blank=True)
+    url_task                    = models.CharField(max_length=255, blank=True)
+    url_patch                   = models.CharField(max_length=255, blank=True)
+    url_extralink_documentation = models.CharField(max_length=255, blank=True)
+
+    # Admin override (unused)
+    #dir_cvs = models.CharField(max_length=255)
+    #dir_arch = models.CharField(max_length=255)
+    #dir_svn = models.CharField(max_length=255)
+    #dir_git = models.CharField(max_length=255)
+    #dir_hg = models.CharField(max_length=255)
+    #dir_bzr = models.CharField(max_length=255)
+    #dir_homepage = models.CharField(max_length=255)
+    #dir_download = models.CharField(max_length=255)
+
+    # Deprecated
+    #url_forum = models.CharField(max_length=255, blank=True)
+    #use_forum = models.BooleanField(default=False)
+
+    # TODO: split trackers configuration
+    #bugs_preamble = models.TextField()
+    #task_preamble = models.TextField()
+    #patch_preamble = models.TextField()
+    #support_preamble = models.TextField()
+    #cookbook_preamble = models.TextField()
+
+    #new_bugs_address text NOT NULL
+    #new_patch_address text NOT NULL
+    #new_support_address text NOT NULL
+    #new_task_address text NOT NULL
+    #new_news_address text NOT NULL
+    #new_cookbook_address text NOT NULL
+
+    #bugs_glnotif int(11) NOT NULL default '1'
+    #support_glnotif int(11) NOT NULL default '1'
+    #task_glnotif int(11) NOT NULL default '1'
+    #patch_glnotif int(11) NOT NULL default '1'
+    #cookbook_glnotif int(11) NOT NULL default '1'
+    #send_all_bugs int(11) NOT NULL default '0'
+    #send_all_patch int(11) NOT NULL default '0'
+    #send_all_support int(11) NOT NULL default '0'
+    #send_all_task int(11) NOT NULL default '0'
+    #send_all_cookbook int(11) NOT NULL default '0'
+    #bugs_private_exclude_address text
+    #task_private_exclude_address text
+    #support_private_exclude_address text
+    #patch_private_exclude_address text
+    #cookbook_private_exclude_address text
+
+    def __unicode__(self):
+        return self.name
+
+    class Meta:
+        ordering = ['name']
+
new file mode 100644
--- /dev/null
+++ b/src/savane/svmain/urls.py
@@ -0,0 +1,30 @@
+# URL dispatching for presentation pages
+# Copyright (C) 2009  Sylvain Beucler
+# Copyright (C) 2009  Jonathan Gonzalez V.
+#
+# This file is part of Savane.
+# 
+# Savane is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+# 
+# Savane is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns ('',
+  url(r'^$', 'django.views.generic.simple.direct_to_template',
+      { 'template' : 'index.html',
+        'extra_context' : { 'has_left_menu': False } },
+      name='homepage'),
+  url(r'^contact$', 'django.views.generic.simple.direct_to_template',
+      { 'template' : 'contact.html' },
+      name='contact'),
+)
--- a/src/settings.py
+++ b/src/settings.py
@@ -90,7 +90,7 @@
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.sites',
-#    'django.contrib.admin',
+    'django.contrib.admin',
+    'savane.svmain',
     'savane.my',
-    'savane.main',
 )
--- a/src/urls.py
+++ b/src/urls.py
@@ -22,7 +22,7 @@
 
 # Home/presentation pages
 urlpatterns = patterns('',
-  (r'', include('savane.main.urls')),
+  (r'', include('savane.svmain.urls')),
 )
 
 # User account
@@ -34,16 +34,16 @@
   (r'^accounts/', include('django.contrib.auth.urls')),
 )
 
+# Enable the auto-admin:
+from django.contrib import admin
+admin.autodiscover()
+urlpatterns += patterns('',
+  (r'^admin/(.*)', admin.site.root),
+)
+
 # Static content
 if settings.DEBUG:
     urlpatterns += patterns('django.views.static',
         (r'^' + settings.STATIC_MEDIA_URL[1:] + '(?P<path>.*)$', 'serve',
          {'document_root' : settings.STATIC_MEDIA_ROOT, 'show_indexes' : True}),
     )
-
-# Uncomment the next lines to enable the admin:
-# from django.contrib import admin
-# admin.autodiscover()
-# urlpatterns += patterns(
-#   (r'^admin/(.*)', admin.site.root),
-# )