You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by di...@apache.org on 2021/12/30 18:20:35 UTC
[sling-org-apache-sling-sitemap] 01/01: SLING-10562: add google news extension
This is an automated email from the ASF dual-hosted git repository.
diru pushed a commit to branch issue/SLING-10562
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-sitemap.git
commit c68d96a563b2150102937f4a575c7da94b26236c
Author: Dirk Rudolph <di...@apache.org>
AuthorDate: Thu Dec 30 19:20:21 2021 +0100
SLING-10562: add google news extension
---
pom.xml | 2 +-
.../builder/extensions/GoogleNewsExtension.java | 98 +++++++++++
.../extensions/GoogleNewsExtensionProvider.java | 184 +++++++++++++++++++++
.../sitemap/impl/builder/AbstractBuilderTest.java | 3 +-
.../extensions/AlternateLanguageExtensionTest.java | 1 +
.../extensions/GoogleNewsExtensionTest.java | 158 ++++++++++++++++++
src/test/resources/sitemap-news-0.9.xsd | 159 ++++++++++++++++++
7 files changed, 603 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index f36c88c..7b6fee6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,13 +60,13 @@
<excludes combine.children="append">
<!-- Obtained from http://sitemaps.org/ -->
<exclude>src/test/resources/sitemap-0.9.xsd</exclude>
- <!-- Obtained from http://sitemaps.org/ -->
<exclude>src/test/resources/siteindex-0.9.xsd</exclude>
<!-- Obtained from http://w3.org/ -->
<exclude>src/test/resources/xhtml1-strict.xsd</exclude>
<exclude>src/test/resources/xml.xsd</exclude>
<!-- Obtained from https://www.google.com/ -->
<exclude>src/test/resources/sitemap-image-1.1.xsd</exclude>
+ <exclude>src/test/resources/sitemap-news-0.9.xsd</exclude>
</excludes>
</configuration>
</plugin>
diff --git a/src/main/java/org/apache/sling/sitemap/builder/extensions/GoogleNewsExtension.java b/src/main/java/org/apache/sling/sitemap/builder/extensions/GoogleNewsExtension.java
new file mode 100644
index 0000000..46dd5f2
--- /dev/null
+++ b/src/main/java/org/apache/sling/sitemap/builder/extensions/GoogleNewsExtension.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.sitemap.builder.extensions;
+
+import org.apache.sling.sitemap.builder.Extension;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.annotation.versioning.ProviderType;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import java.util.Locale;
+
+/**
+ * An extension to add news metadata to an {@link org.apache.sling.sitemap.builder.Url}.
+ *
+ * @see <a href="https://developers.google.com/search/docs/advanced/sitemaps/news-sitemap">Google News sitemaps</a>
+ */
+@ProviderType
+public interface GoogleNewsExtension extends Extension {
+
+ enum AccessRestriction {
+ SUBSCRIPTION("Subscription"),
+ REGISTRATION("Registration");
+
+ private final String value;
+
+ AccessRestriction(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ enum Genre {
+ PRESS_RELEASE("PressRelease"),
+ SATIRE("Satire"),
+ BLOG("Blog"),
+ OP_ED("OpEd"),
+ OPINION("Opinion"),
+ USER_GENERATED("UserGenerated");
+
+ private final String value;
+
+ Genre(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ @NotNull
+ GoogleNewsExtension setPublicationName(@NotNull String name);
+
+ @NotNull
+ GoogleNewsExtension setPublicationLanguage(@NotNull Locale locale);
+
+ @NotNull
+ GoogleNewsExtension setPublicationDate(@NotNull OffsetDateTime date);
+
+ @NotNull
+ GoogleNewsExtension setPublicationDate(@NotNull LocalDate date);
+
+ @NotNull
+ GoogleNewsExtension setTitle(@NotNull String title);
+
+ @NotNull
+ GoogleNewsExtension setAccessRestriction(AccessRestriction accessRestriction);
+
+ @NotNull
+ GoogleNewsExtension setGenres(Genre... genres);
+
+ @NotNull
+ GoogleNewsExtension setKeywords(String... keywords);
+
+ @NotNull
+ GoogleNewsExtension setStockTickers(String ...stockTickers);
+}
diff --git a/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/GoogleNewsExtensionProvider.java b/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/GoogleNewsExtensionProvider.java
new file mode 100644
index 0000000..14239a5
--- /dev/null
+++ b/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/GoogleNewsExtensionProvider.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.sitemap.impl.builder.extensions;
+
+import org.apache.sling.sitemap.builder.extensions.GoogleNewsExtension;
+import org.apache.sling.sitemap.spi.builder.AbstractExtension;
+import org.apache.sling.sitemap.spi.builder.SitemapExtensionProvider;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.service.component.annotations.Component;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.stream.Collectors;
+
+@Component(
+ property = {
+ SitemapExtensionProvider.PROPERTY_INTERFACE + "=org.apache.sling.sitemap.builder.extensions.GoogleNewsExtension",
+ SitemapExtensionProvider.PROPERTY_PREFIX + "=news",
+ SitemapExtensionProvider.PROPERTY_NAMESPACE + "=http://www.google.com/schemas/sitemap-news/0.9",
+ SitemapExtensionProvider.PROPERTY_LOCAL_NAME + "=news"
+ }
+)
+public class GoogleNewsExtensionProvider implements SitemapExtensionProvider {
+
+ @Override
+ public AbstractExtension newInstance() {
+ return new ExtensionImpl();
+ }
+
+ private static class ExtensionImpl extends AbstractExtension implements GoogleNewsExtension {
+
+ private String publicationName;
+ private String publicationLanguage;
+ private String publicationDate;
+ private String title;
+ private String accessRestriction;
+ private String genres;
+ private String keywords;
+ private String stockTickers;
+
+ @Override
+ @NotNull
+ public GoogleNewsExtension setPublicationName(@NotNull String name) {
+ this.publicationName = name;
+ return this;
+ }
+
+ @Override
+ @NotNull
+ public GoogleNewsExtension setPublicationLanguage(@NotNull Locale locale) {
+ this.publicationLanguage = locale.toLanguageTag().toLowerCase(Locale.ROOT);
+ return this;
+ }
+
+ @Override
+ @NotNull
+ public GoogleNewsExtension setPublicationDate(@NotNull OffsetDateTime date) {
+ this.publicationDate = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(date);
+ return this;
+ }
+
+ @Override
+ @NotNull
+ public GoogleNewsExtension setPublicationDate(@NotNull LocalDate date) {
+ this.publicationDate = DateTimeFormatter.ISO_LOCAL_DATE.format(date);
+ return this;
+ }
+
+ @Override
+ @NotNull
+ public GoogleNewsExtension setTitle(@NotNull String title) {
+ this.title = title;
+ return this;
+ }
+
+ @Override
+ @NotNull
+ public GoogleNewsExtension setAccessRestriction(AccessRestriction accessRestriction) {
+ this.accessRestriction = accessRestriction != null ? accessRestriction.getValue() : null;
+ return this;
+ }
+
+ @Override
+ @NotNull
+ public GoogleNewsExtension setGenres(Genre... genres) {
+ this.genres = genres.length > 0
+ ? Arrays.stream(genres).map(Genre::getValue).collect(Collectors.joining(","))
+ : null;
+ return this;
+ }
+
+ @Override
+ @NotNull
+ public GoogleNewsExtension setKeywords(String... keywords) {
+ this.keywords = keywords.length > 0
+ ? String.join(",", keywords)
+ : null;
+ return this;
+ }
+
+ @Override
+ @NotNull
+ public GoogleNewsExtension setStockTickers(String... stockTickers) {
+ this.stockTickers = stockTickers.length > 0
+ ? String.join(",", stockTickers)
+ : null;
+ return this;
+ }
+
+ @Override
+ public void writeTo(@NotNull XMLStreamWriter writer) throws XMLStreamException {
+ writer.writeStartElement("publication");
+ writer.writeStartElement("name");
+ writer.writeCharacters(required(publicationName,"publication name missing"));
+ writer.writeEndElement();
+ writer.writeStartElement("language");
+ writer.writeCharacters(required(publicationLanguage, "publication language missing"));
+ writer.writeEndElement();
+ writer.writeEndElement();
+
+ if (accessRestriction != null) {
+ writer.writeStartElement("access");
+ writer.writeCharacters(accessRestriction);
+ writer.writeEndElement();
+ }
+
+ if (genres != null) {
+ writer.writeStartElement("genres");
+ writer.writeCharacters(genres);
+ writer.writeEndElement();
+ }
+
+ writer.writeStartElement("publication_date");
+ writer.writeCharacters(required(publicationDate, "publication date missing"));
+ writer.writeEndElement();
+
+ writer.writeStartElement("title");
+ writer.writeCharacters(required(title,"title missing"));
+ writer.writeEndElement();
+
+ if (keywords != null) {
+ writer.writeStartElement("keywords");
+ writer.writeCharacters(keywords);
+ writer.writeEndElement();
+ }
+
+ if (stockTickers != null) {
+ writer.writeStartElement("stock_tickers");
+ writer.writeCharacters(stockTickers);
+ writer.writeEndElement();
+ }
+ }
+
+ private static String required(String object, String message) throws XMLStreamException {
+ if (object == null) {
+ throw new XMLStreamException(message);
+ }
+ return object;
+ }
+ }
+}
diff --git a/src/test/java/org/apache/sling/sitemap/impl/builder/AbstractBuilderTest.java b/src/test/java/org/apache/sling/sitemap/impl/builder/AbstractBuilderTest.java
index e1ceeca..2da65a7 100644
--- a/src/test/java/org/apache/sling/sitemap/impl/builder/AbstractBuilderTest.java
+++ b/src/test/java/org/apache/sling/sitemap/impl/builder/AbstractBuilderTest.java
@@ -43,9 +43,10 @@ public abstract class AbstractBuilderTest {
private static final URL SITEMAP_XSD = AbstractBuilderTest.class.getClassLoader().getResource("sitemap-0.9.xsd");
private static final URL SITEMAP_INDEX_XSD = AbstractBuilderTest.class.getClassLoader().getResource("siteindex-0.9.xsd");
private static final URL SITEMAP_IMAGE_XSD = AbstractBuilderTest.class.getClassLoader().getResource("sitemap-image-1.1.xsd");
+ private static final URL SITEMAP_NEWS_XSD = AbstractBuilderTest.class.getClassLoader().getResource("sitemap-news-0.9.xsd");
protected void assertSitemap(String expected, String given) {
- assertEqualsAndValid(expected, given, XML_XSD, XHTML_XSD, SITEMAP_XSD, SITEMAP_IMAGE_XSD);
+ assertEqualsAndValid(expected, given, XML_XSD, XHTML_XSD, SITEMAP_XSD, SITEMAP_IMAGE_XSD, SITEMAP_NEWS_XSD);
}
protected void assertSitemapIndex(String expected, String given) {
diff --git a/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionTest.java b/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionTest.java
index f4d1dc9..e25c08e 100644
--- a/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionTest.java
+++ b/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionTest.java
@@ -88,6 +88,7 @@ class AlternateLanguageExtensionTest extends AbstractBuilderTest {
// when
Url url = sitemap.addUrl("http://example.ch/de.html");
+ url.addExtension(AlternateLanguageExtension.class);
url.addExtension(AlternateLanguageExtension.class)
.setLocale(Locale.forLanguageTag("fr-ch"));
url.addExtension(AlternateLanguageExtension.class)
diff --git a/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/GoogleNewsExtensionTest.java b/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/GoogleNewsExtensionTest.java
new file mode 100644
index 0000000..bf0bf6a
--- /dev/null
+++ b/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/GoogleNewsExtensionTest.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.sitemap.impl.builder.extensions;
+
+import org.apache.sling.sitemap.SitemapException;
+import org.apache.sling.sitemap.builder.Url;
+import org.apache.sling.sitemap.builder.extensions.GoogleImageExtension;
+import org.apache.sling.sitemap.builder.extensions.GoogleNewsExtension;
+import org.apache.sling.sitemap.impl.builder.AbstractBuilderTest;
+import org.apache.sling.sitemap.impl.builder.SitemapImpl;
+import org.apache.sling.testing.mock.sling.junit5.SlingContext;
+import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
+import org.junit.Ignore;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Locale;
+
+@ExtendWith({SlingContextExtension.class})
+class GoogleNewsExtensionTest extends AbstractBuilderTest {
+
+ final SlingContext context = new SlingContext();
+
+ private ExtensionProviderManager extensionProviderManager;
+
+ @BeforeEach
+ void setup() {
+ context.registerInjectActivateService(new GoogleNewsExtensionProvider());
+ extensionProviderManager = context.registerInjectActivateService(new ExtensionProviderManager());
+ }
+
+ @Test
+ void testGoogleNewsCombinations() throws SitemapException, IOException {
+ // given
+ StringWriter writer = new StringWriter();
+ SitemapImpl sitemap = new SitemapImpl(writer, extensionProviderManager);
+
+ // when
+ Url url = sitemap.addUrl("http://example.ch/de.html");
+ url.addExtension(GoogleNewsExtension.class)
+ .setPublicationName("News 1")
+ .setPublicationLanguage(Locale.ENGLISH)
+ .setPublicationDate(LocalDate.parse("2021-12-30"))
+ .setTitle("News Title 1");
+ url.addExtension(GoogleNewsExtension.class)
+ .setPublicationName("News 2")
+ .setPublicationLanguage(Locale.GERMAN)
+ .setPublicationDate(OffsetDateTime.parse("2021-12-30T12:34:56.000+01:00"))
+ .setTitle("News Title 2");
+ url.addExtension(GoogleNewsExtension.class)
+ .setPublicationName("News 3")
+ .setPublicationLanguage(new Locale("zh", "tw"))
+ .setPublicationDate(OffsetDateTime.parse("2021-12-30T12:34:56.000+02:00"))
+ .setTitle("News Title 3")
+ .setGenres(GoogleNewsExtension.Genre.PRESS_RELEASE, GoogleNewsExtension.Genre.SATIRE)
+ .setAccessRestriction(GoogleNewsExtension.AccessRestriction.SUBSCRIPTION)
+ .setKeywords("foo", "bar")
+ .setStockTickers("NASDAQ:FOO");
+ sitemap.close();
+
+ // then
+
+ assertSitemap(
+ AbstractBuilderTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" " +
+ "xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\">"
+ + "<url>"
+ + "<loc>http://example.ch/de.html</loc>"
+ + "<news:news>"
+ + "<news:publication>"
+ + "<news:name>News 1</news:name>"
+ + "<news:language>en</news:language>"
+ + "</news:publication>"
+ + "<news:publication_date>2021-12-30</news:publication_date>"
+ + "<news:title>News Title 1</news:title>"
+ + "</news:news>"
+ + "<news:news>"
+ + "<news:publication>"
+ + "<news:name>News 2</news:name>"
+ + "<news:language>de</news:language>"
+ + "</news:publication>"
+ + "<news:publication_date>2021-12-30T12:34:56+01:00</news:publication_date>"
+ + "<news:title>News Title 2</news:title>"
+ + "</news:news>"
+ + "<news:news>"
+ + "<news:publication>"
+ + "<news:name>News 3</news:name>"
+ + "<news:language>zh-tw</news:language>"
+ + "</news:publication>"
+ + "<news:access>Subscription</news:access>"
+ + "<news:genres>PressRelease,Satire</news:genres>"
+ + "<news:publication_date>2021-12-30T12:34:56+02:00</news:publication_date>"
+ + "<news:title>News Title 3</news:title>"
+ + "<news:keywords>foo,bar</news:keywords>"
+ + "<news:stock_tickers>NASDAQ:FOO</news:stock_tickers>"
+ + "</news:news>"
+ + "</url>"
+ + "</urlset>",
+ writer.toString()
+ );
+ }
+
+ @Test
+ void testNothingWrittenWhenExtensionMissesMandatoryProperties() throws SitemapException, IOException {
+ // given
+ StringWriter writer = new StringWriter();
+ SitemapImpl sitemap = new SitemapImpl(writer, extensionProviderManager);
+
+ // when
+ Url url = sitemap.addUrl("http://example.ch/de.html");
+ url.addExtension(GoogleNewsExtension.class);
+ url.addExtension(GoogleNewsExtension.class)
+ .setPublicationName("name");
+ url.addExtension(GoogleNewsExtension.class)
+ .setPublicationName("name")
+ .setPublicationLanguage(Locale.ENGLISH);
+ url.addExtension(GoogleNewsExtension.class)
+ .setPublicationName("name")
+ .setPublicationLanguage(Locale.ENGLISH)
+ .setPublicationDate(LocalDate.now());
+ sitemap.close();
+
+ // then
+ assertSitemap(
+ AbstractBuilderTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" " +
+ "xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\">"
+ + "<url>"
+ + "<loc>http://example.ch/de.html</loc>"
+ + "</url>"
+ + "</urlset>",
+ writer.toString()
+ );
+ }
+
+}
diff --git a/src/test/resources/sitemap-news-0.9.xsd b/src/test/resources/sitemap-news-0.9.xsd
new file mode 100644
index 0000000..7e3e7eb
--- /dev/null
+++ b/src/test/resources/sitemap-news-0.9.xsd
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://www.google.com/schemas/sitemap-news/0.9"
+ xmlns="http://www.google.com/schemas/sitemap-news/0.9"
+ elementFormDefault="qualified">
+
+<xsd:annotation>
+ <xsd:documentation>
+ XML Schema for the News Sitemap extension. This schema defines the
+ News-specific elements only; the core Sitemap elements are defined
+ separately.
+
+ Help Center documentation for the News Sitemap extension:
+
+ http://www.google.com/support/news_pub/bin/topic.py?topic=11666
+
+ Copyright 2010 Google Inc. All Rights Reserved.
+ </xsd:documentation>
+</xsd:annotation>
+
+<xsd:element name="news">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="publication">
+ <xsd:annotation>
+ <xsd:documentation>
+ The publication in which the article appears. Required.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="name" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation>
+ Name of the news publication. It must exactly match
+ the name as it appears on your articles in news.google.com,
+ omitting any trailing parentheticals.
+ For example, if the name appears in Google News as
+ "The Example Times (subscription)", you should use
+ "The Example Times". Required.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name="language">
+ <xsd:annotation>
+ <xsd:documentation>
+ Language of the publication. It should be an
+ ISO 639 Language Code (either 2 or 3 letters); see:
+ http://www.loc.gov/standards/iso639-2/php/code_list.php
+ Exception: For Chinese, please use zh-cn for Simplified
+ Chinese or zh-tw for Traditional Chinese. Required.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="zh-cn|zh-tw|([a-z]{2,3})"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="access" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation>
+ Accessibility of the article. Required if access is not open,
+ otherwise this tag should be omitted.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="Subscription"/>
+ <xsd:enumeration value="Registration"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element name="genres" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation>
+ A comma-separated list of properties characterizing the content
+ of the article, such as "PressRelease" or "UserGenerated".
+ For a list of possible values, see:
+ http://www.google.com/support/news_pub/bin/answer.py?answer=93992
+ Required if any genres apply to the article, otherwise this tag
+ should be omitted.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="(PressRelease|Satire|Blog|OpEd|Opinion|UserGenerated)(, *(PressRelease|Satire|Blog|OpEd|Opinion|UserGenerated))*"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element name="publication_date">
+ <xsd:annotation>
+ <xsd:documentation>
+ Article publication date in W3C format, specifying the complete
+ date (YYYY-MM-DD) with optional timestamp. See:
+ http://www.w3.org/TR/NOTE-datetime
+ Please ensure that you give the original date and time at which
+ the article was published on your site; do not give the time
+ at which the article was added to your Sitemap. Required.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:union>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:date"/>
+ </xsd:simpleType>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:dateTime"/>
+ </xsd:simpleType>
+ </xsd:union>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element name="title" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation>
+ Title of the news article. Required.
+ Note: The title may be truncated for space reasons when shown
+ on Google News.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name="keywords" type="xsd:string" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation>
+ Comma-separated list of keywords describing the topic of
+ the article. Keywords may be drawn from, but are not limited to,
+ the list of existing Google News keywords; see:
+ http://www.google.com/support/news_pub/bin/answer.py?answer=116037
+ Optional.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name="stock_tickers" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation>
+ Comma-separated list of up to 5 stock tickers of the companies,
+ mutual funds, or other financial entities that are the main subject
+ of the article. Relevant primarily for business articles.
+ Each ticker must be prefixed by the name of its stock exchange,
+ and must match its entry in Google Finance.
+ For example, "NASDAQ:AMAT" (but not "NASD:AMAT"),
+ or "BOM:500325" (but not "BOM:RIL"). Optional.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="(\w+:\w+(, *\w+:\w+){0,4})?"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+</xsd:element>
+
+</xsd:schema>