/** * @file llfloaterproject.cpp * @brief LLFloaterChat class implementation * * Copyright (c) 2002-2007, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. */ #include "llviewerprecompiledheaders.h" #include "llfloaterproject.h" #include "llvieweruictrlfactory.h" // builds floaters from XML #include "llviewerimagelist.h" #include "llscrollcontainer.h" #include "llhttpclient.h" #include "llcallbacklist.h" #include "lltexteditor.h" #include "llagent.h" #include "llviewercommunication.h" #include "llviewercontrol.h" #include "llviewerwindow.h" #include #include const LLString ROOT_URL = "https://network2080.net/slviewer/home.php"; const LLString FOLDER_URL = "https://network2080.net/slviewer/forward.php?metaid="; const LLString PAGE_URL = "https://network2080.net/slviewer/page.php?metaid="; const LLString PLAYABLE_PAGE_URL = "https://network2080.net/slviewer/playcontent.php?metaid="; const LLString QUEUE_URL = "https://network2080.net/slviewer/queue.php?metaid="; const LLString EXIT_QUEUE_URL = "https://network2080.net/slviewer/equeue.php?metaid="; const LLString MY_INFO_URL = "https://network2080.net/slviewer/minfo.php?metaid="; const LLString NOTECARD_URL = "https://network2080.net/slviewer/note.php?metaid="; /** * @brief Handles HTTP request tree responses */ class LLHTTPFetchTree : public LLHTTPClient::Responder { public: LLHTTPFetchTree(S32 id = 0) : mID(id) {} virtual void error(U32 status, const std::string& reason) { llwarns << "Request failed: " << reason << llendl; std::ostringstream o; o << "Error " << status << ": " << reason; } virtual void result(const LLSD& content) { llinfos << "Request completed" << llendl; if ( !content.isArray() ) { llwarns << "Content is not an array! Aborting" << llendl; return; } std::stack new_nodes; for (LLSD::array_const_iterator it = content.beginArray(); it != content.endArray(); ++it) { const LLSD &item = *it; LLNetwork2080Bridge *bridge = NULL; LLString type = item["type"].asString(); LLString name = item["name"].asString(); S32 metaid = item["metaid"].asInteger(); llinfos << "DATA: " << name << ", id " << metaid << "; " << ", type" << type << llendl; if ( type == "PAGE" ) { bridge = new LLNetwork2080PageBridge(metaid, name); } else if ( type == "FOLDER" ) { bridge = new LLNetwork2080FolderBridge(metaid, name, 1); } else if ( type == "PLAYABLE" ) { bridge = new LLNetwork2080PlayableContentBridge(metaid, name); } else if ( type == "PLACEHOLDER" ) { // Folder without children bridge = new LLNetwork2080FolderBridge(metaid, name, 0); } else { llwarns << "I don't know what to do with an item of type " << type << llendl; return; } new_nodes.push(bridge); } while( !new_nodes.empty() ) { gFloaterProject->addNode(mID, new_nodes.top()); new_nodes.pop(); } llinfos << "Finished handling request" << llendl; } private: S32 mID; }; #ifdef DG_INCLUDE_WHITEBOARD /** * @brief Handles HTTP request content responses */ class LLHTTPFetchContent : public LLHTTPClient::Responder { public: LLHTTPFetchContent(S32 id = 0) : mID(id) {} virtual void error(U32 status, const std::string& reason) { llwarns << "Request failed: " << reason << llendl; std::ostringstream o; o << "Error " << status << ": " << reason; } virtual void result(const LLSD& content) { llinfos << "Request completed" << llendl; LLString text = ""; if ( content.isArray() ) { llinfos << "Content is array" << llendl; for (LLSD::array_const_iterator arr_it = content.beginArray(); arr_it != content.endArray(); ++arr_it) { const LLSD &item = *arr_it; if ( item.isMap() ) { for (LLSD::map_const_iterator map_it = item.beginMap(); map_it != item.endMap(); ++map_it) { const LLString key = (*map_it).first; const LLSD &item = (*map_it).second; text += key + ": " + item.asString() + "\n"; } } else { text = "Bad reply format from server.\nReply should have contained a map inside an array."; } } } else { //text = "Bad reply format from server.\nReply should have contained an array at top level."; llinfos << "Content is text" << llendl; text = content.asString(); } gFloaterProject->setWhiteboardText(text); llinfos << "Finished handling request" << llendl; } private: S32 mID; }; #endif LLFloaterNetwork2080* gFloaterProject = NULL; LLViewerImage* LLNetwork2080FolderBridge::getIcon() const { LLUUID icon_uuid("86f00960-c3e9-9680-145d-3beffd743e9c"); LLViewerImage* imagep = gImageList.getImage(icon_uuid, MIPMAP_FALSE, TRUE); imagep->setClamp(TRUE, TRUE); return imagep; } LLString LLNetwork2080FolderBridge::getChildrenURL() const { std::ostringstream o; o << FOLDER_URL << mID; return o.str(); } LLViewerImage* LLNetwork2080PlayableContentBridge::getIcon() const { LLUUID icon_uuid("59a3df81-ed76-06c9-7264-6dada535e7a3"); LLViewerImage* imagep = gImageList.getImage(icon_uuid, MIPMAP_FALSE, TRUE); imagep->setClamp(TRUE, TRUE); return imagep; } LLString LLNetwork2080PlayableContentBridge::getURL() const { std::ostringstream o; o << PLAYABLE_PAGE_URL << mID; return o.str(); } LLViewerImage* LLNetwork2080PageBridge::getIcon() const { LLUUID icon_uuid("23ce8a2c-9ea2-d863-6572-806f0645b0c7"); LLViewerImage* imagep = gImageList.getImage(icon_uuid, MIPMAP_FALSE, TRUE); imagep->setClamp(TRUE, TRUE); return imagep; } LLString LLNetwork2080PageBridge::getURL() const { std::ostringstream o; o << PAGE_URL << mID; return o.str(); } void LLNetwork2080Bridge::openItem() { // no-op. llinfos << "LLNetwork2080Bridge::openItem()" << llendl; } void LLNetwork2080FolderBridge::openItem() { // no-op. llinfos << "LLNetwork2080Bridge::openItem(" << mID << ")" << llendl; if ( mChildren > 0 ) { gFloaterProject->requestChildren(mID); } } void LLNetwork2080Bridge::previewItem() { llinfos << "LLNetwork2080Bridge::previewItem()" << llendl; openItem(); } BOOL LLNetwork2080Bridge::renameItem(const LLString& new_name) { llinfos << "LLNetwork2080Bridge::renameItem()" << llendl; return FALSE; } BOOL LLNetwork2080Bridge::removeItem() { llinfos << "LLNetwork2080Bridge::removeItem()" << llendl; return TRUE; } void LLNetwork2080Bridge::removeBatch(LLDynamicArray& batch) { llinfos << "LLNetwork2080Bridge::removeBatch()" << llendl; } void LLNetwork2080Bridge::move(LLFolderViewEventListener* parent_listener) { llinfos << "LLNetwork2080Bridge::move()" << llendl; } void LLNetwork2080Bridge::cutToClipboard() { llinfos << "LLNetwork2080Bridge::cutToClipboard()" << llendl; } void LLNetwork2080Bridge::pasteFromClipboard() { llinfos << "LLNetwork2080Bridge::pasteFromClipboard()" << llendl; } BOOL LLNetwork2080Bridge::startDrag(EDragAndDropType* type, LLUUID* id) { llinfos << "LLNetwork2080Bridge::startDrag()" << llendl; return FALSE; } BOOL LLNetwork2080Bridge::dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data) { //llinfos << "LLTaskInvFVBridge::dragOrDrop()" << llendl; return FALSE; } // virtual void LLNetwork2080Bridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) { llinfos << "LLNetwork2080Bridge::performAction(" << action << ")" << llendl; } void LLNetwork2080Bridge::buildContextMenu(LLMenuGL& menu, U32 flags) { llinfos << "LLNetwork2080Bridge::buildContextMenu(" << flags << ")" << llendl; } LLFloaterNetwork2080::LLFloaterNetwork2080() { gUICtrlFactory->buildFloater(this, "floater_project.xml"); setVisible(FALSE); mSelectedID = 0; mNodeCount = 0; mWhiteboard = LLViewerUICtrlFactory::getTextEditorByName(this, "whiteboard_text"); mScroller = LLViewerUICtrlFactory::getScrollableContainerByName(this, "tree_scroller"); mTree = new LLFolderView("tree", NULL, mScroller->getRect(), LLNetwork2080Bridge::getRandomID(), this); mTree->setAllowMultiSelect(FALSE); mTree->setSelectCallback(selectionCallback, this); mScroller->setScrolledView(mTree); mScroller->addChild(mTree); mScroller->setFollowsAll(); mTree->setScrollContainer( mScroller ); mTree->getFilter()->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); mTree->getFilter()->setSortOrder(LLInventoryFilter::SO_DATE); mTree->getFilter()->markDefault(); mTree->setSortOrder(LLInventoryFilter::SO_DATE); mTree->getSortFunction()->setComparisonFunction(compare); requestRoot(); childSetAction("queue_btn", onClickQueue, this); childSetAction("exit_queue_btn", onClickExitQueue, this); childSetAction("myinfo_btn", onClickMyInfo, this); childSetAction("queue_status_btn", onClickQueueStatus, this); childSetAction("notecard_btn", onClickNotecard, this); setQueueButtonsStatus(false); gIdleCallbacks.addFunction(idle, this); gViewerCommunication->registerExtension( LLViewerExtension("SirusKnight.Authenticate", 1, viewerCommCallback, this, "Sirus Knight", "Network2080 Authentication") ); mPassword = gSavedPerAccountSettings.getString("Network2080Password"); } LLFloaterNetwork2080::~LLFloaterNetwork2080() { llinfos << "Terminating LLFloaterNetwork2080" << llendl; gIdleCallbacks.deleteFunction(idle, this); } //static void LLFloaterNetwork2080::idle(void* user_data) { LLFloaterNetwork2080* self = (LLFloaterNetwork2080*)user_data; if ( self ) { // Do the real idle self->doIdle(); } } void LLFloaterNetwork2080::doIdle() { if ( mTree ) { if ( getVisible() ) { mTree->arrangeFromRoot(); } } } //static void LLFloaterNetwork2080::selectionCallback(const std::deque< LLFolderViewItem * > &items, BOOL user_action, void *data) { if ( items.empty() ) { llwarns << "Selection empty, returning" << llendl; return; } LLFloaterNetwork2080 *floater = (LLFloaterNetwork2080*)data; LLFolderViewItem *selected = items.front(); LLNetwork2080Bridge *bridge = dynamic_cast(selected->getListener()); LLString url = bridge->getURL(); floater->setQueueButtonsStatus(bridge->getQueueButtonsEnabled()); floater->mSelectedID = bridge->getID(); if ( !url.empty() ) { #ifdef DG_INCLUDE_WHITEBOARD floater->setWhiteboardText("Loading..."); llinfos << "Requesting whiteboard content for " << bridge->getID() << ", URL " << url << llendl; LLHTTPClient::get(url, new LLHTTPFetchContent(bridge->getID()), 30.0f); #else floater->setWhiteboardText("Whiteboard disabled"); #endif } else { floater->setWhiteboardText(""); } } //static void LLFloaterNetwork2080::toggle(void*) { if (!gFloaterProject) { llinfos << "No project floater!" << llendl; return; } if (gFloaterProject->getVisible()) { gFloaterProject->close(); } else { gFloaterProject->show(); } } void LLFloaterNetwork2080::addNode(S32 parent, LLNetwork2080Bridge *bridge) { S32 id = bridge->getID(); LLFolderViewFolder *child = NULL; LLFolderViewFolder *parent_folder = NULL; if ( id == parent ) { llwarns << "Ignoring child " << id << ", as it's a child of itself" << llendl; delete bridge; return; } llinfos << "Adding child: parent " << parent << ", id " << id << ", name " << bridge->getName() << llendl; if ( 0 == parent ) { parent_folder = mTree; } else { parent_folder = dynamic_cast(mTree->getItemByID( LLNetwork2080Bridge::makeID( parent ) )); } if ( !parent_folder ) { llwarns << "Failed to find root to add entry '" << bridge->getName() << "' to parent " << parent << ", with id " << id << llendl; delete bridge; return; } llinfos << "Parent folder: " << parent_folder->getName() << llendl; mNodeCount++; llinfos << "Creating node with order " << mNodeCount << llendl; bridge->setSortOrder(mNodeCount); child = new LLFolderViewFolder(bridge->getName(), bridge->getIcon(), mTree, bridge); child->setOpen(FALSE); child->addToFolder( parent_folder, mTree ); child->getSortFunction()->setComparisonFunction(compare); child->resort(NULL); mTree->sortBy(0); mTree->sortBy(LLInventoryFilter::SO_FOLDERS_BY_NAME | LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); mTree->resort(NULL); LLNetwork2080FolderBridge *parent_bridge = dynamic_cast( parent_folder->getListener() ); if ( parent_bridge ) { parent_bridge->setLoaded(true); } } void LLFloaterNetwork2080::show() { // Make sure we make a noise. open(); } void LLFloaterNetwork2080::requestRoot() { mTree->deleteAllChildren(); LLHTTPClient::get(ROOT_URL , new LLHTTPFetchTree(0), 30.0f); llinfos << "Requesting root" << llendl; } void LLFloaterNetwork2080::requestChildren(S32 parent_id) { LLUUID uid = LLNetwork2080Bridge::makeID( parent_id ); LLNetwork2080FolderBridge *bridge; LLString url; LLFolderViewFolder *parent = dynamic_cast(mTree->getItemByID( uid )); if ( !parent ) return; bridge = dynamic_cast(parent->getListener()); if ( bridge ) { if ( bridge->isLoaded() || bridge->isLoading() ) { llinfos << "Already retrieved" << llendl; return; } else if ( !bridge->hasChildren() ) { llinfos << "No children" << llendl; return; } else { //bridge->setLoaded(true); bridge->setLoading(true); } } else { llwarns << "Failed to get bridge!" << llendl; return; } url = bridge->getChildrenURL(); if ( !url.empty() ) { llinfos << "Requesting child, parent " << parent_id << ", UID " << uid << ", URL " << url << llendl; LLHTTPClient::get(url, new LLHTTPFetchTree(parent_id), 30.0f); } } void LLFloaterNetwork2080::setWhiteboardText(const LLString &text) { mWhiteboard->setText(text); } void LLFloaterNetwork2080::setQueueButtonsStatus(bool status) { childSetEnabled("queue_btn", status); childSetEnabled("myinfo_btn", status); childSetEnabled("notecard_btn", status); childSetEnabled("queue_status_btn", status); childSetEnabled("exit_queue_btn", status); } LLString LLFloaterNetwork2080::buildURL(const LLString &baseurl, S32 id, bool send_key) { std::ostringstream o; LLUUID key("7aa440e7-4971-4a6c-8289-6960f2e7781e"); // HACK if ( ! gAgent.getID().isNull() ) { key = gAgent.getID(); } else { llwarns << "Agent's key not set, using hardcoded one" << llendl; } o << baseurl << id; if ( send_key ) { o << "&key=" << key << "&password=" << mPassword; } return o.str(); } void LLFloaterNetwork2080::doSelectedPageCommand(const LLString &baseurl) { #ifdef DG_INCLUDE_WHITEBOARD setWhiteboardText("Loading..."); LLString url = buildURL(baseurl, mSelectedID, true); llinfos << "Loading url " << url << llendl; LLHTTPClient::get(url, new LLHTTPFetchContent(mSelectedID), 30.0f); #else setWhiteboardText("Whiteboard disabled"); #endif } //static void LLFloaterNetwork2080::onClickQueue(void *userdata) { LLFloaterNetwork2080 *floater = static_cast(userdata); if ( floater->mPassword.empty() ) { gViewerWindow->alertXml("Network2080NoPassword"); return; } floater->doSelectedPageCommand(QUEUE_URL); } //static void LLFloaterNetwork2080::onClickExitQueue(void *userdata) { LLFloaterNetwork2080 *floater = static_cast(userdata); floater->doSelectedPageCommand(EXIT_QUEUE_URL); } //static void LLFloaterNetwork2080::onClickMyInfo(void *userdata) { LLFloaterNetwork2080 *floater = static_cast(userdata); floater->doSelectedPageCommand(MY_INFO_URL); } //static void LLFloaterNetwork2080::onClickNotecard(void *userdata) { LLFloaterNetwork2080 *floater = static_cast(userdata); floater->doSelectedPageCommand(NOTECARD_URL);} //static void LLFloaterNetwork2080::onClickQueueStatus(void *userdata) { LLFloaterNetwork2080 *floater = static_cast(userdata); floater->doSelectedPageCommand(PLAYABLE_PAGE_URL); } //static bool LLFloaterNetwork2080::compare(LLInventorySort*self, LLFolderViewItem* a, LLFolderViewItem* b) { LLNetwork2080Bridge* bridge_a = dynamic_cast(a->getListener()); LLNetwork2080Bridge* bridge_b = dynamic_cast(b->getListener()); if ( ! bridge_a ) { llwarns << "Can't compare " << a->getName() << " with " << b->getName() << ": element " << a->getName() << " has a bridge of the wrong type" << llendl; return false; } if ( ! bridge_b ) { llwarns << "Can't compare " << a->getName() << " with " << b->getName() << ": element " << b->getName() << " has a bridge of the wrong type" << llendl; return false; } return ( bridge_a->getSortOrder() > bridge_b->getSortOrder() ); } //static void LLFloaterNetwork2080::viewerCommCallback(LLString& data, LLViewerCircuit& circuit, void* userdata) { LLFloaterNetwork2080 *self = static_cast(userdata); self->mPassword = data; gSavedPerAccountSettings.setString("Network2080Password", data); llinfos << "Received Network2080 password: " << data << llendl; circuit.sendReply("OK"); } //