mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-05 16:58:59 +00:00
1113 lines
30 KiB
Python
1113 lines
30 KiB
Python
"""$Id: extension.py 750 2007-04-06 18:40:28Z rubys $"""
|
|
|
|
__author__ = "Sam Ruby <http://intertwingly.net>, Mark Pilgrim <http://diveintomark.org/> and Phil Ringnalda <http://philringnalda.com>"
|
|
__version__ = "$Revision: 750 $"
|
|
__date__ = "$Date: 2007-04-06 18:40:28 +0000 (Fri, 06 Apr 2007) $"
|
|
__copyright__ = "Copyright (c) 2002 Sam Ruby, Mark Pilgrim and Phil Ringnalda"
|
|
|
|
from validators import *
|
|
from logging import *
|
|
|
|
########################################################################
|
|
# Extensions that are valid everywhere #
|
|
########################################################################
|
|
|
|
class extension_everywhere:
|
|
def do_dc_title(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_dc_description(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_dc_publisher(self):
|
|
if "webMaster" in self.children:
|
|
self.log(DuplicateSemantics({"core":"webMaster", "ext":"dc:publisher"}))
|
|
return text() # duplicates allowed
|
|
|
|
def do_dc_contributor(self):
|
|
return text() # duplicates allowed
|
|
|
|
def do_dc_type(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_dc_format(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_dc_identifier(self):
|
|
return text()
|
|
|
|
def do_dc_source(self):
|
|
if "source" in self.children:
|
|
self.log(DuplicateItemSemantics({"core":"source", "ext":"dc:source"}))
|
|
return text(), noduplicates()
|
|
|
|
def do_dc_language(self):
|
|
if "language" in self.children:
|
|
self.log(DuplicateSemantics({"core":"language", "ext":"dc:language"}))
|
|
return iso639(), noduplicates()
|
|
|
|
def do_dc_relation(self):
|
|
return text(), # duplicates allowed
|
|
|
|
def do_dc_coverage(self):
|
|
return text(), # duplicates allowed
|
|
|
|
def do_dc_rights(self):
|
|
if "copyright" in self.children:
|
|
self.log(DuplicateSemantics({"core":"copyright", "ext":"dc:rights"}))
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_dcterms_alternative(self):
|
|
return text() #duplicates allowed
|
|
|
|
def do_dcterms_abstract(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_dcterms_tableOfContents(self):
|
|
return rdfResourceURI(), noduplicates()
|
|
|
|
def do_dcterms_created(self):
|
|
return w3cdtf(), noduplicates()
|
|
|
|
def do_dcterms_valid(self):
|
|
return eater()
|
|
|
|
def do_dcterms_available(self):
|
|
return eater()
|
|
|
|
def do_dcterms_issued(self):
|
|
return w3cdtf(), noduplicates()
|
|
|
|
def do_dcterms_modified(self):
|
|
if "lastBuildDate" in self.children:
|
|
self.log(DuplicateSemantics({"core":"lastBuildDate", "ext":"dcterms:modified"}))
|
|
return w3cdtf(), noduplicates()
|
|
|
|
def do_dcterms_dateAccepted(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_dcterms_dateCopyrighted(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_dcterms_dateSubmitted(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_dcterms_extent(self):
|
|
return positiveInteger(), nonblank(), noduplicates()
|
|
|
|
# def do_dcterms_medium(self):
|
|
# spec defines it as something that should never be used
|
|
# undefined element'll do for now
|
|
|
|
def do_dcterms_isVersionOf(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_hasVersion(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_isReplacedBy(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_replaces(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_isRequiredBy(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_requires(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_isPartOf(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_hasPart(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_isReferencedBy(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_references(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_isFormatOf(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_hasFormat(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_conformsTo(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_dcterms_spatial(self):
|
|
return eater()
|
|
|
|
def do_dcterms_temporal(self):
|
|
return eater()
|
|
|
|
def do_dcterms_audience(self):
|
|
return text()
|
|
|
|
def do_dcterms_mediator(self):
|
|
return text(), noduplicates()
|
|
|
|
# added to DMCI, but no XML mapping has been defined
|
|
def do_dcterms_accessRights(self):
|
|
return eater()
|
|
|
|
def do_dcterms_accrualMethod(self):
|
|
return eater()
|
|
|
|
def do_dcterms_accrualPeriodicity(self):
|
|
return eater()
|
|
|
|
def do_dcterms_accrualPolicy(self):
|
|
return eater()
|
|
|
|
def do_dcterms_bibliographicCitation(self):
|
|
return eater()
|
|
|
|
def do_dcterms_educationLevel(self):
|
|
return eater()
|
|
|
|
def do_dcterms_instructionalMethod(self):
|
|
return eater()
|
|
|
|
def do_dcterms_license(self):
|
|
return eater()
|
|
|
|
def do_dcterms_provenance(self):
|
|
return eater()
|
|
|
|
def do_dcterms_rightsHolder(self):
|
|
return eater()
|
|
|
|
def do_rdfs_seeAlso(self):
|
|
return rdfResourceURI() # duplicates allowed
|
|
|
|
def do_geo_Point(self):
|
|
return geo_point()
|
|
|
|
def do_geo_lat(self):
|
|
return latitude()
|
|
|
|
def do_geo_long(self):
|
|
return longitude()
|
|
|
|
def do_geo_alt(self):
|
|
return decimal()
|
|
|
|
def do_geourl_latitude(self):
|
|
return latitude()
|
|
|
|
def do_geourl_longitude(self):
|
|
return longitude()
|
|
|
|
def do_icbm_latitude(self):
|
|
return latitude()
|
|
|
|
def do_icbm_longitude(self):
|
|
return longitude()
|
|
|
|
########################################################################
|
|
# Extensions that are valid at either the channel or item levels #
|
|
########################################################################
|
|
|
|
from media import media_elements, media_content, media_group
|
|
class extension_channel_item(extension_everywhere, media_elements):
|
|
def do_taxo_topics(self):
|
|
return eater()
|
|
|
|
def do_l_link(self):
|
|
return l_link()
|
|
|
|
|
|
########################################################################
|
|
# Extensions that are valid at only at the item level #
|
|
########################################################################
|
|
|
|
class extension_item(extension_channel_item):
|
|
def do_annotate_reference(self):
|
|
return rdfResourceURI(), noduplicates()
|
|
|
|
def do_ag_source(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_ag_sourceURL(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_ag_timestamp(self):
|
|
return iso8601(), noduplicates()
|
|
|
|
def do_ev_startdate(self):
|
|
return iso8601(), noduplicates()
|
|
|
|
def do_ev_enddate(self):
|
|
return iso8601(), noduplicates()
|
|
|
|
def do_ev_location(self):
|
|
return eater()
|
|
|
|
def do_ev_organizer(self):
|
|
return eater()
|
|
|
|
def do_ev_type(self):
|
|
return text(), noduplicates()
|
|
|
|
def do_foaf_maker(self):
|
|
return eater()
|
|
|
|
def do_foaf_primaryTopic(self):
|
|
return eater()
|
|
|
|
def do_slash_comments(self):
|
|
return nonNegativeInteger()
|
|
|
|
def do_slash_section(self):
|
|
return text()
|
|
|
|
def do_slash_department(self):
|
|
return text()
|
|
|
|
def do_slash_hit_parade(self):
|
|
return commaSeparatedIntegers(), noduplicates()
|
|
|
|
def do_thr_children(self):
|
|
return eater()
|
|
|
|
def do_thr_in_reply_to(self):
|
|
return in_reply_to()
|
|
|
|
def do_wfw_comment(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_wfw_commentRss(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_wfw_commentRSS(self):
|
|
self.log(CommentRSS({"parent":self.parent.name, "element":self.name}))
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_wiki_diff(self):
|
|
return text()
|
|
|
|
def do_wiki_history(self):
|
|
return text()
|
|
|
|
def do_wiki_importance(self):
|
|
return text()
|
|
|
|
def do_wiki_status(self):
|
|
return text()
|
|
|
|
def do_wiki_version(self):
|
|
return text()
|
|
|
|
def do_g_actor(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_age(self):
|
|
return nonNegativeInteger(), noduplicates()
|
|
|
|
def do_g_agent(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_area(self):
|
|
return nonhtml(), noduplicates() # intUnit
|
|
|
|
def do_g_apparel_type(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_artist(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_author(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_bathrooms(self):
|
|
return nonNegativeInteger(), noduplicates()
|
|
|
|
def do_g_bedrooms(self):
|
|
return nonNegativeInteger(), noduplicates()
|
|
|
|
def do_g_brand(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_calories(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_cholesterol(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_color(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_cooking_time(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_condition(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_course(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_course_date_range(self):
|
|
return g_dateTimeRange(), noduplicates()
|
|
|
|
def do_g_course_number(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_course_times(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_cuisine(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_currency(self):
|
|
return iso4217(), noduplicates()
|
|
|
|
def do_g_delivery_notes(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_delivery_radius(self):
|
|
return floatUnit(), noduplicates()
|
|
|
|
def do_g_education(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_employer(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_ethnicity(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_event_date_range(self):
|
|
return g_dateTimeRange(), noduplicates()
|
|
|
|
def do_g_expiration_date(self):
|
|
return iso8601_date(), noduplicates()
|
|
|
|
def do_g_expiration_date_time(self):
|
|
return iso8601(), noduplicates()
|
|
|
|
def do_g_fiber(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_from_location(self):
|
|
return g_locationType(), noduplicates()
|
|
|
|
def do_g_gender(self):
|
|
return g_genderEnumeration(), noduplicates()
|
|
|
|
def do_g_hoa_dues(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_format(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_id(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_image_link(self):
|
|
return rfc2396_full(), maxten()
|
|
|
|
def do_g_immigration_status(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_interested_in(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_isbn(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_job_function(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_job_industry(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_job_type(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_label(self):
|
|
return g_labelType(), maxten()
|
|
|
|
def do_g_listing_type(self):
|
|
return truefalse(), noduplicates()
|
|
|
|
def do_g_location(self):
|
|
return g_full_locationType(), noduplicates()
|
|
|
|
def do_g_main_ingredient(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_make(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_manufacturer(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_manufacturer_id(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_marital_status(self):
|
|
return g_maritalStatusEnumeration(), noduplicates()
|
|
|
|
def do_g_meal_type(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_megapixels(self):
|
|
return floatUnit(), noduplicates()
|
|
|
|
def do_g_memory(self):
|
|
return floatUnit(), noduplicates()
|
|
|
|
def do_g_mileage(self):
|
|
return g_intUnit(), noduplicates()
|
|
|
|
def do_g_model(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_model_number(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_name_of_item_being_reviewed(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_news_source(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_occupation(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_payment_notes(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_pages(self):
|
|
return positiveInteger(), nonblank(), noduplicates()
|
|
|
|
def do_g_payment_accepted(self):
|
|
return g_paymentMethodEnumeration()
|
|
|
|
def do_g_pickup(self):
|
|
return truefalse(), noduplicates()
|
|
|
|
def do_g_preparation_time(self):
|
|
return floatUnit(), noduplicates()
|
|
|
|
def do_g_price(self):
|
|
return floatUnit(), noduplicates()
|
|
|
|
def do_g_price_type(self):
|
|
return g_priceTypeEnumeration(), noduplicates()
|
|
|
|
def do_g_processor_speed(self):
|
|
return floatUnit(), noduplicates()
|
|
|
|
def do_g_product_type(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_property_type(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_protein(self):
|
|
return floatUnit(), noduplicates()
|
|
|
|
def do_g_publication_name(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_publication_volume(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_publish_date(self):
|
|
return iso8601_date(), noduplicates()
|
|
|
|
def do_g_quantity(self):
|
|
return nonNegativeInteger(), nonblank(), noduplicates()
|
|
|
|
def do_g_rating(self):
|
|
return g_ratingTypeEnumeration(), noduplicates()
|
|
|
|
def do_g_review_type(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_reviewer_type(self):
|
|
return g_reviewerTypeEnumeration(), noduplicates()
|
|
|
|
def do_g_salary(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_salary_type(self):
|
|
return g_salaryTypeEnumeration(), noduplicates()
|
|
|
|
def do_g_saturated_fat(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_school_district(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_service_type(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_servings(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_sexual_orientation(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_size(self):
|
|
return nonhtml(), noduplicates() # TODO: expressed in either two or three dimensions.
|
|
|
|
def do_g_shipping(self):
|
|
return g_shipping(), noduplicates()
|
|
|
|
def do_g_sodium(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_subject(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_subject_area(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_tax_percent(self):
|
|
return percentType(), noduplicates()
|
|
|
|
def do_g_tax_region(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_to_location(self):
|
|
return g_locationType(), noduplicates()
|
|
|
|
def do_g_total_carbs(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_total_fat(self):
|
|
return g_float(), noduplicates()
|
|
|
|
def do_g_travel_date_range(self):
|
|
return g_dateTimeRange(), noduplicates()
|
|
|
|
def do_g_university(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_upc(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_url_of_item_being_reviewed(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_g_vehicle_type(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_vin(self):
|
|
return nonhtml(), noduplicates()
|
|
|
|
def do_g_weight(self):
|
|
return floatUnit(), noduplicates()
|
|
|
|
def do_g_year(self):
|
|
return g_year(), noduplicates()
|
|
|
|
def do_media_group(self):
|
|
return media_group()
|
|
|
|
def do_media_content(self):
|
|
return media_content()
|
|
|
|
def do_georss_where(self):
|
|
return georss_where()
|
|
|
|
def do_georss_point(self):
|
|
return gml_pos()
|
|
|
|
def do_georss_line(self):
|
|
return gml_posList()
|
|
|
|
def do_georss_polygon(self):
|
|
return gml_posList()
|
|
|
|
def do_georss_featuretypetag(self):
|
|
return text()
|
|
|
|
def do_georss_relationshiptag(self):
|
|
return text()
|
|
|
|
def do_georss_featurename(self):
|
|
return text()
|
|
|
|
def do_georss_elev(self):
|
|
return decimal()
|
|
|
|
def do_georss_floor(self):
|
|
return Integer()
|
|
|
|
def do_georss_radius(self):
|
|
return Float()
|
|
|
|
class georss_where(validatorBase):
|
|
def do_gml_Point(self):
|
|
return gml_point()
|
|
def do_gml_LineString(self):
|
|
return gml_line()
|
|
def do_gml_Polygon(self):
|
|
return gml_polygon()
|
|
def do_gml_Envelope(self):
|
|
return gml_envelope()
|
|
|
|
class geo_srsName(validatorBase):
|
|
def getExpectedAttrNames(self):
|
|
return [(None, u'srsName')]
|
|
|
|
class gml_point(geo_srsName):
|
|
def do_gml_pos(self):
|
|
return gml_pos()
|
|
|
|
class geo_point(validatorBase):
|
|
def do_geo_lat(self):
|
|
return latitude()
|
|
|
|
def do_geo_long(self):
|
|
return longitude()
|
|
|
|
def validate(self):
|
|
if "geo_lat" not in self.children:
|
|
self.log(MissingElement({"parent":self.name.replace('_',':'), "element":"geo:lat"}))
|
|
if "geo_long" not in self.children:
|
|
self.log(MissingElement({"parent":self.name.replace('_',':'), "element":"geo:long"}))
|
|
|
|
|
|
class gml_pos(text):
|
|
def validate(self):
|
|
if not re.match('^[-+]?\d+\.?\d*[ ,][-+]?\d+\.?\d*$', self.value):
|
|
return self.log(InvalidCoord({'value':self.value}))
|
|
if self.value.find(',')>=0:
|
|
self.log(CoordComma({'value':self.value}))
|
|
|
|
class gml_line(geo_srsName):
|
|
def do_gml_posList(self):
|
|
return gml_posList()
|
|
|
|
class gml_posList(text):
|
|
def validate(self):
|
|
if self.value.find(',')>=0:
|
|
# ensure that commas are only used to separate lat and long
|
|
if not re.match('^[-+.0-9]+[, ][-+.0-9]( [-+.0-9]+[, ][-+.0-9])+$',
|
|
value.strip()):
|
|
return self.log(InvalidCoordList({'value':self.value}))
|
|
self.log(CoordComma({'value':self.value}))
|
|
self.value=self.value.replace(',',' ')
|
|
values = self.value.strip().split()
|
|
if len(values)<3 or len(values)%2 == 1:
|
|
return self.log(InvalidCoordList({'value':self.value}))
|
|
for value in values:
|
|
if not re.match('^[-+]?\d+\.?\d*$', value):
|
|
return self.log(InvalidCoordList({'value':value}))
|
|
|
|
class gml_polygon(geo_srsName):
|
|
def do_gml_exterior(self):
|
|
return gml_exterior()
|
|
|
|
class gml_exterior(validatorBase):
|
|
def do_gml_LinearRing(self):
|
|
return gml_linearRing()
|
|
|
|
class gml_linearRing(geo_srsName):
|
|
def do_gml_posList(self):
|
|
return gml_posList()
|
|
|
|
class gml_envelope(geo_srsName):
|
|
def do_gml_lowerCorner(self):
|
|
return gml_pos()
|
|
def do_gml_upperCorner(self):
|
|
return gml_pos()
|
|
|
|
class access_restriction(enumeration):
|
|
error = InvalidAccessRestrictionRel
|
|
valuelist = ["allow", "deny"]
|
|
|
|
def getExpectedAttrNames(self):
|
|
return [(None, u'relationship')]
|
|
|
|
def prevalidate(self):
|
|
self.children.append(True) # force warnings about "mixed" content
|
|
|
|
if not self.attrs.has_key((None,"relationship")):
|
|
self.log(MissingAttribute({"parent":self.parent.name, "element":self.name, "attr":"relationship"}))
|
|
else:
|
|
self.value=self.attrs.getValue((None,"relationship"))
|
|
|
|
########################################################################
|
|
# Extensions that are valid at only at the RSS 2.0 item level #
|
|
########################################################################
|
|
|
|
class extension_rss20_item(extension_item):
|
|
def do_trackback_ping(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_trackback_about(self):
|
|
return rfc2396_full()
|
|
|
|
def do_dcterms_accessRights(self):
|
|
return eater()
|
|
|
|
def do_dcterms_accrualMethod(self):
|
|
return eater()
|
|
|
|
def do_dcterms_accrualPeriodicity(self):
|
|
return eater()
|
|
|
|
def do_dcterms_accrualPolicy(self):
|
|
return eater()
|
|
|
|
def do_dcterms_bibliographicCitation(self):
|
|
return eater()
|
|
|
|
def do_dcterms_educationLevel(self):
|
|
return eater()
|
|
|
|
def do_dcterms_instructionalMethod(self):
|
|
return eater()
|
|
|
|
def do_dcterms_license(self):
|
|
return eater()
|
|
|
|
def do_dcterms_provenance(self):
|
|
return eater()
|
|
|
|
def do_dcterms_rightsHolder(self):
|
|
return eater()
|
|
|
|
########################################################################
|
|
# Extensions that are valid at only at the RSS 1.0 item level #
|
|
########################################################################
|
|
|
|
class extension_rss10_item(extension_item):
|
|
def do_trackback_ping(self):
|
|
return rdfResourceURI(), noduplicates()
|
|
|
|
def do_trackback_about(self):
|
|
return rdfResourceURI()
|
|
|
|
def do_l_permalink(self):
|
|
return l_permalink()
|
|
|
|
class l_permalink(rdfResourceURI, MimeType):
|
|
lNS = u'http://purl.org/rss/1.0/modules/link/'
|
|
def getExpectedAttrNames(self):
|
|
return rdfResourceURI.getExpectedAttrNames(self) + [(self.lNS, u'type')]
|
|
def validate(self):
|
|
if (self.lNS, 'type') in self.attrs.getNames():
|
|
self.value=self.attrs.getValue((self.lNS, 'type'))
|
|
MimeType.validate(self)
|
|
return rdfResourceURI.validate(self)
|
|
|
|
class l_link(rdfResourceURI, MimeType):
|
|
lNS = u'http://purl.org/rss/1.0/modules/link/'
|
|
def getExpectedAttrNames(self):
|
|
return rdfResourceURI.getExpectedAttrNames(self) + [
|
|
(self.lNS, u'lang'), (self.lNS, u'rel'),
|
|
(self.lNS, u'type'), (self.lNS, u'title')
|
|
]
|
|
def prevalidate(self):
|
|
self.validate_optional_attribute((self.lNS,'lang'), iso639)
|
|
self.validate_required_attribute((self.lNS,'rel'), rfc2396_full)
|
|
self.validate_optional_attribute((self.lNS,'title'), nonhtml)
|
|
|
|
if self.attrs.has_key((self.lNS, "type")):
|
|
if self.attrs.getValue((self.lNS, "type")).find(':') < 0:
|
|
self.validate_optional_attribute((self.lNS,'type'), MimeType)
|
|
else:
|
|
self.validate_optional_attribute((self.lNS,'type'), rfc2396_full)
|
|
|
|
|
|
|
|
########################################################################
|
|
# Extensions that are valid at only at the Atom entry level #
|
|
########################################################################
|
|
|
|
class extension_entry(extension_item):
|
|
def do_dc_creator(self): # atom:creator
|
|
return text() # duplicates allowed
|
|
def do_dc_subject(self): # atom:category
|
|
return text() # duplicates allowed
|
|
def do_dc_date(self): # atom:published
|
|
return w3cdtf(), noduplicates()
|
|
def do_creativeCommons_license(self):
|
|
return rfc2396_full()
|
|
|
|
def do_trackback_ping(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
# XXX This should have duplicate semantics with link[@rel='related']
|
|
def do_trackback_about(self):
|
|
return rfc2396_full()
|
|
|
|
########################################################################
|
|
# Extensions that are valid at only at the channel level #
|
|
########################################################################
|
|
|
|
class extension_channel(extension_channel_item):
|
|
def do_admin_generatorAgent(self):
|
|
if "generator" in self.children:
|
|
self.log(DuplicateSemantics({"core":"generator", "ext":"admin:generatorAgent"}))
|
|
return admin_generatorAgent(), noduplicates()
|
|
|
|
def do_admin_errorReportsTo(self):
|
|
return admin_errorReportsTo(), noduplicates()
|
|
|
|
def do_blogChannel_blogRoll(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_blogChannel_mySubscriptions(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_blogChannel_blink(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_blogChannel_changes(self):
|
|
return rfc2396_full(), noduplicates()
|
|
|
|
def do_sy_updatePeriod(self):
|
|
return sy_updatePeriod(), noduplicates()
|
|
|
|
def do_sy_updateFrequency(self):
|
|
return positiveInteger(), nonblank(), noduplicates()
|
|
|
|
def do_sy_updateBase(self):
|
|
return w3cdtf(), noduplicates()
|
|
|
|
def do_foaf_maker(self):
|
|
return eater()
|
|
|
|
def do_cp_server(self):
|
|
return rdfResourceURI()
|
|
|
|
def do_wiki_interwiki(self):
|
|
return text()
|
|
|
|
def do_thr_in_reply_to(self):
|
|
return in_reply_to()
|
|
|
|
def do_cf_listinfo(self):
|
|
from cf import listinfo
|
|
return listinfo()
|
|
|
|
def do_cf_treatAs(self):
|
|
from cf import treatAs
|
|
return treatAs()
|
|
|
|
def do_opensearch_totalResults(self):
|
|
return nonNegativeInteger(), noduplicates()
|
|
|
|
def do_opensearch_startIndex(self):
|
|
return Integer(), noduplicates()
|
|
|
|
def do_opensearch_itemsPerPage(self):
|
|
return nonNegativeInteger(), noduplicates()
|
|
|
|
def do_opensearch_Query(self):
|
|
from opensearch import Query
|
|
return Query()
|
|
|
|
def do_xhtml_div(self):
|
|
return eater()
|
|
|
|
def do_xhtml_meta(self):
|
|
return xhtml_meta()
|
|
|
|
class xhtml_meta(validatorBase):
|
|
def getExpectedAttrNames(self):
|
|
return [ (None, u'name'), (None, u'content') ]
|
|
def prevalidate(self):
|
|
self.validate_required_attribute((None,'name'), xhtmlMetaEnumeration)
|
|
self.validate_required_attribute((None,'content'), robotsEnumeration)
|
|
|
|
class xhtmlMetaEnumeration(caseinsensitive_enumeration):
|
|
error = InvalidMetaName
|
|
valuelist = ["robots"]
|
|
|
|
class robotsEnumeration(caseinsensitive_enumeration):
|
|
error = InvalidMetaContent
|
|
valuelist = [
|
|
"all", "none",
|
|
"index", "index,follow", "index,nofollow",
|
|
"noindex", "noindex,follow", "noindex,nofollow",
|
|
"follow", "follow,index", "follow,noindex",
|
|
"nofollow", "nofollow,index", "nofollow,noindex"]
|
|
|
|
########################################################################
|
|
# Extensions that are valid at only at the Atom feed level #
|
|
########################################################################
|
|
|
|
class extension_feed(extension_channel):
|
|
def do_dc_creator(self): # atom:creator
|
|
return text() # duplicates allowed
|
|
def do_dc_subject(self): # atom:category
|
|
return text() # duplicates allowed
|
|
def do_dc_date(self): # atom:updated
|
|
return w3cdtf(), noduplicates()
|
|
def do_creativeCommons_license(self):
|
|
return rfc2396_full()
|
|
def do_access_restriction(self):
|
|
return access_restriction()
|
|
|
|
########################################################################
|
|
# Validators #
|
|
########################################################################
|
|
|
|
class admin_generatorAgent(rdfResourceURI): pass
|
|
class admin_errorReportsTo(rdfResourceURI): pass
|
|
|
|
class sy_updatePeriod(text):
|
|
def validate(self):
|
|
if self.value not in ('hourly', 'daily', 'weekly', 'monthly', 'yearly'):
|
|
self.log(InvalidUpdatePeriod({"parent":self.parent.name, "element":self.name, "value":self.value}))
|
|
else:
|
|
self.log(ValidUpdatePeriod({"parent":self.parent.name, "element":self.name, "value":self.value}))
|
|
|
|
class g_complex_type(validatorBase):
|
|
def getExpectedAttrNames(self):
|
|
if self.getFeedType() == TYPE_RSS1:
|
|
return [(u'http://www.w3.org/1999/02/22-rdf-syntax-ns#', u'parseType')]
|
|
else:
|
|
return []
|
|
|
|
class g_shipping(g_complex_type):
|
|
def do_g_service(self):
|
|
return g_serviceTypeEnumeration(), noduplicates()
|
|
def do_g_country(self):
|
|
return iso3166(), noduplicates()
|
|
def do_g_price(self):
|
|
return floatUnit(), noduplicates()
|
|
|
|
class g_dateTimeRange(g_complex_type):
|
|
def do_g_start(self):
|
|
return iso8601(), noduplicates()
|
|
def do_g_end(self):
|
|
return iso8601(), noduplicates()
|
|
|
|
class g_labelType(text):
|
|
def validate(self):
|
|
if self.value.find(',')>=0:
|
|
self.log(InvalidLabel({"parent":self.parent.name, "element":self.name,
|
|
"attr": ':'.join(self.name.split('_',1)), "value":self.value}))
|
|
|
|
class g_locationType(text):
|
|
def validate(self):
|
|
if len(self.value.split(',')) not in [2,3]:
|
|
self.log(InvalidLocation({"parent":self.parent.name, "element":self.name,
|
|
"attr": ':'.join(self.name.split('_',1)), "value":self.value}))
|
|
|
|
class g_full_locationType(text):
|
|
def validate(self):
|
|
fields = self.value.split(',')
|
|
if len(fields) != 5 or 0 in [len(f.strip()) for f in fields]:
|
|
self.log(InvalidFullLocation({"parent":self.parent.name, "element":self.name,
|
|
"attr": ':'.join(self.name.split('_',1)), "value":self.value}))
|
|
|
|
class g_genderEnumeration(enumeration):
|
|
error = InvalidGender
|
|
valuelist = ["Male", "M", "Female", "F"]
|
|
|
|
class g_maritalStatusEnumeration(enumeration):
|
|
error = InvalidMaritalStatus
|
|
valuelist = ["single", "divorced", "separated", "widowed", "married", "in relationship"]
|
|
|
|
class g_paymentMethodEnumeration(enumeration):
|
|
error = InvalidPaymentMethod
|
|
valuelist = ["Cash", "Check", "Visa", "MasterCard",
|
|
"AmericanExpress", "Discover", "WireTransfer"]
|
|
|
|
class g_priceTypeEnumeration(enumeration):
|
|
error = InvalidPriceType
|
|
valuelist = ["negotiable", "starting"]
|
|
|
|
class g_ratingTypeEnumeration(enumeration):
|
|
error = InvalidRatingType
|
|
valuelist = ["1", "2", "3", "4", "5"]
|
|
|
|
class g_reviewerTypeEnumeration(enumeration):
|
|
error = InvalidReviewerType
|
|
valuelist = ["editorial", "user"]
|
|
|
|
class g_salaryTypeEnumeration(enumeration):
|
|
error = InvalidSalaryType
|
|
valuelist = ["starting", "negotiable"]
|
|
|
|
class g_serviceTypeEnumeration(enumeration):
|
|
error = InvalidServiceType
|
|
valuelist = ['FedEx', 'UPS', 'DHL', 'Mail', 'Other', 'Overnight', 'Standard']
|
|
|
|
class g_float(text):
|
|
def validate(self):
|
|
import re
|
|
if not re.match('\d+\.?\d*\s*\w*', self.value):
|
|
self.log(InvalidFloat({"parent":self.parent.name, "element":self.name,
|
|
"attr": ':'.join(self.name.split('_',1)), "value":self.value}))
|
|
|
|
class floatUnit(text):
|
|
def validate(self):
|
|
import re
|
|
if not re.match('\d+\.?\d*\s*\w*$', self.value):
|
|
self.log(InvalidFloatUnit({"parent":self.parent.name, "element":self.name,
|
|
"attr": ':'.join(self.name.split('_',1)), "value":self.value}))
|
|
|
|
class decimal(text):
|
|
def validate(self):
|
|
import re
|
|
if not re.match('[-+]?\d+\.?\d*\s*$', self.value):
|
|
self.log(InvalidFloatUnit({"parent":self.parent.name, "element":self.name,
|
|
"attr": ':'.join(self.name.split('_',1)), "value":self.value}))
|
|
|
|
class g_year(text):
|
|
def validate(self):
|
|
import time
|
|
try:
|
|
year = int(self.value)
|
|
if year < 1900 or year > time.localtime()[0]+4: raise InvalidYear
|
|
except:
|
|
self.log(InvalidYear({"parent":self.parent.name, "element":self.name,
|
|
"attr": ':'.join(self.name.split('_',1)), "value":self.value}))
|
|
|
|
class g_intUnit(text):
|
|
def validate(self):
|
|
try:
|
|
if int(self.value.split(' ')[0].replace(',','')) < 0: raise InvalidIntUnit
|
|
except:
|
|
self.log(InvalidIntUnit({"parent":self.parent.name, "element":self.name,
|
|
"attr": ':'.join(self.name.split('_',1)), "value":self.value}))
|
|
|
|
class maxten(validatorBase):
|
|
def textOK(self):
|
|
pass
|
|
|
|
def prevalidate(self):
|
|
if 10 == len([1 for child in self.parent.children if self.name==child]):
|
|
self.log(TooMany({"parent":self.parent.name, "element":self.name}))
|
|
|
|
class in_reply_to(canonicaluri, xmlbase):
|
|
def getExpectedAttrNames(self):
|
|
return [(None, u'href'), (None, u'ref'), (None, u'source'), (None, u'type')]
|
|
|
|
def validate(self):
|
|
if self.attrs.has_key((None, "href")):
|
|
self.value = self.attrs.getValue((None, "href"))
|
|
self.name = "href"
|
|
xmlbase.validate(self)
|
|
|
|
if self.attrs.has_key((None, "ref")):
|
|
self.value = self.attrs.getValue((None, "ref"))
|
|
self.name = "ref"
|
|
canonicaluri.validate(self)
|
|
|
|
if self.attrs.has_key((None, "source")):
|
|
self.value = self.attrs.getValue((None, "source"))
|
|
self.name = "source"
|
|
xmlbase.validate(self)
|
|
|
|
if self.attrs.has_key((None, "type")):
|
|
self.value = self.attrs.getValue((None, "type"))
|
|
if not mime_re.match(self.value):
|
|
self.log(InvalidMIMEType({"parent":self.parent.name, "element":self.name, "attr":"type", "value":self.value}))
|
|
else:
|
|
self.log(ValidMIMEAttribute({"parent":self.parent.name, "element":self.name, "attr":"type", "value":self.value}))
|