00001
00029 #include "llviewerprecompiledheaders.h"
00030 #include "llfloaterproject.h"
00031 #include "llvieweruictrlfactory.h"
00032 #include "llviewerimagelist.h"
00033 #include "llscrollcontainer.h"
00034 #include "llhttpclient.h"
00035 #include "llcallbacklist.h"
00036 #include "lltexteditor.h"
00037 #include "llagent.h"
00038 #include "llviewercommunication.h"
00039 #include "llviewercontrol.h"
00040 #include "llviewerwindow.h"
00041
00042 #include <string>
00043
00044 #include <stack>
00045
00046
00047
00048 const LLString ROOT_URL = "https://network2080.net/slviewer/home.php";
00049 const LLString FOLDER_URL = "https://network2080.net/slviewer/forward.php?metaid=";
00050 const LLString PAGE_URL = "https://network2080.net/slviewer/page.php?metaid=";
00051 const LLString PLAYABLE_PAGE_URL = "https://network2080.net/slviewer/playcontent.php?metaid=";
00052
00053 const LLString QUEUE_URL = "https://network2080.net/slviewer/queue.php?metaid=";
00054 const LLString EXIT_QUEUE_URL = "https://network2080.net/slviewer/equeue.php?metaid=";
00055 const LLString MY_INFO_URL = "https://network2080.net/slviewer/minfo.php?metaid=";
00056 const LLString NOTECARD_URL = "https://network2080.net/slviewer/note.php?metaid=";
00057
00058
00062 class LLHTTPFetchTree : public LLHTTPClient::Responder
00063 {
00064 public:
00065 LLHTTPFetchTree(S32 id = 0) : mID(id) {}
00066
00067 virtual void error(U32 status, const std::string& reason)
00068 {
00069 llwarns << "Request failed: " << reason << llendl;
00070
00071 std::ostringstream o;
00072 o << "Error " << status << ": " << reason;
00073 }
00074 virtual void result(const LLSD& content)
00075 {
00076 llinfos << "Request completed" << llendl;
00077 if ( !content.isArray() )
00078 {
00079 llwarns << "Content is not an array! Aborting" << llendl;
00080 return;
00081 }
00082
00083 std::stack<LLNetwork2080Bridge*> new_nodes;
00084
00085 for (LLSD::array_const_iterator it = content.beginArray(); it != content.endArray(); ++it)
00086 {
00087 const LLSD &item = *it;
00088 LLNetwork2080Bridge *bridge = NULL;
00089
00090 LLString type = item["type"].asString();
00091 LLString name = item["name"].asString();
00092 S32 metaid = item["metaid"].asInteger();
00093
00094
00095 llinfos << "DATA: " << name << ", id " << metaid << "; " << ", type" << type << llendl;
00096
00097
00098 if ( type == "PAGE" )
00099 {
00100 bridge = new LLNetwork2080PageBridge(metaid, name);
00101 }
00102 else if ( type == "FOLDER" )
00103 {
00104 bridge = new LLNetwork2080FolderBridge(metaid, name, 1);
00105 }
00106 else if ( type == "PLAYABLE" )
00107 {
00108 bridge = new LLNetwork2080PlayableContentBridge(metaid, name);
00109 }
00110 else if ( type == "PLACEHOLDER" )
00111 {
00112
00113 bridge = new LLNetwork2080FolderBridge(metaid, name, 0);
00114 }
00115 else
00116 {
00117 llwarns << "I don't know what to do with an item of type " << type << llendl;
00118 return;
00119 }
00120
00121 new_nodes.push(bridge);
00122 }
00123
00124 while( !new_nodes.empty() )
00125 {
00126 gFloaterProject->addNode(mID, new_nodes.top());
00127 new_nodes.pop();
00128 }
00129
00130 llinfos << "Finished handling request" << llendl;
00131 }
00132 private:
00133 S32 mID;
00134 };
00135
00136 #ifdef DG_INCLUDE_WHITEBOARD
00137
00141 class LLHTTPFetchContent : public LLHTTPClient::Responder
00142 {
00143 public:
00144 LLHTTPFetchContent(S32 id = 0) : mID(id) {}
00145
00146 virtual void error(U32 status, const std::string& reason)
00147 {
00148 llwarns << "Request failed: " << reason << llendl;
00149
00150 std::ostringstream o;
00151 o << "Error " << status << ": " << reason;
00152 }
00153 virtual void result(const LLSD& content)
00154 {
00155 llinfos << "Request completed" << llendl;
00156 LLString text = "";
00157
00158 if ( content.isArray() )
00159 {
00160 llinfos << "Content is array" << llendl;
00161 for (LLSD::array_const_iterator arr_it = content.beginArray(); arr_it != content.endArray(); ++arr_it)
00162 {
00163 const LLSD &item = *arr_it;
00164
00165 if ( item.isMap() )
00166 {
00167 for (LLSD::map_const_iterator map_it = item.beginMap(); map_it != item.endMap(); ++map_it)
00168 {
00169 const LLString key = (*map_it).first;
00170 const LLSD &item = (*map_it).second;
00171 text += key + ": " + item.asString() + "\n";
00172 }
00173 }
00174 else
00175 {
00176 text = "Bad reply format from server.\nReply should have contained a map inside an array.";
00177 }
00178 }
00179 }
00180 else
00181 {
00182
00183 llinfos << "Content is text" << llendl;
00184 text = content.asString();
00185 }
00186
00187 gFloaterProject->setWhiteboardText(text);
00188
00189 llinfos << "Finished handling request" << llendl;
00190 }
00191 private:
00192 S32 mID;
00193 };
00194
00195 #endif
00196
00197 LLFloaterNetwork2080* gFloaterProject = NULL;
00198
00199
00200
00201
00202 LLViewerImage* LLNetwork2080FolderBridge::getIcon() const
00203 {
00204 LLUUID icon_uuid("86f00960-c3e9-9680-145d-3beffd743e9c");
00205
00206 LLViewerImage* imagep = gImageList.getImage(icon_uuid, MIPMAP_FALSE, TRUE);
00207 imagep->setClamp(TRUE, TRUE);
00208 return imagep;
00209 }
00210
00211 LLString LLNetwork2080FolderBridge::getChildrenURL() const
00212 {
00213 std::ostringstream o;
00214 o << FOLDER_URL << mID;
00215 return o.str();
00216 }
00217
00218 LLViewerImage* LLNetwork2080PlayableContentBridge::getIcon() const
00219 {
00220 LLUUID icon_uuid("59a3df81-ed76-06c9-7264-6dada535e7a3");
00221
00222 LLViewerImage* imagep = gImageList.getImage(icon_uuid, MIPMAP_FALSE, TRUE);
00223 imagep->setClamp(TRUE, TRUE);
00224 return imagep;
00225 }
00226
00227 LLString LLNetwork2080PlayableContentBridge::getURL() const
00228 {
00229 std::ostringstream o;
00230 o << PLAYABLE_PAGE_URL << mID;
00231 return o.str();
00232 }
00233
00234
00235 LLViewerImage* LLNetwork2080PageBridge::getIcon() const
00236 {
00237 LLUUID icon_uuid("23ce8a2c-9ea2-d863-6572-806f0645b0c7");
00238
00239 LLViewerImage* imagep = gImageList.getImage(icon_uuid, MIPMAP_FALSE, TRUE);
00240 imagep->setClamp(TRUE, TRUE);
00241 return imagep;
00242 }
00243
00244 LLString LLNetwork2080PageBridge::getURL() const
00245 {
00246 std::ostringstream o;
00247 o << PAGE_URL << mID;
00248 return o.str();
00249 }
00250
00251 void LLNetwork2080Bridge::openItem()
00252 {
00253
00254 llinfos << "LLNetwork2080Bridge::openItem()" << llendl;
00255 }
00256
00257
00258 void LLNetwork2080FolderBridge::openItem()
00259 {
00260
00261 llinfos << "LLNetwork2080Bridge::openItem(" << mID << ")" << llendl;
00262
00263 if ( mChildren > 0 )
00264 {
00265 gFloaterProject->requestChildren(mID);
00266 }
00267 }
00268
00269
00270 void LLNetwork2080Bridge::previewItem()
00271 {
00272 llinfos << "LLNetwork2080Bridge::previewItem()" << llendl;
00273 openItem();
00274 }
00275
00276
00277 BOOL LLNetwork2080Bridge::renameItem(const LLString& new_name)
00278 {
00279 llinfos << "LLNetwork2080Bridge::renameItem()" << llendl;
00280 return FALSE;
00281 }
00282
00283
00284 BOOL LLNetwork2080Bridge::removeItem()
00285 {
00286 llinfos << "LLNetwork2080Bridge::removeItem()" << llendl;
00287 return TRUE;
00288 }
00289
00290 void LLNetwork2080Bridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch)
00291 {
00292 llinfos << "LLNetwork2080Bridge::removeBatch()" << llendl;
00293 }
00294
00295 void LLNetwork2080Bridge::move(LLFolderViewEventListener* parent_listener)
00296 {
00297 llinfos << "LLNetwork2080Bridge::move()" << llendl;
00298 }
00299
00300 void LLNetwork2080Bridge::cutToClipboard()
00301 {
00302 llinfos << "LLNetwork2080Bridge::cutToClipboard()" << llendl;
00303 }
00304
00305 void LLNetwork2080Bridge::pasteFromClipboard()
00306 {
00307 llinfos << "LLNetwork2080Bridge::pasteFromClipboard()" << llendl;
00308 }
00309
00310 BOOL LLNetwork2080Bridge::startDrag(EDragAndDropType* type, LLUUID* id)
00311 {
00312 llinfos << "LLNetwork2080Bridge::startDrag()" << llendl;
00313 return FALSE;
00314 }
00315
00316 BOOL LLNetwork2080Bridge::dragOrDrop(MASK mask, BOOL drop,
00317 EDragAndDropType cargo_type,
00318 void* cargo_data)
00319 {
00320
00321 return FALSE;
00322 }
00323
00324
00325 void LLNetwork2080Bridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
00326 {
00327 llinfos << "LLNetwork2080Bridge::performAction(" << action << ")" << llendl;
00328 }
00329
00330 void LLNetwork2080Bridge::buildContextMenu(LLMenuGL& menu, U32 flags)
00331 {
00332 llinfos << "LLNetwork2080Bridge::buildContextMenu(" << flags << ")" << llendl;
00333 }
00334
00335
00336
00337
00338
00339 LLFloaterNetwork2080::LLFloaterNetwork2080()
00340 {
00341 gUICtrlFactory->buildFloater(this, "floater_project.xml");
00342 setVisible(FALSE);
00343
00344 mSelectedID = 0;
00345 mNodeCount = 0;
00346
00347
00348 mWhiteboard = LLViewerUICtrlFactory::getTextEditorByName(this, "whiteboard_text");
00349 mScroller = LLViewerUICtrlFactory::getScrollableContainerByName(this, "tree_scroller");
00350 mTree = new LLFolderView("tree", NULL, mScroller->getRect(), LLNetwork2080Bridge::getRandomID(), this);
00351 mTree->setAllowMultiSelect(FALSE);
00352 mTree->setSelectCallback(selectionCallback, this);
00353
00354 mScroller->setScrolledView(mTree);
00355 mScroller->addChild(mTree);
00356
00357
00358 mScroller->setFollowsAll();
00359 mTree->setScrollContainer( mScroller );
00360
00361 mTree->getFilter()->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS);
00362 mTree->getFilter()->setSortOrder(LLInventoryFilter::SO_DATE);
00363 mTree->getFilter()->markDefault();
00364 mTree->setSortOrder(LLInventoryFilter::SO_DATE);
00365 mTree->getSortFunction()->setComparisonFunction(compare);
00366
00367 requestRoot();
00368
00369
00370 childSetAction("queue_btn", onClickQueue, this);
00371 childSetAction("exit_queue_btn", onClickExitQueue, this);
00372 childSetAction("myinfo_btn", onClickMyInfo, this);
00373 childSetAction("queue_status_btn", onClickQueueStatus, this);
00374 childSetAction("notecard_btn", onClickNotecard, this);
00375
00376
00377 setQueueButtonsStatus(false);
00378 gIdleCallbacks.addFunction(idle, this);
00379
00380 gViewerCommunication->registerExtension( LLViewerExtension("SirusKnight.Authenticate", 1, viewerCommCallback, this, "Sirus Knight", "Network2080 Authentication") );
00381 mPassword = gSavedPerAccountSettings.getString("Network2080Password");
00382 }
00383
00384 LLFloaterNetwork2080::~LLFloaterNetwork2080()
00385 {
00386 llinfos << "Terminating LLFloaterNetwork2080" << llendl;
00387 gIdleCallbacks.deleteFunction(idle, this);
00388 }
00389
00390
00391 void LLFloaterNetwork2080::idle(void* user_data)
00392 {
00393 LLFloaterNetwork2080* self = (LLFloaterNetwork2080*)user_data;
00394 if ( self )
00395 {
00396 self->doIdle();
00397 }
00398 }
00399
00400 void LLFloaterNetwork2080::doIdle()
00401 {
00402 if ( mTree )
00403 {
00404 if ( getVisible() )
00405 {
00406 mTree->arrangeFromRoot();
00407 }
00408 }
00409 }
00410
00411
00412 void LLFloaterNetwork2080::selectionCallback(const std::deque< LLFolderViewItem * > &items, BOOL user_action, void *data)
00413 {
00414 if ( items.empty() )
00415 {
00416 llwarns << "Selection empty, returning" << llendl;
00417 return;
00418 }
00419
00420 LLFloaterNetwork2080 *floater = (LLFloaterNetwork2080*)data;
00421 LLFolderViewItem *selected = items.front();
00422 LLNetwork2080Bridge *bridge = dynamic_cast<LLNetwork2080Bridge*>(selected->getListener());
00423
00424 LLString url = bridge->getURL();
00425 floater->setQueueButtonsStatus(bridge->getQueueButtonsEnabled());
00426 floater->mSelectedID = bridge->getID();
00427
00428
00429 if ( !url.empty() )
00430 {
00431 #ifdef DG_INCLUDE_WHITEBOARD
00432 floater->setWhiteboardText("Loading...");
00433 llinfos << "Requesting whiteboard content for " << bridge->getID() << ", URL " << url << llendl;
00434 LLHTTPClient::get(url, new LLHTTPFetchContent(bridge->getID()), 30.0f);
00435 #else
00436 floater->setWhiteboardText("Whiteboard disabled");
00437 #endif
00438 }
00439 else
00440 {
00441 floater->setWhiteboardText("");
00442 }
00443
00444 }
00445
00446
00447 void LLFloaterNetwork2080::toggle(void*) {
00448 if (!gFloaterProject) {
00449 llinfos << "No project floater!" << llendl;
00450 return;
00451 }
00452
00453 if (gFloaterProject->getVisible())
00454 {
00455 gFloaterProject->close();
00456 }
00457 else
00458 {
00459 gFloaterProject->show();
00460 }
00461 }
00462
00463 void LLFloaterNetwork2080::addNode(S32 parent, LLNetwork2080Bridge *bridge)
00464 {
00465 S32 id = bridge->getID();
00466
00467 LLFolderViewFolder *child = NULL;
00468 LLFolderViewFolder *parent_folder = NULL;
00469
00470 if ( id == parent )
00471 {
00472 llwarns << "Ignoring child " << id << ", as it's a child of itself" << llendl;
00473 delete bridge;
00474 return;
00475 }
00476
00477 llinfos << "Adding child: parent " << parent << ", id " << id << ", name " << bridge->getName() << llendl;
00478
00479 if ( 0 == parent )
00480 {
00481 parent_folder = mTree;
00482 }
00483 else
00484 {
00485 parent_folder = dynamic_cast<LLFolderViewFolder*>(mTree->getItemByID( LLNetwork2080Bridge::makeID( parent ) ));
00486 }
00487
00488
00489
00490 if ( !parent_folder )
00491 {
00492 llwarns << "Failed to find root to add entry '" << bridge->getName() << "' to parent " << parent << ", with id " << id << llendl;
00493 delete bridge;
00494 return;
00495 }
00496
00497 llinfos << "Parent folder: " << parent_folder->getName() << llendl;
00498
00499
00500 mNodeCount++;
00501 llinfos << "Creating node with order " << mNodeCount << llendl;
00502
00503 bridge->setSortOrder(mNodeCount);
00504 child = new LLFolderViewFolder(bridge->getName(), bridge->getIcon(), mTree, bridge);
00505 child->setOpen(FALSE);
00506 child->addToFolder( parent_folder, mTree );
00507 child->getSortFunction()->setComparisonFunction(compare);
00508
00509 child->resort(NULL);
00510
00511 mTree->sortBy(0);
00512 mTree->sortBy(LLInventoryFilter::SO_FOLDERS_BY_NAME | LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP);
00513 mTree->resort(NULL);
00514
00515
00516 LLNetwork2080FolderBridge *parent_bridge = dynamic_cast<LLNetwork2080FolderBridge*>( parent_folder->getListener() );
00517 if ( parent_bridge )
00518 {
00519 parent_bridge->setLoaded(true);
00520 }
00521
00522 }
00523
00524 void LLFloaterNetwork2080::show()
00525 {
00526
00527 open();
00528 }
00529
00530 void LLFloaterNetwork2080::requestRoot()
00531 {
00532 mTree->deleteAllChildren();
00533 LLHTTPClient::get(ROOT_URL , new LLHTTPFetchTree(0), 30.0f);
00534
00535 llinfos << "Requesting root" << llendl;
00536 }
00537
00538 void LLFloaterNetwork2080::requestChildren(S32 parent_id)
00539 {
00540
00541 LLUUID uid = LLNetwork2080Bridge::makeID( parent_id );
00542 LLNetwork2080FolderBridge *bridge;
00543
00544 LLString url;
00545
00546
00547
00548 LLFolderViewFolder *parent = dynamic_cast<LLFolderViewFolder*>(mTree->getItemByID( uid ));
00549 if ( !parent ) return;
00550
00551
00552 bridge = dynamic_cast<LLNetwork2080FolderBridge*>(parent->getListener());
00553 if ( bridge )
00554 {
00555 if ( bridge->isLoaded() || bridge->isLoading() )
00556 {
00557 llinfos << "Already retrieved" << llendl;
00558 return;
00559 }
00560 else if ( !bridge->hasChildren() )
00561 {
00562 llinfos << "No children" << llendl;
00563 return;
00564 }
00565 else
00566 {
00567
00568 bridge->setLoading(true);
00569 }
00570 }
00571 else
00572 {
00573 llwarns << "Failed to get bridge!" << llendl;
00574 return;
00575 }
00576
00577 url = bridge->getChildrenURL();
00578
00579 if ( !url.empty() )
00580 {
00581 llinfos << "Requesting child, parent " << parent_id << ", UID " << uid << ", URL " << url << llendl;
00582 LLHTTPClient::get(url, new LLHTTPFetchTree(parent_id), 30.0f);
00583 }
00584 }
00585
00586 void LLFloaterNetwork2080::setWhiteboardText(const LLString &text)
00587 {
00588 mWhiteboard->setText(text);
00589 }
00590
00591 void LLFloaterNetwork2080::setQueueButtonsStatus(bool status)
00592 {
00593 childSetEnabled("queue_btn", status);
00594 childSetEnabled("myinfo_btn", status);
00595 childSetEnabled("notecard_btn", status);
00596 childSetEnabled("queue_status_btn", status);
00597 childSetEnabled("exit_queue_btn", status);
00598 }
00599
00600 LLString LLFloaterNetwork2080::buildURL(const LLString &baseurl, S32 id, bool send_key)
00601 {
00602 std::ostringstream o;
00603 LLUUID key("7aa440e7-4971-4a6c-8289-6960f2e7781e");
00604 if ( ! gAgent.getID().isNull() )
00605 {
00606 key = gAgent.getID();
00607 }
00608 else
00609 {
00610 llwarns << "Agent's key not set, using hardcoded one" << llendl;
00611 }
00612
00613 o << baseurl << id;
00614 if ( send_key )
00615 {
00616 o << "&key=" << key << "&password=" << mPassword;
00617 }
00618
00619 return o.str();
00620 }
00621
00622 void LLFloaterNetwork2080::doSelectedPageCommand(const LLString &baseurl)
00623 {
00624 #ifdef DG_INCLUDE_WHITEBOARD
00625 setWhiteboardText("Loading...");
00626 LLString url = buildURL(baseurl, mSelectedID, true);
00627 llinfos << "Loading url " << url << llendl;
00628 LLHTTPClient::get(url, new LLHTTPFetchContent(mSelectedID), 30.0f);
00629 #else
00630 setWhiteboardText("Whiteboard disabled");
00631 #endif
00632 }
00633
00634
00635 void LLFloaterNetwork2080::onClickQueue(void *userdata)
00636 {
00637 LLFloaterNetwork2080 *floater = static_cast<LLFloaterNetwork2080*>(userdata);
00638 if ( floater->mPassword.empty() )
00639 {
00640 gViewerWindow->alertXml("Network2080NoPassword");
00641 return;
00642 }
00643
00644 floater->doSelectedPageCommand(QUEUE_URL);
00645 }
00646
00647
00648 void LLFloaterNetwork2080::onClickExitQueue(void *userdata)
00649 {
00650 LLFloaterNetwork2080 *floater = static_cast<LLFloaterNetwork2080*>(userdata);
00651
00652 floater->doSelectedPageCommand(EXIT_QUEUE_URL);
00653 }
00654
00655
00656 void LLFloaterNetwork2080::onClickMyInfo(void *userdata)
00657 {
00658 LLFloaterNetwork2080 *floater = static_cast<LLFloaterNetwork2080*>(userdata);
00659 floater->doSelectedPageCommand(MY_INFO_URL);
00660 }
00661
00662
00663 void LLFloaterNetwork2080::onClickNotecard(void *userdata)
00664 {
00665 LLFloaterNetwork2080 *floater = static_cast<LLFloaterNetwork2080*>(userdata);
00666 floater->doSelectedPageCommand(NOTECARD_URL);}
00667
00668
00669 void LLFloaterNetwork2080::onClickQueueStatus(void *userdata)
00670 {
00671 LLFloaterNetwork2080 *floater = static_cast<LLFloaterNetwork2080*>(userdata);
00672 floater->doSelectedPageCommand(PLAYABLE_PAGE_URL);
00673 }
00674
00675
00676 bool LLFloaterNetwork2080::compare(LLInventorySort*self, LLFolderViewItem* a, LLFolderViewItem* b)
00677 {
00678 LLNetwork2080Bridge* bridge_a = dynamic_cast<LLNetwork2080Bridge*>(a->getListener());
00679 LLNetwork2080Bridge* bridge_b = dynamic_cast<LLNetwork2080Bridge*>(b->getListener());
00680
00681 if ( ! bridge_a )
00682 {
00683 llwarns << "Can't compare " << a->getName() << " with " << b->getName() << ": element " << a->getName() << " has a bridge of the wrong type" << llendl;
00684 return false;
00685 }
00686
00687 if ( ! bridge_b )
00688 {
00689 llwarns << "Can't compare " << a->getName() << " with " << b->getName() << ": element " << b->getName() << " has a bridge of the wrong type" << llendl;
00690 return false;
00691 }
00692
00693 return ( bridge_a->getSortOrder() > bridge_b->getSortOrder() );
00694 }
00695
00696
00697 void LLFloaterNetwork2080::viewerCommCallback(LLString& data, LLViewerCircuit& circuit, void* userdata)
00698 {
00699 LLFloaterNetwork2080 *self = static_cast<LLFloaterNetwork2080*>(userdata);
00700
00701 self->mPassword = data;
00702 gSavedPerAccountSettings.setString("Network2080Password", data);
00703 llinfos << "Received Network2080 password: " << data << llendl;
00704
00705 circuit.sendReply("OK");
00706 }
00707