Bug 1900280 - allow RSS2 feed with empty title and description, fall back to feed url, like RSS1. r=aleca
We fall back to url for RSS1 as well: https://searchfox.org/comm-central/rev/9154a515faba4ae8533e85edd2b9938bc0f361d2/mailnews/extensions/newsblog/FeedParser.sys.mjs#408 Differential Revision: https://phabricator.services.mozilla.com/D212394 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
84457db292
Коммит
464ccdd29d
|
@ -134,6 +134,10 @@ FeedParser.prototype = {
|
|||
aFeed.description = this.getNodeValueFormatted(tags ? tags[0] : null);
|
||||
tags = this.childrenByTagNameNS(channel, nsURI, "link");
|
||||
aFeed.link = this.validLink(this.getNodeValue(tags ? tags[0] : null));
|
||||
if (!(aFeed.title || aFeed.description) && aFeed.link) {
|
||||
const url = new URL(aFeed.link);
|
||||
aFeed.title = `${url.hostname} - ${url.pathname}`;
|
||||
}
|
||||
|
||||
if (!(aFeed.title || aFeed.description)) {
|
||||
lazy.FeedUtils.log.error(
|
||||
|
|
|
@ -1182,15 +1182,15 @@ export var FeedUtils = {
|
|||
// 1) Replace line breaks and tabs '\n\r\t' with a space.
|
||||
// 2) Remove nonprintable ascii.
|
||||
// 3) Remove invalid win chars '* | \ / : < > ? "'.
|
||||
// 4) Remove all '.' as starting/ending with one is trouble on osx/win.
|
||||
// 4) Remove all starting/ending '.' as that is trouble on osx/win.
|
||||
// 5) No leading/trailing spaces.
|
||||
/* eslint-disable no-control-regex */
|
||||
let folderName = aProposedName
|
||||
.replace(/[\n\r\t]+/g, " ")
|
||||
.replace(/[\x00-\x1F]+/g, "")
|
||||
.replace(/[*|\\\/:<>?"]+/g, "")
|
||||
.replace(/[\.]+/g, "")
|
||||
.trim();
|
||||
.trim()
|
||||
.replace(/(^[\.]+)|([\.]+$)/g, "");
|
||||
/* eslint-enable no-control-regex */
|
||||
|
||||
// Prefix with __ if name is:
|
||||
|
|
|
@ -12,72 +12,70 @@ var { MockRegistrar } = ChromeUtils.importESModule(
|
|||
"resource://testing-common/MockRegistrar.sys.mjs"
|
||||
);
|
||||
|
||||
add_task(async () => {
|
||||
function folderTreeClick(row, event = {}) {
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
folderTree.rows[row].querySelector(".name"),
|
||||
event,
|
||||
about3Pane
|
||||
);
|
||||
}
|
||||
function threadTreeClick(row, event = {}) {
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
threadTree.getRowAtIndex(row),
|
||||
event,
|
||||
about3Pane
|
||||
);
|
||||
}
|
||||
/** @implements {nsIExternalProtocolService} */
|
||||
const mockExternalProtocolService = {
|
||||
QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]),
|
||||
_loadedURLs: [],
|
||||
loadURI(uri) {
|
||||
this._loadedURLs.push(uri.spec);
|
||||
},
|
||||
isExposedProtocol() {
|
||||
return true;
|
||||
},
|
||||
urlLoaded(url) {
|
||||
return this._loadedURLs.includes(url);
|
||||
},
|
||||
};
|
||||
|
||||
/** @implements {nsIExternalProtocolService} */
|
||||
const mockExternalProtocolService = {
|
||||
QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]),
|
||||
_loadedURLs: [],
|
||||
loadURI(uri) {
|
||||
this._loadedURLs.push(uri.spec);
|
||||
},
|
||||
isExposedProtocol() {
|
||||
return true;
|
||||
},
|
||||
urlLoaded(url) {
|
||||
return this._loadedURLs.includes(url);
|
||||
},
|
||||
};
|
||||
const tabmail = document.getElementById("tabmail");
|
||||
const about3Pane = tabmail.currentAbout3Pane;
|
||||
const { folderTree, threadTree, messageBrowser } = about3Pane;
|
||||
|
||||
const mockExternalProtocolServiceCID = MockRegistrar.register(
|
||||
"@mozilla.org/uriloader/external-protocol-service;1",
|
||||
mockExternalProtocolService
|
||||
// Not `currentAboutMessage` as that's null right now.
|
||||
const aboutMessage = messageBrowser.contentWindow;
|
||||
const messagePane = aboutMessage.getMessagePaneBrowser();
|
||||
|
||||
const account = MailServices.accounts.getAccount("account1");
|
||||
const rootFolder = account.incomingServer.rootFolder;
|
||||
|
||||
function folderTreeClick(row, event = {}) {
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
folderTree.rows[row].querySelector(".name"),
|
||||
event,
|
||||
about3Pane
|
||||
);
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
MockRegistrar.unregister(mockExternalProtocolServiceCID);
|
||||
|
||||
// Some tests that open new windows don't return focus to the main window
|
||||
// in a way that satisfies mochitest, and the test times out.
|
||||
Services.focus.focusedWindow = about3Pane;
|
||||
});
|
||||
|
||||
const tabmail = document.getElementById("tabmail");
|
||||
const about3Pane = tabmail.currentAbout3Pane;
|
||||
const { folderTree, threadTree, messageBrowser } = about3Pane;
|
||||
const menu = about3Pane.document.getElementById("folderPaneContext");
|
||||
let menuItem = about3Pane.document.getElementById(
|
||||
"folderPaneContext-subscribe"
|
||||
}
|
||||
function threadTreeClick(row, event = {}) {
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
threadTree.getRowAtIndex(row),
|
||||
event,
|
||||
about3Pane
|
||||
);
|
||||
// Not `currentAboutMessage` as that's null right now.
|
||||
const aboutMessage = messageBrowser.contentWindow;
|
||||
const messagePane = aboutMessage.getMessagePaneBrowser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select account1, bring up the subscription dialog and subscribe to
|
||||
* the given feed URL.
|
||||
*
|
||||
* @param {string} feedURL - The feed URL to subscribe to.
|
||||
* @returns {Promise} when subscription is done.
|
||||
*/
|
||||
async function subsribeToFeed(feedURL) {
|
||||
const account = MailServices.accounts.getAccount("account1");
|
||||
const rootFolder = account.incomingServer.rootFolder;
|
||||
about3Pane.displayFolder(rootFolder.URI);
|
||||
let index = about3Pane.folderTree.selectedIndex;
|
||||
Assert.equal(index, 0);
|
||||
const index = about3Pane.folderTree.selectedIndex;
|
||||
Assert.equal(index, 0, "index 0 (account1 root folder) should be selected");
|
||||
|
||||
let shownPromise = BrowserTestUtils.waitForEvent(menu, "popupshown");
|
||||
const menu = about3Pane.document.getElementById("folderPaneContext");
|
||||
const menuItem = about3Pane.document.getElementById(
|
||||
"folderPaneContext-subscribe"
|
||||
);
|
||||
const shownPromise = BrowserTestUtils.waitForEvent(menu, "popupshown");
|
||||
folderTreeClick(index, { type: "contextmenu" });
|
||||
await shownPromise;
|
||||
|
||||
let hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden");
|
||||
const hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden");
|
||||
const dialogPromise = BrowserTestUtils.promiseAlertDialog(
|
||||
null,
|
||||
"chrome://messenger-newsblog/content/feed-subscriptions.xhtml",
|
||||
|
@ -93,10 +91,7 @@ add_task(async () => {
|
|||
|
||||
EventUtils.synthesizeMouseAtCenter(locationInput, {}, dialogWindow);
|
||||
await TestUtils.waitForCondition(() => !addFeedButton.disabled);
|
||||
EventUtils.sendString(
|
||||
"https://example.org/browser/comm/mailnews/extensions/newsblog/test/browser/data/rss.xml",
|
||||
dialogWindow
|
||||
);
|
||||
EventUtils.sendString(feedURL, dialogWindow);
|
||||
EventUtils.synthesizeKey("VK_TAB", {}, dialogWindow);
|
||||
|
||||
// There's no good way to know if we're ready to continue.
|
||||
|
@ -120,15 +115,61 @@ add_task(async () => {
|
|||
);
|
||||
menu.activateItem(menuItem);
|
||||
await Promise.all([hiddenPromise, dialogPromise]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from the feed currently selected.
|
||||
*
|
||||
* @returns {Promise} when unsubscription is done.
|
||||
*/
|
||||
async function unsubscribeCurrentRow() {
|
||||
const menu = about3Pane.document.getElementById("folderPaneContext");
|
||||
const shownPromise = BrowserTestUtils.waitForEvent(menu, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
about3Pane.folderTree.selectedRow,
|
||||
{ type: "contextmenu" },
|
||||
about3Pane
|
||||
);
|
||||
await shownPromise;
|
||||
|
||||
const hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden");
|
||||
const promptPromise = BrowserTestUtils.promiseAlertDialog("accept");
|
||||
const menuItem = about3Pane.document.getElementById(
|
||||
"folderPaneContext-remove"
|
||||
);
|
||||
menu.activateItem(menuItem);
|
||||
await Promise.all([hiddenPromise, promptPromise]);
|
||||
}
|
||||
|
||||
add_setup(async () => {
|
||||
const mockExternalProtocolServiceCID = MockRegistrar.register(
|
||||
"@mozilla.org/uriloader/external-protocol-service;1",
|
||||
mockExternalProtocolService
|
||||
);
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
MockRegistrar.unregister(mockExternalProtocolServiceCID);
|
||||
|
||||
// Some tests that open new windows don't return focus to the main window
|
||||
// in a way that satisfies mochitest, and the test times out.
|
||||
Services.focus.focusedWindow = about3Pane;
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function testRSS() {
|
||||
await subsribeToFeed(
|
||||
"https://example.org/browser/comm/mailnews/extensions/newsblog/test/browser/data/rss.xml"
|
||||
);
|
||||
|
||||
about3Pane.displayFolder(rootFolder.URI);
|
||||
|
||||
const folder = rootFolder.subFolders.find(f => f.name == "Test Feed");
|
||||
Assert.ok(folder);
|
||||
Assert.ok(folder, "should have added feed folder");
|
||||
|
||||
about3Pane.displayFolder(folder.URI);
|
||||
index = folderTree.selectedIndex;
|
||||
Assert.equal(threadTree.view.rowCount, 1);
|
||||
Assert.equal(threadTree.view.rowCount, 1, "feed should list one item");
|
||||
await TestUtils.waitForCondition(
|
||||
() => threadTree.table.body.childElementCount == 1,
|
||||
() => threadTree.table.body.childElementCount == threadTree.view.rowCount,
|
||||
"waiting for rows to load in the thread tree"
|
||||
);
|
||||
|
||||
|
@ -212,21 +253,68 @@ add_task(async () => {
|
|||
|
||||
// Clean up.
|
||||
|
||||
shownPromise = BrowserTestUtils.waitForEvent(menu, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
about3Pane.folderTree.selectedRow,
|
||||
{ type: "contextmenu" },
|
||||
about3Pane
|
||||
);
|
||||
await shownPromise;
|
||||
|
||||
hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden");
|
||||
const promptPromise = BrowserTestUtils.promiseAlertDialog("accept");
|
||||
menuItem = about3Pane.document.getElementById("folderPaneContext-remove");
|
||||
menu.activateItem(menuItem);
|
||||
await Promise.all([hiddenPromise, promptPromise]);
|
||||
|
||||
await unsubscribeCurrentRow();
|
||||
window.FeedMessageHandler.onSelectPref = 1;
|
||||
|
||||
folderTree.selectedIndex = 0;
|
||||
});
|
||||
|
||||
add_task(async function testSubscribeSampleRss2() {
|
||||
await subsribeToFeed(
|
||||
"https://example.org/browser/comm/mailnews/extensions/newsblog/test/browser/data/sample-rss-2.xml"
|
||||
);
|
||||
|
||||
const folder = rootFolder.subFolders.find(
|
||||
f => f.name == "NASA Space Station News"
|
||||
);
|
||||
Assert.ok(folder, "should have added rss2 folder");
|
||||
about3Pane.displayFolder(folder.URI);
|
||||
Assert.equal(threadTree.view.rowCount, 5, "feed should have five items");
|
||||
await TestUtils.waitForCondition(
|
||||
() => threadTree.table.body.childElementCount == threadTree.view.rowCount,
|
||||
"waiting for rows to load in the thread tree"
|
||||
);
|
||||
|
||||
await unsubscribeCurrentRow();
|
||||
});
|
||||
|
||||
add_task(async function testSubscribeSampleRss092() {
|
||||
await subsribeToFeed(
|
||||
"https://example.org/browser/comm/mailnews/extensions/newsblog/test/browser/data/sample-rss-092.xml"
|
||||
);
|
||||
|
||||
const folder = rootFolder.subFolders.find(
|
||||
f => f.name == "Winnemac Daily News"
|
||||
);
|
||||
Assert.ok(folder, "should have added rss 0.92 folder");
|
||||
|
||||
about3Pane.displayFolder(folder.URI);
|
||||
Assert.equal(threadTree.view.rowCount, 15, "feed should have fifteen items");
|
||||
await TestUtils.waitForCondition(
|
||||
() => threadTree.table.body.childElementCount == threadTree.view.rowCount,
|
||||
"waiting for rows to load in the thread tree"
|
||||
);
|
||||
|
||||
await unsubscribeCurrentRow();
|
||||
});
|
||||
|
||||
add_task(async function testSubscribeRss2EmptyTitleDesc() {
|
||||
await subsribeToFeed(
|
||||
"https://example.org/browser/comm/mailnews/extensions/newsblog/test/browser/data/rss2-empty-title-desc.xml"
|
||||
);
|
||||
|
||||
// Has no title and no description, should fall back to link, and link
|
||||
// should get sanitized. Link is https://example.org/blog/empty-title
|
||||
const folder = rootFolder.subFolders.find(
|
||||
f => f.name == "example.org - blogempty-title"
|
||||
);
|
||||
Assert.ok(folder, "should have added rss empty title folder");
|
||||
|
||||
about3Pane.displayFolder(folder.URI);
|
||||
Assert.equal(threadTree.view.rowCount, 1, "feed should have fifteen items");
|
||||
await TestUtils.waitForCondition(
|
||||
() => threadTree.table.body.childElementCount == threadTree.view.rowCount,
|
||||
"waiting for rows to load in the thread tree"
|
||||
);
|
||||
|
||||
await unsubscribeCurrentRow();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title></title>
|
||||
<link>https://example.org/blog/empty-title/</link>
|
||||
<description></description>
|
||||
<lastBuildDate>Thu, 21 Jan 2021 17:57:54 +0000</lastBuildDate>
|
||||
<language>en-US</language>
|
||||
|
||||
<item>
|
||||
<title>Test Article</title>
|
||||
<link>https://example.org/browser/comm/mailnews/extensions/newsblog/test/browser/data/article.html</link>
|
||||
<pubDate>Wed, 20 Jan 2021 17:00:39 +0000</pubDate>
|
||||
|
||||
<description><![CDATA[
|
||||
<p>This is the description.</p>
|
||||
<p><a href="https://example.org/link/from/description">Here's a link.</a></p>
|
||||
<script>
|
||||
document.body.style.backgroundColor = "red";
|
||||
</script>
|
||||
<noscript>
|
||||
<p>This noscript should display.</p>
|
||||
</noscript>
|
||||
]]></description>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
|
@ -0,0 +1,90 @@
|
|||
<?xml version="1.0"?>
|
||||
<rss version="0.92">
|
||||
<channel>
|
||||
<title>Winnemac Daily News</title>
|
||||
<link>https://winnemac.example.com/</link>
|
||||
<description>Bringing truth and insight to your doorstep every day</description>
|
||||
<lastBuildDate>Fri, 13 Apr 2001 09:03:49 GMT</lastBuildDate>
|
||||
<docs>https://www.rssboard.org/rss-0-9-2</docs>
|
||||
<managingEditor>eleanor.harper@winnemac.example.com (Eleanor Harper)</managingEditor>
|
||||
<webMaster>ethan.nguyen@winnemac.example.com (Ethan Nguyen)</webMaster>
|
||||
<cloud domain="data.example.com" port="80" path="/rpc" registerProcedure="updates.notify" protocol="xml-rpc" />
|
||||
<item>
|
||||
<title>Cats and Dogs Form Unlikely Friendship</title>
|
||||
<link>https://winnemac.example.com/story/151</link>
|
||||
<description>In a heartwarming turn of events, a cat and a dog were spotted playing together in the park, proving that friendships can transcend species.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Local Artist's Painting Sells for Record Price</title>
|
||||
<link>https://winnemac.example.com/story/150</link>
|
||||
<description>A painting by a local artist recently sold at an auction for a staggering amount, setting a new record in the art world.</description>
|
||||
<source url="https://zenith.example.com/feed/rss.xml">Zenith Times</source>
|
||||
</item>
|
||||
<item>
|
||||
<title>New Movie Breaks Box Office Records</title>
|
||||
<link>https://winnemac.example.com/story/149</link>
|
||||
<description>The latest blockbuster movie has shattered box office records, becoming the highest-grossing film of all time. Moviegoers can't get enough of it.</description>
|
||||
<enclosure url="https://winnemac.example.com/audio/movienews.mp3" length="4846097" type="audio/mpeg" />
|
||||
<category domain="https://directory.example.com/">entertainment/movies</category>
|
||||
</item>
|
||||
<item>
|
||||
<description>Our website will be undergoing scheduled maintenance from 2 a.m. to 6 a.m. tomorrow, as we revamp our servers to bring you an even better online news experience.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>World's Largest Ice Cream Sundae Created</title>
|
||||
<link>https://winnemac.example.com/story/148</link>
|
||||
<description>A team of chefs constructed the world's largest ice cream sundae, complete with a variety of toppings and flavors. It's a sight to behold.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Scientists Discover New Species in Amazon Rainforest</title>
|
||||
<link>https://winnemac.example.com/story/147</link>
|
||||
<description>An expedition into the Amazon rainforest led to the discovery of a new species of colorful birds, captivating the scientific community.</description>
|
||||
<enclosure url="https://winnemac.example.com/audio/sciencenews.mp3" length="6701402" type="audio/mpeg" />
|
||||
</item>
|
||||
<item>
|
||||
<title>World's Longest Bridge Opens to the Public</title>
|
||||
<link>https://winnemac.example.com/story/146</link>
|
||||
<description>A groundbreaking engineering marvel, the world's longest bridge, has finally opened, connecting two continents and easing transportation.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Scientists Develop Cure for Common Cold</title>
|
||||
<link>https://winnemac.example.com/story/145</link>
|
||||
<description>After years of research, scientists have unveiled a groundbreaking cure for the common cold, bringing relief to millions of people.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Robotics Competition Sparks Innovation</title>
|
||||
<link>https://winnemac.example.com/story/144</link>
|
||||
<description>Young minds showcase their creativity at a robotics competition, presenting innovative solutions to real-world challenges.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Ancient City Unearthed in the Desert</title>
|
||||
<link>https://winnemac.example.com/story/143</link>
|
||||
<description>Archaeologists make a historic discovery as they uncover the ruins of an ancient city buried deep in the desert sands.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>International Space Station Welcomes New Crew</title>
|
||||
<link>https://winnemac.example.com/story/142</link>
|
||||
<description>The International Space Station receives a fresh crew of astronauts, continuing scientific research and international cooperation in space.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Magical Forest Enchants Visitors</title>
|
||||
<link>https://winnemac.example.com/story/141</link>
|
||||
<description>A mystical forest with glowing plants and ethereal creatures captivates the imagination of visitors, drawing them into a magical realm.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Record-Breaking Heatwave Hits the Nation</title>
|
||||
<link>https://winnemac.example.com/story/140</link>
|
||||
<description>A scorching heatwave sweeps across the nation, setting new temperature records and prompting people to find creative ways to stay cool.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Lost Treasure Found in Sunken Ship</title>
|
||||
<link>https://winnemac.example.com/story/139</link>
|
||||
<description>Divers stumble upon a sunken pirate ship and recover a long-lost treasure chest, sparking excitement among history enthusiasts.</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>World's Largest Ferris Wheel Opens to Visitors</title>
|
||||
<link>https://winnemac.example.com/story/138</link>
|
||||
<description>Foodies rejoice as the city's famous food festival kicks off, offering a diverse range of mouthwatering cuisines from around the world.</description>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>NASA Space Station News</title>
|
||||
<link>https://example.org/</link>
|
||||
<description>A RSS news feed containing the latest NASA press releases on the International Space Station.</description>
|
||||
<language>en-us</language>
|
||||
<pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
|
||||
<lastBuildDate>Fri, 21 Jul 2023 09:04 EDT</lastBuildDate>
|
||||
<docs>https://www.rssboard.org/rss-specification</docs>
|
||||
<generator>Blosxom 2.1.2</generator>
|
||||
<managingEditor>neil.armstrong@example.com (Neil Armstrong)</managingEditor>
|
||||
<webMaster>sally.ride@example.com (Sally Ride)</webMaster>
|
||||
<atom:link href="https://www.rssboard.org/files/sample-rss-2.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Louisiana Students to Hear from NASA Astronauts Aboard Space Station</title>
|
||||
<link>http://www.nasa.gov/press-release/louisiana-students-to-hear-from-nasa-astronauts-aboard-space-station</link>
|
||||
<description>As part of the state's first Earth-to-space call, students from Louisiana will have an opportunity soon to hear from NASA astronauts aboard the International Space Station.</description>
|
||||
<pubDate>Fri, 21 Jul 2023 09:04 EDT</pubDate>
|
||||
<guid>http://www.nasa.gov/press-release/louisiana-students-to-hear-from-nasa-astronauts-aboard-space-station</guid>
|
||||
</item>
|
||||
<item>
|
||||
<description>NASA has selected KBR Wyle Services, LLC, of Fulton, Maryland, to provide mission and flight crew operations support for the International Space Station and future human space exploration.</description>
|
||||
<link>http://www.nasa.gov/press-release/nasa-awards-integrated-mission-operations-contract-iii</link>
|
||||
<pubDate>Thu, 20 Jul 2023 15:05 EDT</pubDate>
|
||||
<guid>http://www.nasa.gov/press-release/nasa-awards-integrated-mission-operations-contract-iii</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>NASA Expands Options for Spacewalking, Moonwalking Suits</title>
|
||||
<link>http://www.nasa.gov/press-release/nasa-expands-options-for-spacewalking-moonwalking-suits-services</link>
|
||||
<description>NASA has awarded Axiom Space and Collins Aerospace task orders under existing contracts to advance spacewalking capabilities in low Earth orbit, as well as moonwalking services for Artemis missions.</description>
|
||||
<enclosure url="http://www.nasa.gov/sites/default/files/styles/1x1_cardfeed/public/thumbnails/image/iss068e027836orig.jpg?itok=ucNUaaGx" length="1032272" type="image/jpeg" />
|
||||
<pubDate>Mon, 10 Jul 2023 14:14 EDT</pubDate>
|
||||
<guid>http://www.nasa.gov/press-release/nasa-expands-options-for-spacewalking-moonwalking-suits-services</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>NASA to Provide Coverage as Dragon Departs Station</title>
|
||||
<link>http://www.nasa.gov/press-release/nasa-to-provide-coverage-as-dragon-departs-station-with-science</link>
|
||||
<description>NASA is set to receive scientific research samples and hardware as a SpaceX Dragon cargo resupply spacecraft departs the International Space Station on Thursday, June 29.</description>
|
||||
<pubDate>Tue, 20 May 2003 08:56:02 GMT</pubDate>
|
||||
<guid>http://www.nasa.gov/press-release/nasa-to-provide-coverage-as-dragon-departs-station-with-science</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>NASA Plans Coverage of Roscosmos Spacewalk Outside Space Station</title>
|
||||
<link>http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp</link>
|
||||
<description>Compared to earlier spacecraft, the International Space Station has many luxuries, but laundry facilities are not one of them. Instead, astronauts have other options.</description>
|
||||
<enclosure url="http://www.nasa.gov/sites/default/files/styles/1x1_cardfeed/public/thumbnails/image/spacex_dragon_june_29.jpg?itok=nIYlBLme" length="269866" type="image/jpeg" />
|
||||
<pubDate>Mon, 26 Jun 2023 12:45 EDT</pubDate>
|
||||
<guid>http://liftoff.msfc.nasa.gov/2003/05/20.html#item570</guid>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
Загрузка…
Ссылка в новой задаче