// // aegis - project change supervisor // Copyright (C) 2005 Matthew Lee; // Copyright (C) 2006-2008, 2011, 2012, 2014 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include rss_feed::~rss_feed() { trace(("rss_feed::~rss_feed()\n{\n")); pp = 0; cp.reset(); while (size()) delete pop_back(); delete [] item; item = 0; item_max = 0; trace(("}\n")); } rss_feed::rss_feed( project *the_project, const change::pointer &the_change, const nstring &the_file ) : pp(the_project), cp(the_change), filename(the_file), last_build_date(date_string(now())), generator(nstring::format("%s %s", progname_get(), version_stamp())), docs("http://blogs.law.harvard.edu/tech/rss"), item(0), item_count(0), item_max(0) { trace(("rss_feed::rss_feed()\n")); } rss_feed::rss_feed(const rss_feed &arg) : pp(arg.pp), cp(arg.cp), filename(arg.filename) { } rss_feed & rss_feed::operator=(const rss_feed &arg) { if (this != &arg) { pp = arg.pp; cp = arg.cp; filename = arg.filename; } return *this; } class xml_node_rss_channel_item: public xml_node { public: xml_node_rss_channel_item(rss_feed *arg) : other(arg) { } void element_begin(const nstring &) { other->handle_item(); } private: rss_feed *other; }; void rss_feed::handle_item(void) { trace(("rss_feed::handle_item()\n{\n")); push_back(new rss_item()); trace(("}\n")); } rss_item * rss_feed::back(void) const { assert(item_count > 0); return item[item_count - 1]; } rss_item * rss_feed::pop_back(void) { assert(item_count > 0); --item_count; return item[item_count]; } class xml_node_rss_channel_item_title: public xml_node { public: xml_node_rss_channel_item_title(rss_feed *arg) : other(arg) { } void text(const nstring &value) { other->handle_item_title(value); } private: rss_feed *other; }; void rss_feed::handle_item_title(const nstring &value) { trace(("rss_feed::handle_item_title()\n{\n")); back()->handle_title(value); trace(("}\n")); } class xml_node_rss_channel_item_description: public xml_node { public: xml_node_rss_channel_item_description(rss_feed *arg) : other(arg) { } void text(const nstring &value) { other->handle_item_description(value); } private: rss_feed *other; }; void rss_feed::handle_item_description(const nstring &value) { back()->handle_description(value); } class xml_node_rss_channel_item_pub_date: public xml_node { public: xml_node_rss_channel_item_pub_date(rss_feed *arg) : other(arg) { } void text(const nstring &value) { other->handle_item_pub_date(value); } private: rss_feed *other; }; void rss_feed::handle_item_pub_date(const nstring &value) { back()->handle_pub_date(value); } class xml_node_rss_channel_item_link: public xml_node { public: xml_node_rss_channel_item_link(rss_feed *arg) : other(arg) { } void text(const nstring &value) { other->handle_item_link(value); } private: rss_feed *other; }; void rss_feed::handle_item_link(const nstring &value) { back()->handle_link(value); } class xml_node_rss_channel_item_author: public xml_node { public: xml_node_rss_channel_item_author(rss_feed *arg) : other(arg) { } void text(const nstring &value) { other->handle_item_author(value); } private: rss_feed *other; }; void rss_feed::handle_item_author(const nstring &value) { back()->handle_author(value); } class xml_node_rss_channel_item_category: public xml_node { public: xml_node_rss_channel_item_category(rss_feed *arg) : other(arg) { } void text(const nstring &value) { other->handle_item_category(value); } private: rss_feed *other; }; void rss_feed::handle_item_category(const nstring &value) { back()->handle_category(value); } class xml_node_rss_channel_item_comments: public xml_node { public: xml_node_rss_channel_item_comments(rss_feed *arg) : other(arg) { } void text(const nstring &value) { other->handle_item_comments(value); } private: rss_feed *other; }; void rss_feed::handle_item_comments(const nstring &value) { back()->handle_comments(value); } class xml_node_rss_channel_item_enclosure: public xml_node { public: xml_node_rss_channel_item_enclosure(rss_feed *arg) : other(arg) { } void text(const nstring &value) { other->handle_item_enclosure(value); } private: rss_feed *other; }; void rss_feed::handle_item_enclosure(const nstring &value) { back()->handle_enclosure(value); } class xml_node_rss_channel_item_guid: public xml_node { public: xml_node_rss_channel_item_guid(rss_feed *arg) : other(arg), attr(true) { } void attribute(const nstring &name, const nstring &value) { if (name.downcase() == "ispermalink") attr = (value.downcase() == "true"); } void text(const nstring &value) { other->handle_item_guid(value, attr); } private: rss_feed *other; bool attr; }; void rss_feed::handle_item_guid(const nstring &value, bool attr) { back()->handle_guid(value, attr); } class xml_node_rss_channel_item_source: public xml_node { public: xml_node_rss_channel_item_source(rss_feed *arg) : other(arg) { } void text(const nstring &value) { other->handle_item_source(value); } private: rss_feed *other; }; void rss_feed::handle_item_source(const nstring &value) { back()->handle_source(value); } void rss_feed::push_back(rss_item *rip) { if (item_count >= item_max) { size_t new_item_max = item_max * 2 + 16; rss_item **new_item = new rss_item * [new_item_max]; for (size_t j = 0; j < item_count; ++j) new_item[j] = item[j]; delete [] item; item = new_item; item_max = new_item_max; } item[item_count++] = rip; } void rss_feed::parse(void) { trace(("rss_feed::parse()\n{\n")); user_ty::become scoped(pp->get_user()); if (!os_exists(filename)) { trace(("}\n")); return; } input::pointer ip = input_file::open(filename); // // Create the XML file reader, but don't read the file yet. // xml_text_reader_by_node reader(ip); // // Register handlers for the various XML elements. // // It is intentional the none of the elements are // registered, because they are ignored, and the various // descriptions of the channel re-written each time. // xml_node_rss_channel_item i0(this); reader.register_node_handler("rss/channel/item", i0); xml_node_rss_channel_item_title i1(this); reader.register_node_handler("rss/channel/item/title", i1); xml_node_rss_channel_item_description i2(this); reader.register_node_handler("rss/channel/item/description", i2); xml_node_rss_channel_item_pub_date i3(this); reader.register_node_handler("rss/channel/item/pubDate", i3); xml_node_rss_channel_item_link i4(this); reader.register_node_handler("rss/channel/item/link", i4); xml_node_rss_channel_item_author i5(this); reader.register_node_handler("rss/channel/item/author", i5); xml_node_rss_channel_item_category i6(this); reader.register_node_handler("rss/channel/item/category", i6); xml_node_rss_channel_item_comments i7(this); reader.register_node_handler("rss/channel/item/comments", i7); xml_node_rss_channel_item_enclosure i8(this); reader.register_node_handler("rss/channel/item/enclosure", i8); xml_node_rss_channel_item_guid i9(this); reader.register_node_handler("rss/channel/item/guid", i9); xml_node_rss_channel_item_source i10(this); reader.register_node_handler("rss/channel/item/source", i10); // // Read the XML file, processing registered nodes and ignoring all // the rest. // reader.parse(); ip.reset(); trace(("}\n")); } void rss_feed::print(void) const { trace(("rss_feed::print()\n{\n")); // Open a temp file for writing. nstring out_file_name(os_edit_filename(0)); user_ty::become scoped(pp->get_user()); nstring dir(project_rss_path_get(pp, 0)); if (!os_exists(dir)) os_mkdir(dir, 0755); output::pointer op = output_file::open(out_file_name, false); print(op); op.reset(); // // Move the temporary output file to the real feed file. // copy_whole_file(out_file_name.get_ref(), filename.get_ref(), 0); os_unlink_errok(out_file_name); trace(("}\n")); } void rss_feed::print(output::pointer op) const { op->fputs("\n"); op->fputs("\n"); op->fputs("\n"); if (!title.empty()) string_write_xml(op, "title", title); if (!description.empty()) string_write_xml(op, "description", description); if (!language.empty()) string_write_xml(op, "language", language); if (!link.empty()) string_write_xml(op, "link", link); if (!pub_date.empty()) string_write_xml(op, "pubDate", pub_date); if (!last_build_date.empty()) string_write_xml(op, "lastBuildDate", last_build_date); if (!generator.empty()) string_write_xml(op, "generator", generator); if (!docs.empty()) string_write_xml(op, "docs", docs); // Now emit each item for (size_t j = 0; j < item_count; ++j) { const rss_item *ip = item[j]; ip->print(op); } op->fputs("\n"); op->fputs("\n"); } void rss_feed::channel_elements_from_project(void) { trace(("rss_feed::channel_elements_from_project()\n{\n")); title = rss_feed_attribute(pp, filename, rss_feed_title); if (title.empty() && cp) { title = nstring::format ( "Changes in state %s", cstate_state_ename(cp->cstate_get()->state) ); } nstring projname(project_name_get(pp)); title = "Project " + projname + ", " + title; // // "Phrase or sentence describing the channel." // // We will set it from the project attributes every time. // That way it can be changed. // description = rss_feed_attribute(pp, filename, rss_feed_description); if (description.empty() && cp) { cstate_ty *cstate_data = cp->cstate_get(); description = nstring::format ( "Feed of changes in state %s", cstate_state_ename(cstate_data->state) ); } link = rss_script_name_placeholder; link += "/"; link += nstring(project_name_get(pp)); link += "/?menu"; language = rss_feed_attribute(pp, filename, rss_feed_language); if (language.empty()) language = "en-US"; trace(("}\n")); } void rss_feed::channel_elements_from_change(void) { rss_item *rip = new rss_item(); rip->handle_change(cp); push_back(rip); } void rss_feed::title_set(const nstring &arg) { nstring projname(project_name_get(pp)); title = "Project " + projname + ", " + arg.capitalize(); } void rss_feed::description_set(const nstring &arg) { description = arg; } // vim: set ts=8 sw=4 et :