view extension/content/subscribe.js @ 139:8763c90ce556

better support for feeds that require authentication; we now display a custom authentication dialog that hopefully makes more sense when it appears out of the blue; we also use it when subscribing the user to the feed, and it includes UI for the user to request that their credentials be remembered; finally, we use remembered credentials automatically across sessions instead of prompting at least once per session
author Myk Melez <myk@mozilla.org>
date Mon, 07 Jul 2008 01:22:44 -0700
parents 58b04d16257c
children 8e61b480af10
line wrap: on
line source

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;

// modules that come with Firefox
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

// modules that should come with Firefox
Cu.import("resource://snowl/modules/Observers.js");
Cu.import("resource://snowl/modules/URI.js");
Cu.import("resource://snowl/modules/log4moz.js");

// Snowl-specific modules
Cu.import("resource://snowl/modules/service.js");
Cu.import("resource://snowl/modules/datastore.js");
Cu.import("resource://snowl/modules/feed.js");

window.addEventListener("load", function() { Subscriber.init() }, false);

function SubscriptionListener(subject, topic, data) {
  if (subject != Subscriber.feed)
    return;

  switch(topic) {
    case "snowl:subscribe:connect:start":
      document.getElementById("connectingBox").disabled = false;
      document.getElementById("connectingBox").setAttribute("status", "active");
      document.getElementById("gettingMessagesBox").disabled = true;
      document.getElementById("gettingMessagesBox").removeAttribute("status");
      document.getElementById("doneBox").disabled = true;
      document.getElementById("doneBox").removeAttribute("status");
      break;
    case "snowl:subscribe:connect:end":
      if (data < 200 || data > 299)
        document.getElementById("connectingBox").setAttribute("status", "error");
      else
        document.getElementById("connectingBox").setAttribute("status", "complete");
      break;
    case "snowl:subscribe:get:start":
      document.getElementById("gettingMessagesBox").disabled = false;
      document.getElementById("gettingMessagesBox").setAttribute("status", "active");
      break;
    case "snowl:subscribe:get:progress":
      break;
    case "snowl:subscribe:get:end":
      document.getElementById("gettingMessagesBox").setAttribute("status", "complete");
      document.getElementById("doneBox").disabled = false;
      document.getElementById("doneBox").setAttribute("status", "complete");
      break;
  }
}

let Subscriber = {
  _log: Log4Moz.Service.getLogger("Snowl.Subscriber"),


  //**************************************************************************//
  // Initialization & Destruction

  init: function() {
    Observers.add(SubscriptionListener, "snowl:subscribe:connect:start");
    Observers.add(SubscriptionListener, "snowl:subscribe:connect:end");
    Observers.add(SubscriptionListener, "snowl:subscribe:get:start");
    Observers.add(SubscriptionListener, "snowl:subscribe:get:progress");
    Observers.add(SubscriptionListener, "snowl:subscribe:get:end");

    // Parse URL parameters
    let paramString = window.location.search.substr(1);
    let params = {};
    for each (let param in paramString.split("&")) {
      let [name, value] = param.split("=");
      if (value)
        params[name] = decodeURIComponent(value);
      else
        params[name] = value;
    }

    if (params.feed) {
      document.getElementById("snowlLocationTextbox").value = params.feed;
      this.doSubscribe();
    }
  },

  destroy: function() {
    Observers.remove(SubscriptionListener, "snowl:subscribe:connect:start");
    Observers.remove(SubscriptionListener, "snowl:subscribe:connect:end");
    Observers.remove(SubscriptionListener, "snowl:subscribe:get:start");
    Observers.remove(SubscriptionListener, "snowl:subscribe:get:progress");
    Observers.remove(SubscriptionListener, "snowl:subscribe:get:end");
  },


  //**************************************************************************//
  // Event Handlers

  doSubscribe: function() {
    let uri = URI.get(document.getElementById("snowlLocationTextbox").value);
    let feed = new SnowlFeed(null, null, uri);
    this._subscribe(feed);
  },

  doImportOPML: function() {
    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    fp.init(window, "Import OPML", Ci.nsIFilePicker.modeOpen);
    fp.appendFilter("OPML Files", "*.opml");
    fp.appendFilters(Ci.nsIFilePicker.filterXML);
    fp.appendFilters(Ci.nsIFilePicker.filterAll);

    let rv = fp.show();
    if (rv != Ci.nsIFilePicker.returnOK)
      return;

    // FIXME: use a file utility to open the file instead of XMLHttpRequest.
    let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
                  createInstance(Ci.nsIXMLHttpRequest);
    request.open("GET", fp.fileURL.spec, false);
    // Since the file probably ends in .opml, we have to force XHR to treat it
    // as XML by overriding the MIME type it would otherwise select.
    request.overrideMimeType("text/xml");
    request.send(null);
    let xmlDocument = request.responseXML;

    let outline = xmlDocument.getElementsByTagName("body")[0];

    this._importOutline(outline);
  },

  doClose: function() {
    window.close();
  },


  //**************************************************************************//
  // OPML Import

  _importOutline: strand(function(aOutline) {
    // If this outline represents a feed, subscribe the user to the feed.
    let uri = URI.get(aOutline.getAttribute("xmlUrl"));
    if (uri) {
      let name = aOutline.getAttribute("title") || aOutline.getAttribute("text");
      let feed = new SnowlFeed(null, name || "untitled", uri);

      // XXX Should the next line be refactored into _subscribe?
      document.getElementById("sourceTitle").value = "Subscribing to " + (name || uri.spec);

      let future = new Future();
      this._subscribe(feed, future.fulfill);
      yield future.result();
    }

    if (aOutline.hasChildNodes()) {
      let children = aOutline.childNodes;
      for (let i = 0; i < children.length; i++) {
        let child = children[i];

        // Only deal with "outline" elements; ignore text, etc. nodes.
        if (child.nodeName != "outline")
          continue;

        yield this._importOutline(child);
      }
    }
  }),

  _subscribe: strand(function(feed, callback) {
    // FIXME: make sure the user isn't already subscribed to the feed
    // before subscribing them to it.

    // Store a reference to the feed to which we are currently subscribing
    // so the progress listener can filter out events for some other feed.
    this.feed = feed;

    let future = new Future();
    feed.subscribe(future.fulfill);
    yield future.result();

    this.feed = null;

    if (callback)
      callback();
  })

};