diff options
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llui/llurlentry.cpp | 44 | ||||
-rw-r--r-- | indra/llui/llurlentry.h | 12 | ||||
-rw-r--r-- | indra/llui/llurlregistry.cpp | 1 | ||||
-rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
-rw-r--r-- | indra/newview/llagentlistener.cpp | 2 | ||||
-rw-r--r-- | indra/newview/llfloatersearch.cpp | 16 | ||||
-rw-r--r-- | indra/newview/llinspectremoteobject.cpp | 200 | ||||
-rw-r--r-- | indra/newview/llinspectremoteobject.h | 40 | ||||
-rw-r--r-- | indra/newview/lltexturecache.cpp | 19 | ||||
-rw-r--r-- | indra/newview/llurldispatcher.cpp | 6 | ||||
-rw-r--r-- | indra/newview/llviewerfloaterreg.cpp | 2 | ||||
-rw-r--r-- | indra/newview/llviewermessage.cpp | 27 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_media_settings.xml | 12 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_search.xml | 4 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/inspect_remote_object.xml | 99 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 5 |
16 files changed, 409 insertions, 82 deletions
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index dae4b512d1..219fae84be 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -512,50 +512,6 @@ std::string LLUrlEntryTeleport::getLocation(const std::string &url) const return ::getStringAfterToken(url, "app/teleport/"); } -/// -/// LLUrlEntryObjectIM Describes a Second Life object instant msg Url, e.g., -/// secondlife:///app/objectim/<sessionid> -/// -LLUrlEntryObjectIM::LLUrlEntryObjectIM() -{ - mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\\??\\S*", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_objectim.xml"; - mTooltip = LLTrans::getString("TooltipObjectIMUrl"); -} - -std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - LLURI uri(url); - LLSD params = uri.queryMap(); - if (params.has("name")) - { - // look for a ?name=<obj-name> param in the url - // and use that as the label if present. - std::string name = params.get("name"); - LLStringUtil::trim(name); - if (name.empty()) - { - name = LLTrans::getString("Unnamed"); - } - return name; - } - - return unescapeUrl(url); -} - -std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const -{ - LLURI uri(url); - LLSD params = uri.queryMap(); - if (params.has("slurl")) - { - return params.get("slurl"); - } - - return ""; -} - // // LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts // with secondlife:// (used as a catch-all for cases not matched above) diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 4507572b1e..7970b48eb5 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -209,18 +209,6 @@ public: }; /// -/// LLUrlEntryObjectIM Describes a Second Life object instant msg Url, e.g., -/// secondlife:///app/objectim/<sessionid>?name=Foo -/// -class LLUrlEntryObjectIM : public LLUrlEntryBase -{ -public: - LLUrlEntryObjectIM(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getLocation(const std::string &url) const; -}; - -/// /// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts /// with secondlife:// (used as a catch-all for cases not matched above) /// diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 60275b60bc..a6922b019b 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -52,7 +52,6 @@ LLUrlRegistry::LLUrlRegistry() registerUrl(new LLUrlEntryParcel()); registerUrl(new LLUrlEntryTeleport()); registerUrl(new LLUrlEntryWorldMap()); - registerUrl(new LLUrlEntryObjectIM()); registerUrl(new LLUrlEntryPlace()); registerUrl(new LLUrlEntrySL()); registerUrl(new LLUrlEntrySLLabel()); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e69d3f417f..b129bca1f3 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -246,6 +246,7 @@ set(viewer_SOURCE_FILES llinspectavatar.cpp llinspectgroup.cpp llinspectobject.cpp + llinspectremoteobject.cpp llinventorybridge.cpp llinventoryclipboard.cpp llinventoryfilter.cpp @@ -741,6 +742,7 @@ set(viewer_HEADER_FILES llinspectavatar.h llinspectgroup.h llinspectobject.h + llinspectremoteobject.h llinventorybridge.h llinventoryclipboard.h llinventoryfilter.h diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 3da6a4e3f4..b3ed7c353e 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -48,7 +48,7 @@ void LLAgentListener::requestTeleport(LLSD const & event_data) const params.append(event_data["y"]); params.append(event_data["z"]); LLCommandDispatcher::dispatch("teleport", params, LLSD(), NULL, true); - // *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "objectim", "parcel", "login", login_refresh", "balance", "chat" + // *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "parcel", "login", login_refresh", "balance", "chat" // should we just compose LLCommandHandler and LLDispatchListener? } else diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index ca2cdffcf8..e2df2ffdf7 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -124,9 +124,19 @@ void LLFloaterSearch::search(const LLSD &key) url += "&p=" + search_token.asString(); // also append the user's preferred maturity (can be changed via prefs) - std::string maturity = "pg"; - if (gAgent.prefersMature()) maturity += ",mature"; - if (gAgent.prefersAdult()) maturity += ",adult"; + std::string maturity; + if (gAgent.prefersAdult()) + { + maturity = "42"; // PG,Mature,Adult + } + else if (gAgent.prefersMature()) + { + maturity = "21"; // PG,Mature + } + else + { + maturity = "13"; // PG + } url += "&r=" + maturity; // and load the URL in the web view diff --git a/indra/newview/llinspectremoteobject.cpp b/indra/newview/llinspectremoteobject.cpp new file mode 100644 index 0000000000..e4d2eec242 --- /dev/null +++ b/indra/newview/llinspectremoteobject.cpp @@ -0,0 +1,200 @@ +/** + * @file llinspectremoteobject.cpp + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, 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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinspectremoteobject.h" +#include "llinspect.h" +#include "llslurl.h" +#include "llmutelist.h" +#include "llurlaction.h" +#include "llpanelblockedlist.h" +#include "llfloaterreg.h" +#include "llui.h" +#include "lluictrl.h" + +class LLViewerObject; + +////////////////////////////////////////////////////////////////////////////// +// LLInspectRemoteObject +////////////////////////////////////////////////////////////////////////////// + +// Remote Object Inspector, a small information window used to +// display information about potentially-remote objects. Used +// to display details about objects sending messages to the user. +class LLInspectRemoteObject : public LLInspect +{ + friend class LLFloaterReg; + +public: + LLInspectRemoteObject(const LLSD& object_id); + virtual ~LLInspectRemoteObject() {}; + + /*virtual*/ BOOL postBuild(void); + /*virtual*/ void onOpen(const LLSD& avatar_id); + + void onClickMap(); + void onClickBlock(); + void onClickClose(); + +private: + void update(); + static void nameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data); + +private: + LLUUID mObjectID; + LLUUID mOwnerID; + std::string mOwner; + std::string mSLurl; + std::string mName; + bool mGroupOwned; +}; + +LLInspectRemoteObject::LLInspectRemoteObject(const LLSD& sd) : + LLInspect(LLSD()), + mObjectID(NULL), + mOwnerID(NULL), + mOwner(""), + mSLurl(""), + mName(""), + mGroupOwned(false) +{ +} + +/*virtual*/ +BOOL LLInspectRemoteObject::postBuild(void) +{ + // hook up the inspector's buttons + getChild<LLUICtrl>("map_btn")->setCommitCallback( + boost::bind(&LLInspectRemoteObject::onClickMap, this)); + getChild<LLUICtrl>("block_btn")->setCommitCallback( + boost::bind(&LLInspectRemoteObject::onClickBlock, this)); + getChild<LLUICtrl>("close_btn")->setCommitCallback( + boost::bind(&LLInspectRemoteObject::onClickClose, this)); + + return TRUE; +} + +/*virtual*/ +void LLInspectRemoteObject::onOpen(const LLSD& data) +{ + // Start animation + LLInspect::onOpen(data); + + // Extract appropriate object information from input LLSD + // (Eventually, it might be nice to query server for details + // rather than require caller to pass in the information.) + mObjectID = data["object_id"].asUUID(); + mName = data["name"].asString(); + mOwnerID = data["owner_id"].asUUID(); + mGroupOwned = data["group_owned"].asBoolean(); + mSLurl = data["slurl"].asString(); + + // work out the owner's name + mOwner = ""; + if (gCacheName) + { + gCacheName->get(mOwnerID, mGroupOwned, nameCallback, this); + } + + // update the inspector with the current object state + update(); + + // Position the inspector relative to the mouse cursor + LLUI::positionViewNearMouse(this); +} + +void LLInspectRemoteObject::onClickMap() +{ + std::string url = "secondlife://" + mSLurl; + LLUrlAction::showLocationOnMap(url); + closeFloater(); +} + +void LLInspectRemoteObject::onClickBlock() +{ + LLMute::EType mute_type = mGroupOwned ? LLMute::GROUP : LLMute::AGENT; + LLMute mute(mOwnerID, mOwner, mute_type); + LLMuteList::getInstance()->add(mute); + LLPanelBlockedList::showPanelAndSelect(mute.mID); + closeFloater(); +} + +void LLInspectRemoteObject::onClickClose() +{ + closeFloater(); +} + +//static +void LLInspectRemoteObject::nameCallback(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* data) +{ + LLInspectRemoteObject *self = (LLInspectRemoteObject*)data; + self->mOwner = first; + if (!last.empty()) + { + self->mOwner += " " + last; + } + self->update(); +} + +void LLInspectRemoteObject::update() +{ + // show the object name as the inspector's title + getChild<LLUICtrl>("object_name")->setValue(mName); + + // show the object's owner - click it to show profile + std::string owner = mOwner; + if (! mOwnerID.isNull()) + { + if (mGroupOwned) + { + owner = LLSLURL::buildCommand("group", mOwnerID, "about"); + } + else + { + owner = LLSLURL::buildCommand("agent", mOwnerID, "about"); + } + } + getChild<LLUICtrl>("object_owner")->setValue(owner); + + // display the object's SLurl - click it to teleport + std::string url = "secondlife:///app/teleport/" + mSLurl; + getChild<LLUICtrl>("object_slurl")->setValue(url); +} + +////////////////////////////////////////////////////////////////////////////// +// LLInspectRemoteObjectUtil +////////////////////////////////////////////////////////////////////////////// +void LLInspectRemoteObjectUtil::registerFloater() +{ + LLFloaterReg::add("inspect_remote_object", "inspect_remote_object.xml", + &LLFloaterReg::build<LLInspectRemoteObject>); +} diff --git a/indra/newview/llinspectremoteobject.h b/indra/newview/llinspectremoteobject.h new file mode 100644 index 0000000000..e756f1caf4 --- /dev/null +++ b/indra/newview/llinspectremoteobject.h @@ -0,0 +1,40 @@ +/** + * @file llinspectremoteobject.h + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, 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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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. + * $/LicenseInfo$ + */ + +#ifndef LLINSPECTREMOTEOBJECT_H +#define LLINSPECTREMOTEOBJECT_H + +namespace LLInspectRemoteObjectUtil +{ + void registerFloater(); +} + +#endif diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 6a4b967487..9be342c424 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -1541,23 +1541,24 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort) { lockWorkers(); handle_map_t::iterator iter = mReaders.find(handle); - llassert_always(iter != mReaders.end() || abort); - LLTextureCacheWorker* worker = iter->second; - if (!worker) - return false; - bool res = worker->complete(); - if (res || abort) + LLTextureCacheWorker* worker = NULL; + bool complete = false; + if (iter != mReaders.end()) { - mReaders.erase(handle); + worker = iter->second; + complete = worker->complete(); + } + if (worker && (complete || abort)) + { + mReaders.erase(iter); unlockWorkers(); worker->scheduleDelete(); - return true; } else { unlockWorkers(); - return false; } + return (complete || abort); } LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 priority, diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index a3daca6fa4..9e064d8135 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -364,9 +364,9 @@ bool LLURLDispatcher::dispatchRightClick(const std::string& url) bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url) { // *NOTE: Text editors are considered sources of trusted URLs - // in order to make objectim and avatar profile links in chat - // history work. While a malicious resident could chat an app - // SLURL, the receiving resident will see it and must affirmatively + // in order to make avatar profile links in chat history work. + // While a malicious resident could chat an app SLURL, the + // receiving resident will see it and must affirmatively // click on it. // *TODO: Make this trust model more refined. JC const bool trusted_browser = true; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index edbac69e1b..964d3bc2fa 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -112,6 +112,7 @@ #include "llinspectavatar.h" #include "llinspectgroup.h" #include "llinspectobject.h" +#include "llinspectremoteobject.h" #include "llmediaremotectrl.h" #include "llmoveview.h" #include "llnearbychat.h" @@ -176,6 +177,7 @@ void LLViewerFloaterReg::registerFloaters() LLInspectAvatarUtil::registerFloater(); LLInspectGroupUtil::registerFloater(); LLInspectObjectUtil::registerFloater(); + LLInspectRemoteObjectUtil::registerFloater(); LLFloaterReg::add("lagmeter", "floater_lagmeter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLagMeter>); LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLandHoldings>); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1a4c849f25..4088eafe16 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1431,6 +1431,17 @@ bool goto_url_callback(const LLSD& notification, const LLSD& response) } static LLNotificationFunctorRegistration goto_url_callback_reg("GotoURL", goto_url_callback); +bool inspect_remote_object_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + if (0 == option) + { + LLFloaterReg::showInstance("inspect_remote_object", notification["payload"]); + } + return false; +} +static LLNotificationFunctorRegistration inspect_remote_object_callback_reg("ServerObjectMessage", inspect_remote_object_callback); + void process_improved_im(LLMessageSystem *msg, void **user_data) { if (gNoRender) @@ -1943,9 +1954,23 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) return; } + // Build a link to open the object IM info window. + std::string location = ll_safe_string((char*)binary_bucket, binary_bucket_size-1); + LLSD substitutions; + substitutions["NAME"] = name; substitutions["MSG"] = message; - LLNotifications::instance().add("ServerObjectMessage", substitutions); + + LLSD payload; + payload["object_id"] = session_id; + payload["owner_id"] = from_id; + payload["slurl"] = location; + payload["name"] = name; + if (from_group) + { + payload["groupowned"] = "true"; + } + LLNotifications::instance().add("ServerObjectMessage", substitutions, payload); } break; case IM_FROM_TASK_AS_ALERT: diff --git a/indra/newview/skins/default/xui/en/floater_media_settings.xml b/indra/newview/skins/default/xui/en/floater_media_settings.xml index 68dd2001af..8122386fae 100644 --- a/indra/newview/skins/default/xui/en/floater_media_settings.xml +++ b/indra/newview/skins/default/xui/en/floater_media_settings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater legacy_header_height="18" - bottom="-666" + bottom="666" can_close="true" can_drag_on_left="false" can_minimize="true" @@ -12,14 +12,14 @@ width="365" height="535" left="330" - min_height="430" - min_width="620" + min_height="535" + min_width="365" mouse_opaque="true" - name="Media Settings" + name="media_settings" help_topic = "media_settings" title="MEDIA SETTINGS"> <button - bottom="-525" + bottom="525" enabled="true" follows="right|bottom" font="SansSerif" @@ -61,7 +61,7 @@ scale_image="true" width="90" /> <tab_container - bottom="-500" + bottom="500" enabled="true" follows="left|top|right|bottom" height="485" diff --git a/indra/newview/skins/default/xui/en/floater_search.xml b/indra/newview/skins/default/xui/en/floater_search.xml index b9cf456842..d9498586af 100644 --- a/indra/newview/skins/default/xui/en/floater_search.xml +++ b/indra/newview/skins/default/xui/en/floater_search.xml @@ -25,7 +25,7 @@ Done </floater.string> <layout_stack - bottom="400" + bottom="512" follows="left|right|top|bottom" layout="topleft" left="10" @@ -54,7 +54,7 @@ layout="topleft" left_delta="0" name="status_text" - top_pad="4" + top_pad="5" width="150" /> </layout_panel> </layout_stack> diff --git a/indra/newview/skins/default/xui/en/inspect_remote_object.xml b/indra/newview/skins/default/xui/en/inspect_remote_object.xml new file mode 100644 index 0000000000..07c684d904 --- /dev/null +++ b/indra/newview/skins/default/xui/en/inspect_remote_object.xml @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<!-- + Not can_close / no title to avoid window chrome + Single instance - only have one at a time, recycle it each spawn +--> +<floater + legacy_header_height="18" + bevel_style="in" + bg_opaque_image="Inspector_Background" + can_close="false" + can_minimize="false" + height="145" + layout="topleft" + name="inspect_remote_object" + single_instance="true" + sound_flags="0" + visible="true" + width="300"> + <text + follows="all" + font="SansSerifLargeBold" + height="16" + left="8" + name="object_name" + text_color="White" + top="5" + use_ellipses="true" + width="290"> + Test Object Name That Is Really Long + </text> + <text + follows="all" + font="SansSerif" + height="20" + left="8" + name="object_owner_label" + width="55" + top_pad="20"> + Owner: + </text> + <text + follows="top|left" + font="SansSerif" + height="20" + left_pad="10" + name="object_owner" + use_ellipses="true" + width="200" + word_wrap="false"> + Longavatarname Johnsonlongstonnammer + </text> + <text + follows="top|left" + font="SansSerif" + height="20" + left="8" + name="object_slurl_label" + top_pad="10" + width="55"> + Location: + </text> + <text + follows="top|left" + height="20" + left_pad="10" + name="object_slurl" + width="240" + use_ellipses="true" + word_wrap="false"> + http://slurl.com/Ahern/50/50/50 + </text> + <button + follows="top|left" + font="SansSerif" + height="20" + label="Map" + left="10" + name="map_btn" + top="114" + width="75" /> + <button + follows="top|left" + font="SansSerif" + height="20" + label="Block" + left_pad="5" + name="block_btn" + top_delta="0" + width="75" /> + <button + follows="top|left" + font="SansSerif" + height="20" + label="Close" + right="-10" + name="close_btn" + top_delta="0" + width="75" /> +</floater> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 96c6d970c2..ff0cd7ffeb 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4696,7 +4696,12 @@ The objects on the selected parcel that are NOT owned by you have been returned icon="notify.tga" name="ServerObjectMessage" type="notify"> +Message from [NAME]: [MSG] + <usetemplate + name="okcancelbuttons" + notext="OK" + yestext="Inspect"/> </notification> <notification |