[ckan-changes] commit/ckanextiati: 13 new changesets

Bitbucket commits-noreply at bitbucket.org
Wed Sep 14 17:34:51 UTC 2011


13 new changesets in ckanextiati:

http://bitbucket.org/okfn/ckanextiati/changeset/3f5b0b038046/
changeset:   3f5b0b038046
branch:      new-forms
user:        amercader
date:        2011-09-12 17:48:18
summary:     [new forms] Add new package form, with custom template, schema and validators. Existing authz management is not affected
affected #:  7 files (25.7 KB)

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/__init__.py	Mon Sep 12 16:48:18 2011 +0100
@@ -0,0 +1,1 @@
+


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/countries.py	Mon Sep 12 16:48:18 2011 +0100
@@ -0,0 +1,6 @@
+# -*- coding: UTF-8 -*-
+
+# This Python file uses the following encoding: utf-8
+# Country and regions names and codes sent by Bill Anderson on 2011-06-28
+
+COUNTRIES = (("", u"(No country assigned)"), ("AF",u"Afghanistan"),("AX",u"Åland Islands"),("AL",u"Albania"),("DZ",u"Algeria"),("AS",u"American Samoa"),("AD",u"Andorra"),("AO",u"Angola"),("AI",u"Anguilla"),("AQ",u"Antarctica"),("AG",u"Antigua And Barbuda"),("AR",u"Argentina"),("AM",u"Armenia"),("AW",u"Aruba"),("AU",u"Australia"),("AT",u"Austria"),("AZ",u"Azerbaijan"),("BS",u"Bahamas"),("BH",u"Bahrain"),("BD",u"Bangladesh"),("BB",u"Barbados"),("BY",u"Belarus"),("BE",u"Belgium"),("BZ",u"Belize"),("BJ",u"Benin"),("BM",u"Bermuda"),("BT",u"Bhutan"),("BO",u"Bolivia, Plurinational State Of"),("BQ",u"Bonaire, Sint Eustatius And Saba"),("BA",u"Bosnia And Herzegovina"),("BW",u"Botswana"),("BV",u"Bouvet Island"),("BR",u"Brazil"),("IO",u"British Indian Ocean Territory"),("BN",u"Brunei Darussalam"),("BG",u"Bulgaria"),("BF",u"Burkina Faso"),("BI",u"Burundi"),("KH",u"Cambodia"),("CM",u"Cameroon"),("CA",u"Canada"),("CV",u"Cape Verde"),("KY",u"Cayman Islands"),("CF",u"Central African Republic"),("TD",u"Chad"),("CL",u"Chile"),("CN",u"China"),("CX",u"Christmas Island"),("CC",u"Cocos (Keeling) Islands"),("CO",u"Colombia"),("KM",u"Comoros"),("CG",u"Congo"),("CD",u"Congo, The Democratic Republic Of The"),("CK",u"Cook Islands"),("CR",u"Costa Rica"),("CI",u"Côte D'Ivoire"),("HR",u"Croatia"),("CU",u"Cuba"),("CW",u"Curaçao"),("CY",u"Cyprus"),("CZ",u"Czech Republic"),("DK",u"Denmark"),("DJ",u"Djibouti"),("DM",u"Dominica"),("DO",u"Dominican Republic"),("EC",u"Ecuador"),("EG",u"Egypt"),("SV",u"El Salvador"),("GQ",u"Equatorial Guinea"),("ER",u"Eritrea"),("EE",u"Estonia"),("ET",u"Ethiopia"),("FK",u"Falkland Islands (Malvinas)"),("FO",u"Faroe Islands"),("FJ",u"Fiji"),("FI",u"Finland"),("FR",u"France"),("GF",u"French Guiana"),("PF",u"French Polynesia"),("TF",u"French Southern Territories"),("GA",u"Gabon"),("GM",u"Gambia"),("GE",u"Georgia"),("DE",u"Germany"),("GH",u"Ghana"),("GI",u"Gibraltar"),("GR",u"Greece"),("GL",u"Greenland"),("GD",u"Grenada"),("GP",u"Guadeloupe"),("GU",u"Guam"),("GT",u"Guatemala"),("GG",u"Guernsey"),("GN",u"Guinea"),("GW",u"Guinea-Bissau"),("GY",u"Guyana"),("HT",u"Haiti"),("HM",u"Heard Island And Mcdonald Islands"),("VA",u"Holy See (Vatican City State)"),("HN",u"Honduras"),("HK",u"Hong Kong"),("HU",u"Hungary"),("IS",u"Iceland"),("IN",u"India"),("ID",u"Indonesia"),("IR",u"Iran, Islamic Republic Of"),("IQ",u"Iraq"),("IE",u"Ireland"),("IM",u"Isle Of Man"),("IL",u"Israel"),("IT",u"Italy"),("JM",u"Jamaica"),("JP",u"Japan"),("JE",u"Jersey"),("JO",u"Jordan"),("KZ",u"Kazakhstan"),("KE",u"Kenya"),("KI",u"Kiribati"),("KP",u"Korea, Democratic People's Republic Of"),("KR",u"Korea, Republic Of"),("XK",u"Kosovo"),("KW",u"Kuwait"),("KG",u"Kyrgyzstan"),("LA",u"Lao People's Democratic Republic"),("LV",u"Latvia"),("LB",u"Lebanon"),("LS",u"Lesotho"),("LR",u"Liberia"),("LY",u"Libyan Arab Jamahiriya"),("LI",u"Liechtenstein"),("LT",u"Lithuania"),("LU",u"Luxembourg"),("MO",u"Macao"),("MK",u"Macedonia, The Former Yugoslav Republic Of"),("MG",u"Madagascar"),("MW",u"Malawi"),("MY",u"Malaysia"),("MV",u"Maldives"),("ML",u"Mali"),("MT",u"Malta"),("MH",u"Marshall Islands"),("MQ",u"Martinique"),("MR",u"Mauritania"),("MU",u"Mauritius"),("YT",u"Mayotte"),("MX",u"Mexico"),("FM",u"Micronesia, Federated States Of"),("MD",u"Moldova, Republic Of"),("MC",u"Monaco"),("MN",u"Mongolia"),("ME",u"Montenegro"),("MS",u"Montserrat"),("MA",u"Morocco"),("MZ",u"Mozambique"),("MM",u"Myanmar"),("NA",u"Namibia"),("NR",u"Nauru"),("NP",u"Nepal"),("NL",u"Netherlands"),("NC",u"New Caledonia"),("NZ",u"New Zealand"),("NI",u"Nicaragua"),("NE",u"Niger"),("NG",u"Nigeria"),("NU",u"Niue"),("NF",u"Norfolk Island"),("MP",u"Northern Mariana Islands"),("NO",u"Norway"),("OM",u"Oman"),("PK",u"Pakistan"),("PW",u"Palau"),("PS",u"Palestinian Territory, Occupied"),("PA",u"Panama"),("PG",u"Papua New Guinea"),("PY",u"Paraguay"),("PE",u"Peru"),("PH",u"Philippines"),("PN",u"Pitcairn"),("PL",u"Poland"),("PT",u"Portugal"),("PR",u"Puerto Rico"),("QA",u"Qatar"),("RE",u"Réunion"),("RO",u"Romania"),("RU",u"Russian Federation"),("RW",u"Rwanda"),("BL",u"Saint Barthélemy"),("SH",u"Saint Helena, Ascension And Tristan Da Cunha"),("KN",u"Saint Kitts And Nevis"),("LC",u"Saint Lucia"),("MF",u"Saint Martin (French Part)"),("PM",u"Saint Pierre And Miquelon"),("VC",u"Saint Vincent And The Grenadines"),("WS",u"Samoa"),("SM",u"San Marino"),("ST",u"Sao Tome And Principe"),("SA",u"Saudi Arabia"),("SN",u"Senegal"),("RS",u"Serbia"),("SC",u"Seychelles"),("SL",u"Sierra Leone"),("SG",u"Singapore"),("SX",u"Sint Maarten (Dutch Part)"),("SK",u"Slovakia"),("SI",u"Slovenia"),("SB",u"Solomon Islands"),("SO",u"Somalia"),("ZA",u"South Africa"),("GS",u"South Georgia And The South Sandwich Islands"),("ES",u"Spain"),("LK",u"Sri Lanka"),("SD",u"Sudan"),("SR",u"Suriname"),("SJ",u"Svalbard And Jan Mayen"),("SZ",u"Swaziland"),("SE",u"Sweden"),("CH",u"Switzerland"),("SY",u"Syrian Arab Republic"),("TW",u"Taiwan, Province Of China"),("TJ",u"Tajikistan"),("TZ",u"Tanzania, United Republic Of"),("TH",u"Thailand"),("TL",u"Timor-Leste"),("TG",u"Togo"),("TK",u"Tokelau"),("TO",u"Tonga"),("TT",u"Trinidad And Tobago"),("TN",u"Tunisia"),("TR",u"Turkey"),("TM",u"Turkmenistan"),("TC",u"Turks And Caicos Islands"),("TV",u"Tuvalu"),("UG",u"Uganda"),("UA",u"Ukraine"),("AE",u"United Arab Emirates"),("GB",u"United Kingdom"),("US",u"United States"),("UM",u"United States Minor Outlying Islands"),("UY",u"Uruguay"),("UZ",u"Uzbekistan"),("VU",u"Vanuatu"),("VE",u"Venezuela, Bolivarian Republic Of"),("VN",u"Viet Nam"),("VG",u"Virgin Islands, British"),("VI",u"Virgin Islands, U.S."),("WF",u"Wallis And Futuna"),("EH",u"Western Sahara"),("YE",u"Yemen"),("ZM",u"Zambia"),("ZW",u"Zimbabwe"),("298",u"Africa"),("189",u"Africa - North"),("289",u"Africa - Sub-Saharan"),("089",u"Europe"),("498",u"America"),("389",u"America - North & Central"),("489",u"America - South"),("380",u"West Indies"),("589",u"Middle East"),("798",u"Asia"),("619",u"Asia - Central"),("679",u"Asia - South"),("689",u"Asia - South & Central"),("789",u"Asia - Far East"),("889",u"Oceania"),("998",u"Unspecified"),)


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/package_iati.py	Mon Sep 12 16:48:18 2011 +0100
@@ -0,0 +1,136 @@
+from ckan.lib.base import c
+from ckan.lib.helpers import json
+from ckan import model
+from ckan.controllers.package import PackageController
+from ckan.authz import Authorizer
+
+from ckan.logic.schema import package_form_schema
+from ckan.lib.navl.validators import (ignore_missing,
+                                      not_empty,
+                                      empty,
+                                      ignore,
+                                      keep_extras,
+                                     )
+from ckan.lib.navl.dictization_functions import Missing, Invalid
+from ckan.lib.field_types import DateType, DateConvertError
+
+from countries import COUNTRIES
+
+class PackageIatiController(PackageController):
+
+    package_form = 'package/form_iati.html'
+
+    def _setup_template_variables(self, context, data_dict=None):
+
+        super(PackageIatiController,self)._setup_template_variables(context,data_dict)
+
+        c.groups_authz = self.get_groups()
+        c.groups_available = self.get_groups(available_only=True)
+
+        c.countries = [(v, k) for k, v in COUNTRIES]
+
+    def _form_to_db_schema(self):
+        schema = package_form_schema()
+        schema.update({
+            'department': [unicode,convert_to_extras,ignore_missing],
+            'country': [convert_to_extras, ignore_missing],
+            'donors': [unicode, convert_from_comma_list, convert_to_extras, ignore_missing],
+            'donors_type': [unicode, convert_from_comma_list, convert_to_extras, ignore_missing],
+            'donors_country': [unicode, convert_from_comma_list, convert_to_extras, ignore_missing],
+            'record_updated': [date_to_db, convert_to_extras,ignore_missing],
+            'data_updated': [date_to_db, convert_to_extras,ignore_missing],
+            'activity_period-from': [date_to_db, convert_to_extras,ignore_missing],
+            'activity_period-to': [date_to_db, convert_to_extras,ignore_missing],
+            'activity_count': [integer,convert_to_extras,ignore_missing],
+            'archive_file': [checkbox_value, convert_to_extras,ignore_missing],
+            'verified': [checkbox_value, convert_to_extras,ignore_missing],
+        })
+
+        return schema
+
+    def _db_to_form_schema(self):
+        schema = package_form_schema()
+        schema.update({
+            'department': [convert_from_extras,ignore_missing],
+            'country': [convert_from_extras, ignore_missing],
+            'donors': [convert_from_extras, convert_to_comma_list, ignore_missing],
+            'donors_type': [convert_from_extras, convert_to_comma_list, ignore_missing],
+            'donors_country': [convert_from_extras, convert_to_comma_list, ignore_missing],
+            'record_updated': [convert_from_extras,ignore_missing, date_to_form],
+            'data_updated': [convert_from_extras,ignore_missing, date_to_form],
+            'activity_period-from': [convert_from_extras,ignore_missing, date_to_form],
+            'activity_period-to': [convert_from_extras,ignore_missing, date_to_form],
+            'activity_count': [convert_from_extras,ignore_missing],
+            'archive_file': [convert_from_extras,ignore_missing],
+            'verified': [convert_from_extras,ignore_missing],
+        })
+
+        return schema
+
+    def _check_data_dict(self, data_dict):
+        return
+
+    # End hooks
+
+    def get_groups(self,available_only=False):
+
+        query = Authorizer().authorized_query(c.user, model.Group, model.Action.EDIT)
+        groups = set(query.all())
+
+        if available_only:
+            package = c.pkg
+            if package:
+                groups = groups - set(package.groups)
+
+        return [{'id':group.id,'name':group.name, 'title':group.title} for group in groups if group.state==model.State.ACTIVE]
+
+
+def convert_to_extras(key, data, errors, context):
+
+    extras = data.get(('extras',), [])
+    if not extras:
+        data[('extras',)] = extras
+
+    extras.append({'key': key[-1], 'value': data[key]})
+
+def convert_from_extras(key, data, errors, context):
+
+    for data_key, data_value in data.iteritems():
+        if (data_key[0] == 'extras'
+            and data_key[-1] == 'key'
+            and data_value == key[-1]):
+            data[key] = data[('extras', data_key[1], 'value')]
+
+def date_to_db(value, context):
+    try:
+        value = DateType.form_to_db(value)
+    except DateConvertError, e:
+        raise Invalid(str(e))
+    return value
+
+def date_to_form(value, context):
+    try:
+        value = DateType.db_to_form(value)
+    except DateConvertError, e:
+        raise Invalid(str(e))
+    return value
+
+def convert_to_comma_list(value, context):
+
+    return ', '.join(json.loads(value))
+
+def convert_from_comma_list(value, context):
+
+    return [x.strip() for x in value.split(',') if len(x)]
+
+def checkbox_value(value,context):
+
+    return 'yes' if not isinstance(value, Missing) else 'no'
+
+def integer(value,context):
+    try:
+        value = int(value)
+    except ValueError,e:
+        raise Invalid(str(e))
+    return value
+


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/plugin.py	Mon Sep 12 16:48:18 2011 +0100
@@ -0,0 +1,51 @@
+import os
+
+from logging import getLogger
+
+from ckan.plugins import implements, SingletonPlugin
+from ckan.plugins import IRoutes
+from ckan.plugins import IConfigurer
+
+import ckanext.iati
+
+log = getLogger(__name__)
+
+def configure_template_directory(config, relative_path):
+    configure_served_directory(config, relative_path, 'extra_template_paths')
+
+def configure_public_directory(config, relative_path):
+    configure_served_directory(config, relative_path, 'extra_public_paths')
+
+def configure_served_directory(config, relative_path, config_var):
+    'Configure serving of public/template directories.'
+    assert config_var in ('extra_template_paths', 'extra_public_paths')
+    this_dir = os.path.dirname(ckanext.iati.__file__)
+    absolute_path = os.path.join(this_dir, relative_path)
+    if absolute_path not in config.get(config_var, ''):
+        if config.get(config_var):
+            config[config_var] += ',' + absolute_path
+        else:
+            config[config_var] = absolute_path
+
+class IatiForms(SingletonPlugin):
+
+    implements(IRoutes)
+    implements(IConfigurer)
+
+    def before_map(self, map):
+        package_controller = 'ckanext.iati.controllers.package_iati:PackageIatiController'
+        group_controller = 'ckanext.iati.controllers.group_iati:GroupIatiController'
+
+        map.redirect('/package/new','/dataset/new')
+        map.redirect('/package/edit/{id}','/dataset/edit/{id}')
+        map.connect('/dataset/new', controller=package_controller, action='new')
+        map.connect('/dataset/edit/{id}', controller=package_controller, action='edit')
+
+        return map
+
+    def after_map(self, map):
+        return map
+
+    def update_config(self, config):
+        configure_template_directory(config, 'templates')
+


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/templates/package/form_iati.html	Mon Sep 12 16:48:18 2011 +0100
@@ -0,0 +1,221 @@
+<form id="package-edit" class="package_create_form ckan" method="post"
+    py:attrs="{'class':'has-errors'} if errors else {}"
+    xmlns:i18n="http://genshi.edgewall.org/i18n"
+    xmlns:py="http://genshi.edgewall.org/"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+    <div class="error-explanation" py:if="error_summary">
+        <h2>Errors in form</h2>
+        <p>The form contains invalid entries:</p>
+        <ul>
+            <li py:for="key, error in error_summary.items()">${"%s: %s" % (key, error)}</li>
+        </ul>
+    </div>
+
+    <fieldset>
+        <legend>Basic information </legend>
+        <dl>
+            <dt><label class="field_req" for="name">Name *</label></dt>
+            <dd><input id="name" maxlength="100" name="name" type="text" value="${data.get('name', '')}" /></dd>
+            <dd class="field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</dd>
+            <dd class="instructions basic">A unique identifier for the activity record.</dd>
+            <dd class="instructions further">It should be broadly humanly readable, in the spirit of Semantic Web URIs. Only use an acronym if it is widely recognised. Renaming is possible but discouraged.</dd>
+            <dd class="hints">2+ characters, lowercase, using only 'a-z0-9' and '-_'</dd>
+            <script type="text/javascript">
+                //<![CDATA[
+                $(document).ready(function () { if (!$('#preview').length) {$("#name").focus(); } });
+                //]]>
+            </script>
+
+            <dt><label class="field_opt" for="title">Title </label></dt>
+            <dd><input id="title" name="title" type="text" value="${data.get('title', '')}" /></dd>
+            <dd class="field_error" py:if="errors.get('title', '')">${errors.get('title', '')}</dd>
+            <dd class="instructions basic">A short descriptive title for the data set.</dd>
+            <dd class="instructions further">It should not be a description though - save that for the Notes field. Do not give a trailing full stop.</dd>
+
+            <dt><label class="field_opt" for="author">Contact</label></dt>
+            <dd><input id="author" name="author" type="text" value="${data.get('author', '')}" /></dd>
+            <dd class="instructions basic">The name of the main contact, for enquiries about this particular dataset, using the e-mail address in the following field.</dd>
+
+            <dt><label class="field_opt" for="author_email">Contact e-mail</label></dt>
+            <dd><input id="author_email" name="author_email" type="text" value="${data.get('author_email', '')}" /></dd>
+
+            <dt><label class="field_opt" for="department">Department</label></dt>
+            <dd><input id="department" name="department" size="40" type="text" value="${data.get('department', '')}" /></dd>
+        </dl>
+    </fieldset>
+
+    <fieldset id="groups">
+        <legend>Publisher</legend>
+        <dl>
+        <py:for each="num, group in enumerate(data.get('groups', []))">
+            <?python
+            authorized_group = [group_authz for group_authz in c.groups_authz if group_authz['id'] == group['id']]
+            authorized_group = authorized_group[0] if authorized_group else None
+            ?>
+
+            <dt py:if="'id' in group">
+                <input type="${'checkbox' if authorized_group else 'hidden'}" name="groups__${num}__id" checked="checked" value="${group['id']}" />
+                <input type="hidden" name="groups__${num}__name" value="${group.get('name', authorized_group['name'] if authorized_group else '')}" />
+            </dt>
+            <dd py:if="'id' in group"><label for="groups__${num}__checked">${group.get('name', authorized_group['name'] if authorized_group else '')}</label></dd>
+        </py:for>
+
+        <dt>Publisher</dt>
+            <dd py:if="c.groups_available">
+            <select id="groups__${len(data.get('groups', []))}__id" name="groups__${len(data.get('groups', []))}__id">
+                <option value="" >(None)</option>
+                <py:for each="group in c.groups_available">
+                    <option value="${group['id']}" >${group['title']}</option>
+                </py:for>
+            </select>
+            </dd>
+            <dd py:if="not c.groups_available">Cannot add any publisher.</dd>
+        </dl>
+
+    </fieldset>
+
+    <fieldset>
+        <legend>Details</legend>
+        <dl>
+            <dt><label class="field_opt" for="country">Recipient country</label></dt>
+            <dd>
+                <py:with vars="country = data.get('country','')">
+                <select id="country" name="country">
+                    <py:for each="name,id in c.countries">
+                    <option value="${id}" py:attrs="{'selected': 'selected' if country == id else None}">${name}</option>
+                    </py:for>
+                </select>
+                </py:with>
+            </dd>
+
+            <dt><label class="field_opt" for="donors">Donors</label></dt>
+            <dd><input id="donors" name="donors" type="text" value="${data.get('donors', '')}" /></dd>
+            <dd class="instructions basic">Separate multiple entries using commas.</dd>
+
+            <dt><label class="field_opt" for="donors_type">Donor type</label></dt>
+            <dd><input id="donors_type" name="donors_type" type="text" value="${data.get('donors_type', '')}" /></dd>
+            <dd class="instructions basic">Separate multiple entries using commas.</dd>
+
+            <dt><label class="field_opt" for="donors_country">Donor country</label></dt>
+            <dd><input id="donors_country" name="donors_country" type="text" value="${data.get('donors_country', '')}" /></dd>
+            <dd class="instructions basic">Separate multiple entries using commas.</dd>
+
+            <dt><label class="field_opt" for="record_updated">Record updated</label></dt>
+            <dd><input id="record_updated" name="record_updated" size="40" type="text" value="${data.get('record_updated', '')}" /></dd>
+            <dd class="instructions basic">Acceptable formats: 'DD/MM/YYYY HH:MM', 'DD/MM/YYYY', 'MM/YYYY', 'YYYY'.</dd>
+
+            <dt><label class="field_opt" for="data_updated">Data updated</label></dt>
+            <dd><input id="data_updated" name="data_updated" size="40" type="text" value="${data.get('data_updated', '')}"/></dd>
+            <dd class="instructions basic">Acceptable formats: 'DD/MM/YYYY HH:MM', 'DD/MM/YYYY', 'MM/YYYY', 'YYYY'.</dd>
+
+            <dt><label class="field_opt" for="license_id">License</label></dt>
+            <dd>
+                <select id="license_id" name="license_id">
+                    <py:for each="licence_desc, licence_id in c.licences">
+                        <option value="${licence_id}" py:attrs="{'selected': 'selected' if data.get('license_id', '') == licence_id else None}" >${licence_desc}</option>
+                    </py:for>
+                </select>
+            </dd>
+            <dd class="instructions basic">The licence under which the dataset is released.</dd>
+
+            <dt><label class="field_opt" for="tags">Tags</label></dt>
+            <dd>
+                <input class="autocomplete-tag" id="tag_string" name="tag_string" size="40" type="text"
+                       value="${data.get('tag_string') or ' '.join([tag['name'] for tag in data.get('tags', [])])}" />
+            </dd>
+            <dd class="field_error" py:if="errors.get('tag_string', '')">${errors.get('tag_string', '')}</dd>
+            <dd class="instructions basic">Terms that may link this dataset to similar ones. For more information on conventions, see <a href="http://wiki.okfn.org/ckan/doc/faq#TagConventions">this wiki page</a>.</dd>
+            <dd class="hints">e.g. pollution rivers water-quality</dd>
+
+            <dt><label class="field_opt" for="notes">Notes</label></dt>
+            <dd><textarea cols="60" id="notes" name="notes" rows="15">${data.get('notes', '')}</textarea></dd>
+            <dd class="instructions basic">The main description of the dataset</dd>
+            <dd class="instructions further">It is often displayed with the record title. In particular, it should start with a short sentence that describes the data set succinctly, because the first few words alone may be used in some views of the data sets.</dd>
+            <dd class="hints">You can use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown formatting</a> here.</dd>
+        </dl>
+    </fieldset>
+
+    <fieldset id="resources">
+        <legend>Resources</legend>
+        <table class="flexitable">
+            <thead>
+                <tr>
+                    <th class="field_req resource-url">URL*</th>
+                    <th class="field_opt resource-format">Format</th>
+                    <th class="field_opt resource-description">Description</th>
+                    <th class="field_opt resource-hash">Hash</th>
+                </tr>
+            </thead>
+            <tbody>
+                <py:for each="num, res in enumerate(data.get('resources', []) + [{}])">
+                <tr>
+                    <py:for each="col in c.resource_columns">
+                    <td py:choose="" class="resource-${col}">
+                        <input py:when="col == 'format'" name="resources__${num}__${col}" type="text" value="${res.get(col, '')}" class="autocomplete-format short" />
+
+                        <input py:otherwise="" name="resources__${num}__${col}" type="text" value="${res.get(col, '')}" class="${'medium-width' if col == 'url' else 'short'}" />
+                    </td>
+                    </py:for>
+                    <td class="resource-id"><input name="resources__${num}__id" type="hidden" value="${res.get('id', '')}" /></td>
+                </tr>
+                </py:for>
+            </tbody>
+        </table>
+
+        <div class="instructions basic">The files containing the data or address of the APIs for accessing it.</div>
+        <div class="instructions further"><br /><b>URL:</b> This is the Internet link directly to the data - by selecting this link in a web browser, the user will immediately download the full dataset. Note that datasets are not hosted on this site, but by the publisher of the data.<br /><b>Format:</b> This should give the file format in which the data is supplied. (i.e. IATI-XML)<br /><b>Description</b> Any information you want to add to describe the resource.<br /></div>
+        <div class="field_error" py:if="errors.get('resources', '')">Dataset resource(s) incomplete.</div>
+    </fieldset>
+
+    <fieldset>
+        <legend>Verification and Analysis </legend>
+        <dl>
+            <dt><label class="field_opt" for="activity_period">Activitiy Period</label></dt>
+            <dd>
+                <input class="short" id="activity_period-from" name="activity_period-from" type="text" value="${data.get('activity_period-from', '')}" /> -
+                <input class="short" id="activity_period-to" name="activity_period-to" type="text" value="${data.get('activity_period-to', '')}" />
+            </dd>
+            <dd class="instructions basic">Acceptable formats: 'DD/MM/YYYY HH:MM', 'DD/MM/YYYY', 'MM/YYYY', 'YYYY'.</dd>
+
+            <dt><label class="field_opt" for="activity_count">Num. Activities</label></dt>
+            <dd><input id="activity_count" name="activity_count" size="40" type="text" value="${data.get('activity_count', '')}"/></dd>
+
+            <dt><label class="field_opt" for="archive_file">Archive</label></dt>
+            <dd><input id="archive_file" name="archive_file" size="40" type="checkbox" py:attrs="{'checked': 'checked' if data.get('archive_file','') == 'yes' else None}" /></dd>
+            <py:choose>
+                <py:when test="c.is_sysadmin">
+                <dt><label class="field_opt" for="verified">Verification</label></dt>
+                <dd><input id="verified" name="verified" size="40" type="checkbox" py:attrs="{'checked': 'checked' if data.get('verified','') == 'yes' else None}" /></dd>
+
+                <dt><label class="field_opt" for="state">State</label></dt>
+                <dd>
+                    <select id="state" name="state">
+                        <option py:attrs="{'selected': 'selected' if data.get('state') == 'active' else None}" value="active">active</option>
+                        <option py:attrs="{'selected': 'selected' if data.get('state') == 'deleted' else None}" value="deleted">deleted</option>
+                        <option py:attrs="{'selected': 'selected' if data.get('state') == 'pending' else None}" value="pending">pending</option>
+                    </select>
+                </dd>
+                </py:when>
+            </py:choose>
+
+        </dl>
+    </fieldset>
+
+    <hr />
+    <label for="log_message">Edit summary (briefly describe the changes you have made)</label>
+    <textarea id="log_message" name="log_message" class="short wide"></textarea>
+
+    <div class="ckan-logged-in">
+    <p>Author: ${c.author}</p>
+    </div>
+
+    <div class="submit">
+    <input id="save" name="save" type="submit" value="Save" />
+    </div>
+    <p class="hints">
+    <strong>Important:</strong> By submitting content, you agree to release your contributions
+    under the open license specified on the <a href="/license">license page</a>. Please <strong>refrain</strong> from editing this page if you are <strong>not</strong> happy to do this.
+    </p>
+
+</form>


--- a/ckanext/iati/templates/package/read_core.html	Thu Sep 01 13:59:28 2011 +0100
+++ b/ckanext/iati/templates/package/read_core.html	Mon Sep 12 16:48:18 2011 +0100
@@ -34,7 +34,7 @@
     <div class="notes" py:if="str(c.pkg_notes_formatted).strip()">
       ${c.pkg_notes_formatted}
     </div>
-    
+
     <py:choose test=""><table py:when="c.pkg.resources" width="100%"><tr><th>URL</th><th>Format</th><th>Preview</th></tr>
@@ -50,7 +50,7 @@
       </table><table py:otherwise=""><tr><th>Resources</th><td>None given for this package.</td></tr></table></py:choose>
-      
+
     <py:def function="details_item(label, value)"><tr py:if="value is not None and len(value) > 0"><td class="package-label">
@@ -61,18 +61,18 @@
           </td></tr></py:def>
-    
+
     <py:def function="details_asbool(label, value)"><tr><td class="package-label">
             ${label}
           </td><td class="package-details">
-            ${'yes' if value else 'no'}
+            ${'yes' if value == 'yes' else 'no'}
           </td></tr></py:def>
-    
+
     <py:def function="details_list(label, values)"><tr py:if="values is not None and len(values) > 0"><td class="package-label" valign="top">
@@ -95,14 +95,14 @@
         ${details_list('Donor types', c.pkg.extras.get('donors_type', []))}
         ${details_list('Donor countries', [h.country_name(x) for x in c.pkg.extras.get('donors_country', [])])}
         ${details_item('Recipient Country', h.country_name(c.pkg.extras.get('country', '(Unknown)')))}
-        
+
         ${details_asbool('Verified', c.pkg.extras.get('verified', False))}
         ${details_asbool('Archive File', c.pkg.extras.get('archive_file', False))}
         ${details_item('Department', c.pkg.extras.get('department', '-'))}
-        
+
         ${details_item('Data updated', c.pkg.extras.get('data_updated', '-'))}
         ${details_item('Record updated', c.pkg.extras.get('record_updated', '-'))}
-        
+
         <tr><td class="package-label">
             License
@@ -122,14 +122,18 @@
         </tr></tbody></table>
-    
-    
-    
+
+
+
     <h4>Contents</h4><table width="100%"><tbody>
-        ${details_item('# of Activities', c.pkg.extras.get('activity_count', '-'))}
-        
+        <tr>
+          <td class="package-label"># of Activities</td>
+          <td class="package-details">
+            ${c.pkg.extras.get('activity_count', '-')}</td>
+        </tr>
+
         <tr><td class="package-label">Activity Period</td><td class="package-details">


--- a/setup.py	Thu Sep 01 13:59:28 2011 +0100
+++ b/setup.py	Mon Sep 12 16:48:18 2011 +0100
@@ -31,7 +31,8 @@
       iati_approval = ckanext.iati.approval:IatiGroupApprovalExtension
       iati_group_authz = ckanext.iati.authz:IatiGroupAuthzExtension
       iati_package_authz = ckanext.iati.authz:IatiPackageAuthzExtension
-      
+      iati_forms = ckanext.iati.plugin:IatiForms
+
       [ckan.forms]
       iati_package = ckanext.iati.forms:get_iati_package_fieldset
       iati_group = ckanext.iati.forms:get_iati_group_fieldset


http://bitbucket.org/okfn/ckanextiati/changeset/816a476c85c2/
changeset:   816a476c85c2
branch:      new-forms
user:        amercader
date:        2011-09-13 12:08:00
summary:     [new forms] Move converters to ckan core
affected #:  1 file (716 bytes)

--- a/ckanext/iati/controllers/package_iati.py	Mon Sep 12 16:48:18 2011 +0100
+++ b/ckanext/iati/controllers/package_iati.py	Tue Sep 13 11:08:00 2011 +0100
@@ -11,6 +11,7 @@
                                       ignore,
                                       keep_extras,
                                      )
+from ckan.logic.converters import convert_from_extras, convert_to_extras, date_to_db, date_to_form
 from ckan.lib.navl.dictization_functions import Missing, Invalid
 from ckan.lib.field_types import DateType, DateConvertError
 
@@ -85,36 +86,6 @@
         return [{'id':group.id,'name':group.name, 'title':group.title} for group in groups if group.state==model.State.ACTIVE]
 
 
-def convert_to_extras(key, data, errors, context):
-
-    extras = data.get(('extras',), [])
-    if not extras:
-        data[('extras',)] = extras
-
-    extras.append({'key': key[-1], 'value': data[key]})
-
-def convert_from_extras(key, data, errors, context):
-
-    for data_key, data_value in data.iteritems():
-        if (data_key[0] == 'extras'
-            and data_key[-1] == 'key'
-            and data_value == key[-1]):
-            data[key] = data[('extras', data_key[1], 'value')]
-
-def date_to_db(value, context):
-    try:
-        value = DateType.form_to_db(value)
-    except DateConvertError, e:
-        raise Invalid(str(e))
-    return value
-
-def date_to_form(value, context):
-    try:
-        value = DateType.db_to_form(value)
-    except DateConvertError, e:
-        raise Invalid(str(e))
-    return value
-
 def convert_to_comma_list(value, context):
 
     return ', '.join(json.loads(value))


http://bitbucket.org/okfn/ckanextiati/changeset/b73e7df7b245/
changeset:   b73e7df7b245
branch:      new-forms
user:        amercader
date:        2011-09-13 12:08:26
summary:     [new forms] Minor adjustments in package form
affected #:  2 files (122 bytes)

--- a/ckanext/iati/controllers/package_iati.py	Tue Sep 13 11:08:00 2011 +0100
+++ b/ckanext/iati/controllers/package_iati.py	Tue Sep 13 11:08:26 2011 +0100
@@ -99,9 +99,11 @@
     return 'yes' if not isinstance(value, Missing) else 'no'
 
 def integer(value,context):
-    try:
-        value = int(value)
-    except ValueError,e:
-        raise Invalid(str(e))
-    return value
 
+    if not value == '':
+        try:
+            value = int(value)
+        except ValueError,e:
+            raise Invalid(str(e))
+        return value
+


--- a/ckanext/iati/templates/package/form_iati.html	Tue Sep 13 11:08:00 2011 +0100
+++ b/ckanext/iati/templates/package/form_iati.html	Tue Sep 13 11:08:26 2011 +0100
@@ -64,7 +64,9 @@
         <dt>Publisher</dt><dd py:if="c.groups_available"><select id="groups__${len(data.get('groups', []))}__id" name="groups__${len(data.get('groups', []))}__id">
-                <option value="" >(None)</option>
+                <dd py:if="data.get('groups',[])">
+                    <option value="" >(None)</option>
+                </dd><py:for each="group in c.groups_available"><option value="${group['id']}" >${group['title']}</option></py:for>


http://bitbucket.org/okfn/ckanextiati/changeset/b966e7deb66d/
changeset:   b966e7deb66d
branch:      new-forms
user:        amercader
date:        2011-09-14 14:12:21
summary:     [new forms] Change order of validators
affected #:  1 file (10 bytes)

--- a/ckanext/iati/controllers/package_iati.py	Tue Sep 13 11:08:26 2011 +0100
+++ b/ckanext/iati/controllers/package_iati.py	Wed Sep 14 13:12:21 2011 +0100
@@ -54,9 +54,9 @@
         schema.update({
             'department': [convert_from_extras,ignore_missing],
             'country': [convert_from_extras, ignore_missing],
-            'donors': [convert_from_extras, convert_to_comma_list, ignore_missing],
-            'donors_type': [convert_from_extras, convert_to_comma_list, ignore_missing],
-            'donors_country': [convert_from_extras, convert_to_comma_list, ignore_missing],
+            'donors': [ignore_missing, convert_from_extras, convert_to_comma_list],
+            'donors_type': [ignore_missing, convert_from_extras, convert_to_comma_list],
+            'donors_country': [ignore_missing, convert_from_extras, convert_to_comma_list],
             'record_updated': [convert_from_extras,ignore_missing, date_to_form],
             'data_updated': [convert_from_extras,ignore_missing, date_to_form],
             'activity_period-from': [convert_from_extras,ignore_missing, date_to_form],
@@ -87,11 +87,11 @@
 
 
 def convert_to_comma_list(value, context):
-
+     
     return ', '.join(json.loads(value))
 
 def convert_from_comma_list(value, context):
-
+     
     return [x.strip() for x in value.split(',') if len(x)]
 
 def checkbox_value(value,context):


http://bitbucket.org/okfn/ckanextiati/changeset/c2b5d5745b08/
changeset:   c2b5d5745b08
branch:      new-forms
user:        amercader
date:        2011-09-14 14:27:10
summary:     [new forms] Add new group form
affected #:  3 files (12.6 KB)

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/group_iati.py	Wed Sep 14 13:27:10 2011 +0100
@@ -0,0 +1,76 @@
+from ckan.lib.base import c
+from ckan import model
+
+from ckan.lib.navl.validators import ignore_missing, not_empty
+
+from ckan.logic.schema import group_form_schema
+from ckan.logic.converters import convert_from_extras, convert_to_extras
+from ckan.controllers.group import GroupController
+
+PUBLISHER_TYPES = ['Primary source', 'Secondary source']
+
+
+class GroupIatiController(GroupController):
+
+    group_form = 'group/form_iati.html'
+
+    def _setup_template_variables(self, context):
+
+        super(GroupIatiController,self)._setup_template_variables(context)
+
+        c.licences = [('', '')] + model.Package.get_license_options()
+
+    def _form_to_db_schema(self):
+        schema = group_form_schema()
+        schema.update({
+            'type': [not_empty, publisher_type_validator, convert_to_extras],
+            'license_id': [convert_to_extras],
+            'publisher_segmentation': [unicode, convert_to_extras, ignore_missing],
+            'publisher_ui': [unicode, convert_to_extras, ignore_missing],
+            'publisher_frequency': [unicode, convert_to_extras, ignore_missing],
+            'publisher_thresholds': [unicode, convert_to_extras, ignore_missing],
+            'publisher_units': [unicode, convert_to_extras, ignore_missing],
+            'publisher_contact': [unicode, convert_to_extras, ignore_missing],
+            'publisher_agencies': [unicode, convert_to_extras, ignore_missing],
+            'publisher_field_exclusions': [unicode, convert_to_extras, ignore_missing],
+            'publisher_description': [unicode, convert_to_extras, ignore_missing],
+            'publisher_record_exclusions': [unicode, convert_to_extras, ignore_missing],
+            'publisher_timeliness': [unicode, convert_to_extras, ignore_missing],
+            'publisher_refs': [unicode, convert_to_extras, ignore_missing],
+            'publisher_constraints': [unicode, convert_to_extras, ignore_missing],
+            'publisher_data_quality': [unicode, convert_to_extras, ignore_missing],
+        })
+
+        return schema
+
+    def _db_to_form_schema(self):
+        schema = group_form_schema()
+        schema.update({
+            'type': [convert_from_extras],
+            'license_id': [convert_from_extras],
+            'publisher_segmentation': [convert_from_extras],
+            'publisher_ui': [convert_from_extras],
+            'publisher_frequency': [convert_from_extras],
+            'publisher_thresholds': [convert_from_extras],
+            'publisher_units': [convert_from_extras],
+            'publisher_contact': [convert_from_extras],
+            'publisher_agencies': [convert_from_extras],
+            'publisher_field_exclusions': [convert_from_extras],
+            'publisher_description': [convert_from_extras],
+            'publisher_record_exclusions': [convert_from_extras],
+            'publisher_timeliness': [convert_from_extras],
+            'publisher_refs': [convert_from_extras],
+            'publisher_constraints': [convert_from_extras],
+            'publisher_data_quality': [convert_from_extras],
+        })
+
+        return schema
+
+    def _check_data_dict(self, data_dict):
+        return
+
+
+def publisher_type_validator(value,context):
+    if not value in PUBLISHER_TYPES:
+        raise Invalid('Unknown publisher type, allowed values: [%s]' % ', '.join(PUBLISHER_TYPES))
+    return value


--- a/ckanext/iati/plugin.py	Wed Sep 14 13:12:21 2011 +0100
+++ b/ckanext/iati/plugin.py	Wed Sep 14 13:27:10 2011 +0100
@@ -41,6 +41,9 @@
         map.connect('/dataset/new', controller=package_controller, action='new')
         map.connect('/dataset/edit/{id}', controller=package_controller, action='edit')
 
+        map.connect('/group/new', controller=group_controller, action='new')
+        map.connect('/group/edit/{id}', controller=group_controller, action='edit')
+
         return map
 
     def after_map(self, map):


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/templates/group/form_iati.html	Wed Sep 14 13:27:10 2011 +0100
@@ -0,0 +1,151 @@
+<form id="group-edit" action="" method="post" 
+    py:attrs="{'class':'has-errors'} if errors else {}"
+    xmlns:i18n="http://genshi.edgewall.org/i18n"
+    xmlns:py="http://genshi.edgewall.org/"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+    <div class="error-explanation" py:if="error_summary">
+        <h2>Errors in form</h2>
+        <p>The form contains invalid entries:</p>
+        <ul>
+            <li py:for="key, error in error_summary.items()">${"%s: %s" % (key, error)}</li>
+        </ul>
+    </div>
+    
+    <fieldset>
+        <legend>Basic information</legend> 
+        <dl>
+            <dt><label class="field_req" for="name">Unique Name (required) *</label></dt> 
+            <dd><input id="name" name="name" type="text" value="${data.get('name', '')}" /></dd>
+            <dd class="field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</dd>
+            <dd class="instructions basic"><strong>Unique identifier</strong> for group.<br/>2+ chars, lowercase, using only 'a-z0-9' and '-_'</dd> 
+            <script type="text/javascript"> 
+                //<![CDATA[
+                $(document).ready(function () { if (!$('#preview').length) { $("#name").focus();}});
+                //]]>
+            </script>
+            
+            <dt><label class="field_opt" for="title">Title</label></dt> 
+            <dd><input id="title" name="title" type="text" value="${data.get('title', '')}" /></dd>
+            <dd class="field_error" py:if="errors.get('title', '')">${errors.get('title', '')}</dd>
+
+            <dt><label class="field_req" for="type">Source</label></dt> 
+            <dd>
+                <select id="type" name="type">
+                    <option value="Primary source" py:attrs="{'selected': 'selected' if data.get('type', '') == 'Primary source' else None}">Primary source</option>
+                    <option value="Secondary source" py:attrs="{'selected': 'selected' if data.get('type', '') == 'Secondary source' else None}">Secondary source</option>
+                </select>
+            </dd> 
+            
+            <dt><label class="field_opt" for="license_id">License</label></dt> 
+            <dd>
+                <select id="license_id" name="license_id">
+                    <py:for each="licence_desc, licence_id in c.licences">
+                        <option value="${licence_id}" py:attrs="{'selected': 'selected' if data.get('license_id', '') == licence_id else None}" >${licence_desc}</option>
+                    </py:for>
+
+                </select>
+            </dd>
+
+            <py:choose>
+                <py:when test="c.is_sysadmin">
+                    <dt><label class="field_opt" for="state">State</label></dt> 
+                    <dd>
+                        <select id="state" name="state">
+                            <option py:attrs="{'selected': 'selected' if data.get('state') == 'active' else None}" value="active">active</option>
+                            <option py:attrs="{'selected': 'selected' if data.get('state') == 'deleted' else None}" value="deleted">deleted</option>
+                            <option py:attrs="{'selected': 'selected' if data.get('state') == 'pending' else None}" value="pending">pending</option>
+                        </select>
+                    </dd>
+                </py:when>
+            </py:choose>
+
+        </dl>
+    </fieldset>
+
+    <fieldset>
+        <legend>Details</legend> 
+        <dl>
+
+            <dt><label class="field_opt" for="publisher_contact">Contact</label></dt>
+            <dd><textarea id="publisher_contact" name="publisher_contact">${data.get('publisher_contact', '')}</textarea></dd> 
+            <dd class="instructions basic">Contact details for publisher</dd>
+ 
+            <dt><label class="field_opt" for="publisher_description">Description</label></dt> 
+            <dd><textarea id="publisher_description" name="publisher_description">${data.get('publisher_description', '')}</textarea></dd> 
+            <dd class="instructions basic">General description of publisher's role and activities</dd>
+
+            <dt><label class="field_opt" for="publisher_agencies">Organisations / agencies covered</label></dt> 
+            <dd><textarea id="publisher_agencies" name="publisher_agencies">${data.get('publisher_agencies', '')}</textarea></dd> 
+            <dd class="instructions basic">Whose activities does this publisher publish?</dd>
+
+            <dt><label class="field_opt" for="publisher_timeliness">Timeliness of Data</label></dt> 
+            <dd><textarea id="publisher_timeliness" name="publisher_timeliness">${data.get('publisher_timeliness','')}</textarea></dd> 
+            <dd class="instructions basic">How up do date is the data when published?</dd> 
+
+            <dt><label class="field_opt" for="publisher_frequency">Frequency of publication</label></dt> 
+            <dd><textarea id="publisher_frequency" name="publisher_frequency">${data.get('publisher_frequency','')}</textarea></dd>
+            <dd class="instructions basic">How often is IATI data refreshed? Monthly/Quarterly?</dd>
+
+            <dt><label class="field_opt" for="publisher_units">Units of Aid</label></dt>
+            <dd><textarea id="publisher_units" name="publisher_units">${data.get('publisher_units','')}</textarea></dd> 
+            <dd class="instructions basic">A description of any hierarchical reporting units used and how they are applied</dd>
+
+            <dt><label class="field_opt" for="publisher_segmentation">Segmentation of Published Data</label></dt> 
+            <dd><textarea id="publisher_segmentation" name="publisher_segmentation">${data.get('publisher_segmentation','')}</textarea></dd>
+            <dd class="instructions basic">Is IATI data published by country, regions?</dd>
+
+            <dt><label class="field_opt" for="publisher_refs">Data Definitions and References</label></dt> 
+            <dd><textarea id="publisher_refs" name="publisher_refs">${data.get('publisher_refs','')}</textarea></dd> 
+            <dd class="instructions basic">Links to guides, explanations, codelists on the publisher's own site that clarify their data</dd> 
+
+            <dt><label class="field_opt" for="publisher_field_exclusions">Field Exclusions</label></dt> 
+            <dd><textarea id="publisher_field_exclusions" name="publisher_field_exclusions">${data.get('publisher_field_exclusions','')}</textarea></dd> 
+            <dd class="instructions basic">What fields does the publisher never use - and for what reason</dd> 
+
+            <dt><label class="field_opt" for="publisher_record_exclusions">Record Exclusions</label></dt> 
+            <dd><textarea id="publisher_record_exclusions" name="publisher_record_exclusions">${data.get('publisher_record_exclusions','')}</textarea></dd> 
+            <dd class="instructions basic">What are the policies for excluding particular activities, or parts of an activity's data?</dd> 
+
+            <dt><label class="field_opt" for="publisher_thresholds">Thresholds</label></dt> 
+            <dd><textarea id="publisher_thresholds" name="publisher_thresholds">${data.get('publisher_thresholds','')}</textarea></dd> 
+            <dd class="instructions basic">What are the thresholds below which data or whole activities are not published?</dd> 
+
+            <dt><label class="field_opt" for="publisher_constraints">Other Constraints</label></dt> 
+            <dd><textarea id="publisher_constraints" name="publisher_constraints">${data.get('publisher_constraints','')}</textarea></dd> 
+            <dd class="instructions basic">Other policies that restrict full compliance with the standard</dd> 
+            
+            <dt><label class="field_opt" for="publisher_data_quality">Data Quality</label></dt> 
+            <dd><textarea id="publisher_data_quality" name="publisher_data_quality">${data.get('publisher_data_quality','')}</textarea></dd> 
+            <dd class="instructions basic">Publisher's comment on the status and accuracyof the data - audited/verified, operational/sub to change, etc</dd> 
+             
+            <dt><label class="field_opt" for="publisher_ui">User Interface</label></dt> 
+            <dd><textarea id="publisher_ui" name="publisher_ui">${data.get('publisher_ui','')}</textarea></dd> 
+            <dd class="instructions basic">Link to publisher's own public user activity interface</dd> 
+        </dl>
+    </fieldset>
+
+    <fieldset>
+        <legend>Records</legend>
+        <dl py:if="data.get('packages')">
+            <py:for each="num, package in enumerate(data.get('packages'))">
+                <dt><input checked="checked" id="datasets__${num}__name" name="packages__${num}__name" type="checkbox" value="${package['name']}"/></dt>
+                <dd><label for="packages__${num}__name">${package['name']}</label></dd>
+            </py:for>
+        </dl>
+        <p py:if="not data.get('packages')">There are no records currently in this group.</p>
+    </fieldset>
+    
+    <fieldset>
+        <legend>Add records</legend>
+        <dl>
+            <dt><label class="field_opt" for="packages__${len(data.get('packages', []))}__name">Dataset</label></dt>
+            <dd><input class="autocomplete-dataset" id="datasets__${len(data.get('packages', []))}__name" name="packages__${len(data.get('packages', []))}__name" type="text" /></dd>
+        </dl>
+    </fieldset>
+
+    <div class="submit">
+        <input id="save" name="save" type="submit" value="Save" />
+    </div>
+
+</form>


http://bitbucket.org/okfn/ckanextiati/changeset/54b195a61bab/
changeset:   54b195a61bab
branch:      new-forms
user:        amercader
date:        2011-09-14 14:52:49
summary:     [new forms] Remove old forms
affected #:  6 files (12 bytes)

--- a/ckanext/iati/forms/__init__.py	Wed Sep 14 13:27:10 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-
-from package import get_package_fieldset as get_iati_package_fieldset
-from group import get_group_fieldset as get_iati_group_fieldset


--- a/ckanext/iati/forms/countries.py	Wed Sep 14 13:27:10 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-# -*- coding: UTF-8 -*-
-
-# This Python file uses the following encoding: utf-8
-# Country and regions names and codes sent by Bill Anderson on 2011-06-28
-
-COUNTRIES = (("", u"(No country assigned)"), ("AF",u"Afghanistan"),("AX",u"Åland Islands"),("AL",u"Albania"),("DZ",u"Algeria"),("AS",u"American Samoa"),("AD",u"Andorra"),("AO",u"Angola"),("AI",u"Anguilla"),("AQ",u"Antarctica"),("AG",u"Antigua And Barbuda"),("AR",u"Argentina"),("AM",u"Armenia"),("AW",u"Aruba"),("AU",u"Australia"),("AT",u"Austria"),("AZ",u"Azerbaijan"),("BS",u"Bahamas"),("BH",u"Bahrain"),("BD",u"Bangladesh"),("BB",u"Barbados"),("BY",u"Belarus"),("BE",u"Belgium"),("BZ",u"Belize"),("BJ",u"Benin"),("BM",u"Bermuda"),("BT",u"Bhutan"),("BO",u"Bolivia, Plurinational State Of"),("BQ",u"Bonaire, Sint Eustatius And Saba"),("BA",u"Bosnia And Herzegovina"),("BW",u"Botswana"),("BV",u"Bouvet Island"),("BR",u"Brazil"),("IO",u"British Indian Ocean Territory"),("BN",u"Brunei Darussalam"),("BG",u"Bulgaria"),("BF",u"Burkina Faso"),("BI",u"Burundi"),("KH",u"Cambodia"),("CM",u"Cameroon"),("CA",u"Canada"),("CV",u"Cape Verde"),("KY",u"Cayman Islands"),("CF",u"Central African Republic"),("TD",u"Chad"),("CL",u"Chile"),("CN",u"China"),("CX",u"Christmas Island"),("CC",u"Cocos (Keeling) Islands"),("CO",u"Colombia"),("KM",u"Comoros"),("CG",u"Congo"),("CD",u"Congo, The Democratic Republic Of The"),("CK",u"Cook Islands"),("CR",u"Costa Rica"),("CI",u"Côte D'Ivoire"),("HR",u"Croatia"),("CU",u"Cuba"),("CW",u"Curaçao"),("CY",u"Cyprus"),("CZ",u"Czech Republic"),("DK",u"Denmark"),("DJ",u"Djibouti"),("DM",u"Dominica"),("DO",u"Dominican Republic"),("EC",u"Ecuador"),("EG",u"Egypt"),("SV",u"El Salvador"),("GQ",u"Equatorial Guinea"),("ER",u"Eritrea"),("EE",u"Estonia"),("ET",u"Ethiopia"),("FK",u"Falkland Islands (Malvinas)"),("FO",u"Faroe Islands"),("FJ",u"Fiji"),("FI",u"Finland"),("FR",u"France"),("GF",u"French Guiana"),("PF",u"French Polynesia"),("TF",u"French Southern Territories"),("GA",u"Gabon"),("GM",u"Gambia"),("GE",u"Georgia"),("DE",u"Germany"),("GH",u"Ghana"),("GI",u"Gibraltar"),("GR",u"Greece"),("GL",u"Greenland"),("GD",u"Grenada"),("GP",u"Guadeloupe"),("GU",u"Guam"),("GT",u"Guatemala"),("GG",u"Guernsey"),("GN",u"Guinea"),("GW",u"Guinea-Bissau"),("GY",u"Guyana"),("HT",u"Haiti"),("HM",u"Heard Island And Mcdonald Islands"),("VA",u"Holy See (Vatican City State)"),("HN",u"Honduras"),("HK",u"Hong Kong"),("HU",u"Hungary"),("IS",u"Iceland"),("IN",u"India"),("ID",u"Indonesia"),("IR",u"Iran, Islamic Republic Of"),("IQ",u"Iraq"),("IE",u"Ireland"),("IM",u"Isle Of Man"),("IL",u"Israel"),("IT",u"Italy"),("JM",u"Jamaica"),("JP",u"Japan"),("JE",u"Jersey"),("JO",u"Jordan"),("KZ",u"Kazakhstan"),("KE",u"Kenya"),("KI",u"Kiribati"),("KP",u"Korea, Democratic People's Republic Of"),("KR",u"Korea, Republic Of"),("XK",u"Kosovo"),("KW",u"Kuwait"),("KG",u"Kyrgyzstan"),("LA",u"Lao People's Democratic Republic"),("LV",u"Latvia"),("LB",u"Lebanon"),("LS",u"Lesotho"),("LR",u"Liberia"),("LY",u"Libyan Arab Jamahiriya"),("LI",u"Liechtenstein"),("LT",u"Lithuania"),("LU",u"Luxembourg"),("MO",u"Macao"),("MK",u"Macedonia, The Former Yugoslav Republic Of"),("MG",u"Madagascar"),("MW",u"Malawi"),("MY",u"Malaysia"),("MV",u"Maldives"),("ML",u"Mali"),("MT",u"Malta"),("MH",u"Marshall Islands"),("MQ",u"Martinique"),("MR",u"Mauritania"),("MU",u"Mauritius"),("YT",u"Mayotte"),("MX",u"Mexico"),("FM",u"Micronesia, Federated States Of"),("MD",u"Moldova, Republic Of"),("MC",u"Monaco"),("MN",u"Mongolia"),("ME",u"Montenegro"),("MS",u"Montserrat"),("MA",u"Morocco"),("MZ",u"Mozambique"),("MM",u"Myanmar"),("NA",u"Namibia"),("NR",u"Nauru"),("NP",u"Nepal"),("NL",u"Netherlands"),("NC",u"New Caledonia"),("NZ",u"New Zealand"),("NI",u"Nicaragua"),("NE",u"Niger"),("NG",u"Nigeria"),("NU",u"Niue"),("NF",u"Norfolk Island"),("MP",u"Northern Mariana Islands"),("NO",u"Norway"),("OM",u"Oman"),("PK",u"Pakistan"),("PW",u"Palau"),("PS",u"Palestinian Territory, Occupied"),("PA",u"Panama"),("PG",u"Papua New Guinea"),("PY",u"Paraguay"),("PE",u"Peru"),("PH",u"Philippines"),("PN",u"Pitcairn"),("PL",u"Poland"),("PT",u"Portugal"),("PR",u"Puerto Rico"),("QA",u"Qatar"),("RE",u"Réunion"),("RO",u"Romania"),("RU",u"Russian Federation"),("RW",u"Rwanda"),("BL",u"Saint Barthélemy"),("SH",u"Saint Helena, Ascension And Tristan Da Cunha"),("KN",u"Saint Kitts And Nevis"),("LC",u"Saint Lucia"),("MF",u"Saint Martin (French Part)"),("PM",u"Saint Pierre And Miquelon"),("VC",u"Saint Vincent And The Grenadines"),("WS",u"Samoa"),("SM",u"San Marino"),("ST",u"Sao Tome And Principe"),("SA",u"Saudi Arabia"),("SN",u"Senegal"),("RS",u"Serbia"),("SC",u"Seychelles"),("SL",u"Sierra Leone"),("SG",u"Singapore"),("SX",u"Sint Maarten (Dutch Part)"),("SK",u"Slovakia"),("SI",u"Slovenia"),("SB",u"Solomon Islands"),("SO",u"Somalia"),("ZA",u"South Africa"),("GS",u"South Georgia And The South Sandwich Islands"),("ES",u"Spain"),("LK",u"Sri Lanka"),("SD",u"Sudan"),("SR",u"Suriname"),("SJ",u"Svalbard And Jan Mayen"),("SZ",u"Swaziland"),("SE",u"Sweden"),("CH",u"Switzerland"),("SY",u"Syrian Arab Republic"),("TW",u"Taiwan, Province Of China"),("TJ",u"Tajikistan"),("TZ",u"Tanzania, United Republic Of"),("TH",u"Thailand"),("TL",u"Timor-Leste"),("TG",u"Togo"),("TK",u"Tokelau"),("TO",u"Tonga"),("TT",u"Trinidad And Tobago"),("TN",u"Tunisia"),("TR",u"Turkey"),("TM",u"Turkmenistan"),("TC",u"Turks And Caicos Islands"),("TV",u"Tuvalu"),("UG",u"Uganda"),("UA",u"Ukraine"),("AE",u"United Arab Emirates"),("GB",u"United Kingdom"),("US",u"United States"),("UM",u"United States Minor Outlying Islands"),("UY",u"Uruguay"),("UZ",u"Uzbekistan"),("VU",u"Vanuatu"),("VE",u"Venezuela, Bolivarian Republic Of"),("VN",u"Viet Nam"),("VG",u"Virgin Islands, British"),("VI",u"Virgin Islands, U.S."),("WF",u"Wallis And Futuna"),("EH",u"Western Sahara"),("YE",u"Yemen"),("ZM",u"Zambia"),("ZW",u"Zimbabwe"),("298",u"Africa"),("189",u"Africa - North"),("289",u"Africa - Sub-Saharan"),("089",u"Europe"),("498",u"America"),("389",u"America - North & Central"),("489",u"America - South"),("380",u"West Indies"),("589",u"Middle East"),("798",u"Asia"),("619",u"Asia - Central"),("679",u"Asia - South"),("689",u"Asia - South & Central"),("789",u"Asia - Far East"),("889",u"Oceania"),("998",u"Unspecified"),)


--- a/ckanext/iati/forms/group.py	Wed Sep 14 13:27:10 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-import formalchemy
-from formalchemy import helpers as fa_h
-import ckan.lib.helpers as h
-
-from ckan.forms.builder import FormBuilder
-from sqlalchemy.util import OrderedDict
-from pylons.i18n import _, ungettext, N_, gettext
-import ckan.model as model
-import ckan.forms.common as common
-import ckan.forms.group as group
-from ckan.forms.common import ExtrasField, PackageNameField, SelectExtraField
-from ckan.forms.common import TextAreaExtraField
-from ckan.lib.helpers import literal
-
-from group_schema import fields
-
-
-__all__ = ['get_group_dict', 'edit_group_dict']
-
-
-def build_group_form(is_admin=False, with_packages=False):
-    PUBLISHER_TYPES = [_("Primary source"),
-                       _("Secondary source")
-                       ]
-    publisher_record_fields = fields
-    builder = FormBuilder(model.Group)
-    builder.set_field_text('name', 'Unique Name (required)',
-            literal("<br/><strong>Unique identifier</strong> for group.<br/>2+ chars, lowercase, using only 'a-z0-9' and '-_'"))
-    builder.set_field_option('name', 'validate', common.group_name_validator)
-    builder.set_field_option('state', 'dropdown', {'options': model.State.all})
-    builder.add_field(SelectExtraField('type',
-                                       options=PUBLISHER_TYPES,
-                                       allow_empty=False))
-    builder.set_field_text('type', 'Source')
-    
-    builder.add_field(SelectExtraField('license_id',
-                                       options=[('', None)] + model.Package.get_license_options()))
-    builder.set_field_text('license_id', _('License'))
-
-    for name, title, description in publisher_record_fields:
-        builder.add_field(TextAreaExtraField(name))
-        builder.set_field_text(name, title, description)
-    displayed_fields = ['name', 'title', 'type', 'license_id'] +\
-                       [x[0] for x in publisher_record_fields]
-    from ckan.authz import Authorizer
-    from ckan.lib.base import c
-    if Authorizer.is_sysadmin(c.user):
-        displayed_fields.append('state')
-
-    if with_packages:
-        builder.add_field(group.PackagesField('packages'))
-        displayed_fields.append('packages')
-    builder.set_displayed_fields(OrderedDict([('Details', displayed_fields)]))
-    builder.set_label_prettifier(common.prettify)
-    return builder
-
-
-def get_group_fieldset(is_admin=False, combined=False):
-    return build_group_form(is_admin=is_admin,
-                            with_packages=combined).get_fieldset()
-


--- a/ckanext/iati/forms/group_schema.py	Wed Sep 14 13:27:10 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-fields = (('publisher_contact',
-           'Contact',
-           'Email or URL for publisher'),
-          ('publisher_description',
-           'Description',
-           "General description of Publisher's role and activities"),
-          ('publisher_agencies',
-           'Organisations / agencies covered',
-           'Whose activities does this publisher publish?'),
-          ('publisher_timeliness',
-           'Timeliness of Data',
-           'How up do date is the data when published?'),
-          ('publisher_frequency',
-           'Frequency of publication',
-           'How often is IATI data refreshed?  Monthly/Quarterly?'),
-          ('publisher_units',
-           'Units of Aid',
-           'A description of any hierarchical reporting units used and how they are applied'),
-          ('publisher_segmentation',
-           'Segmentation of Published Data',
-           'Is IATI data published by country, regions?'),
-          ('publisher_refs',
-           'Data Definitions and References',
-           "Links to guides, explanations, codelists on the publisher's own site that clarify their data"),
-          ('publisher_field_exclusions',
-           'Field Exclusions',
-           'What fields does the publisher never use - and for what reason'),
-          ('publisher_record_exclusions',
-           'Record Exclusions',
-           "What are the policies for excluding particular activities, or parts of an activity's data?"),     
-          ('publisher_thresholds',
-           'Thresholds',
-           'What are the thresholds below which data or whole activities are not published?'),
-          ('publisher_constraints',
-           'Other Constraints',
-           'Other policies that restrict full compliance with the standard'),
-          ('publisher_data_quality',
-           'Data Quality',
-           "Publisher's comment on the status and accuracyof the data - audited/verified, operational/sub to change, etc"),
-          ('publisher_ui',
-           'User Interface',
-           "Link to publisher's own public user activity interface"),
-          )
-publisher_record_fields = fields


--- a/ckanext/iati/forms/package.py	Wed Sep 14 13:27:10 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,226 +0,0 @@
-import formalchemy
-from formalchemy import helpers as h
-from sqlalchemy.util import OrderedDict
-from pylons.i18n import _, ungettext, N_, gettext
-
-from ckan.lib.helpers import literal
-import ckan.forms.common as common
-from ckan.forms.common import fa_h, TextExtraField, RegExRangeValidatingField, GroupSelectField
-import ckan.model as model
-import ckan.forms.package as package
-from ckan.lib import field_types
-
-from countries import COUNTRIES
-
-__all__ = ['get_iati_fieldset']
-
-class CommaListExtraField(RegExRangeValidatingField):
-    '''A form field for two TextType fields, representing a range,
-    stored in 'extra' fields.'''
-    def get_configured(self):
-        field = self.CommaListField(self.name).with_renderer(self.CommaListRenderer)
-        return RegExRangeValidatingField.get_configured(self, field)
-
-    class CommaListField(formalchemy.Field):
-        def sync(self):
-            if not self.is_readonly():
-                pkg = self.model
-                vals = self._deserialize() or []
-                #vals = [v.strip()for v in vals.split(',') if len(v.strip())]
-                pkg.extras[self.name] = vals
-
-    class CommaListRenderer(formalchemy.fields.FieldRenderer):
-        def _get_value(self):
-            if self.value:
-                return self.value
-            extras = self.field.parent.model.extras
-            return extras.get(self.field.name, [])
-
-        def render(self, **kwargs):
-            values = self._get_value()
-            html = fa_h.text_field(self.name, value=', '.join(values), **kwargs)
-            return html
-
-        def render_readonly(self, **kwargs):
-            val = ', '.join(self._get_value())
-            return field_readonly_renderer(self.field.key, val_str)
-
-        def _serialized_value(self):
-            param_val = self.params.get(self.name, u'')
-            return [v.strip()for v in param_val.split(',') if len(v.strip())]
-
-        def deserialize(self):
-            return self._serialized_value()
-
-
-class SelectExtraField(TextExtraField):
-    '''A form field for text from from a list of options, that is
-    stored in an "extras" field.'''
-    def __init__(self, name, options):
-        self.options = options[:]
-        # ensure options have key and value, not just a value
-        for i, option in enumerate(self.options):
-            if not isinstance(option, (tuple, list)):
-                self.options[i] = (option, option)
-        super(SelectExtraField, self).__init__(name)
-
-    def get_configured(self):
-        return self.TextExtraField(self.name, options=self.options).with_renderer(self.SelectRenderer)
-
-    class SelectRenderer(formalchemy.fields.FieldRenderer):
-        def _get_value(self, **kwargs):
-            extras = self.field.parent.model.extras
-            return unicode(kwargs.get('selected', '') or self.value or extras.get(self.field.name, ''))
-
-        def render(self, options, **kwargs):
-            selected = self._get_value()
-            options = [('', '')] + options
-            option_keys = [key for value, key in options]
-            if selected in option_keys:
-                select_field_selected = selected
-            else:
-                select_field_selected = u''
-            fa_version_nums = formalchemy.__version__.split('.')
-            # Requires FA 1.3.2 onwards for this select i/f
-            html = literal(fa_h.select(self.name, select_field_selected, options, **kwargs))
-                
-            return html
-
-        def render_readonly(self, **kwargs):
-            return field_readonly_renderer(self.field.key, self._get_value())
-
-        def _serialized_value(self):
-            main_value = self.params.get(self.name, u'')
-            return main_value
-
-class AtLeastOneGroupSelectField(GroupSelectField):
-    
-    def get_configured(self):
-        field = self.GroupSelectionField(self.name, self.allow_empty).with_renderer(self.GroupSelectEditRenderer)
-        field.set(multiple=self.multiple)
-        field = field.validate(self.validate_groups)
-        field.user_editable_groups = self.user_editable_groups
-        return field
-        
-    def validate_groups(self, val, field):
-        if len(val) < 1:
-            raise formalchemy.ValidationError(_("Need at least one publishing entity assigned"))
-
-            
-# Setup the fieldset
-def build_package_iati_form(is_admin=False, user_editable_groups=None, **kwargs):
-    builder = package.build_package_form(is_admin=is_admin, 
-                                         user_editable_groups=user_editable_groups, **kwargs)
-    
-    # IATI specifics
-    
-    #Publishing Entity: 
-    builder.set_field_text('groups', _('Publisher'))
-    builder.add_field(AtLeastOneGroupSelectField('groups', allow_empty=False, 
-                      user_editable_groups=user_editable_groups))
-    
-    #builder.add_field(common.TextExtraField('publisher'))
-    #builder.set_field_text('publisher', _('Publishing entity'))
-    
-    #Publishing Entity Type: (Donor, Recipient, Community Data..)
-    #builder.add_field(SelectExtraField('publisher_type', options=PUBLISHER_TYPES))
-    #builder.set_field_text('publisher_type', _('Publishing entity type'))
-    
-    #Donor (TODO: Generate from crawler)   
-    # Editable List, CSV? 
-    builder.add_field(CommaListExtraField('donors'))
-    builder.set_field_text('donors', _('Donors'), "Separate multiple entries using commas.")
-    
-    builder.add_field(CommaListExtraField('donors_type'))
-    builder.set_field_text('donors_type', _('Donor type'), "Separate multiple entries using commas.")
-
-    builder.add_field(CommaListExtraField('donors_country'))
-    builder.set_field_text('donors_country', _('Donor country'), "Separate multiple entries using commas.")
-    
-    # TODO: Enforce validation
-    countries = [(v, k) for k, v in COUNTRIES]
-    builder.add_field(SelectExtraField('country', options=countries))
-    builder.set_field_text('country', _('Recipient country'))
-    
-    #Verification status: enumeration of statuses (checked, not checked etc)
-    # TODO: Enforce validation, can probably only be set by admins
-    builder.add_field(common.CheckboxExtraField('verified'))
-    builder.set_field_text('verified', _('Verification'))
-    
-    builder.add_field(common.CheckboxExtraField('archive_file'))
-    builder.set_field_text('archive_file', _('Archive'))
-    
-    #Activity period: (Generate from crawler) 
-    builder.add_field(common.DateRangeExtraField('activity_period'))
-    builder.set_field_text('activity_period', _('Activitiy Period'))
-    
-    #Resource links: to the actual IATI record
-    #Number of activities: (Generate from crawler) 
-    builder.add_field(common.TextExtraField('activity_count'))
-    builder.set_field_text('activity_count', _('Num. Activities'))
-    
-    #Date record updated:
-    builder.add_field(common.TextExtraField('record_updated'))
-    builder.set_field_text('record_updated', _('Record updated'))
-    
-    #Date data updated: 
-    builder.add_field(common.TextExtraField('data_updated'))
-    builder.set_field_text('data_updated', _('Data updated'))
-    
-    
-    #License: Need this field even if it may be a standard license
-    builder.add_field(common.TextExtraField('license'))
-    builder.set_field_text('license', _('License'))
-    
-    #Department 
-    # TODO: Make this a group property instead? 
-    builder.add_field(common.TextExtraField('department'))
-    builder.set_field_text('department', _('Department'))
-    
-    #Contact   
-    builder.set_field_text('author', _('Contact'))
-    
-    #Contact e-mail
-    builder.set_field_text('author_email', _('Contact e-mail'))
-    
-    #Licence    
-    builder.set_field_text('license_id', _('License'))
-
-    #Resource format    
-    #Resource URL     
-    #Resource ID    
-    #  -- do we have an ID? 
-    
-    # Layout
-    field_groups = OrderedDict([
-        (_('Basic information'), ['name', 'title', 
-                                  'author', 'author_email', 'department',]),
-        (_('Publisher'), ['groups']),
-        (_('Details'), ['country', 'donors', 'donors_type', 'donors_country',
-                        'record_updated', 'data_updated',
-                        'license_id', 'tags', 'notes']),
-        (_('Resources'), ['resources']),
-        (_('Verification and Analysis'), [
-                        'activity_period', 
-                        'activity_count', 'archive_file',
-                              ]),
-        ])
-    if is_admin:
-        field_groups[_('Verification and Analysis')].append('verified')
-        field_groups[_('Verification and Analysis')].append('state')
-    
-    builder.set_displayed_fields(field_groups)
-    
-    return builder
-    # Strings for i18n:
-    [_('External reference'),  _('Date released'), _('Date updated'),
-     _('Update frequency'), _('Geographic granularity'),
-     _('Geographic coverage'), _('Temporal granularity'),
-     _('Temporal coverage'), _('Categories'), _('National Statistic'),
-     _('Precision'), _('Taxonomy URL'), _('Department'), _('Agency'), 
-     ]
-
-def get_package_fieldset(is_admin=False, user_editable_groups=None, **kwargs):
-    return build_package_iati_form(is_admin=is_admin, 
-                                   user_editable_groups=user_editable_groups, **kwargs).get_fieldset()
-    


--- a/ckanext/iati/patch.py	Wed Sep 14 13:27:10 2011 +0100
+++ b/ckanext/iati/patch.py	Wed Sep 14 13:52:49 2011 +0100
@@ -1,13 +1,13 @@
 import logging
 import re
 
-from forms.countries import COUNTRIES
+from controllers.countries import COUNTRIES
 import ckan.lib.helpers as h
 import ckan.authz as authz
 from ckan.lib.base import *
 from ckan.model import Package
 
-from ckanext.iati.forms.group_schema import fields
+from ckanext.iati.controllers.group_schema import fields
 
 log = logging.getLogger(__name__)
 


http://bitbucket.org/okfn/ckanextiati/changeset/ab4c12e22ceb/
changeset:   ab4c12e22ceb
branch:      new-forms
user:        amercader
date:        2011-09-14 16:42:33
summary:     Update i18n
affected #:  1 file (5 bytes)

--- a/i18n/en/ckan.po	Wed Sep 14 13:52:49 2011 +0100
+++ b/i18n/en/ckan.po	Wed Sep 14 15:42:33 2011 +0100
@@ -930,7 +930,7 @@
 
 #: ckan/templates/layout_base.html:99
 #: ckan/templates/package/search.html:14
-msgid "Add a package"
+msgid "Add a Dataset"
 msgstr "Add a record"
 
 #: ckan/templates/layout_base.html:101
@@ -1186,7 +1186,7 @@
 msgstr "Publishers History"
 
 #: ckan/templates/group/index.html:6
-msgid "Groups of Data Packages"
+msgid "Groups of Datasets"
 msgstr "Publishers"
 
 #: ckan/templates/group/index.html:11


http://bitbucket.org/okfn/ckanextiati/changeset/0bacf6bba669/
changeset:   0bacf6bba669
branch:      new-forms
user:        amercader
date:        2011-09-14 16:43:57
summary:     [new forms] Add missing file
affected #:  1 file (2.1 KB)

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/group_schema.py	Wed Sep 14 15:43:57 2011 +0100
@@ -0,0 +1,44 @@
+fields = (('publisher_contact',
+           'Contact',
+           'Email or URL for publisher'),
+          ('publisher_description',
+           'Description',
+           "General description of Publisher's role and activities"),
+          ('publisher_agencies',
+           'Organisations / agencies covered',
+           'Whose activities does this publisher publish?'),
+          ('publisher_timeliness',
+           'Timeliness of Data',
+           'How up do date is the data when published?'),
+          ('publisher_frequency',
+           'Frequency of publication',
+           'How often is IATI data refreshed?  Monthly/Quarterly?'),
+          ('publisher_units',
+           'Units of Aid',
+           'A description of any hierarchical reporting units used and how they are applied'),
+          ('publisher_segmentation',
+           'Segmentation of Published Data',
+           'Is IATI data published by country, regions?'),
+          ('publisher_refs',
+           'Data Definitions and References',
+           "Links to guides, explanations, codelists on the publisher's own site that clarify their data"),
+          ('publisher_field_exclusions',
+           'Field Exclusions',
+           'What fields does the publisher never use - and for what reason'),
+          ('publisher_record_exclusions',
+           'Record Exclusions',
+           "What are the policies for excluding particular activities, or parts of an activity's data?"),     
+          ('publisher_thresholds',
+           'Thresholds',
+           'What are the thresholds below which data or whole activities are not published?'),
+          ('publisher_constraints',
+           'Other Constraints',
+           'Other policies that restrict full compliance with the standard'),
+          ('publisher_data_quality',
+           'Data Quality',
+           "Publisher's comment on the status and accuracyof the data - audited/verified, operational/sub to change, etc"),
+          ('publisher_ui',
+           'User Interface',
+           "Link to publisher's own public user activity interface"),
+          )
+publisher_record_fields = fields


http://bitbucket.org/okfn/ckanextiati/changeset/9c3782833010/
changeset:   9c3782833010
branch:      new-forms
user:        amercader
date:        2011-09-14 16:44:31
summary:     [new forms] Style changes in forms
affected #:  4 files (325 bytes)

--- a/ckanext/iati/public/css/overrides.css	Wed Sep 14 15:43:57 2011 +0100
+++ b/ckanext/iati/public/css/overrides.css	Wed Sep 14 15:44:31 2011 +0100
@@ -560,6 +560,21 @@
   color:#536FA1;
 }
 
+/*-------- Form errors --------*/
+
+.error-explanation {
+    border: 1px solid red;
+    background-color: #FFE0E0;
+    padding: 0.5em;
+    margin-bottom: 1em;
+}
+
+.field_error{
+    margin-top: 0;
+    color: red
+}
+
+
 /*-------- Flash messages --------*/
 
 /*-------- Wordpress Specific --------*/


--- a/ckanext/iati/templates/group/form_iati.html	Wed Sep 14 15:43:57 2011 +0100
+++ b/ckanext/iati/templates/group/form_iati.html	Wed Sep 14 15:44:31 2011 +0100
@@ -5,7 +5,7 @@
     xmlns:xi="http://www.w3.org/2001/XInclude"><div class="error-explanation" py:if="error_summary">
-        <h2>Errors in form</h2>
+        <h3>Errors in form</h3><p>The form contains invalid entries:</p><ul><li py:for="key, error in error_summary.items()">${"%s: %s" % (key, error)}</li>


--- a/ckanext/iati/templates/layout_base.html	Wed Sep 14 15:43:57 2011 +0100
+++ b/ckanext/iati/templates/layout_base.html	Wed Sep 14 15:44:31 2011 +0100
@@ -149,7 +149,8 @@
             <div id="minornavigation"><minornavigation></minornavigation></div>
-            <!-- support both options for defining content -->
+            <h2 py:if="defined('page_heading')">${page_heading()}</h2>
+        <!-- support both options for defining content --><py:if test="defined('content')">
             ${content()}
             </py:if>


--- a/ckanext/iati/templates/package/form_iati.html	Wed Sep 14 15:43:57 2011 +0100
+++ b/ckanext/iati/templates/package/form_iati.html	Wed Sep 14 15:44:31 2011 +0100
@@ -1,11 +1,11 @@
-<form id="package-edit" class="package_create_form ckan" method="post"
+<form id="package-edit" method="post"
     py:attrs="{'class':'has-errors'} if errors else {}"
     xmlns:i18n="http://genshi.edgewall.org/i18n"
     xmlns:py="http://genshi.edgewall.org/"
     xmlns:xi="http://www.w3.org/2001/XInclude">
-
+    
     <div class="error-explanation" py:if="error_summary">
-        <h2>Errors in form</h2>
+        <h3>Errors in form</h3><p>The form contains invalid entries:</p><ul><li py:for="key, error in error_summary.items()">${"%s: %s" % (key, error)}</li>
@@ -16,7 +16,7 @@
         <legend>Basic information </legend><dl><dt><label class="field_req" for="name">Name *</label></dt>
-            <dd><input id="name" maxlength="100" name="name" type="text" value="${data.get('name', '')}" /></dd>
+            <dd><input id="name" name="name" type="text" value="${data.get('name', '')}" /></dd><dd class="field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</dd><dd class="instructions basic">A unique identifier for the activity record.</dd><dd class="instructions further">It should be broadly humanly readable, in the spirit of Semantic Web URIs. Only use an acronym if it is widely recognised. Renaming is possible but discouraged.</dd>


http://bitbucket.org/okfn/ckanextiati/changeset/2068b94a30b1/
changeset:   2068b94a30b1
user:        amercader
date:        2011-09-14 18:22:37
summary:     [merge] from new-forms
affected #:  19 files (40.1 KB)

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/__init__.py	Wed Sep 14 17:22:37 2011 +0100
@@ -0,0 +1,1 @@
+


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/countries.py	Wed Sep 14 17:22:37 2011 +0100
@@ -0,0 +1,6 @@
+# -*- coding: UTF-8 -*-
+
+# This Python file uses the following encoding: utf-8
+# Country and regions names and codes sent by Bill Anderson on 2011-06-28
+
+COUNTRIES = (("", u"(No country assigned)"), ("AF",u"Afghanistan"),("AX",u"Åland Islands"),("AL",u"Albania"),("DZ",u"Algeria"),("AS",u"American Samoa"),("AD",u"Andorra"),("AO",u"Angola"),("AI",u"Anguilla"),("AQ",u"Antarctica"),("AG",u"Antigua And Barbuda"),("AR",u"Argentina"),("AM",u"Armenia"),("AW",u"Aruba"),("AU",u"Australia"),("AT",u"Austria"),("AZ",u"Azerbaijan"),("BS",u"Bahamas"),("BH",u"Bahrain"),("BD",u"Bangladesh"),("BB",u"Barbados"),("BY",u"Belarus"),("BE",u"Belgium"),("BZ",u"Belize"),("BJ",u"Benin"),("BM",u"Bermuda"),("BT",u"Bhutan"),("BO",u"Bolivia, Plurinational State Of"),("BQ",u"Bonaire, Sint Eustatius And Saba"),("BA",u"Bosnia And Herzegovina"),("BW",u"Botswana"),("BV",u"Bouvet Island"),("BR",u"Brazil"),("IO",u"British Indian Ocean Territory"),("BN",u"Brunei Darussalam"),("BG",u"Bulgaria"),("BF",u"Burkina Faso"),("BI",u"Burundi"),("KH",u"Cambodia"),("CM",u"Cameroon"),("CA",u"Canada"),("CV",u"Cape Verde"),("KY",u"Cayman Islands"),("CF",u"Central African Republic"),("TD",u"Chad"),("CL",u"Chile"),("CN",u"China"),("CX",u"Christmas Island"),("CC",u"Cocos (Keeling) Islands"),("CO",u"Colombia"),("KM",u"Comoros"),("CG",u"Congo"),("CD",u"Congo, The Democratic Republic Of The"),("CK",u"Cook Islands"),("CR",u"Costa Rica"),("CI",u"Côte D'Ivoire"),("HR",u"Croatia"),("CU",u"Cuba"),("CW",u"Curaçao"),("CY",u"Cyprus"),("CZ",u"Czech Republic"),("DK",u"Denmark"),("DJ",u"Djibouti"),("DM",u"Dominica"),("DO",u"Dominican Republic"),("EC",u"Ecuador"),("EG",u"Egypt"),("SV",u"El Salvador"),("GQ",u"Equatorial Guinea"),("ER",u"Eritrea"),("EE",u"Estonia"),("ET",u"Ethiopia"),("FK",u"Falkland Islands (Malvinas)"),("FO",u"Faroe Islands"),("FJ",u"Fiji"),("FI",u"Finland"),("FR",u"France"),("GF",u"French Guiana"),("PF",u"French Polynesia"),("TF",u"French Southern Territories"),("GA",u"Gabon"),("GM",u"Gambia"),("GE",u"Georgia"),("DE",u"Germany"),("GH",u"Ghana"),("GI",u"Gibraltar"),("GR",u"Greece"),("GL",u"Greenland"),("GD",u"Grenada"),("GP",u"Guadeloupe"),("GU",u"Guam"),("GT",u"Guatemala"),("GG",u"Guernsey"),("GN",u"Guinea"),("GW",u"Guinea-Bissau"),("GY",u"Guyana"),("HT",u"Haiti"),("HM",u"Heard Island And Mcdonald Islands"),("VA",u"Holy See (Vatican City State)"),("HN",u"Honduras"),("HK",u"Hong Kong"),("HU",u"Hungary"),("IS",u"Iceland"),("IN",u"India"),("ID",u"Indonesia"),("IR",u"Iran, Islamic Republic Of"),("IQ",u"Iraq"),("IE",u"Ireland"),("IM",u"Isle Of Man"),("IL",u"Israel"),("IT",u"Italy"),("JM",u"Jamaica"),("JP",u"Japan"),("JE",u"Jersey"),("JO",u"Jordan"),("KZ",u"Kazakhstan"),("KE",u"Kenya"),("KI",u"Kiribati"),("KP",u"Korea, Democratic People's Republic Of"),("KR",u"Korea, Republic Of"),("XK",u"Kosovo"),("KW",u"Kuwait"),("KG",u"Kyrgyzstan"),("LA",u"Lao People's Democratic Republic"),("LV",u"Latvia"),("LB",u"Lebanon"),("LS",u"Lesotho"),("LR",u"Liberia"),("LY",u"Libyan Arab Jamahiriya"),("LI",u"Liechtenstein"),("LT",u"Lithuania"),("LU",u"Luxembourg"),("MO",u"Macao"),("MK",u"Macedonia, The Former Yugoslav Republic Of"),("MG",u"Madagascar"),("MW",u"Malawi"),("MY",u"Malaysia"),("MV",u"Maldives"),("ML",u"Mali"),("MT",u"Malta"),("MH",u"Marshall Islands"),("MQ",u"Martinique"),("MR",u"Mauritania"),("MU",u"Mauritius"),("YT",u"Mayotte"),("MX",u"Mexico"),("FM",u"Micronesia, Federated States Of"),("MD",u"Moldova, Republic Of"),("MC",u"Monaco"),("MN",u"Mongolia"),("ME",u"Montenegro"),("MS",u"Montserrat"),("MA",u"Morocco"),("MZ",u"Mozambique"),("MM",u"Myanmar"),("NA",u"Namibia"),("NR",u"Nauru"),("NP",u"Nepal"),("NL",u"Netherlands"),("NC",u"New Caledonia"),("NZ",u"New Zealand"),("NI",u"Nicaragua"),("NE",u"Niger"),("NG",u"Nigeria"),("NU",u"Niue"),("NF",u"Norfolk Island"),("MP",u"Northern Mariana Islands"),("NO",u"Norway"),("OM",u"Oman"),("PK",u"Pakistan"),("PW",u"Palau"),("PS",u"Palestinian Territory, Occupied"),("PA",u"Panama"),("PG",u"Papua New Guinea"),("PY",u"Paraguay"),("PE",u"Peru"),("PH",u"Philippines"),("PN",u"Pitcairn"),("PL",u"Poland"),("PT",u"Portugal"),("PR",u"Puerto Rico"),("QA",u"Qatar"),("RE",u"Réunion"),("RO",u"Romania"),("RU",u"Russian Federation"),("RW",u"Rwanda"),("BL",u"Saint Barthélemy"),("SH",u"Saint Helena, Ascension And Tristan Da Cunha"),("KN",u"Saint Kitts And Nevis"),("LC",u"Saint Lucia"),("MF",u"Saint Martin (French Part)"),("PM",u"Saint Pierre And Miquelon"),("VC",u"Saint Vincent And The Grenadines"),("WS",u"Samoa"),("SM",u"San Marino"),("ST",u"Sao Tome And Principe"),("SA",u"Saudi Arabia"),("SN",u"Senegal"),("RS",u"Serbia"),("SC",u"Seychelles"),("SL",u"Sierra Leone"),("SG",u"Singapore"),("SX",u"Sint Maarten (Dutch Part)"),("SK",u"Slovakia"),("SI",u"Slovenia"),("SB",u"Solomon Islands"),("SO",u"Somalia"),("ZA",u"South Africa"),("GS",u"South Georgia And The South Sandwich Islands"),("ES",u"Spain"),("LK",u"Sri Lanka"),("SD",u"Sudan"),("SR",u"Suriname"),("SJ",u"Svalbard And Jan Mayen"),("SZ",u"Swaziland"),("SE",u"Sweden"),("CH",u"Switzerland"),("SY",u"Syrian Arab Republic"),("TW",u"Taiwan, Province Of China"),("TJ",u"Tajikistan"),("TZ",u"Tanzania, United Republic Of"),("TH",u"Thailand"),("TL",u"Timor-Leste"),("TG",u"Togo"),("TK",u"Tokelau"),("TO",u"Tonga"),("TT",u"Trinidad And Tobago"),("TN",u"Tunisia"),("TR",u"Turkey"),("TM",u"Turkmenistan"),("TC",u"Turks And Caicos Islands"),("TV",u"Tuvalu"),("UG",u"Uganda"),("UA",u"Ukraine"),("AE",u"United Arab Emirates"),("GB",u"United Kingdom"),("US",u"United States"),("UM",u"United States Minor Outlying Islands"),("UY",u"Uruguay"),("UZ",u"Uzbekistan"),("VU",u"Vanuatu"),("VE",u"Venezuela, Bolivarian Republic Of"),("VN",u"Viet Nam"),("VG",u"Virgin Islands, British"),("VI",u"Virgin Islands, U.S."),("WF",u"Wallis And Futuna"),("EH",u"Western Sahara"),("YE",u"Yemen"),("ZM",u"Zambia"),("ZW",u"Zimbabwe"),("298",u"Africa"),("189",u"Africa - North"),("289",u"Africa - Sub-Saharan"),("089",u"Europe"),("498",u"America"),("389",u"America - North & Central"),("489",u"America - South"),("380",u"West Indies"),("589",u"Middle East"),("798",u"Asia"),("619",u"Asia - Central"),("679",u"Asia - South"),("689",u"Asia - South & Central"),("789",u"Asia - Far East"),("889",u"Oceania"),("998",u"Unspecified"),)


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/group_iati.py	Wed Sep 14 17:22:37 2011 +0100
@@ -0,0 +1,76 @@
+from ckan.lib.base import c
+from ckan import model
+
+from ckan.lib.navl.validators import ignore_missing, not_empty
+
+from ckan.logic.schema import group_form_schema
+from ckan.logic.converters import convert_from_extras, convert_to_extras
+from ckan.controllers.group import GroupController
+
+PUBLISHER_TYPES = ['Primary source', 'Secondary source']
+
+
+class GroupIatiController(GroupController):
+
+    group_form = 'group/form_iati.html'
+
+    def _setup_template_variables(self, context):
+
+        super(GroupIatiController,self)._setup_template_variables(context)
+
+        c.licences = [('', '')] + model.Package.get_license_options()
+
+    def _form_to_db_schema(self):
+        schema = group_form_schema()
+        schema.update({
+            'type': [not_empty, publisher_type_validator, convert_to_extras],
+            'license_id': [convert_to_extras],
+            'publisher_segmentation': [unicode, convert_to_extras, ignore_missing],
+            'publisher_ui': [unicode, convert_to_extras, ignore_missing],
+            'publisher_frequency': [unicode, convert_to_extras, ignore_missing],
+            'publisher_thresholds': [unicode, convert_to_extras, ignore_missing],
+            'publisher_units': [unicode, convert_to_extras, ignore_missing],
+            'publisher_contact': [unicode, convert_to_extras, ignore_missing],
+            'publisher_agencies': [unicode, convert_to_extras, ignore_missing],
+            'publisher_field_exclusions': [unicode, convert_to_extras, ignore_missing],
+            'publisher_description': [unicode, convert_to_extras, ignore_missing],
+            'publisher_record_exclusions': [unicode, convert_to_extras, ignore_missing],
+            'publisher_timeliness': [unicode, convert_to_extras, ignore_missing],
+            'publisher_refs': [unicode, convert_to_extras, ignore_missing],
+            'publisher_constraints': [unicode, convert_to_extras, ignore_missing],
+            'publisher_data_quality': [unicode, convert_to_extras, ignore_missing],
+        })
+
+        return schema
+
+    def _db_to_form_schema(self):
+        schema = group_form_schema()
+        schema.update({
+            'type': [convert_from_extras],
+            'license_id': [convert_from_extras],
+            'publisher_segmentation': [convert_from_extras],
+            'publisher_ui': [convert_from_extras],
+            'publisher_frequency': [convert_from_extras],
+            'publisher_thresholds': [convert_from_extras],
+            'publisher_units': [convert_from_extras],
+            'publisher_contact': [convert_from_extras],
+            'publisher_agencies': [convert_from_extras],
+            'publisher_field_exclusions': [convert_from_extras],
+            'publisher_description': [convert_from_extras],
+            'publisher_record_exclusions': [convert_from_extras],
+            'publisher_timeliness': [convert_from_extras],
+            'publisher_refs': [convert_from_extras],
+            'publisher_constraints': [convert_from_extras],
+            'publisher_data_quality': [convert_from_extras],
+        })
+
+        return schema
+
+    def _check_data_dict(self, data_dict):
+        return
+
+
+def publisher_type_validator(value,context):
+    if not value in PUBLISHER_TYPES:
+        raise Invalid('Unknown publisher type, allowed values: [%s]' % ', '.join(PUBLISHER_TYPES))
+    return value


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/group_schema.py	Wed Sep 14 17:22:37 2011 +0100
@@ -0,0 +1,44 @@
+fields = (('publisher_contact',
+           'Contact',
+           'Email or URL for publisher'),
+          ('publisher_description',
+           'Description',
+           "General description of Publisher's role and activities"),
+          ('publisher_agencies',
+           'Organisations / agencies covered',
+           'Whose activities does this publisher publish?'),
+          ('publisher_timeliness',
+           'Timeliness of Data',
+           'How up do date is the data when published?'),
+          ('publisher_frequency',
+           'Frequency of publication',
+           'How often is IATI data refreshed?  Monthly/Quarterly?'),
+          ('publisher_units',
+           'Units of Aid',
+           'A description of any hierarchical reporting units used and how they are applied'),
+          ('publisher_segmentation',
+           'Segmentation of Published Data',
+           'Is IATI data published by country, regions?'),
+          ('publisher_refs',
+           'Data Definitions and References',
+           "Links to guides, explanations, codelists on the publisher's own site that clarify their data"),
+          ('publisher_field_exclusions',
+           'Field Exclusions',
+           'What fields does the publisher never use - and for what reason'),
+          ('publisher_record_exclusions',
+           'Record Exclusions',
+           "What are the policies for excluding particular activities, or parts of an activity's data?"),     
+          ('publisher_thresholds',
+           'Thresholds',
+           'What are the thresholds below which data or whole activities are not published?'),
+          ('publisher_constraints',
+           'Other Constraints',
+           'Other policies that restrict full compliance with the standard'),
+          ('publisher_data_quality',
+           'Data Quality',
+           "Publisher's comment on the status and accuracyof the data - audited/verified, operational/sub to change, etc"),
+          ('publisher_ui',
+           'User Interface',
+           "Link to publisher's own public user activity interface"),
+          )
+publisher_record_fields = fields


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/controllers/package_iati.py	Wed Sep 14 17:22:37 2011 +0100
@@ -0,0 +1,109 @@
+from ckan.lib.base import c
+from ckan.lib.helpers import json
+from ckan import model
+from ckan.controllers.package import PackageController
+from ckan.authz import Authorizer
+
+from ckan.logic.schema import package_form_schema
+from ckan.lib.navl.validators import (ignore_missing,
+                                      not_empty,
+                                      empty,
+                                      ignore,
+                                      keep_extras,
+                                     )
+from ckan.logic.converters import convert_from_extras, convert_to_extras, date_to_db, date_to_form
+from ckan.lib.navl.dictization_functions import Missing, Invalid
+from ckan.lib.field_types import DateType, DateConvertError
+
+from countries import COUNTRIES
+
+class PackageIatiController(PackageController):
+
+    package_form = 'package/form_iati.html'
+
+    def _setup_template_variables(self, context, data_dict=None):
+
+        super(PackageIatiController,self)._setup_template_variables(context,data_dict)
+
+        c.groups_authz = self.get_groups()
+        c.groups_available = self.get_groups(available_only=True)
+
+        c.countries = [(v, k) for k, v in COUNTRIES]
+
+    def _form_to_db_schema(self):
+        schema = package_form_schema()
+        schema.update({
+            'department': [unicode,convert_to_extras,ignore_missing],
+            'country': [convert_to_extras, ignore_missing],
+            'donors': [unicode, convert_from_comma_list, convert_to_extras, ignore_missing],
+            'donors_type': [unicode, convert_from_comma_list, convert_to_extras, ignore_missing],
+            'donors_country': [unicode, convert_from_comma_list, convert_to_extras, ignore_missing],
+            'record_updated': [date_to_db, convert_to_extras,ignore_missing],
+            'data_updated': [date_to_db, convert_to_extras,ignore_missing],
+            'activity_period-from': [date_to_db, convert_to_extras,ignore_missing],
+            'activity_period-to': [date_to_db, convert_to_extras,ignore_missing],
+            'activity_count': [integer,convert_to_extras,ignore_missing],
+            'archive_file': [checkbox_value, convert_to_extras,ignore_missing],
+            'verified': [checkbox_value, convert_to_extras,ignore_missing],
+        })
+
+        return schema
+
+    def _db_to_form_schema(self):
+        schema = package_form_schema()
+        schema.update({
+            'department': [convert_from_extras,ignore_missing],
+            'country': [convert_from_extras, ignore_missing],
+            'donors': [ignore_missing, convert_from_extras, convert_to_comma_list],
+            'donors_type': [ignore_missing, convert_from_extras, convert_to_comma_list],
+            'donors_country': [ignore_missing, convert_from_extras, convert_to_comma_list],
+            'record_updated': [convert_from_extras,ignore_missing, date_to_form],
+            'data_updated': [convert_from_extras,ignore_missing, date_to_form],
+            'activity_period-from': [convert_from_extras,ignore_missing, date_to_form],
+            'activity_period-to': [convert_from_extras,ignore_missing, date_to_form],
+            'activity_count': [convert_from_extras,ignore_missing],
+            'archive_file': [convert_from_extras,ignore_missing],
+            'verified': [convert_from_extras,ignore_missing],
+        })
+
+        return schema
+
+    def _check_data_dict(self, data_dict):
+        return
+
+    # End hooks
+
+    def get_groups(self,available_only=False):
+
+        query = Authorizer().authorized_query(c.user, model.Group, model.Action.EDIT)
+        groups = set(query.all())
+
+        if available_only:
+            package = c.pkg
+            if package:
+                groups = groups - set(package.groups)
+
+        return [{'id':group.id,'name':group.name, 'title':group.title} for group in groups if group.state==model.State.ACTIVE]
+
+
+def convert_to_comma_list(value, context):
+     
+    return ', '.join(json.loads(value))
+
+def convert_from_comma_list(value, context):
+     
+    return [x.strip() for x in value.split(',') if len(x)]
+
+def checkbox_value(value,context):
+
+    return 'yes' if not isinstance(value, Missing) else 'no'
+
+def integer(value,context):
+
+    if not value == '':
+        try:
+            value = int(value)
+        except ValueError,e:
+            raise Invalid(str(e))
+        return value
+


--- a/ckanext/iati/forms/__init__.py	Thu Sep 01 13:59:28 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-
-from package import get_package_fieldset as get_iati_package_fieldset
-from group import get_group_fieldset as get_iati_group_fieldset


--- a/ckanext/iati/forms/countries.py	Thu Sep 01 13:59:28 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-# -*- coding: UTF-8 -*-
-
-# This Python file uses the following encoding: utf-8
-# Country and regions names and codes sent by Bill Anderson on 2011-06-28
-
-COUNTRIES = (("", u"(No country assigned)"), ("AF",u"Afghanistan"),("AX",u"Åland Islands"),("AL",u"Albania"),("DZ",u"Algeria"),("AS",u"American Samoa"),("AD",u"Andorra"),("AO",u"Angola"),("AI",u"Anguilla"),("AQ",u"Antarctica"),("AG",u"Antigua And Barbuda"),("AR",u"Argentina"),("AM",u"Armenia"),("AW",u"Aruba"),("AU",u"Australia"),("AT",u"Austria"),("AZ",u"Azerbaijan"),("BS",u"Bahamas"),("BH",u"Bahrain"),("BD",u"Bangladesh"),("BB",u"Barbados"),("BY",u"Belarus"),("BE",u"Belgium"),("BZ",u"Belize"),("BJ",u"Benin"),("BM",u"Bermuda"),("BT",u"Bhutan"),("BO",u"Bolivia, Plurinational State Of"),("BQ",u"Bonaire, Sint Eustatius And Saba"),("BA",u"Bosnia And Herzegovina"),("BW",u"Botswana"),("BV",u"Bouvet Island"),("BR",u"Brazil"),("IO",u"British Indian Ocean Territory"),("BN",u"Brunei Darussalam"),("BG",u"Bulgaria"),("BF",u"Burkina Faso"),("BI",u"Burundi"),("KH",u"Cambodia"),("CM",u"Cameroon"),("CA",u"Canada"),("CV",u"Cape Verde"),("KY",u"Cayman Islands"),("CF",u"Central African Republic"),("TD",u"Chad"),("CL",u"Chile"),("CN",u"China"),("CX",u"Christmas Island"),("CC",u"Cocos (Keeling) Islands"),("CO",u"Colombia"),("KM",u"Comoros"),("CG",u"Congo"),("CD",u"Congo, The Democratic Republic Of The"),("CK",u"Cook Islands"),("CR",u"Costa Rica"),("CI",u"Côte D'Ivoire"),("HR",u"Croatia"),("CU",u"Cuba"),("CW",u"Curaçao"),("CY",u"Cyprus"),("CZ",u"Czech Republic"),("DK",u"Denmark"),("DJ",u"Djibouti"),("DM",u"Dominica"),("DO",u"Dominican Republic"),("EC",u"Ecuador"),("EG",u"Egypt"),("SV",u"El Salvador"),("GQ",u"Equatorial Guinea"),("ER",u"Eritrea"),("EE",u"Estonia"),("ET",u"Ethiopia"),("FK",u"Falkland Islands (Malvinas)"),("FO",u"Faroe Islands"),("FJ",u"Fiji"),("FI",u"Finland"),("FR",u"France"),("GF",u"French Guiana"),("PF",u"French Polynesia"),("TF",u"French Southern Territories"),("GA",u"Gabon"),("GM",u"Gambia"),("GE",u"Georgia"),("DE",u"Germany"),("GH",u"Ghana"),("GI",u"Gibraltar"),("GR",u"Greece"),("GL",u"Greenland"),("GD",u"Grenada"),("GP",u"Guadeloupe"),("GU",u"Guam"),("GT",u"Guatemala"),("GG",u"Guernsey"),("GN",u"Guinea"),("GW",u"Guinea-Bissau"),("GY",u"Guyana"),("HT",u"Haiti"),("HM",u"Heard Island And Mcdonald Islands"),("VA",u"Holy See (Vatican City State)"),("HN",u"Honduras"),("HK",u"Hong Kong"),("HU",u"Hungary"),("IS",u"Iceland"),("IN",u"India"),("ID",u"Indonesia"),("IR",u"Iran, Islamic Republic Of"),("IQ",u"Iraq"),("IE",u"Ireland"),("IM",u"Isle Of Man"),("IL",u"Israel"),("IT",u"Italy"),("JM",u"Jamaica"),("JP",u"Japan"),("JE",u"Jersey"),("JO",u"Jordan"),("KZ",u"Kazakhstan"),("KE",u"Kenya"),("KI",u"Kiribati"),("KP",u"Korea, Democratic People's Republic Of"),("KR",u"Korea, Republic Of"),("XK",u"Kosovo"),("KW",u"Kuwait"),("KG",u"Kyrgyzstan"),("LA",u"Lao People's Democratic Republic"),("LV",u"Latvia"),("LB",u"Lebanon"),("LS",u"Lesotho"),("LR",u"Liberia"),("LY",u"Libyan Arab Jamahiriya"),("LI",u"Liechtenstein"),("LT",u"Lithuania"),("LU",u"Luxembourg"),("MO",u"Macao"),("MK",u"Macedonia, The Former Yugoslav Republic Of"),("MG",u"Madagascar"),("MW",u"Malawi"),("MY",u"Malaysia"),("MV",u"Maldives"),("ML",u"Mali"),("MT",u"Malta"),("MH",u"Marshall Islands"),("MQ",u"Martinique"),("MR",u"Mauritania"),("MU",u"Mauritius"),("YT",u"Mayotte"),("MX",u"Mexico"),("FM",u"Micronesia, Federated States Of"),("MD",u"Moldova, Republic Of"),("MC",u"Monaco"),("MN",u"Mongolia"),("ME",u"Montenegro"),("MS",u"Montserrat"),("MA",u"Morocco"),("MZ",u"Mozambique"),("MM",u"Myanmar"),("NA",u"Namibia"),("NR",u"Nauru"),("NP",u"Nepal"),("NL",u"Netherlands"),("NC",u"New Caledonia"),("NZ",u"New Zealand"),("NI",u"Nicaragua"),("NE",u"Niger"),("NG",u"Nigeria"),("NU",u"Niue"),("NF",u"Norfolk Island"),("MP",u"Northern Mariana Islands"),("NO",u"Norway"),("OM",u"Oman"),("PK",u"Pakistan"),("PW",u"Palau"),("PS",u"Palestinian Territory, Occupied"),("PA",u"Panama"),("PG",u"Papua New Guinea"),("PY",u"Paraguay"),("PE",u"Peru"),("PH",u"Philippines"),("PN",u"Pitcairn"),("PL",u"Poland"),("PT",u"Portugal"),("PR",u"Puerto Rico"),("QA",u"Qatar"),("RE",u"Réunion"),("RO",u"Romania"),("RU",u"Russian Federation"),("RW",u"Rwanda"),("BL",u"Saint Barthélemy"),("SH",u"Saint Helena, Ascension And Tristan Da Cunha"),("KN",u"Saint Kitts And Nevis"),("LC",u"Saint Lucia"),("MF",u"Saint Martin (French Part)"),("PM",u"Saint Pierre And Miquelon"),("VC",u"Saint Vincent And The Grenadines"),("WS",u"Samoa"),("SM",u"San Marino"),("ST",u"Sao Tome And Principe"),("SA",u"Saudi Arabia"),("SN",u"Senegal"),("RS",u"Serbia"),("SC",u"Seychelles"),("SL",u"Sierra Leone"),("SG",u"Singapore"),("SX",u"Sint Maarten (Dutch Part)"),("SK",u"Slovakia"),("SI",u"Slovenia"),("SB",u"Solomon Islands"),("SO",u"Somalia"),("ZA",u"South Africa"),("GS",u"South Georgia And The South Sandwich Islands"),("ES",u"Spain"),("LK",u"Sri Lanka"),("SD",u"Sudan"),("SR",u"Suriname"),("SJ",u"Svalbard And Jan Mayen"),("SZ",u"Swaziland"),("SE",u"Sweden"),("CH",u"Switzerland"),("SY",u"Syrian Arab Republic"),("TW",u"Taiwan, Province Of China"),("TJ",u"Tajikistan"),("TZ",u"Tanzania, United Republic Of"),("TH",u"Thailand"),("TL",u"Timor-Leste"),("TG",u"Togo"),("TK",u"Tokelau"),("TO",u"Tonga"),("TT",u"Trinidad And Tobago"),("TN",u"Tunisia"),("TR",u"Turkey"),("TM",u"Turkmenistan"),("TC",u"Turks And Caicos Islands"),("TV",u"Tuvalu"),("UG",u"Uganda"),("UA",u"Ukraine"),("AE",u"United Arab Emirates"),("GB",u"United Kingdom"),("US",u"United States"),("UM",u"United States Minor Outlying Islands"),("UY",u"Uruguay"),("UZ",u"Uzbekistan"),("VU",u"Vanuatu"),("VE",u"Venezuela, Bolivarian Republic Of"),("VN",u"Viet Nam"),("VG",u"Virgin Islands, British"),("VI",u"Virgin Islands, U.S."),("WF",u"Wallis And Futuna"),("EH",u"Western Sahara"),("YE",u"Yemen"),("ZM",u"Zambia"),("ZW",u"Zimbabwe"),("298",u"Africa"),("189",u"Africa - North"),("289",u"Africa - Sub-Saharan"),("089",u"Europe"),("498",u"America"),("389",u"America - North & Central"),("489",u"America - South"),("380",u"West Indies"),("589",u"Middle East"),("798",u"Asia"),("619",u"Asia - Central"),("679",u"Asia - South"),("689",u"Asia - South & Central"),("789",u"Asia - Far East"),("889",u"Oceania"),("998",u"Unspecified"),)


--- a/ckanext/iati/forms/group.py	Thu Sep 01 13:59:28 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-import formalchemy
-from formalchemy import helpers as fa_h
-import ckan.lib.helpers as h
-
-from ckan.forms.builder import FormBuilder
-from sqlalchemy.util import OrderedDict
-from pylons.i18n import _, ungettext, N_, gettext
-import ckan.model as model
-import ckan.forms.common as common
-import ckan.forms.group as group
-from ckan.forms.common import ExtrasField, PackageNameField, SelectExtraField
-from ckan.forms.common import TextAreaExtraField
-from ckan.lib.helpers import literal
-
-from group_schema import fields
-
-
-__all__ = ['get_group_dict', 'edit_group_dict']
-
-
-def build_group_form(is_admin=False, with_packages=False):
-    PUBLISHER_TYPES = [_("Primary source"),
-                       _("Secondary source")
-                       ]
-    publisher_record_fields = fields
-    builder = FormBuilder(model.Group)
-    builder.set_field_text('name', 'Unique Name (required)',
-            literal("<br/><strong>Unique identifier</strong> for group.<br/>2+ chars, lowercase, using only 'a-z0-9' and '-_'"))
-    builder.set_field_option('name', 'validate', common.group_name_validator)
-    builder.set_field_option('state', 'dropdown', {'options': model.State.all})
-    builder.add_field(SelectExtraField('type',
-                                       options=PUBLISHER_TYPES,
-                                       allow_empty=False))
-    builder.set_field_text('type', 'Source')
-    
-    builder.add_field(SelectExtraField('license_id',
-                                       options=[('', None)] + model.Package.get_license_options()))
-    builder.set_field_text('license_id', _('License'))
-
-    for name, title, description in publisher_record_fields:
-        builder.add_field(TextAreaExtraField(name))
-        builder.set_field_text(name, title, description)
-    displayed_fields = ['name', 'title', 'type', 'license_id'] +\
-                       [x[0] for x in publisher_record_fields]
-    from ckan.authz import Authorizer
-    from ckan.lib.base import c
-    if Authorizer.is_sysadmin(c.user):
-        displayed_fields.append('state')
-
-    if with_packages:
-        builder.add_field(group.PackagesField('packages'))
-        displayed_fields.append('packages')
-    builder.set_displayed_fields(OrderedDict([('Details', displayed_fields)]))
-    builder.set_label_prettifier(common.prettify)
-    return builder
-
-
-def get_group_fieldset(is_admin=False, combined=False):
-    return build_group_form(is_admin=is_admin,
-                            with_packages=combined).get_fieldset()
-


--- a/ckanext/iati/forms/group_schema.py	Thu Sep 01 13:59:28 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-fields = (('publisher_contact',
-           'Contact',
-           'Email or URL for publisher'),
-          ('publisher_description',
-           'Description',
-           "General description of Publisher's role and activities"),
-          ('publisher_agencies',
-           'Organisations / agencies covered',
-           'Whose activities does this publisher publish?'),
-          ('publisher_timeliness',
-           'Timeliness of Data',
-           'How up do date is the data when published?'),
-          ('publisher_frequency',
-           'Frequency of publication',
-           'How often is IATI data refreshed?  Monthly/Quarterly?'),
-          ('publisher_units',
-           'Units of Aid',
-           'A description of any hierarchical reporting units used and how they are applied'),
-          ('publisher_segmentation',
-           'Segmentation of Published Data',
-           'Is IATI data published by country, regions?'),
-          ('publisher_refs',
-           'Data Definitions and References',
-           "Links to guides, explanations, codelists on the publisher's own site that clarify their data"),
-          ('publisher_field_exclusions',
-           'Field Exclusions',
-           'What fields does the publisher never use - and for what reason'),
-          ('publisher_record_exclusions',
-           'Record Exclusions',
-           "What are the policies for excluding particular activities, or parts of an activity's data?"),     
-          ('publisher_thresholds',
-           'Thresholds',
-           'What are the thresholds below which data or whole activities are not published?'),
-          ('publisher_constraints',
-           'Other Constraints',
-           'Other policies that restrict full compliance with the standard'),
-          ('publisher_data_quality',
-           'Data Quality',
-           "Publisher's comment on the status and accuracyof the data - audited/verified, operational/sub to change, etc"),
-          ('publisher_ui',
-           'User Interface',
-           "Link to publisher's own public user activity interface"),
-          )
-publisher_record_fields = fields


--- a/ckanext/iati/forms/package.py	Thu Sep 01 13:59:28 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,226 +0,0 @@
-import formalchemy
-from formalchemy import helpers as h
-from sqlalchemy.util import OrderedDict
-from pylons.i18n import _, ungettext, N_, gettext
-
-from ckan.lib.helpers import literal
-import ckan.forms.common as common
-from ckan.forms.common import fa_h, TextExtraField, RegExRangeValidatingField, GroupSelectField
-import ckan.model as model
-import ckan.forms.package as package
-from ckan.lib import field_types
-
-from countries import COUNTRIES
-
-__all__ = ['get_iati_fieldset']
-
-class CommaListExtraField(RegExRangeValidatingField):
-    '''A form field for two TextType fields, representing a range,
-    stored in 'extra' fields.'''
-    def get_configured(self):
-        field = self.CommaListField(self.name).with_renderer(self.CommaListRenderer)
-        return RegExRangeValidatingField.get_configured(self, field)
-
-    class CommaListField(formalchemy.Field):
-        def sync(self):
-            if not self.is_readonly():
-                pkg = self.model
-                vals = self._deserialize() or []
-                #vals = [v.strip()for v in vals.split(',') if len(v.strip())]
-                pkg.extras[self.name] = vals
-
-    class CommaListRenderer(formalchemy.fields.FieldRenderer):
-        def _get_value(self):
-            if self.value:
-                return self.value
-            extras = self.field.parent.model.extras
-            return extras.get(self.field.name, [])
-
-        def render(self, **kwargs):
-            values = self._get_value()
-            html = fa_h.text_field(self.name, value=', '.join(values), **kwargs)
-            return html
-
-        def render_readonly(self, **kwargs):
-            val = ', '.join(self._get_value())
-            return field_readonly_renderer(self.field.key, val_str)
-
-        def _serialized_value(self):
-            param_val = self.params.get(self.name, u'')
-            return [v.strip()for v in param_val.split(',') if len(v.strip())]
-
-        def deserialize(self):
-            return self._serialized_value()
-
-
-class SelectExtraField(TextExtraField):
-    '''A form field for text from from a list of options, that is
-    stored in an "extras" field.'''
-    def __init__(self, name, options):
-        self.options = options[:]
-        # ensure options have key and value, not just a value
-        for i, option in enumerate(self.options):
-            if not isinstance(option, (tuple, list)):
-                self.options[i] = (option, option)
-        super(SelectExtraField, self).__init__(name)
-
-    def get_configured(self):
-        return self.TextExtraField(self.name, options=self.options).with_renderer(self.SelectRenderer)
-
-    class SelectRenderer(formalchemy.fields.FieldRenderer):
-        def _get_value(self, **kwargs):
-            extras = self.field.parent.model.extras
-            return unicode(kwargs.get('selected', '') or self.value or extras.get(self.field.name, ''))
-
-        def render(self, options, **kwargs):
-            selected = self._get_value()
-            options = [('', '')] + options
-            option_keys = [key for value, key in options]
-            if selected in option_keys:
-                select_field_selected = selected
-            else:
-                select_field_selected = u''
-            fa_version_nums = formalchemy.__version__.split('.')
-            # Requires FA 1.3.2 onwards for this select i/f
-            html = literal(fa_h.select(self.name, select_field_selected, options, **kwargs))
-                
-            return html
-
-        def render_readonly(self, **kwargs):
-            return field_readonly_renderer(self.field.key, self._get_value())
-
-        def _serialized_value(self):
-            main_value = self.params.get(self.name, u'')
-            return main_value
-
-class AtLeastOneGroupSelectField(GroupSelectField):
-    
-    def get_configured(self):
-        field = self.GroupSelectionField(self.name, self.allow_empty).with_renderer(self.GroupSelectEditRenderer)
-        field.set(multiple=self.multiple)
-        field = field.validate(self.validate_groups)
-        field.user_editable_groups = self.user_editable_groups
-        return field
-        
-    def validate_groups(self, val, field):
-        if len(val) < 1:
-            raise formalchemy.ValidationError(_("Need at least one publishing entity assigned"))
-
-            
-# Setup the fieldset
-def build_package_iati_form(is_admin=False, user_editable_groups=None, **kwargs):
-    builder = package.build_package_form(is_admin=is_admin, 
-                                         user_editable_groups=user_editable_groups, **kwargs)
-    
-    # IATI specifics
-    
-    #Publishing Entity: 
-    builder.set_field_text('groups', _('Publisher'))
-    builder.add_field(AtLeastOneGroupSelectField('groups', allow_empty=False, 
-                      user_editable_groups=user_editable_groups))
-    
-    #builder.add_field(common.TextExtraField('publisher'))
-    #builder.set_field_text('publisher', _('Publishing entity'))
-    
-    #Publishing Entity Type: (Donor, Recipient, Community Data..)
-    #builder.add_field(SelectExtraField('publisher_type', options=PUBLISHER_TYPES))
-    #builder.set_field_text('publisher_type', _('Publishing entity type'))
-    
-    #Donor (TODO: Generate from crawler)   
-    # Editable List, CSV? 
-    builder.add_field(CommaListExtraField('donors'))
-    builder.set_field_text('donors', _('Donors'), "Separate multiple entries using commas.")
-    
-    builder.add_field(CommaListExtraField('donors_type'))
-    builder.set_field_text('donors_type', _('Donor type'), "Separate multiple entries using commas.")
-
-    builder.add_field(CommaListExtraField('donors_country'))
-    builder.set_field_text('donors_country', _('Donor country'), "Separate multiple entries using commas.")
-    
-    # TODO: Enforce validation
-    countries = [(v, k) for k, v in COUNTRIES]
-    builder.add_field(SelectExtraField('country', options=countries))
-    builder.set_field_text('country', _('Recipient country'))
-    
-    #Verification status: enumeration of statuses (checked, not checked etc)
-    # TODO: Enforce validation, can probably only be set by admins
-    builder.add_field(common.CheckboxExtraField('verified'))
-    builder.set_field_text('verified', _('Verification'))
-    
-    builder.add_field(common.CheckboxExtraField('archive_file'))
-    builder.set_field_text('archive_file', _('Archive'))
-    
-    #Activity period: (Generate from crawler) 
-    builder.add_field(common.DateRangeExtraField('activity_period'))
-    builder.set_field_text('activity_period', _('Activitiy Period'))
-    
-    #Resource links: to the actual IATI record
-    #Number of activities: (Generate from crawler) 
-    builder.add_field(common.TextExtraField('activity_count'))
-    builder.set_field_text('activity_count', _('Num. Activities'))
-    
-    #Date record updated:
-    builder.add_field(common.TextExtraField('record_updated'))
-    builder.set_field_text('record_updated', _('Record updated'))
-    
-    #Date data updated: 
-    builder.add_field(common.TextExtraField('data_updated'))
-    builder.set_field_text('data_updated', _('Data updated'))
-    
-    
-    #License: Need this field even if it may be a standard license
-    builder.add_field(common.TextExtraField('license'))
-    builder.set_field_text('license', _('License'))
-    
-    #Department 
-    # TODO: Make this a group property instead? 
-    builder.add_field(common.TextExtraField('department'))
-    builder.set_field_text('department', _('Department'))
-    
-    #Contact   
-    builder.set_field_text('author', _('Contact'))
-    
-    #Contact e-mail
-    builder.set_field_text('author_email', _('Contact e-mail'))
-    
-    #Licence    
-    builder.set_field_text('license_id', _('License'))
-
-    #Resource format    
-    #Resource URL     
-    #Resource ID    
-    #  -- do we have an ID? 
-    
-    # Layout
-    field_groups = OrderedDict([
-        (_('Basic information'), ['name', 'title', 
-                                  'author', 'author_email', 'department',]),
-        (_('Publisher'), ['groups']),
-        (_('Details'), ['country', 'donors', 'donors_type', 'donors_country',
-                        'record_updated', 'data_updated',
-                        'license_id', 'tags', 'notes']),
-        (_('Resources'), ['resources']),
-        (_('Verification and Analysis'), [
-                        'activity_period', 
-                        'activity_count', 'archive_file',
-                              ]),
-        ])
-    if is_admin:
-        field_groups[_('Verification and Analysis')].append('verified')
-        field_groups[_('Verification and Analysis')].append('state')
-    
-    builder.set_displayed_fields(field_groups)
-    
-    return builder
-    # Strings for i18n:
-    [_('External reference'),  _('Date released'), _('Date updated'),
-     _('Update frequency'), _('Geographic granularity'),
-     _('Geographic coverage'), _('Temporal granularity'),
-     _('Temporal coverage'), _('Categories'), _('National Statistic'),
-     _('Precision'), _('Taxonomy URL'), _('Department'), _('Agency'), 
-     ]
-
-def get_package_fieldset(is_admin=False, user_editable_groups=None, **kwargs):
-    return build_package_iati_form(is_admin=is_admin, 
-                                   user_editable_groups=user_editable_groups, **kwargs).get_fieldset()
-    


--- a/ckanext/iati/patch.py	Thu Sep 01 13:59:28 2011 +0100
+++ b/ckanext/iati/patch.py	Wed Sep 14 17:22:37 2011 +0100
@@ -1,13 +1,13 @@
 import logging
 import re
 
-from forms.countries import COUNTRIES
+from controllers.countries import COUNTRIES
 import ckan.lib.helpers as h
 import ckan.authz as authz
 from ckan.lib.base import *
 from ckan.model import Package
 
-from ckanext.iati.forms.group_schema import fields
+from ckanext.iati.controllers.group_schema import fields
 
 log = logging.getLogger(__name__)
 


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/plugin.py	Wed Sep 14 17:22:37 2011 +0100
@@ -0,0 +1,54 @@
+import os
+
+from logging import getLogger
+
+from ckan.plugins import implements, SingletonPlugin
+from ckan.plugins import IRoutes
+from ckan.plugins import IConfigurer
+
+import ckanext.iati
+
+log = getLogger(__name__)
+
+def configure_template_directory(config, relative_path):
+    configure_served_directory(config, relative_path, 'extra_template_paths')
+
+def configure_public_directory(config, relative_path):
+    configure_served_directory(config, relative_path, 'extra_public_paths')
+
+def configure_served_directory(config, relative_path, config_var):
+    'Configure serving of public/template directories.'
+    assert config_var in ('extra_template_paths', 'extra_public_paths')
+    this_dir = os.path.dirname(ckanext.iati.__file__)
+    absolute_path = os.path.join(this_dir, relative_path)
+    if absolute_path not in config.get(config_var, ''):
+        if config.get(config_var):
+            config[config_var] += ',' + absolute_path
+        else:
+            config[config_var] = absolute_path
+
+class IatiForms(SingletonPlugin):
+
+    implements(IRoutes)
+    implements(IConfigurer)
+
+    def before_map(self, map):
+        package_controller = 'ckanext.iati.controllers.package_iati:PackageIatiController'
+        group_controller = 'ckanext.iati.controllers.group_iati:GroupIatiController'
+
+        map.redirect('/package/new','/dataset/new')
+        map.redirect('/package/edit/{id}','/dataset/edit/{id}')
+        map.connect('/dataset/new', controller=package_controller, action='new')
+        map.connect('/dataset/edit/{id}', controller=package_controller, action='edit')
+
+        map.connect('/group/new', controller=group_controller, action='new')
+        map.connect('/group/edit/{id}', controller=group_controller, action='edit')
+
+        return map
+
+    def after_map(self, map):
+        return map
+
+    def update_config(self, config):
+        configure_template_directory(config, 'templates')
+


--- a/ckanext/iati/public/css/overrides.css	Thu Sep 01 13:59:28 2011 +0100
+++ b/ckanext/iati/public/css/overrides.css	Wed Sep 14 17:22:37 2011 +0100
@@ -560,6 +560,21 @@
   color:#536FA1;
 }
 
+/*-------- Form errors --------*/
+
+.error-explanation {
+    border: 1px solid red;
+    background-color: #FFE0E0;
+    padding: 0.5em;
+    margin-bottom: 1em;
+}
+
+.field_error{
+    margin-top: 0;
+    color: red
+}
+
+
 /*-------- Flash messages --------*/
 
 /*-------- Wordpress Specific --------*/


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/templates/group/form_iati.html	Wed Sep 14 17:22:37 2011 +0100
@@ -0,0 +1,151 @@
+<form id="group-edit" action="" method="post" 
+    py:attrs="{'class':'has-errors'} if errors else {}"
+    xmlns:i18n="http://genshi.edgewall.org/i18n"
+    xmlns:py="http://genshi.edgewall.org/"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+    <div class="error-explanation" py:if="error_summary">
+        <h3>Errors in form</h3>
+        <p>The form contains invalid entries:</p>
+        <ul>
+            <li py:for="key, error in error_summary.items()">${"%s: %s" % (key, error)}</li>
+        </ul>
+    </div>
+    
+    <fieldset>
+        <legend>Basic information</legend> 
+        <dl>
+            <dt><label class="field_req" for="name">Unique Name (required) *</label></dt> 
+            <dd><input id="name" name="name" type="text" value="${data.get('name', '')}" /></dd>
+            <dd class="field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</dd>
+            <dd class="instructions basic"><strong>Unique identifier</strong> for group.<br/>2+ chars, lowercase, using only 'a-z0-9' and '-_'</dd> 
+            <script type="text/javascript"> 
+                //<![CDATA[
+                $(document).ready(function () { if (!$('#preview').length) { $("#name").focus();}});
+                //]]>
+            </script>
+            
+            <dt><label class="field_opt" for="title">Title</label></dt> 
+            <dd><input id="title" name="title" type="text" value="${data.get('title', '')}" /></dd>
+            <dd class="field_error" py:if="errors.get('title', '')">${errors.get('title', '')}</dd>
+
+            <dt><label class="field_req" for="type">Source</label></dt> 
+            <dd>
+                <select id="type" name="type">
+                    <option value="Primary source" py:attrs="{'selected': 'selected' if data.get('type', '') == 'Primary source' else None}">Primary source</option>
+                    <option value="Secondary source" py:attrs="{'selected': 'selected' if data.get('type', '') == 'Secondary source' else None}">Secondary source</option>
+                </select>
+            </dd> 
+            
+            <dt><label class="field_opt" for="license_id">License</label></dt> 
+            <dd>
+                <select id="license_id" name="license_id">
+                    <py:for each="licence_desc, licence_id in c.licences">
+                        <option value="${licence_id}" py:attrs="{'selected': 'selected' if data.get('license_id', '') == licence_id else None}" >${licence_desc}</option>
+                    </py:for>
+
+                </select>
+            </dd>
+
+            <py:choose>
+                <py:when test="c.is_sysadmin">
+                    <dt><label class="field_opt" for="state">State</label></dt> 
+                    <dd>
+                        <select id="state" name="state">
+                            <option py:attrs="{'selected': 'selected' if data.get('state') == 'active' else None}" value="active">active</option>
+                            <option py:attrs="{'selected': 'selected' if data.get('state') == 'deleted' else None}" value="deleted">deleted</option>
+                            <option py:attrs="{'selected': 'selected' if data.get('state') == 'pending' else None}" value="pending">pending</option>
+                        </select>
+                    </dd>
+                </py:when>
+            </py:choose>
+
+        </dl>
+    </fieldset>
+
+    <fieldset>
+        <legend>Details</legend> 
+        <dl>
+
+            <dt><label class="field_opt" for="publisher_contact">Contact</label></dt>
+            <dd><textarea id="publisher_contact" name="publisher_contact">${data.get('publisher_contact', '')}</textarea></dd> 
+            <dd class="instructions basic">Contact details for publisher</dd>
+ 
+            <dt><label class="field_opt" for="publisher_description">Description</label></dt> 
+            <dd><textarea id="publisher_description" name="publisher_description">${data.get('publisher_description', '')}</textarea></dd> 
+            <dd class="instructions basic">General description of publisher's role and activities</dd>
+
+            <dt><label class="field_opt" for="publisher_agencies">Organisations / agencies covered</label></dt> 
+            <dd><textarea id="publisher_agencies" name="publisher_agencies">${data.get('publisher_agencies', '')}</textarea></dd> 
+            <dd class="instructions basic">Whose activities does this publisher publish?</dd>
+
+            <dt><label class="field_opt" for="publisher_timeliness">Timeliness of Data</label></dt> 
+            <dd><textarea id="publisher_timeliness" name="publisher_timeliness">${data.get('publisher_timeliness','')}</textarea></dd> 
+            <dd class="instructions basic">How up do date is the data when published?</dd> 
+
+            <dt><label class="field_opt" for="publisher_frequency">Frequency of publication</label></dt> 
+            <dd><textarea id="publisher_frequency" name="publisher_frequency">${data.get('publisher_frequency','')}</textarea></dd>
+            <dd class="instructions basic">How often is IATI data refreshed? Monthly/Quarterly?</dd>
+
+            <dt><label class="field_opt" for="publisher_units">Units of Aid</label></dt>
+            <dd><textarea id="publisher_units" name="publisher_units">${data.get('publisher_units','')}</textarea></dd> 
+            <dd class="instructions basic">A description of any hierarchical reporting units used and how they are applied</dd>
+
+            <dt><label class="field_opt" for="publisher_segmentation">Segmentation of Published Data</label></dt> 
+            <dd><textarea id="publisher_segmentation" name="publisher_segmentation">${data.get('publisher_segmentation','')}</textarea></dd>
+            <dd class="instructions basic">Is IATI data published by country, regions?</dd>
+
+            <dt><label class="field_opt" for="publisher_refs">Data Definitions and References</label></dt> 
+            <dd><textarea id="publisher_refs" name="publisher_refs">${data.get('publisher_refs','')}</textarea></dd> 
+            <dd class="instructions basic">Links to guides, explanations, codelists on the publisher's own site that clarify their data</dd> 
+
+            <dt><label class="field_opt" for="publisher_field_exclusions">Field Exclusions</label></dt> 
+            <dd><textarea id="publisher_field_exclusions" name="publisher_field_exclusions">${data.get('publisher_field_exclusions','')}</textarea></dd> 
+            <dd class="instructions basic">What fields does the publisher never use - and for what reason</dd> 
+
+            <dt><label class="field_opt" for="publisher_record_exclusions">Record Exclusions</label></dt> 
+            <dd><textarea id="publisher_record_exclusions" name="publisher_record_exclusions">${data.get('publisher_record_exclusions','')}</textarea></dd> 
+            <dd class="instructions basic">What are the policies for excluding particular activities, or parts of an activity's data?</dd> 
+
+            <dt><label class="field_opt" for="publisher_thresholds">Thresholds</label></dt> 
+            <dd><textarea id="publisher_thresholds" name="publisher_thresholds">${data.get('publisher_thresholds','')}</textarea></dd> 
+            <dd class="instructions basic">What are the thresholds below which data or whole activities are not published?</dd> 
+
+            <dt><label class="field_opt" for="publisher_constraints">Other Constraints</label></dt> 
+            <dd><textarea id="publisher_constraints" name="publisher_constraints">${data.get('publisher_constraints','')}</textarea></dd> 
+            <dd class="instructions basic">Other policies that restrict full compliance with the standard</dd> 
+            
+            <dt><label class="field_opt" for="publisher_data_quality">Data Quality</label></dt> 
+            <dd><textarea id="publisher_data_quality" name="publisher_data_quality">${data.get('publisher_data_quality','')}</textarea></dd> 
+            <dd class="instructions basic">Publisher's comment on the status and accuracyof the data - audited/verified, operational/sub to change, etc</dd> 
+             
+            <dt><label class="field_opt" for="publisher_ui">User Interface</label></dt> 
+            <dd><textarea id="publisher_ui" name="publisher_ui">${data.get('publisher_ui','')}</textarea></dd> 
+            <dd class="instructions basic">Link to publisher's own public user activity interface</dd> 
+        </dl>
+    </fieldset>
+
+    <fieldset>
+        <legend>Records</legend>
+        <dl py:if="data.get('packages')">
+            <py:for each="num, package in enumerate(data.get('packages'))">
+                <dt><input checked="checked" id="datasets__${num}__name" name="packages__${num}__name" type="checkbox" value="${package['name']}"/></dt>
+                <dd><label for="packages__${num}__name">${package['name']}</label></dd>
+            </py:for>
+        </dl>
+        <p py:if="not data.get('packages')">There are no records currently in this group.</p>
+    </fieldset>
+    
+    <fieldset>
+        <legend>Add records</legend>
+        <dl>
+            <dt><label class="field_opt" for="packages__${len(data.get('packages', []))}__name">Dataset</label></dt>
+            <dd><input class="autocomplete-dataset" id="datasets__${len(data.get('packages', []))}__name" name="packages__${len(data.get('packages', []))}__name" type="text" /></dd>
+        </dl>
+    </fieldset>
+
+    <div class="submit">
+        <input id="save" name="save" type="submit" value="Save" />
+    </div>
+
+</form>


--- a/ckanext/iati/templates/layout_base.html	Thu Sep 01 13:59:28 2011 +0100
+++ b/ckanext/iati/templates/layout_base.html	Wed Sep 14 17:22:37 2011 +0100
@@ -149,7 +149,8 @@
             <div id="minornavigation"><minornavigation></minornavigation></div>
-            <!-- support both options for defining content -->
+            <h2 py:if="defined('page_heading')">${page_heading()}</h2>
+        <!-- support both options for defining content --><py:if test="defined('content')">
             ${content()}
             </py:if>


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/templates/package/form_iati.html	Wed Sep 14 17:22:37 2011 +0100
@@ -0,0 +1,223 @@
+<form id="package-edit" method="post"
+    py:attrs="{'class':'has-errors'} if errors else {}"
+    xmlns:i18n="http://genshi.edgewall.org/i18n"
+    xmlns:py="http://genshi.edgewall.org/"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+    
+    <div class="error-explanation" py:if="error_summary">
+        <h3>Errors in form</h3>
+        <p>The form contains invalid entries:</p>
+        <ul>
+            <li py:for="key, error in error_summary.items()">${"%s: %s" % (key, error)}</li>
+        </ul>
+    </div>
+
+    <fieldset>
+        <legend>Basic information </legend>
+        <dl>
+            <dt><label class="field_req" for="name">Name *</label></dt>
+            <dd><input id="name" name="name" type="text" value="${data.get('name', '')}" /></dd>
+            <dd class="field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</dd>
+            <dd class="instructions basic">A unique identifier for the activity record.</dd>
+            <dd class="instructions further">It should be broadly humanly readable, in the spirit of Semantic Web URIs. Only use an acronym if it is widely recognised. Renaming is possible but discouraged.</dd>
+            <dd class="hints">2+ characters, lowercase, using only 'a-z0-9' and '-_'</dd>
+            <script type="text/javascript">
+                //<![CDATA[
+                $(document).ready(function () { if (!$('#preview').length) {$("#name").focus(); } });
+                //]]>
+            </script>
+
+            <dt><label class="field_opt" for="title">Title </label></dt>
+            <dd><input id="title" name="title" type="text" value="${data.get('title', '')}" /></dd>
+            <dd class="field_error" py:if="errors.get('title', '')">${errors.get('title', '')}</dd>
+            <dd class="instructions basic">A short descriptive title for the data set.</dd>
+            <dd class="instructions further">It should not be a description though - save that for the Notes field. Do not give a trailing full stop.</dd>
+
+            <dt><label class="field_opt" for="author">Contact</label></dt>
+            <dd><input id="author" name="author" type="text" value="${data.get('author', '')}" /></dd>
+            <dd class="instructions basic">The name of the main contact, for enquiries about this particular dataset, using the e-mail address in the following field.</dd>
+
+            <dt><label class="field_opt" for="author_email">Contact e-mail</label></dt>
+            <dd><input id="author_email" name="author_email" type="text" value="${data.get('author_email', '')}" /></dd>
+
+            <dt><label class="field_opt" for="department">Department</label></dt>
+            <dd><input id="department" name="department" size="40" type="text" value="${data.get('department', '')}" /></dd>
+        </dl>
+    </fieldset>
+
+    <fieldset id="groups">
+        <legend>Publisher</legend>
+        <dl>
+        <py:for each="num, group in enumerate(data.get('groups', []))">
+            <?python
+            authorized_group = [group_authz for group_authz in c.groups_authz if group_authz['id'] == group['id']]
+            authorized_group = authorized_group[0] if authorized_group else None
+            ?>
+
+            <dt py:if="'id' in group">
+                <input type="${'checkbox' if authorized_group else 'hidden'}" name="groups__${num}__id" checked="checked" value="${group['id']}" />
+                <input type="hidden" name="groups__${num}__name" value="${group.get('name', authorized_group['name'] if authorized_group else '')}" />
+            </dt>
+            <dd py:if="'id' in group"><label for="groups__${num}__checked">${group.get('name', authorized_group['name'] if authorized_group else '')}</label></dd>
+        </py:for>
+
+        <dt>Publisher</dt>
+            <dd py:if="c.groups_available">
+            <select id="groups__${len(data.get('groups', []))}__id" name="groups__${len(data.get('groups', []))}__id">
+                <dd py:if="data.get('groups',[])">
+                    <option value="" >(None)</option>
+                </dd>
+                <py:for each="group in c.groups_available">
+                    <option value="${group['id']}" >${group['title']}</option>
+                </py:for>
+            </select>
+            </dd>
+            <dd py:if="not c.groups_available">Cannot add any publisher.</dd>
+        </dl>
+
+    </fieldset>
+
+    <fieldset>
+        <legend>Details</legend>
+        <dl>
+            <dt><label class="field_opt" for="country">Recipient country</label></dt>
+            <dd>
+                <py:with vars="country = data.get('country','')">
+                <select id="country" name="country">
+                    <py:for each="name,id in c.countries">
+                    <option value="${id}" py:attrs="{'selected': 'selected' if country == id else None}">${name}</option>
+                    </py:for>
+                </select>
+                </py:with>
+            </dd>
+
+            <dt><label class="field_opt" for="donors">Donors</label></dt>
+            <dd><input id="donors" name="donors" type="text" value="${data.get('donors', '')}" /></dd>
+            <dd class="instructions basic">Separate multiple entries using commas.</dd>
+
+            <dt><label class="field_opt" for="donors_type">Donor type</label></dt>
+            <dd><input id="donors_type" name="donors_type" type="text" value="${data.get('donors_type', '')}" /></dd>
+            <dd class="instructions basic">Separate multiple entries using commas.</dd>
+
+            <dt><label class="field_opt" for="donors_country">Donor country</label></dt>
+            <dd><input id="donors_country" name="donors_country" type="text" value="${data.get('donors_country', '')}" /></dd>
+            <dd class="instructions basic">Separate multiple entries using commas.</dd>
+
+            <dt><label class="field_opt" for="record_updated">Record updated</label></dt>
+            <dd><input id="record_updated" name="record_updated" size="40" type="text" value="${data.get('record_updated', '')}" /></dd>
+            <dd class="instructions basic">Acceptable formats: 'DD/MM/YYYY HH:MM', 'DD/MM/YYYY', 'MM/YYYY', 'YYYY'.</dd>
+
+            <dt><label class="field_opt" for="data_updated">Data updated</label></dt>
+            <dd><input id="data_updated" name="data_updated" size="40" type="text" value="${data.get('data_updated', '')}"/></dd>
+            <dd class="instructions basic">Acceptable formats: 'DD/MM/YYYY HH:MM', 'DD/MM/YYYY', 'MM/YYYY', 'YYYY'.</dd>
+
+            <dt><label class="field_opt" for="license_id">License</label></dt>
+            <dd>
+                <select id="license_id" name="license_id">
+                    <py:for each="licence_desc, licence_id in c.licences">
+                        <option value="${licence_id}" py:attrs="{'selected': 'selected' if data.get('license_id', '') == licence_id else None}" >${licence_desc}</option>
+                    </py:for>
+                </select>
+            </dd>
+            <dd class="instructions basic">The licence under which the dataset is released.</dd>
+
+            <dt><label class="field_opt" for="tags">Tags</label></dt>
+            <dd>
+                <input class="autocomplete-tag" id="tag_string" name="tag_string" size="40" type="text"
+                       value="${data.get('tag_string') or ' '.join([tag['name'] for tag in data.get('tags', [])])}" />
+            </dd>
+            <dd class="field_error" py:if="errors.get('tag_string', '')">${errors.get('tag_string', '')}</dd>
+            <dd class="instructions basic">Terms that may link this dataset to similar ones. For more information on conventions, see <a href="http://wiki.okfn.org/ckan/doc/faq#TagConventions">this wiki page</a>.</dd>
+            <dd class="hints">e.g. pollution rivers water-quality</dd>
+
+            <dt><label class="field_opt" for="notes">Notes</label></dt>
+            <dd><textarea cols="60" id="notes" name="notes" rows="15">${data.get('notes', '')}</textarea></dd>
+            <dd class="instructions basic">The main description of the dataset</dd>
+            <dd class="instructions further">It is often displayed with the record title. In particular, it should start with a short sentence that describes the data set succinctly, because the first few words alone may be used in some views of the data sets.</dd>
+            <dd class="hints">You can use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown formatting</a> here.</dd>
+        </dl>
+    </fieldset>
+
+    <fieldset id="resources">
+        <legend>Resources</legend>
+        <table class="flexitable">
+            <thead>
+                <tr>
+                    <th class="field_req resource-url">URL*</th>
+                    <th class="field_opt resource-format">Format</th>
+                    <th class="field_opt resource-description">Description</th>
+                    <th class="field_opt resource-hash">Hash</th>
+                </tr>
+            </thead>
+            <tbody>
+                <py:for each="num, res in enumerate(data.get('resources', []) + [{}])">
+                <tr>
+                    <py:for each="col in c.resource_columns">
+                    <td py:choose="" class="resource-${col}">
+                        <input py:when="col == 'format'" name="resources__${num}__${col}" type="text" value="${res.get(col, '')}" class="autocomplete-format short" />
+
+                        <input py:otherwise="" name="resources__${num}__${col}" type="text" value="${res.get(col, '')}" class="${'medium-width' if col == 'url' else 'short'}" />
+                    </td>
+                    </py:for>
+                    <td class="resource-id"><input name="resources__${num}__id" type="hidden" value="${res.get('id', '')}" /></td>
+                </tr>
+                </py:for>
+            </tbody>
+        </table>
+
+        <div class="instructions basic">The files containing the data or address of the APIs for accessing it.</div>
+        <div class="instructions further"><br /><b>URL:</b> This is the Internet link directly to the data - by selecting this link in a web browser, the user will immediately download the full dataset. Note that datasets are not hosted on this site, but by the publisher of the data.<br /><b>Format:</b> This should give the file format in which the data is supplied. (i.e. IATI-XML)<br /><b>Description</b> Any information you want to add to describe the resource.<br /></div>
+        <div class="field_error" py:if="errors.get('resources', '')">Dataset resource(s) incomplete.</div>
+    </fieldset>
+
+    <fieldset>
+        <legend>Verification and Analysis </legend>
+        <dl>
+            <dt><label class="field_opt" for="activity_period">Activitiy Period</label></dt>
+            <dd>
+                <input class="short" id="activity_period-from" name="activity_period-from" type="text" value="${data.get('activity_period-from', '')}" /> -
+                <input class="short" id="activity_period-to" name="activity_period-to" type="text" value="${data.get('activity_period-to', '')}" />
+            </dd>
+            <dd class="instructions basic">Acceptable formats: 'DD/MM/YYYY HH:MM', 'DD/MM/YYYY', 'MM/YYYY', 'YYYY'.</dd>
+
+            <dt><label class="field_opt" for="activity_count">Num. Activities</label></dt>
+            <dd><input id="activity_count" name="activity_count" size="40" type="text" value="${data.get('activity_count', '')}"/></dd>
+
+            <dt><label class="field_opt" for="archive_file">Archive</label></dt>
+            <dd><input id="archive_file" name="archive_file" size="40" type="checkbox" py:attrs="{'checked': 'checked' if data.get('archive_file','') == 'yes' else None}" /></dd>
+            <py:choose>
+                <py:when test="c.is_sysadmin">
+                <dt><label class="field_opt" for="verified">Verification</label></dt>
+                <dd><input id="verified" name="verified" size="40" type="checkbox" py:attrs="{'checked': 'checked' if data.get('verified','') == 'yes' else None}" /></dd>
+
+                <dt><label class="field_opt" for="state">State</label></dt>
+                <dd>
+                    <select id="state" name="state">
+                        <option py:attrs="{'selected': 'selected' if data.get('state') == 'active' else None}" value="active">active</option>
+                        <option py:attrs="{'selected': 'selected' if data.get('state') == 'deleted' else None}" value="deleted">deleted</option>
+                        <option py:attrs="{'selected': 'selected' if data.get('state') == 'pending' else None}" value="pending">pending</option>
+                    </select>
+                </dd>
+                </py:when>
+            </py:choose>
+
+        </dl>
+    </fieldset>
+
+    <hr />
+    <label for="log_message">Edit summary (briefly describe the changes you have made)</label>
+    <textarea id="log_message" name="log_message" class="short wide"></textarea>
+
+    <div class="ckan-logged-in">
+    <p>Author: ${c.author}</p>
+    </div>
+
+    <div class="submit">
+    <input id="save" name="save" type="submit" value="Save" />
+    </div>
+    <p class="hints">
+    <strong>Important:</strong> By submitting content, you agree to release your contributions
+    under the open license specified on the <a href="/license">license page</a>. Please <strong>refrain</strong> from editing this page if you are <strong>not</strong> happy to do this.
+    </p>
+
+</form>


--- a/ckanext/iati/templates/package/read_core.html	Thu Sep 01 13:59:28 2011 +0100
+++ b/ckanext/iati/templates/package/read_core.html	Wed Sep 14 17:22:37 2011 +0100
@@ -34,7 +34,7 @@
     <div class="notes" py:if="str(c.pkg_notes_formatted).strip()">
       ${c.pkg_notes_formatted}
     </div>
-    
+
     <py:choose test=""><table py:when="c.pkg.resources" width="100%"><tr><th>URL</th><th>Format</th><th>Preview</th></tr>
@@ -50,7 +50,7 @@
       </table><table py:otherwise=""><tr><th>Resources</th><td>None given for this package.</td></tr></table></py:choose>
-      
+
     <py:def function="details_item(label, value)"><tr py:if="value is not None and len(value) > 0"><td class="package-label">
@@ -61,18 +61,18 @@
           </td></tr></py:def>
-    
+
     <py:def function="details_asbool(label, value)"><tr><td class="package-label">
             ${label}
           </td><td class="package-details">
-            ${'yes' if value else 'no'}
+            ${'yes' if value == 'yes' else 'no'}
           </td></tr></py:def>
-    
+
     <py:def function="details_list(label, values)"><tr py:if="values is not None and len(values) > 0"><td class="package-label" valign="top">
@@ -95,14 +95,14 @@
         ${details_list('Donor types', c.pkg.extras.get('donors_type', []))}
         ${details_list('Donor countries', [h.country_name(x) for x in c.pkg.extras.get('donors_country', [])])}
         ${details_item('Recipient Country', h.country_name(c.pkg.extras.get('country', '(Unknown)')))}
-        
+
         ${details_asbool('Verified', c.pkg.extras.get('verified', False))}
         ${details_asbool('Archive File', c.pkg.extras.get('archive_file', False))}
         ${details_item('Department', c.pkg.extras.get('department', '-'))}
-        
+
         ${details_item('Data updated', c.pkg.extras.get('data_updated', '-'))}
         ${details_item('Record updated', c.pkg.extras.get('record_updated', '-'))}
-        
+
         <tr><td class="package-label">
             License
@@ -122,14 +122,18 @@
         </tr></tbody></table>
-    
-    
-    
+
+
+
     <h4>Contents</h4><table width="100%"><tbody>
-        ${details_item('# of Activities', c.pkg.extras.get('activity_count', '-'))}
-        
+        <tr>
+          <td class="package-label"># of Activities</td>
+          <td class="package-details">
+            ${c.pkg.extras.get('activity_count', '-')}</td>
+        </tr>
+
         <tr><td class="package-label">Activity Period</td><td class="package-details">


--- a/i18n/en/ckan.po	Thu Sep 01 13:59:28 2011 +0100
+++ b/i18n/en/ckan.po	Wed Sep 14 17:22:37 2011 +0100
@@ -930,7 +930,7 @@
 
 #: ckan/templates/layout_base.html:99
 #: ckan/templates/package/search.html:14
-msgid "Add a package"
+msgid "Add a Dataset"
 msgstr "Add a record"
 
 #: ckan/templates/layout_base.html:101
@@ -1186,7 +1186,7 @@
 msgstr "Publishers History"
 
 #: ckan/templates/group/index.html:6
-msgid "Groups of Data Packages"
+msgid "Groups of Datasets"
 msgstr "Publishers"
 
 #: ckan/templates/group/index.html:11


--- a/setup.py	Thu Sep 01 13:59:28 2011 +0100
+++ b/setup.py	Wed Sep 14 17:22:37 2011 +0100
@@ -31,7 +31,8 @@
       iati_approval = ckanext.iati.approval:IatiGroupApprovalExtension
       iati_group_authz = ckanext.iati.authz:IatiGroupAuthzExtension
       iati_package_authz = ckanext.iati.authz:IatiPackageAuthzExtension
-      
+      iati_forms = ckanext.iati.plugin:IatiForms
+
       [ckan.forms]
       iati_package = ckanext.iati.forms:get_iati_package_fieldset
       iati_group = ckanext.iati.forms:get_iati_group_fieldset


http://bitbucket.org/okfn/ckanextiati/changeset/08e8d8fe9ad1/
changeset:   08e8d8fe9ad1
user:        amercader
date:        2011-09-14 18:43:30
summary:     Update i18n
affected #:  1 file (23 bytes)

--- a/i18n/en/ckan.po	Wed Sep 14 17:22:37 2011 +0100
+++ b/i18n/en/ckan.po	Wed Sep 14 17:43:30 2011 +0100
@@ -931,7 +931,7 @@
 #: ckan/templates/layout_base.html:99
 #: ckan/templates/package/search.html:14
 msgid "Add a Dataset"
-msgstr "Add a record"
+msgstr "Register a New IATI Activity Record"
 
 #: ckan/templates/layout_base.html:101
 #: ckan/templates/home/about.html:6


http://bitbucket.org/okfn/ckanextiati/changeset/5f484113b4ef/
changeset:   5f484113b4ef
user:        amercader
date:        2011-09-14 18:57:33
summary:     [theme] Ensure that the theme uses the CKAN rules from the old theme
affected #:  7 files (20.9 KB)

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/public/css/buttons.css	Wed Sep 14 17:57:33 2011 +0100
@@ -0,0 +1,120 @@
+.buttons {
+	display: inline-block;
+}
+
+.buttons:after {
+	content: ".";
+	display: block;
+	height: 0;
+	clear: both;
+	visibility: hidden;
+}
+
+* html .buttons {
+	height: 1px;
+}
+
+.buttons img {
+	vertical-align: baseline;
+	float: none;
+}
+
+.buttons a, button {
+	display: block;
+	margin: 0 7px 0 0;
+	background-color: #f0f0f0;
+	border: 1px solid #dedede;
+	border-top: 1px solid #eee;
+	border-left: 1px solid #eee;
+	-moz-border-radius: 0.5em;
+	-webkit-border-radius: 0.5em;
+	font-size: 100%;
+	line-height: 1.4;
+	text-decoration: none;
+	font-weight: bold;
+	color: #565656;
+	cursor: pointer;
+	padding: 5px 10px 6px 7px;
+	background-image: url(/images/button-shadow.png);
+	background-repeat: no-repeat;
+	background-position: left top;
+}
+
+button {
+	width: auto;
+	overflow: visible;
+	padding: 4px 10px 3px 7px;
+}
+
+button[type] {
+	padding: 5px 10px 5px 7px;
+	line-height: 17px;
+}
+
+*:first-child+html button[type] {
+	padding: 4px 10px 3px 7px;
+}
+
+.buttons a.secondary, button.secondary {
+	float: right;
+}
+
+button img, .buttons a img {
+	margin: 0 3px -3px 0 !important;
+	padding: 0;
+	border: none;
+	width: 16px;
+	height: 16px;
+}
+
+button:hover, .buttons a:hover {
+	border: 1px solid #ccc;
+	color: #333;
+	background-color: #f0f0f0;
+}
+
+button:active, .buttons a:active {
+	background-image: none;
+	background-color: #888;
+	border: 1px solid #444;
+	/* override position indicator */
+	color: #fff ! important; 
+}
+
+button.positive, .buttons a.positive {
+	color: #529214;
+}
+
+.buttons a.positive:hover, button.positive:hover {
+	background-image: none;
+	background-color: #e6efc2;
+	border: 1px solid #c6d880;
+	color: #529214;
+}
+
+.buttons a.positive:active {
+	background-image: none;
+	background-color: #529214;
+	border: 1px solid #529214;
+	color: #fff;
+}
+
+/* negative (cancel, delete) */
+
+.buttons a.negative, button.negative {
+	color: #d12f19;
+}
+
+.buttons a.negative:hover, button.negative:hover {
+	background-image: none;
+	background: #fbe3e4;
+	border: 1px solid #fbc2c4;
+	color: #d12f19;
+}
+
+.buttons a.negative:active {
+	background-image: none;
+	background-color: #d12f19;
+	border: 1px solid #d12f19;
+	color: #fff;
+}


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/public/css/ckan.css	Wed Sep 14 17:57:33 2011 +0100
@@ -0,0 +1,1052 @@
+/* The main css styles for CKAN (specific UI elements may have thier own css linked via style.css) */
+
+body, input, textarea, .page-title span, .pingback a.url {
+    font-family: "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+#content, #content input, #content textarea {
+    font-size: 14px;
+    line-height: 20px;
+}
+
+a { 
+  color: #b00; 
+  text-decoration: none;
+}
+a:link {
+  color: #b22;
+  text-decoration: none;
+}
+a:visited {
+  color: #b22;
+  text-decoration: none;
+}
+#main a:visited {
+  text-decoration: none;
+}
+
+a:active {
+  color: #f22;
+  text-decoration: none;
+}
+a:hover {
+  text-decoration: underline;
+}
+
+#content p, #content ul, #content ol, #content dd, #content pre, #content hr, #content dl {
+  margin-bottom: 14px;
+}
+
+/* ===========================
+ * Flash messages
+*/
+
+.flash-banner-box {
+  margin-top: -20px;
+  margin-bottom: 10px;
+}
+
+.flash-banner {
+  width: auto;
+  line-height: 1em;
+  font-size: 14px;
+  padding: 0.8em 1.5%; 
+  font-weight: bold;
+  text-align: center;
+  border-bottom: solid 1px #9f9f9f;
+  color: #133362;
+}
+
+.flash-banner.notice {
+  background: #f4a83d;
+  border-bottom-color: #dddddd;
+}
+
+.flash-banner.success {
+  background-color: #e7f9e0;
+  border-bottom-color: #ccc;
+  color: #2d6b00;
+}
+
+.flash-banner.error {
+  background-color: #ffeae8;
+  border-bottom-color: #ccc;
+  color: #b50000;
+}
+
+/* ===========================
+ * Masthead
+*/
+
+#header {
+  padding-top: 15px;
+  padding-bottom: 5px;
+}
+
+#site-title {
+  line-height: normal;
+  width: auto;
+  float: left;
+  font-size: 40px;
+  margin: 0;
+  padding: 0 0 5px 0;
+  font-weight: normal;
+  letter-spacing: -0.032em;
+}
+
+#site-title a,
+#site-title a:link, #site-title a:visited {
+  font-weight:normal;
+  color: #333;
+}
+
+/* hide the main twentyten image */
+#branding img {
+	display:none;
+}
+
+#site-title a img {
+  background: transparent;
+  display: block;
+  border: none;
+  margin: 0;
+  max-height: 50px;
+}
+
+#site-description {
+  width:auto;
+  float: left;
+  clear: left;
+  color: #000;
+  font-style: normal;
+  font-size: 13px;
+  letter-spacing: normal;
+  text-transform: none;
+  margin: -5px 0 10px 3px;
+  padding: 0;
+  font-family: 'Lucida Grande', 'Lucida Sans Unicode', Lucida, Arial, Helvetica, sans-serif;
+  font-family: 'Helvetica Neue', Arial, Helvetica, 'Nimbus Sans L', sans-serif;
+}
+
+/* ===========================
+ * Top Bar
+*/
+
+#top-bar {
+  text-align: right;
+}
+
+#top-bar-login {
+  max-width: 150px;
+  display: inline-block;
+}
+
+#top-bar .search-form, #top-bar .search-form form {
+}
+
+#top-bar .search-form input.search {
+  width: 120px;
+}
+
+#top-bar-login .ckan-logged-in img {
+  margin-bottom: -3px;
+}
+
+
+/* =Menu
+-------------------------------------------------------------- */
+
+#access {
+  border-top: 1px solid #aaa; 
+  border-bottom: 1px solid #aaa; 
+  background: transparent;
+}
+
+#access ul li a {
+  color: #b22;
+  text-decoration: none;
+  font-weight: bold;
+}
+
+#access ul li a.active {
+  color: black;
+  font-weight: bold;
+}
+
+#access ul li a:hover {
+  color: #333;
+}
+
+#access ul ul a {
+	background: #fff;
+}
+#access li:hover > a,
+#access ul ul :hover > a {
+	background: #fff;
+	color: #333;
+}
+#access ul li.current_page_item > a,
+#access ul li.current-menu-ancestor > a,
+#access ul li.current-menu-item > a,
+#access ul li.current-menu-parent > a {
+	color: #333;
+}
+* html #access ul li.current_page_item a,
+* html #access ul li.current-menu-ancestor a,
+* html #access ul li.current-menu-item a,
+* html #access ul li.current-menu-parent a,
+* html #access ul li a:hover {
+	color: #333;
+	cursor: default;
+  font-weight: bold;
+}
+
+/* ===========================
+ * Main
+ */
+
+#main {
+  padding-top: 20px;
+}
+
+#container {
+}
+
+.entry-content {
+  padding-top: 0;
+}
+
+/* ===========================
+ * Sidebar
+ */
+#primary
+{
+    padding-top:40px;    
+}
+#primary .widget-container
+{
+  background: #f9f2ce;
+  color: #333;
+  margin: 0 0 1em 0;
+  padding: 10px;
+	border: 1px solid #ebd897;
+	border-left: none;
+	border-top: none;
+	border-radius: 0.5em;
+	-moz-border-radius: 0.5em;
+	-webkit-border-radius: 0.5em;
+}
+
+#primary .widget-container h2 {
+  margin-bottom: 10px;
+}
+
+#primary .widget-container h4 {
+  margin-bottom: 10px;
+  font-weight:bold;
+}
+
+#primary .widget-container .widget_action{
+   text-align:right;
+   margin:18px 0 0 0;
+   font-weight:bold;
+}
+
+.hide-sidebar #primary { 
+   display: none;
+}
+
+.hide-sidebar #content {
+   margin: 0 20px 0 20px;
+}
+
+/* ==========================
+ * Facets
+ */
+
+.facet-box {
+
+}
+
+.facet-box h2 {
+    color: #000;
+    font-size: 1.2em;
+    font-weight: bold;
+    margin-top: 1em;
+}
+
+.facet-options {
+    margin-top: 0.5em;
+}
+
+.facet-options li {
+    padding-top: 0.2em;
+    font-size: 1.2em; 
+    color: #000; 
+}
+
+.register-link {
+    padding-top: 10px;
+}
+
+.register-link a {
+    color: white; 
+    background: #199150;
+    font-weight: bold;
+    padding: 5px;
+    width: 100%;
+    font-size: 1.3em;
+    -moz-border-radius: 2px;
+    -webkit-border-radius: 2px;
+    border-radius: 2px;
+    
+}
+
+.package-search-filters {
+    margin-top: 15px;
+}
+
+.search-field {
+    display: inline-block;
+    margin-right: 5px;
+    margin-bottom: 10px;
+    padding: 1px 1px 3px 2px;
+    font-size: 14px;
+    background-color: #eee;
+    line-height: 16px;
+    -moz-box-shadow: 1px 1px 3px #bbb;
+    -webkit-box-shadow: 1px 1px 3px #bbb;
+    box-shadow: 1px 1px 3px #bbb;
+    
+}
+
+.search-field-name::after {
+    content: ":";
+}
+
+.search-field-value {
+    font-weight: bold;
+}
+
+
+/* ===========================
+ * Footer
+ */
+
+#footer {
+  border-top: 1px solid;
+  margin: 1em auto 0 auto;
+  overflow: auto;
+}
+
+#footer ul {
+  margin-top: 0;
+}
+
+#footer-widget-area .widget-area {
+  float: none; 
+  width: 100%;
+  margin: 0;
+}
+
+#footer-widget-area .widget-container {
+  margin-bottom: 0;
+}
+
+#footer-widget-area .widget-container h3 {
+  display: inline-block;
+  margin-top: 0;
+  margin-right: 10px;
+  /* 
+   * IE 6 & 7 don't support inline-block, but we can use the hasLayout 
+   * magical property. 
+   * http://blog.mozilla.com/webdev/2009/02/20/cross-browser-inline-block/ 
+   */ 
+  zoom: 1; 
+  *display: inline; 
+}
+
+#footer-widget-area .widget-container .textwidget {
+  display: inline;
+}
+
+
+#footer-widget-area .widget-container ul {
+  display: inline;
+  margin: 0.3em 0 0 0;
+}
+
+#footer ul li abbr {
+  border-bottom: none;
+}
+
+#footer-widget-area .widget-container ul li {
+  margin: 0 1em 0 0;
+  padding: 0;
+  display: inline-block;
+  /* 
+   * IE 6 & 7 don't support inline-block, but we can use the hasLayout 
+   * magical property. 
+   * http://blog.mozilla.com/webdev/2009/02/20/cross-browser-inline-block/ 
+   */ 
+  zoom: 1; 
+  *display: inline; 
+}
+
+#footer-widget-area #fourth {
+	margin-right: 0;
+}
+
+#site-info {
+}
+
+#site-generator {
+  width: auto;
+}
+
+#site-generator a {
+  background-image: none;
+}
+
+img#footer-okf-logo {
+  margin-bottom: -4px;
+}
+
+
+/* ===========================
+ * Forms
+ */
+
+#content input, #content textarea {
+  line-height: 14px;
+  font-size: 14px;
+}
+
+form #log_message {
+  width: 80%;
+  height: 30px;
+}
+
+.entry-content label {
+  font-size: 14px;
+}
+
+#content form.ckan.package_create_form dt {
+  clear: both; 
+}
+
+#content form.ckan.package_create_form dl, #content form.ckan.package_create_form dd {
+  margin-bottom: 0;
+}
+
+#content form.ckan.package_create_form select {
+  margin-bottom: 12px;
+}
+
+form.simple-form label {
+    display: inline-block; 
+    float: left;
+    min-width: 40%;
+}
+
+form.simple-form fieldset input {
+    border: 1px solid #E7E7E7;
+    padding: 0.3em; 
+    width: 40%;
+}
+
+form.simple-form textarea {
+    width: 99%;
+}
+
+.purge-button {
+  border: none;
+  background: none;
+  background-color: #b33a3a;
+  color: white;
+  display: block;
+  margin-bottom: 1em;
+}
+
+.purge-button:hover {
+  border: none;
+}
+
+/* ===========================
+ * Tables
+ */
+
+#content tr td {
+  padding: 6px 10px;
+}
+
+#content tr th, #content thead th {
+  padding: 6px 12px;
+}
+
+caption {
+    caption-side:bottom;
+    text-align:left;
+    font-size:0.85em;
+    line-height:1.4em;
+    padding-top:0.5em;
+    color:#888;
+    padding-left:0.8em;
+}
+
+
+
+/* ============= */
+/* = Utilities = */
+/* ============= */
+
+.clearfix:after {
+	content: ".";
+	display: block;
+	height: 0;
+	clear: both;
+	visibility: hidden;
+}
+
+.clearfix { display: inline-block; } /* for IE/Mac */
+
+.cleared { clear: both; }
+hr.cleared {
+	height: 0 ! important;
+	visibility: hidden;
+}
+
+hr.nomargin { margin: 0; }
+
+/* ======================= */
+/* = Fixes to KForge CSS = */
+/* ======================= */
+
+table {
+	border-collapse: collapse;
+	border: none;
+	border-radius: 1em;
+	margin: 0 0 1.2em 0;
+}
+
+table th, table td {
+	border: 1px solid #ccc;
+	border-top-color: #eee;
+	border-left-color: #eee;
+	padding: 0.3em;
+}
+
+table td {
+	background: #f0f0f0;
+}
+
+table th {
+	background: #fff;
+}
+
+h1, h2, h3, h4, h5, h6 {
+	font-family: 'Lucida Grande', 'Lucida Sans Unicode', Lucida, Arial, Helvetica, sans-serif;
+	letter-spacing: -0.02em;
+}
+
+/* ================ */
+/* = Search boxes = */
+/* ================ */
+
+form.package-search input.search {
+  width: 99%;
+  padding: 5px;
+  font-size: 1.1em;
+  margin: 0px;
+  -webkit-appearance: textfield;
+}
+
+.package-search input.button {
+    display: inline-block;
+    float: right;
+    margin-top: 5px;
+    margin-right: 10px !important;
+    margin-bottom: 1px !important;
+}
+
+/* ========================================= */
+/* = Changes/additions to kforge forms.css = */
+/* ========================================= */
+
+input.openid {
+	background: transparent url(../images/icons/openid.png) 2px 2px no-repeat;
+	padding-left: 22px;
+}
+
+input.openid:focus {
+	background-position: 1px 1px;
+	padding-left: 21px;
+}
+
+/* ============== */
+/* = Pagination = */
+/* ============== */
+
+.pager {
+	width: 100%;
+	text-align: center;
+	margin: 0 0 1.2em 0;
+	clear: both;
+}
+
+.pager span, .pager a {
+	text-decoration: none;
+	margin: 0em;
+	border: none;
+	padding: 0.3em 0.1em;
+}
+
+.pager a:hover, .pager a:active {
+	color: #fff;
+	background-color: #c22;
+}
+
+.pager span.pager_dotdot {
+	color: #aaa;
+}
+
+.pager span.pager_curpage {
+	font-weight: bold;
+	border: 1px solid #ddd;
+}
+
+/* ======================= */
+/* = Various iconography = */
+/* ======================= */
+
+img.icon {
+	height: 25px;
+}
+
+a.icon {
+	display: block;
+	height: 0;
+	overflow: hidden;
+	padding-top: 20px;
+	width: 20px;
+}
+
+table.no-margin { margin: 0; }
+
+div.extras-new-field label {
+	display: inline;
+	line-height: 2.6em;
+}
+
+label.inline {
+	display:inline;
+	margin: 1em 0 0 1em;
+}
+
+dl.icons dt {
+	float: left;
+	clear: both;
+	margin: 0.4em 0 0 0;
+	height: 16px;
+	width: 16px;
+}
+
+dl.icons dt img {
+	vertical-align: middle;
+}
+
+dl.icons dd {
+	float: left;
+	margin: 0.4em 0 0 10px;
+}
+
+dl.icons dd.tiny {
+	font-size: 75%;
+	color: #888;
+	line-height: 1.2em;
+	margin: 0 0 0 26px;
+}
+
+/* ========================= */
+/* = Tag and Group listing = */
+/* ========================= */
+
+.item-list {
+  border-top: solid 1px #cbcbcb;
+  border-bottom: solid 1px #cbcbcb;
+  margin-top: 5px;
+}
+
+.item-list ul, ul.tags {
+  margin: 0 0 0.75em 0;
+  padding:0;
+  list-style: none;
+}
+
+.item-list ul li, ul.tags li {
+  padding-left: 0;
+  padding-right: 3px;
+}
+
+.tags a {
+	text-decoration: none;
+  color: #b00;
+}
+
+.tags a:hover {
+}
+
+/* tags small is for autocomplete list in package editor */
+.tags.small a {
+  padding-left: 6px;
+  padding-right: 6px;
+}
+
+/* =================== */
+/* = Package listing = */
+/* =================== */
+
+ul.packages {
+	padding-left: 0;
+        margin: 0 0 18px 0;
+}
+
+.packages .header {
+    font-weight: bold;
+}
+
+.packages .extract {
+	font-size: 0.9em;
+	padding-top:10px;
+}
+
+.packages li {
+	list-style: none;
+	padding: 0.4em 0 0.4em 0.0em;
+	border-left: 0.5em solid #fff;
+	border-bottom: 1px solid #ececec;
+	overflow: hidden;
+	/*white-space: nowrap;*/
+}
+
+.packages li a {
+	text-decoration: none;
+}
+
+.packages li img {
+	margin-bottom: -2px;
+}
+
+.search_meta {
+    float:right;
+}
+
+ul.package_formats {
+    float:right;    
+    padding:0 0 3px 0; margin:0;
+}
+
+ul.package_formats li {
+    display:inline;
+    margin:0;
+    padding:0 5px 0 5px;
+    border:none;
+    font-weight:normal;
+    font-size:0.8em;
+    color:#808080;
+    background:#ececec;
+}
+
+.openness {
+    clear:right;
+    float:right;
+    font-size:0.8em;
+}
+
+.openness img {
+    vertical-align:top;
+}
+
+.openness li {
+    margin:0;
+    padding:0;
+    border:none;    
+}
+
+/*
+.packages li.fullyopen {
+	border-left: 0.5em solid #AFC6E9;
+}
+*/
+
+.packages li .tags {
+	margin: 0 0 0 0.5em;
+	font-size: 80%;
+	opacity: 0.3;
+}
+
+.packages li:hover .tags {
+	opacity: 1;
+}
+
+.packages .name, 
+.packages .license {
+    color: #666;
+	font-weight: normal;
+	font-size: 0.9em;
+}
+
+/* ==================== */
+/* = Extra RHS styles = */
+/* ==================== */
+
+.sidebar dl {
+	margin: 0 1em 0.8em 1em;
+	padding: 0;
+}
+
+.sidebar hr {
+	width: 90%;
+	color: #EBDBA7;
+	background-color: #EBDBA7;
+	border: none;
+	height: 1px;
+}
+
+.sidebar .buttons {
+	margin-top: 0;
+}
+
+.sidebar .buttons a, .sidebar .buttons button {
+	margin-bottom: 0.3em;
+}
+
+.okdstripe {
+	/* Amusingly, this has to be a slightly different color to look the same
+	   as the stripe on a white background */
+	border-left: 0.5em solid #8FB0DE;
+	padding-left: 0.4em ! important;
+}
+
+/* ===================== */
+/* = Package read view = */
+/* ===================== */
+
+#primary .widget-container .formats ul {
+  background: transparent;
+}
+
+#primary .widget-container .formats ul li {
+  background: transparent;
+  display: inline;
+}
+
+#content .package .subsection h3,
+#content #comments h3, #content #comments h4 {
+  background-color: #444;
+  padding: 10px;
+  color: white;
+}
+
+.package h3, .package h4 {
+	font-weight: bold;
+	margin: 1em 0 0.6em 0;
+}
+
+.package div.tags {
+  margin: 20px 0;
+}
+
+.package .resources {
+  margin-bottom: 2em;
+}
+
+.package .resources table {
+  width: 100%;
+}
+
+.package h2.head {
+  margin-bottom: 0.3em!important;
+  line-height: 1.0;
+}
+
+.package .name, .group .name {
+	color: #888;
+	font-family: monospace;
+}
+
+.package div.url {
+  margin: 0 ;
+}
+
+.package .url a {
+	text-decoration: none;
+}
+
+.package .notes, .group .description {
+	padding: 0.2em 0 0.2em 0;
+	margin: 0 3% 0.5 0 ! important;
+}
+
+.package .notes span.nonegiven {
+	opacity: 0.5;
+}
+.package .notes span.nonegiven:hover {
+	opacity: 1;
+}
+
+.package .details table {
+  border-collapse: collapse;
+  background: transparent;
+  border-color: grey;
+  border-spacing: 2px 2px;
+}
+
+.package .details table tr {
+  border: none;
+  border-bottom: solid 1px #888;
+}
+
+.package .details td {
+  background: transparent;
+  border: none;
+}
+
+.package .details td.package-label {
+  width: 150px;
+}
+
+.package .api div {
+    background:#f0f0f0;
+    padding:10px;
+}
+
+.package .api h5 {
+    font-weight:bold;
+    margin-bottom:1em!important;
+    font-size:1em;
+}
+
+.package .api code {
+    background:#444;
+    color:#fff;
+    padding:3px 10px ;
+    margin-bottom:1em;
+    display:block;
+}
+.package .api code a {
+    color:#fff;
+}
+
+.relationship_comment {
+  font-style: italic;
+}
+
+p.atom-feed-link {
+  float: right;
+  display: inline;
+  font-size: 14px;
+}
+
+p.atom-feed-link a {
+  background: url('../images/icons/atom_feed.png') no-repeat;
+  padding-left: 20px;
+}
+
+p.atom-feed-link.package-history-link {
+  float: none;
+}
+
+#revision.widget-container
+{
+  background: #f9f2ce;
+  color: #333;
+  margin: 0 0 1em 0;
+  padding: 10px;
+	border: 1px solid #ebd897;
+	border-left: none;
+	border-top: none;
+	border-radius: 0.5em;
+	-moz-border-radius: 0.5em;
+	-webkit-border-radius: 0.5em;
+}
+
+/* ===================== */
+/* = User Listing      = */
+/* ===================== */
+
+body.user-list #content {
+  margin-right: 20px;
+}
+
+ul.userlist, ul.userlist ul {
+  list-style-type: none;
+  margin: 0;
+}
+
+ul.userlist li.user {
+  display: inline-block;
+  width: 200px;
+  padding: 5px;
+  font-size: 90%;
+  margin-bottom: 15px;
+}
+
+ul.userlist li ul span.edits {
+  color: #333;
+  font-size: 110%;
+  font-weight: bold;
+  margin-left: 3px;
+}
+
+ul.userlist li.user .username {
+}
+
+#content ul.userlist .username img {
+  margin-bottom: -3px;
+}
+
+ul.userlist .created {
+  font-size: 80%;
+}
+
+ul.userlist .badge {
+  color: #fc0;
+}
+
+body.user-list .sort {
+  float: right;
+}
+
+body.user-list .sort a {
+  font-size: 85%;
+  background: #eee;
+  padding: 5px;
+  border-bottom: 1px solid #ccc; 
+  border-right: 1px solid #ccc; 
+}
+
+/* ===================== */
+/* = Stateful stuff    = */
+/* ===================== */
+
+.state-deleted, .state-deleted a, .state-deleted * {
+  color: rgba(0, 0, 0, 0.4);
+}
+
+.state-deleted {
+  padding-left: 3px;
+}
+
+.state-deleted:hover * {
+  color: rgba(0, 0, 0, 0.8);
+}
+
+.state-notice {
+  text-transform: uppercase;
+  font-size: 120%;
+  background: #f4a83d;
+  padding: 15px;
+  text-align: center;
+  color: #fff;
+}


--- a/ckanext/iati/public/css/overrides.css	Wed Sep 14 17:43:30 2011 +0100
+++ b/ckanext/iati/public/css/overrides.css	Wed Sep 14 17:57:33 2011 +0100
@@ -560,7 +560,7 @@
   color:#536FA1;
 }
 
-/*-------- Form errors --------*/
+/*-------- Form --------*/
 
 .error-explanation {
     border: 1px solid red;
@@ -574,6 +574,10 @@
     color: red
 }
 
+#content dl,
+#content dd {
+    margin-bottom: 0px;
+}
 
 /*-------- Flash messages --------*/
 


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/public/css/stars.css	Wed Sep 14 17:57:33 2011 +0100
@@ -0,0 +1,64 @@
+/* based on http://www.pmob.co.uk/temp/star-rating.htm */
+
+div.inline-rating{
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display:inline-block;
+	/* vertical-align:middle; */
+	margin-bottom: -1px;
+}
+
+.stars{
+	width:80px;
+	height:16px;
+	margin: 0;
+	padding:0;
+	list-style:none;
+	clear:both;
+	position:relative;
+	background: url(/images/stars.png) no-repeat 0 0;
+}
+
+.default0star {background-position:0 0}
+.default1star {background-position:0 -16px}
+.default2star {background-position:0 -32px}
+.default3star {background-position:0 -48px}
+.default4star {background-position:0 -64px}
+.default5star {background-position:0 -80px}
+
+ul.stars li {
+	cursor: pointer;
+	float:left;
+	text-indent:-999em;
+}
+
+ul.stars li a {
+	position:absolute;
+	left:0;
+	top:0;
+	width:16px;
+	height:16px;
+	text-decoration:none;
+	z-index: 200;
+}
+
+ul.stars li.one a {left:0}
+ul.stars li.two a {left:16px;}
+ul.stars li.three a {left:32px;}
+ul.stars li.four a {left:48px;}
+ul.stars li.five a {left:64px;}
+
+ul.stars li a:hover {
+	z-index:2;
+	width:80px;
+	height:16px;
+	overflow:hidden;
+	left:0;  
+	background: url(/images/stars.png) no-repeat 0 0
+}
+
+ul.stars li.one a:hover {background-position:0 -96px;}
+ul.stars li.two a:hover {background-position:0 -112px;}
+ul.stars li.three a:hover {background-position:0 -128px}
+ul.stars li.four a:hover {background-position:0 -144px}
+ul.stars li.five a:hover {background-position:0 -160px}


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/public/css/style.ckan.css	Wed Sep 14 17:57:33 2011 +0100
@@ -0,0 +1,8 @@
+/* This file is just for importing other css files */
+ at import url(http://assets.okfn.org/themes/twentyten/style.css);
+ at import url(ckan.css);
+ at import url(buttons.css);
+ at import url(stars.css);
+ at import url(tabs.css);
+ at import url(forms.css);
+


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckanext/iati/public/css/tabs.css	Wed Sep 14 17:57:33 2011 +0100
@@ -0,0 +1,63 @@
+/* @override http://localhost:5000/style/tabs.css */
+
+ul.tabbed {
+	position: relative;
+	float: left;
+	width: 100%;
+	padding: 0;
+	margin: 0 0 1em 0;
+	list-style: none;
+	line-height: 1em;
+	border-bottom: 1px solid #aaa; 
+}
+
+ul.tabbed li {
+	float: left;
+	margin: 0 0 0 0.5em;
+	border-left: 1px solid #eaeaea;
+	border-top: 1px solid #eaeaea;
+	border-right: 1px solid #eaeaea; 
+	-moz-border-radius: 0.4em 0.4em 0 0;
+	-webkit-border-top-left-radius: 0.4em;
+	-webkit-border-top-right-radius: 0.4em;
+}
+
+ul.tabbed a {
+	display: block;
+	color: #bbb;
+	text-decoration: none;
+	font-weight: bold;
+	background: #fff;
+	margin: 0;
+	padding: 4px 0.6em;
+}
+
+ul.tabbed li a:hover,
+ul.tabbed li a:active {
+	color: #444;
+	background: #fff;
+	border-color: #aaa;
+	border-bottom-color: #fff;
+}
+
+ul.tabbed .hidden a {
+	display: none;
+}
+
+ul.tabbed a.active,
+ul.tabbed a.active:link,
+ul.tabbed a.active:visited
+{
+	position: relative;
+	top: 1px;
+	z-index: 102;
+	color: #444;
+	background: #fff;
+	border-color: #aaa;
+	border-bottom-color: #fff;
+	display: block;
+}
+
+ul.tabbed a img {
+  margin-bottom: -2px;
+}


--- a/ckanext/iati/templates/layout_base.html	Wed Sep 14 17:43:30 2011 +0100
+++ b/ckanext/iati/templates/layout_base.html	Wed Sep 14 17:57:33 2011 +0100
@@ -28,7 +28,7 @@
     @import url(http://openbiblio.net/wp-content/themes/twentyten/style.css);
   </style><link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/themes/ui-lightness/jquery-ui.css" type="text/css" media="screen, print" />
-  <link rel="stylesheet" href="/css/style.css" type="text/css" media="screen, print" />
+  <link rel="stylesheet" href="/css/style.ckan.css" type="text/css" media="screen, print" /><link rel="stylesheet" href="/css/iati.css" type="text/css" media="screen, print" /><link rel="stylesheet" href="/fonts/fonts.css" type="text/css" media="screen, print" /><link rel="stylesheet" href="/css/overrides.css" type="text/css" media="screen, print" />
@@ -149,8 +149,8 @@
             <div id="minornavigation"><minornavigation></minornavigation></div>
-            <h2 py:if="defined('page_heading')">${page_heading()}</h2>
-        <!-- support both options for defining content -->
+            <h2 py:if="defined('page_heading')" class="page_heading">${page_heading()}</h2>
+            <!-- support both options for defining content --><py:if test="defined('content')">
             ${content()}
             </py:if>


http://bitbucket.org/okfn/ckanextiati/changeset/76db02abad5e/
changeset:   76db02abad5e
user:        amercader
date:        2011-09-14 19:33:43
summary:     [forms] Fix resources table and prevent showing an ugly flash message when creating a dataset
affected #:  2 files (869 bytes)

--- a/ckanext/iati/controllers/package_iati.py	Wed Sep 14 17:57:33 2011 +0100
+++ b/ckanext/iati/controllers/package_iati.py	Wed Sep 14 18:33:43 2011 +0100
@@ -1,4 +1,5 @@
-from ckan.lib.base import c
+from pylons.i18n import  _
+from ckan.lib.base import c, request, config, h, redirect
 from ckan.lib.helpers import json
 from ckan import model
 from ckan.controllers.package import PackageController
@@ -71,6 +72,26 @@
     def _check_data_dict(self, data_dict):
         return
 
+    def _form_save_redirect(self, pkgname, action):
+        '''This redirects the user to the CKAN package/read page,
+        unless there is request parameter giving an alternate location,
+        perhaps an external website.
+        @param pkgname - Name of the package just edited
+        @param action - What the action of the edit was
+        '''
+        assert action in ('new', 'edit')
+        if action == 'new':
+            msg = _('IATI Record created')
+            h.flash_success(msg,allow_html=True)
+
+        url = request.params.get('return_to') or \
+              config.get('package_%s_return_url' % action)
+        if url:
+            url = url.replace('<NAME>', pkgname)
+        else:
+            url = h.url_for(controller='package', action='read', id=pkgname)
+        redirect(url)        
+
     # End hooks
 
     def get_groups(self,available_only=False):


--- a/ckanext/iati/templates/package/form_iati.html	Wed Sep 14 17:57:33 2011 +0100
+++ b/ckanext/iati/templates/package/form_iati.html	Wed Sep 14 18:33:43 2011 +0100
@@ -146,20 +146,22 @@
                     <th class="field_req resource-url">URL*</th><th class="field_opt resource-format">Format</th><th class="field_opt resource-description">Description</th>
-                    <th class="field_opt resource-hash">Hash</th></tr></thead><tbody><py:for each="num, res in enumerate(data.get('resources', []) + [{}])"><tr>
-                    <py:for each="col in c.resource_columns">
-                    <td py:choose="" class="resource-${col}">
-                        <input py:when="col == 'format'" name="resources__${num}__${col}" type="text" value="${res.get(col, '')}" class="autocomplete-format short" />
+                    <td class="resource-url">
+                        <input name="resources__${num}__url" type="text" value="${res.get('url', '')}" class="medium-width" />
+                    </td>
+                    <td class="resource-format">
+                        <input name="resources__${num}__format" type="text" value="${res.get('format', '')}" class="short"  />
+                    </td>
+                    <td class="resource-description">
+                        <input name="resources__${num}__description" type="text" value="${res.get('description', '')}" class="short" />
+                        <input name="resources__${num}__id" type="hidden" value="${res.get('id', '')}" />
+                    </td>
 
-                        <input py:otherwise="" name="resources__${num}__${col}" type="text" value="${res.get(col, '')}" class="${'medium-width' if col == 'url' else 'short'}" />
-                    </td>
-                    </py:for>
-                    <td class="resource-id"><input name="resources__${num}__id" type="hidden" value="${res.get('id', '')}" /></td></tr></py:for></tbody>

Repository URL: https://bitbucket.org/okfn/ckanextiati/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.




More information about the ckan-changes mailing list