summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rwxr-xr-xindra/llinventory/llparcel.cpp88
-rwxr-xr-xindra/llinventory/llparcel.h25
-rwxr-xr-xindra/llinventory/llparcelflags.h6
-rwxr-xr-xindra/llmessage/CMakeLists.txt2
-rw-r--r--indra/llmessage/llexperiencecache.cpp641
-rw-r--r--indra/llmessage/llexperiencecache.h104
-rwxr-xr-xindra/llmessage/llregionflags.h47
-rwxr-xr-xindra/llmessage/message_prehash.cpp2
-rwxr-xr-xindra/llmessage/message_prehash.h2
-rwxr-xr-xindra/llui/llnotifications.cpp32
-rwxr-xr-xindra/llui/llnotifications.h4
-rwxr-xr-xindra/llui/llnotificationtemplate.h1
-rwxr-xr-xindra/llui/lltabcontainer.cpp5
-rwxr-xr-xindra/llui/lltabcontainer.h5
-rwxr-xr-xindra/llui/llurlentry.cpp49
-rwxr-xr-xindra/llui/llurlentry.h14
-rwxr-xr-xindra/llui/llurlregistry.cpp1
-rwxr-xr-xindra/llui/tests/llurlentry_test.cpp14
-rwxr-xr-xindra/lscript/lscript_compile/indra.l17
-rwxr-xr-xindra/newview/CMakeLists.txt20
-rwxr-xr-xindra/newview/app_settings/keywords.ini20
-rwxr-xr-xindra/newview/llappviewer.cpp50
-rwxr-xr-xindra/newview/llappviewer.h4
-rwxr-xr-xindra/newview/llassetuploadqueue.cpp5
-rwxr-xr-xindra/newview/llassetuploadqueue.h4
-rwxr-xr-xindra/newview/llcompilequeue.cpp152
-rwxr-xr-xindra/newview/llcompilequeue.h12
-rw-r--r--indra/newview/llexperienceassociationresponder.cpp97
-rw-r--r--indra/newview/llexperienceassociationresponder.h58
-rw-r--r--indra/newview/llexperiencelog.cpp273
-rw-r--r--indra/newview/llexperiencelog.h85
-rwxr-xr-xindra/newview/llfloaterauction.cpp33
-rwxr-xr-xindra/newview/llfloaterauction.h2
-rw-r--r--indra/newview/llfloaterexperiencepicker.cpp162
-rw-r--r--indra/newview/llfloaterexperiencepicker.h67
-rw-r--r--indra/newview/llfloaterexperienceprofile.cpp998
-rw-r--r--indra/newview/llfloaterexperienceprofile.h104
-rw-r--r--indra/newview/llfloaterexperiences.cpp325
-rw-r--r--indra/newview/llfloaterexperiences.h58
-rwxr-xr-xindra/newview/llfloaterland.cpp713
-rwxr-xr-xindra/newview/llfloaterland.h82
-rwxr-xr-xindra/newview/llfloaterregioninfo.cpp371
-rwxr-xr-xindra/newview/llfloaterregioninfo.h34
-rwxr-xr-xindra/newview/llfloaterreporter.cpp49
-rwxr-xr-xindra/newview/llfloaterreporter.h7
-rwxr-xr-xindra/newview/llnotificationhandler.h2
-rwxr-xr-xindra/newview/llnotificationscripthandler.cpp69
-rw-r--r--indra/newview/llpanelexperiencelisteditor.cpp254
-rw-r--r--indra/newview/llpanelexperiencelisteditor.h100
-rw-r--r--indra/newview/llpanelexperiencelog.cpp264
-rw-r--r--indra/newview/llpanelexperiencelog.h64
-rw-r--r--indra/newview/llpanelexperiencepicker.cpp431
-rw-r--r--indra/newview/llpanelexperiencepicker.h94
-rw-r--r--indra/newview/llpanelexperiences.cpp218
-rw-r--r--indra/newview/llpanelexperiences.h97
-rwxr-xr-xindra/newview/llpanelgroup.cpp10
-rw-r--r--indra/newview/llpanelgroupexperiences.cpp143
-rw-r--r--indra/newview/llpanelgroupexperiences.h53
-rwxr-xr-xindra/newview/llpreviewscript.cpp344
-rwxr-xr-xindra/newview/llpreviewscript.h39
-rwxr-xr-xindra/newview/llscreenchannel.cpp14
-rwxr-xr-xindra/newview/llsidepaneliteminfo.cpp35
-rwxr-xr-xindra/newview/llsidepaneliteminfo.h2
-rwxr-xr-xindra/newview/llstartup.cpp14
-rwxr-xr-xindra/newview/llstartup.h1
-rwxr-xr-xindra/newview/llviewerfloaterreg.cpp10
-rwxr-xr-xindra/newview/llviewermessage.cpp306
-rwxr-xr-xindra/newview/llviewerparcelmgr.cpp193
-rwxr-xr-xindra/newview/llviewerparcelmgr.h4
-rwxr-xr-xindra/newview/llviewerregion.cpp13
-rwxr-xr-xindra/newview/llvoavatar.cpp3
-rw-r--r--indra/newview/roles_constants.h3
-rwxr-xr-xindra/newview/skins/default/textures/textures.xml1
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_about_land.xml12
-rw-r--r--indra/newview/skins/default/xui/en/floater_experience_search.xml14
-rw-r--r--indra/newview/skins/default/xui/en/floater_experienceprofile.xml687
-rw-r--r--indra/newview/skins/default/xui/en/floater_experiences.xml28
-rwxr-xr-xindra/newview/skins/default/xui/en/floater_live_lsleditor.xml171
-rw-r--r--indra/newview/skins/default/xui/en/menu_url_experience.xml12
-rwxr-xr-xindra/newview/skins/default/xui/en/menu_viewer.xml7
-rwxr-xr-xindra/newview/skins/default/xui/en/notifications.xml230
-rw-r--r--indra/newview/skins/default/xui/en/panel_experience_info.xml377
-rw-r--r--indra/newview/skins/default/xui/en/panel_experience_list_editor.xml83
-rw-r--r--indra/newview/skins/default/xui/en/panel_experience_list_item.xml21
-rw-r--r--indra/newview/skins/default/xui/en/panel_experience_log.xml152
-rw-r--r--indra/newview/skins/default/xui/en/panel_experience_search.xml182
-rw-r--r--indra/newview/skins/default/xui/en/panel_experiences.xml65
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_group_info_sidetray.xml16
-rw-r--r--indra/newview/skins/default/xui/en/panel_region_experiences.xml136
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_script_ed.xml376
-rw-r--r--indra/newview/skins/default/xui/en/panel_script_experience.xml97
-rwxr-xr-xindra/newview/skins/default/xui/en/role_actions.xml12
-rwxr-xr-xindra/newview/skins/default/xui/en/sidepanel_item_info.xml890
-rwxr-xr-xindra/newview/skins/default/xui/en/strings.xml35
-rw-r--r--indra/tools/vstool/DispatchUtility.cs271
-rw-r--r--indra/tools/vstool/app.config3
96 files changed, 10080 insertions, 1422 deletions
diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp
index e400877b3b..0784986e3a 100755
--- a/indra/llinventory/llparcel.cpp
+++ b/indra/llinventory/llparcel.cpp
@@ -40,6 +40,8 @@
#include "llsdutil_math.h"
#include "message.h"
#include "u64.h"
+#include "llregionflags.h"
+#include <boost/range/adaptor/map.hpp>
static const F32 SOME_BIG_NUMBER = 1000.0f;
static const F32 SOME_BIG_NEG_NUMBER = -1000.0f;
@@ -627,8 +629,8 @@ void LLParcel::unpackMessage(LLMessageSystem* msg)
void LLParcel::packAccessEntries(LLMessageSystem* msg,
const std::map<LLUUID,LLAccessEntry>& list)
{
- access_map_const_iterator cit = list.begin();
- access_map_const_iterator end = list.end();
+ LLAccessEntry::map::const_iterator cit = list.begin();
+ LLAccessEntry::map::const_iterator end = list.end();
if (cit == end)
{
@@ -679,9 +681,28 @@ void LLParcel::unpackAccessEntries(LLMessageSystem* msg,
}
+void LLParcel::unpackExperienceEntries( LLMessageSystem* msg, U32 type )
+{
+ LLUUID id;
+
+ S32 i;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_List);
+ for (i = 0; i < count; i++)
+ {
+ msg->getUUIDFast(_PREHASH_List, _PREHASH_ID, id, i);
+
+ if (id.notNull())
+ {
+ mExperienceKeys[id]=type;
+ }
+ }
+}
+
+
+
void LLParcel::expirePasses(S32 now)
{
- access_map_iterator itor = mAccessList.begin();
+ LLAccessEntry::map::iterator itor = mAccessList.begin();
while (itor != mAccessList.end())
{
const LLAccessEntry& entry = (*itor).second;
@@ -771,7 +792,7 @@ BOOL LLParcel::addToAccessList(const LLUUID& agent_id, S32 time)
// Can't add owner to these lists
return FALSE;
}
- access_map_iterator itor = mAccessList.begin();
+ LLAccessEntry::map::iterator itor = mAccessList.begin();
while (itor != mAccessList.end())
{
const LLAccessEntry& entry = (*itor).second;
@@ -816,7 +837,7 @@ BOOL LLParcel::addToBanList(const LLUUID& agent_id, S32 time)
return FALSE;
}
- access_map_iterator itor = mBanList.begin();
+ LLAccessEntry::map::iterator itor = mBanList.begin();
while (itor != mBanList.end())
{
const LLAccessEntry& entry = (*itor).second;
@@ -852,7 +873,7 @@ BOOL remove_from_access_array(std::map<LLUUID,LLAccessEntry>* list,
const LLUUID& agent_id)
{
BOOL removed = FALSE;
- access_map_iterator itor = list->begin();
+ LLAccessEntry::map::iterator itor = list->begin();
while (itor != list->end())
{
const LLAccessEntry& entry = (*itor).second;
@@ -1195,3 +1216,58 @@ LLParcel::ECategory category_ui_string_to_category(const std::string& s)
// is a distinct option from "None" and "Other"
return LLParcel::C_ANY;
}
+
+LLAccessEntry::map LLParcel::getExperienceKeysByType( U32 type ) const
+{
+ LLAccessEntry::map access;
+ LLAccessEntry entry;
+ xp_type_map_t::const_iterator it = mExperienceKeys.begin();
+ for(/**/; it != mExperienceKeys.end(); ++it)
+ {
+ if(it->second == type)
+ {
+ entry.mID = it->first;
+ access[entry.mID] = entry;
+ }
+ }
+ return access;
+}
+
+void LLParcel::clearExperienceKeysByType( U32 type )
+{
+ xp_type_map_t::iterator it = mExperienceKeys.begin();
+ while(it != mExperienceKeys.end())
+ {
+ if(it->second == type)
+ {
+ mExperienceKeys.erase(it++);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+void LLParcel::setExperienceKeyType( const LLUUID& experience_key, U32 type )
+{
+ if(type == EXPERIENCE_KEY_TYPE_NONE)
+ {
+ mExperienceKeys.erase(experience_key);
+ }
+ else
+ {
+ if(countExperienceKeyType(type) < PARCEL_MAX_EXPERIENCE_LIST)
+ {
+ mExperienceKeys[experience_key] = type;
+ }
+ }
+}
+
+U32 LLParcel::countExperienceKeyType( U32 type )
+{
+ return std::count_if(
+ boost::begin(mExperienceKeys | boost::adaptors::map_values),
+ boost::end(mExperienceKeys | boost::adaptors::map_values),
+ std::bind2nd(std::equal_to<U32>(), type));
+}
diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h
index c4363a48df..e68331b99a 100755
--- a/indra/llinventory/llparcel.h
+++ b/indra/llinventory/llparcel.h
@@ -53,6 +53,9 @@ const S32 PARCEL_MAX_ACCESS_LIST = 300;
//for access/ban lists.
const F32 PARCEL_MAX_ENTRIES_PER_PACKET = 48.f;
+// Maximum number of experiences
+const S32 PARCEL_MAX_EXPERIENCE_LIST = 24;
+
// Weekly charge for listing a parcel in the directory
const S32 PARCEL_DIRECTORY_FEE = 30;
@@ -130,9 +133,11 @@ class LLSD;
class LLAccessEntry
{
public:
+
+ typedef std::map<LLUUID,LLAccessEntry> map;
+
LLAccessEntry()
- : mID(),
- mTime(0),
+ : mTime(0),
mFlags(0)
{}
@@ -141,8 +146,6 @@ public:
U32 mFlags; // Not used - currently should always be zero
};
-typedef std::map<LLUUID,LLAccessEntry>::iterator access_map_iterator;
-typedef std::map<LLUUID,LLAccessEntry>::const_iterator access_map_const_iterator;
class LLParcel
{
@@ -320,6 +323,9 @@ public:
void unpackAccessEntries(LLMessageSystem* msg,
std::map<LLUUID,LLAccessEntry>* list);
+ void unpackExperienceEntries(LLMessageSystem* msg, U32 type);
+
+
void setAABBMin(const LLVector3& min) { mAABBMin = min; }
void setAABBMax(const LLVector3& max) { mAABBMax = max; }
@@ -665,6 +671,17 @@ public:
std::map<LLUUID,LLAccessEntry> mTempBanList;
std::map<LLUUID,LLAccessEntry> mTempAccessList;
+ typedef std::map<LLUUID, U32> xp_type_map_t;
+
+ void setExperienceKeyType(const LLUUID& experience_key, U32 type);
+ U32 countExperienceKeyType(U32 type);
+ U32 getExperienceKeyType(const LLUUID& experience_key)const;
+ LLAccessEntry::map getExperienceKeysByType(U32 type)const;
+ void clearExperienceKeysByType(U32 type);
+
+private:
+ xp_type_map_t mExperienceKeys;
+
};
diff --git a/indra/llinventory/llparcelflags.h b/indra/llinventory/llparcelflags.h
index b1a917df73..25b27a281a 100755
--- a/indra/llinventory/llparcelflags.h
+++ b/indra/llinventory/llparcelflags.h
@@ -90,8 +90,10 @@ const U32 PF_DEFAULT = PF_ALLOW_FLY
| PF_USE_ESTATE_VOICE_CHAN;
// Access list flags
-const U32 AL_ACCESS = (1 << 0);
-const U32 AL_BAN = (1 << 1);
+const U32 AL_ACCESS = (1 << 0);
+const U32 AL_BAN = (1 << 1);
+const U32 AL_ALLOW_EXPERIENCE = (1 << 3);
+const U32 AL_BLOCK_EXPERIENCE = (1 << 4);
//const U32 AL_RENTER = (1 << 2);
// Block access return values. BA_ALLOWED is the only success case
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index 8bd134dc84..08edf1e5fe 100755
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
@@ -39,6 +39,7 @@ set(llmessage_SOURCE_FILES
llcurl.cpp
lldatapacker.cpp
lldispatcher.cpp
+ llexperiencecache.cpp
llfiltersd2xmlrpc.cpp
llhost.cpp
llhttpassetstorage.cpp
@@ -129,6 +130,7 @@ set(llmessage_HEADER_FILES
lldbstrings.h
lldispatcher.h
lleventflags.h
+ llexperiencecache.h
llextendedstatus.h
llfiltersd2xmlrpc.h
llfollowcamparams.h
diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
new file mode 100644
index 0000000000..52b60a176e
--- /dev/null
+++ b/indra/llmessage/llexperiencecache.cpp
@@ -0,0 +1,641 @@
+/**
+ * @file llexperiencecache.cpp
+ * @brief llexperiencecache and related class definitions
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#include "llexperiencecache.h"
+
+#include "llavatarname.h"
+#include "llframetimer.h"
+#include "llhttpclient.h"
+#include "llsdserialize.h"
+#include <set>
+#include <map>
+#include "boost/tokenizer.hpp"
+
+
+namespace LLExperienceCache
+{
+
+ typedef std::map<LLUUID, LLUUID> KeyMap;
+ KeyMap privateToPublicKeyMap;
+
+ void mapKeys(const LLSD& legacyKeys);
+
+ std::string sLookupURL;
+
+ typedef std::map<LLUUID, std::string> ask_queue_t;
+ ask_queue_t sAskQueue;
+
+ typedef std::map<LLUUID, F64> pending_queue_t;
+ pending_queue_t sPendingQueue;
+
+ cache_t sCache;
+ int sMaximumLookups = 10;
+
+ LLFrameTimer sRequestTimer;
+
+ // Periodically clean out expired entries from the cache
+ LLFrameTimer sEraseExpiredTimer;
+
+ // May have multiple callbacks for a single ID, which are
+ // represented as multiple slots bound to the signal.
+ // Avoid copying signals via pointers.
+ typedef std::map<LLUUID, callback_signal_t*> signal_map_t;
+ signal_map_t sSignalMap;
+
+
+
+ bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
+ void eraseExpired();
+
+ void processExperience( const LLUUID& public_key, const LLSD& experience )
+ {
+ sCache[public_key]=experience;
+ LLSD & row = sCache[public_key];
+
+ if(row.has(EXPIRES))
+ {
+ row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds();
+ }
+
+ if(row.has(EXPERIENCE_ID))
+ {
+ sPendingQueue.erase(row[EXPERIENCE_ID].asUUID());
+ }
+
+ //signal
+ signal_map_t::iterator sig_it = sSignalMap.find(public_key);
+ if (sig_it != sSignalMap.end())
+ {
+ callback_signal_t* signal = sig_it->second;
+ (*signal)(experience);
+
+ sSignalMap.erase(public_key);
+
+ delete signal;
+ }
+ }
+
+ void initClass( )
+ {
+ }
+
+ const cache_t& getCached()
+ {
+ return sCache;
+ }
+
+ void setMaximumLookups( int maximumLookups)
+ {
+ sMaximumLookups = maximumLookups;
+ }
+
+ void bootstrap(const LLSD& legacyKeys, int initialExpiration)
+ {
+ mapKeys(legacyKeys);
+ LLSD::array_const_iterator it = legacyKeys.beginArray();
+ for(/**/; it != legacyKeys.endArray(); ++it)
+ {
+ LLSD experience = *it;
+ if(experience.has(EXPERIENCE_ID))
+ {
+ if(!experience.has(EXPIRES))
+ {
+ experience[EXPIRES] = initialExpiration;
+ }
+ processExperience(experience[EXPERIENCE_ID].asUUID(), experience);
+ }
+ else
+ {
+ LL_WARNS("ExperienceCache")
+ << "Skipping bootstrap entry which is missing " << EXPERIENCE_ID
+ << LL_ENDL;
+ }
+ }
+ }
+
+
+
+ bool expirationFromCacheControl(LLSD headers, F64 *expires)
+ {
+ // Allow the header to override the default
+ LLSD cache_control_header = headers["cache-control"];
+ if (cache_control_header.isDefined())
+ {
+ S32 max_age = 0;
+ std::string cache_control = cache_control_header.asString();
+ if (max_age_from_cache_control(cache_control, &max_age))
+ {
+ LL_WARNS("ExperienceCache")
+ << "got EXPIRES from headers, max_age " << max_age
+ << LL_ENDL;
+ F64 now = LLFrameTimer::getTotalSeconds();
+ *expires = now + (F64)max_age;
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ static const std::string MAX_AGE("max-age");
+ static const boost::char_separator<char> EQUALS_SEPARATOR("=");
+ static const boost::char_separator<char> COMMA_SEPARATOR(",");
+
+ bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
+ {
+ // Split the string on "," to get a list of directives
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ tokenizer directives(cache_control, COMMA_SEPARATOR);
+
+ tokenizer::iterator token_it = directives.begin();
+ for ( ; token_it != directives.end(); ++token_it)
+ {
+ // Tokens may have leading or trailing whitespace
+ std::string token = *token_it;
+ LLStringUtil::trim(token);
+
+ if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
+ {
+ // ...this token starts with max-age, so let's chop it up by "="
+ tokenizer subtokens(token, EQUALS_SEPARATOR);
+ tokenizer::iterator subtoken_it = subtokens.begin();
+
+ // Must have a token
+ if (subtoken_it == subtokens.end()) return false;
+ std::string subtoken = *subtoken_it;
+
+ // Must exactly equal "max-age"
+ LLStringUtil::trim(subtoken);
+ if (subtoken != MAX_AGE) return false;
+
+ // Must have another token
+ ++subtoken_it;
+ if (subtoken_it == subtokens.end()) return false;
+ subtoken = *subtoken_it;
+
+ // Must be a valid integer
+ // *NOTE: atoi() returns 0 for invalid values, so we have to
+ // check the string first.
+ // *TODO: Do servers ever send "0000" for zero? We don't handle it
+ LLStringUtil::trim(subtoken);
+ if (subtoken == "0")
+ {
+ *max_age = 0;
+ return true;
+ }
+ S32 val = atoi( subtoken.c_str() );
+ if (val > 0 && val < S32_MAX)
+ {
+ *max_age = val;
+ return true;
+ }
+ return false;
+ }
+ }
+ return false;
+ }
+
+
+ void importFile(std::istream& istr)
+ {
+ LLSD data;
+ S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr);
+ if(parse_count < 1) return;
+
+ LLSD experiences = data["experiences"];
+
+ LLUUID public_key;
+ LLSD::map_const_iterator it = experiences.beginMap();
+ for(; it != experiences.endMap() ; ++it)
+ {
+ public_key.set(it->first);
+ sCache[public_key]=it->second;
+ }
+
+ LL_DEBUGS("ExperienceCache") << "importFile() loaded " << sCache.size() << LL_ENDL;
+ }
+
+ void exportFile(std::ostream& ostr)
+ {
+ LLSD experiences;
+
+ cache_t::const_iterator it =sCache.begin();
+ for( ; it != sCache.end() ; ++it)
+ {
+ if(!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() ||
+ it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID))
+ continue;
+
+ experiences[it->first.asString()] = it->second;
+ }
+
+ LLSD data;
+ data["experiences"] = experiences;
+
+ LLSDSerialize::toPrettyXML(data, ostr);
+ }
+
+ class LLExperienceResponder : public LLHTTPClient::Responder
+ {
+ public:
+ LLExperienceResponder(const ask_queue_t& keys)
+ :mKeys(keys)
+ {
+
+ }
+
+ /*virtual*/ void httpCompleted()
+ {
+ LLSD experiences = getContent()["experience_keys"];
+ LLSD::array_const_iterator it = experiences.beginArray();
+ for( /**/ ; it != experiences.endArray(); ++it)
+ {
+ const LLSD& row = *it;
+ LLUUID public_key = row[EXPERIENCE_ID].asUUID();
+
+
+ LL_DEBUGS("ExperienceCache") << "Received result for " << public_key
+ << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ;
+
+ processExperience(public_key, row);
+ }
+
+ LLSD error_ids = getContent()["error_ids"];
+ LLSD::array_const_iterator errIt = error_ids.beginArray();
+ for( /**/ ; errIt != error_ids.endArray() ; ++errIt )
+ {
+ LLUUID id = errIt->asUUID();
+ LLSD exp;
+ exp[EXPIRES]=DEFAULT_EXPIRATION;
+ exp[EXPERIENCE_ID] = id;
+ exp[PROPERTIES]=PROPERTY_INVALID;
+ exp[MISSING]=true;
+ exp[QUOTA] = DEFAULT_QUOTA;
+
+ processExperience(id, exp);
+ LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL ;
+ }
+
+ LL_DEBUGS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL;
+ }
+
+ /*virtual*/ void httpFailure()
+ {
+ LL_WARNS("ExperienceCache") << "Request failed "<<getStatus()<<" "<<getReason()<< LL_ENDL;
+ // We're going to construct a dummy record and cache it for a while,
+ // either briefly for a 503 Service Unavailable, or longer for other
+ // errors.
+ F64 retry_timestamp = errorRetryTimestamp(getStatus());
+
+
+ // Add dummy records for all agent IDs in this request
+ ask_queue_t::const_iterator it = mKeys.begin();
+ for ( ; it != mKeys.end(); ++it)
+ {
+
+ LLSD exp = get(it->first);
+ //leave the properties alone if we already have a cache entry for this xp
+ if(exp.isUndefined())
+ {
+ exp[PROPERTIES]=PROPERTY_INVALID;
+ }
+ exp[EXPIRES]=retry_timestamp;
+ exp[EXPERIENCE_ID] = it->first;
+ exp["key_type"] = it->second;
+ exp["uuid"] = it->first;
+ exp["error"] = (LLSD::Integer)getStatus();
+ exp[QUOTA] = DEFAULT_QUOTA;
+
+ LLExperienceCache::processExperience(it->first, exp);
+ }
+
+ }
+
+ // Return time to retry a request that generated an error, based on
+ // error type and headers. Return value is seconds-since-epoch.
+ F64 errorRetryTimestamp(S32 status)
+ {
+
+ // Retry-After takes priority
+ LLSD retry_after = getResponseHeaders()["retry-after"];
+ if (retry_after.isDefined())
+ {
+ // We only support the delta-seconds type
+ S32 delta_seconds = retry_after.asInteger();
+ if (delta_seconds > 0)
+ {
+ // ...valid delta-seconds
+ return F64(delta_seconds);
+ }
+ }
+
+ // If no Retry-After, look for Cache-Control max-age
+ F64 expires = 0.0;
+ if (LLExperienceCache::expirationFromCacheControl(getResponseHeaders(), &expires))
+ {
+ return expires;
+ }
+
+ // No information in header, make a guess
+ if (status == 503)
+ {
+ // ...service unavailable, retry soon
+ const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
+ return SERVICE_UNAVAILABLE_DELAY;
+ }
+ else if (status == 499)
+ {
+ // ...we were probably too busy, retry quickly
+ const F64 BUSY_DELAY = 10.0; // 10 seconds
+ return BUSY_DELAY;
+
+ }
+ else
+ {
+ // ...other unexpected error
+ const F64 DEFAULT_DELAY = 3600.0; // 1 hour
+ return DEFAULT_DELAY;
+ }
+ }
+
+ private:
+ ask_queue_t mKeys;
+ };
+
+ void requestExperiences()
+ {
+ if(sAskQueue.empty() || sLookupURL.empty())
+ return;
+
+ F64 now = LLFrameTimer::getTotalSeconds();
+
+ const U32 EXP_URL_SEND_THRESHOLD = 3000;
+ const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD/UUID_STR_LENGTH;
+
+ std::ostringstream ostr;
+
+ ask_queue_t keys;
+
+ ostr << sLookupURL << "?page_size=" << PAGE_SIZE;
+
+
+ int request_count = 0;
+ while(!sAskQueue.empty() && request_count < sMaximumLookups)
+ {
+ ask_queue_t::iterator it = sAskQueue.begin();
+ const LLUUID& key = it->first;
+ const std::string& key_type = it->second;
+
+ ostr << '&' << key_type << '=' << key.asString() ;
+
+ keys[key]=key_type;
+ request_count++;
+
+ sPendingQueue[key] = now;
+
+ if(ostr.tellp() > EXP_URL_SEND_THRESHOLD)
+ {
+ LL_DEBUGS("ExperienceCache") << "requestExperiences() query: " << ostr.str() << LL_ENDL;
+ LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys));
+ ostr.clear();
+ ostr.str(sLookupURL);
+ ostr << "?page_size=" << PAGE_SIZE;
+ keys.clear();
+ }
+ sAskQueue.erase(it);
+ }
+
+ if(ostr.tellp() > sLookupURL.size())
+ {
+ LL_DEBUGS("ExperienceCache") << "requestExperiences() query 2: " << ostr.str() << LL_ENDL;
+ LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys));
+ }
+ }
+
+ bool isRequestPending(const LLUUID& public_key)
+ {
+ bool isPending = false;
+ const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0;
+
+ pending_queue_t::const_iterator it = sPendingQueue.find(public_key);
+
+ if(it != sPendingQueue.end())
+ {
+ F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS;
+ isPending = (it->second > expire_time);
+ }
+
+ return isPending;
+ }
+
+
+ void setLookupURL( const std::string& lookup_url )
+ {
+ sLookupURL = lookup_url;
+ if(!sLookupURL.empty())
+ {
+ sLookupURL += "id/";
+ }
+ }
+
+ bool hasLookupURL()
+ {
+ return !sLookupURL.empty();
+ }
+
+ void idle()
+ {
+
+ const F32 SECS_BETWEEN_REQUESTS = 0.1f;
+ if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS))
+ {
+ return;
+ }
+
+ // Must be large relative to above
+ const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
+ if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
+ {
+ eraseExpired();
+ }
+
+
+ if(!sAskQueue.empty())
+ {
+ requestExperiences();
+ }
+ }
+
+ void erase( const LLUUID& key )
+ {
+ cache_t::iterator it = sCache.find(key);
+
+ if(it != sCache.end())
+ {
+ sCache.erase(it);
+ }
+ }
+
+ void eraseExpired()
+ {
+ F64 now = LLFrameTimer::getTotalSeconds();
+ cache_t::iterator it = sCache.begin();
+ while (it != sCache.end())
+ {
+ cache_t::iterator cur = it;
+ LLSD& exp = cur->second;
+ ++it;
+
+ if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now)
+ {
+ if(!exp.has(EXPERIENCE_ID))
+ {
+ LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ;
+ sCache.erase(cur);
+ }
+ else
+ {
+ LLUUID id = exp[EXPERIENCE_ID].asUUID();
+ LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null;
+ if(private_key.notNull() || !exp.has("DoesNotExist"))
+ {
+ fetch(id, true);
+ }
+ else
+ {
+ LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ;
+ sCache.erase(cur);
+ }
+ }
+ }
+ }
+ }
+
+
+ bool fetch( const LLUUID& key, bool refresh/* = true*/ )
+ {
+ if(!key.isNull() && !isRequestPending(key) && (refresh || sCache.find(key)==sCache.end()))
+ {
+ LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL ;
+ sAskQueue[key]=EXPERIENCE_ID;
+
+ return true;
+ }
+ return false;
+ }
+
+ void insert(const LLSD& experience_data )
+ {
+ if(experience_data.has(EXPERIENCE_ID))
+ {
+ processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data);
+ }
+ else
+ {
+ LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL;
+ }
+ }
+ static LLSD empty;
+ const LLSD& get(const LLUUID& key)
+ {
+ if(key.isNull()) return empty;
+ cache_t::const_iterator it = sCache.find(key);
+
+ if (it != sCache.end())
+ {
+ return it->second;
+ }
+
+ fetch(key);
+
+ return empty;
+ }
+ void get( const LLUUID& key, callback_slot_t slot )
+ {
+ if(key.isNull()) return;
+
+ cache_t::const_iterator it = sCache.find(key);
+ if (it != sCache.end())
+ {
+ // ...name already exists in cache, fire callback now
+ callback_signal_t signal;
+ signal.connect(slot);
+
+ signal(it->second);
+ return;
+ }
+
+ fetch(key);
+
+ // always store additional callback, even if request is pending
+ signal_map_t::iterator sig_it = sSignalMap.find(key);
+ if (sig_it == sSignalMap.end())
+ {
+ // ...new callback for this id
+ callback_signal_t* signal = new callback_signal_t();
+ signal->connect(slot);
+ sSignalMap[key] = signal;
+ }
+ else
+ {
+ // ...existing callback, bind additional slot
+ callback_signal_t* signal = sig_it->second;
+ signal->connect(slot);
+ }
+ }
+
+}
+
+
+void LLExperienceCache::mapKeys( const LLSD& legacyKeys )
+{
+ LLSD::array_const_iterator exp = legacyKeys.beginArray();
+ for(/**/ ; exp != legacyKeys.endArray() ; ++exp)
+ {
+ if(exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY))
+ {
+ privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()]=(*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID();
+ }
+ }
+}
+
+
+LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found)
+{
+ if (private_key.isNull())
+ return LLUUID::null;
+
+ KeyMap::const_iterator it=privateToPublicKeyMap.find(private_key);
+ if(it == privateToPublicKeyMap.end())
+ {
+ if(null_if_not_found)
+ {
+ return LLUUID::null;
+ }
+ return private_key;
+ }
+ LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL;
+ return it->second;
+}
diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h
new file mode 100644
index 0000000000..e669ee888e
--- /dev/null
+++ b/indra/llmessage/llexperiencecache.h
@@ -0,0 +1,104 @@
+/**
+ * @file llexperiencecache.h
+ * @brief Caches information relating to experience keys
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+
+#ifndef LL_LLEXPERIENCECACHE_H
+#define LL_LLEXPERIENCECACHE_H
+
+#include "linden_common.h"
+#include <boost/signals2.hpp>
+
+class LLSD;
+class LLUUID;
+
+
+
+namespace LLExperienceCache
+{
+ const std::string PRIVATE_KEY = "private_id";
+ const std::string MISSING = "DoesNotExist";
+
+ const std::string AGENT_ID = "agent_id";
+ const std::string GROUP_ID = "group_id";
+ const std::string EXPERIENCE_ID = "public_id";
+ const std::string NAME = "name";
+ const std::string PROPERTIES = "properties";
+ const std::string EXPIRES = "expiration";
+ const std::string DESCRIPTION = "description";
+ const std::string QUOTA = "quota";
+ const std::string MATURITY = "maturity";
+ const std::string METADATA = "extended_metadata";
+ const std::string SLURL = "slurl";
+
+
+ // should be in sync with experience-api/experiences/models.py
+ const int PROPERTY_INVALID = 1 << 0;
+ const int PROPERTY_PRIVILEGED = 1 << 3;
+ const int PROPERTY_GRID = 1 << 4;
+ const int PROPERTY_PRIVATE = 1 << 5;
+ const int PROPERTY_DISABLED = 1 << 6;
+ const int PROPERTY_SUSPENDED = 1 << 7;
+
+
+ // default values
+ const static F64 DEFAULT_EXPIRATION = 600.0;
+ const static S32 DEFAULT_QUOTA = 128; // this is megabytes
+
+ // Callback types for get() below
+ typedef boost::signals2::signal<void (const LLSD& experience)>
+ callback_signal_t;
+ typedef callback_signal_t::slot_type callback_slot_t;
+ typedef std::map<LLUUID, LLSD> cache_t;
+
+
+ void setLookupURL(const std::string& lookup_url);
+ bool hasLookupURL();
+
+ void setMaximumLookups(int maximumLookups);
+
+ void idle();
+ void exportFile(std::ostream& ostr);
+ void importFile(std::istream& istr);
+ void initClass();
+ void bootstrap(const LLSD& legacyKeys, int initialExpiration);
+
+ void erase(const LLUUID& key);
+ bool fetch(const LLUUID& key, bool refresh=false);
+ void insert(const LLSD& experience_data);
+ const LLSD& get(const LLUUID& key);
+
+ // If name information is in cache, callback will be called immediately.
+ void get(const LLUUID& key, callback_slot_t slot);
+
+ const cache_t& getCached();
+
+ // maps an experience private key to the experience id
+ LLUUID getExperienceId(const LLUUID& private_key, bool null_if_not_found=false);
+
+};
+
+#endif // LL_LLEXPERIENCECACHE_H
diff --git a/indra/llmessage/llregionflags.h b/indra/llmessage/llregionflags.h
index 40d7b04a90..eb0c4e6d1e 100755
--- a/indra/llmessage/llregionflags.h
+++ b/indra/llmessage/llregionflags.h
@@ -148,19 +148,20 @@ const U32 ESTATE_ACCESS_ALL = ESTATE_ACCESS_ALLOWED_AGENTS
| ESTATE_ACCESS_BANNED_AGENTS
| ESTATE_ACCESS_MANAGERS;
-// for EstateOwnerRequest, estateaccessdelta message
-const U32 ESTATE_ACCESS_APPLY_TO_ALL_ESTATES = 1 << 0;
-const U32 ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES = 1 << 1;
-
-const U32 ESTATE_ACCESS_ALLOWED_AGENT_ADD = 1 << 2;
-const U32 ESTATE_ACCESS_ALLOWED_AGENT_REMOVE = 1 << 3;
-const U32 ESTATE_ACCESS_ALLOWED_GROUP_ADD = 1 << 4;
-const U32 ESTATE_ACCESS_ALLOWED_GROUP_REMOVE = 1 << 5;
-const U32 ESTATE_ACCESS_BANNED_AGENT_ADD = 1 << 6;
-const U32 ESTATE_ACCESS_BANNED_AGENT_REMOVE = 1 << 7;
-const U32 ESTATE_ACCESS_MANAGER_ADD = 1 << 8;
-const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1 << 9;
-const U32 ESTATE_ACCESS_NO_REPLY = 1 << 10;
+// for EstateOwnerRequest, estateaccessdelta, estateexperiencedelta messages
+const U32 ESTATE_ACCESS_APPLY_TO_ALL_ESTATES = 1U << 0;
+const U32 ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES = 1U << 1;
+
+const U32 ESTATE_ACCESS_ALLOWED_AGENT_ADD = 1U << 2;
+const U32 ESTATE_ACCESS_ALLOWED_AGENT_REMOVE = 1U << 3;
+const U32 ESTATE_ACCESS_ALLOWED_GROUP_ADD = 1U << 4;
+const U32 ESTATE_ACCESS_ALLOWED_GROUP_REMOVE = 1U << 5;
+const U32 ESTATE_ACCESS_BANNED_AGENT_ADD = 1U << 6;
+const U32 ESTATE_ACCESS_BANNED_AGENT_REMOVE = 1U << 7;
+const U32 ESTATE_ACCESS_MANAGER_ADD = 1U << 8;
+const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1U << 9;
+const U32 ESTATE_ACCESS_NO_REPLY = 1U << 10;
+const U32 ESTATE_ACCESS_FAILED_BAN_ESTATE_MANAGER = 1U << 11;
const S32 ESTATE_MAX_MANAGERS = 10;
const S32 ESTATE_MAX_ACCESS_IDS = 500; // max for access, banned
@@ -171,6 +172,26 @@ const U32 SWD_OTHERS_LAND_ONLY = (1 << 0);
const U32 SWD_ALWAYS_RETURN_OBJECTS = (1 << 1);
const U32 SWD_SCRIPTED_ONLY = (1 << 2);
+// Controls experience key validity in the estate
+const U32 EXPERIENCE_KEY_TYPE_NONE = 0;
+const U32 EXPERIENCE_KEY_TYPE_BLOCKED = 1;
+const U32 EXPERIENCE_KEY_TYPE_ALLOWED = 2;
+const U32 EXPERIENCE_KEY_TYPE_TRUSTED = 3;
+
+const U32 EXPERIENCE_KEY_TYPE_FIRST = EXPERIENCE_KEY_TYPE_BLOCKED;
+const U32 EXPERIENCE_KEY_TYPE_LAST = EXPERIENCE_KEY_TYPE_TRUSTED;
+
+//
+const U32 ESTATE_EXPERIENCE_TRUSTED_ADD = 1U << 2;
+const U32 ESTATE_EXPERIENCE_TRUSTED_REMOVE = 1U << 3;
+const U32 ESTATE_EXPERIENCE_ALLOWED_ADD = 1U << 4;
+const U32 ESTATE_EXPERIENCE_ALLOWED_REMOVE = 1U << 5;
+const U32 ESTATE_EXPERIENCE_BLOCKED_ADD = 1U << 6;
+const U32 ESTATE_EXPERIENCE_BLOCKED_REMOVE = 1U << 7;
+
+const S32 ESTATE_MAX_EXPERIENCE_IDS = 8;
+
+
#endif
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
index 39cfb6019e..e0f69dc2d3 100755
--- a/indra/llmessage/message_prehash.cpp
+++ b/indra/llmessage/message_prehash.cpp
@@ -1383,3 +1383,5 @@ char const* const _PREHASH_GroupAVSounds = LLMessageStringTable::getInstance()->
char const* const _PREHASH_AppearanceData = LLMessageStringTable::getInstance()->getString("AppearanceData");
char const* const _PREHASH_AppearanceVersion = LLMessageStringTable::getInstance()->getString("AppearanceVersion");
char const* const _PREHASH_CofVersion = LLMessageStringTable::getInstance()->getString("CofVersion");
+char const* const _PREHASH_Experience = LLMessageStringTable::getInstance()->getString("Experience");
+char const* const _PREHASH_ExperienceID = LLMessageStringTable::getInstance()->getString("ExperienceID");
diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h
index 573e10dc0b..fb8dcc17fb 100755
--- a/indra/llmessage/message_prehash.h
+++ b/indra/llmessage/message_prehash.h
@@ -1383,4 +1383,6 @@ extern char const* const _PREHASH_GroupAVSounds;
extern char const* const _PREHASH_AppearanceData;
extern char const* const _PREHASH_AppearanceVersion;
extern char const* const _PREHASH_CofVersion;
+extern char const* const _PREHASH_Experience;
+extern char const* const _PREHASH_ExperienceID;
#endif
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index de14391d1f..7e235997d8 100755
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -678,7 +678,7 @@ void LLNotification::respond(const LLSD& response)
// and then call it
functor(asLLSD(), response);
}
- else
+ else if (mCombinedNotifications.empty())
{
// no registered responder
return;
@@ -700,6 +700,14 @@ void LLNotification::respond(const LLSD& response)
}
}
+ for (std::vector<LLNotificationPtr>::const_iterator it = mCombinedNotifications.begin(); it != mCombinedNotifications.end(); ++it)
+ {
+ if ((*it))
+ {
+ (*it)->respond(response);
+ }
+ }
+
update();
}
@@ -1322,6 +1330,28 @@ bool LLNotifications::failedUniquenessTest(const LLSD& payload)
}
}
break;
+ case LLNotification::COMBINE_WITH_NEW:
+ // Add to the existing unique notification with the data from this particular instance...
+ // This guarantees that duplicate notifications will be collapsed to the one
+ // most recently triggered
+ for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
+ existing_it != mUniqueNotifications.end();
+ ++existing_it)
+ {
+ LLNotificationPtr existing_notification = existing_it->second;
+ if (pNotif != existing_notification
+ && pNotif->isEquivalentTo(existing_notification))
+ {
+ // copy the notifications from the newest instance into the oldest
+ existing_notification->mCombinedNotifications.push_back(pNotif);
+ existing_notification->mCombinedNotifications.insert(existing_notification->mCombinedNotifications.end(),
+ pNotif->mCombinedNotifications.begin(), pNotif->mCombinedNotifications.end());
+
+ // pop up again
+ existing_notification->update();
+ }
+ }
+ break;
case LLNotification::KEEP_OLD:
break;
case LLNotification::CANCEL_OLD:
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 3cf432f330..0d673d178b 100755
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -414,6 +414,9 @@ private:
using the same mechanism.
*/
bool mTemporaryResponder;
+
+ // keep track of other notifications combined with COMBINE_WITH_NEW
+ std::vector<LLNotificationPtr> mCombinedNotifications;
void init(const std::string& template_name, const LLSD& form_elements);
@@ -560,6 +563,7 @@ public:
typedef enum e_combine_behavior
{
REPLACE_WITH_NEW,
+ COMBINE_WITH_NEW,
KEEP_OLD,
CANCEL_OLD
diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h
index 0315ddbea8..c23fc53763 100755
--- a/indra/llui/llnotificationtemplate.h
+++ b/indra/llui/llnotificationtemplate.h
@@ -43,6 +43,7 @@ struct LLNotificationTemplate
static void declareValues()
{
declare("replace_with_new", LLNotification::REPLACE_WITH_NEW);
+ declare("combine_with_new", LLNotification::COMBINE_WITH_NEW);
declare("keep_old", LLNotification::KEEP_OLD);
declare("cancel_old", LLNotification::CANCEL_OLD);
}
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 6f858cdeb3..4b08798892 100755
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -2094,3 +2094,8 @@ void LLTabContainer::commitHoveredButton(S32 x, S32 y)
}
}
}
+
+S32 LLTabContainer::getTotalTabWidth() const
+{
+ return mTotalTabWidth;
+}
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 57862fc626..057809dc42 100755
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -182,7 +182,8 @@ public:
LLPanel* getPanelByIndex(S32 index);
S32 getIndexForPanel(LLPanel* panel);
S32 getPanelIndexByTitle(const std::string& title);
- LLPanel* getPanelByName(const std::string& name);
+ LLPanel* getPanelByName(const std::string& name);
+ S32 getTotalTabWidth() const;
void setCurrentTabName(const std::string& name);
void selectFirstTab();
@@ -287,7 +288,7 @@ private:
S32 mMaxTabWidth;
S32 mTotalTabWidth;
- S32 mTabHeight;
+ S32 mTabHeight;
// Padding under the text labels of tab buttons
S32 mLabelPadBottom;
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index 6f3122e7a1..67da98b95a 100755
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -37,6 +37,7 @@
#include "lltrans.h"
#include "lluicolortable.h"
#include "message.h"
+#include "llexperiencecache.h"
#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
@@ -1201,3 +1202,51 @@ std::string LLUrlEntryIcon::getIcon(const std::string &url)
LLStringUtil::trim(mIcon);
return mIcon;
}
+
+LLUrlEntryExperienceProfile::LLUrlEntryExperienceProfile()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/\\w+\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mIcon = "Generic_Experience";
+ mMenuName = "menu_url_experience.xml";
+}
+
+std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const LLUrlLabelCallback &cb )
+{
+ if (!gCacheName)
+ {
+ // probably at the login screen, use short string for layout
+ return LLTrans::getString("LoadingData");
+ }
+
+ std::string experience_id_string = getIDStringFromUrl(url);
+ if (experience_id_string.empty())
+ {
+ // something went wrong, just give raw url
+ return unescapeUrl(url);
+ }
+
+ LLUUID experience_id(experience_id_string);
+ if (experience_id.isNull())
+ {
+ return LLTrans::getString("ExperienceNameNull");
+ }
+
+ const LLSD& experience_details = LLExperienceCache::get(experience_id);
+ if(!experience_details.isUndefined())
+ {
+ return experience_details[LLExperienceCache::NAME].asString();
+ }
+
+ addObserver(experience_id_string, url, cb);
+ LLExperienceCache::get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1));
+ return LLTrans::getString("LoadingData");
+
+}
+
+void LLUrlEntryExperienceProfile::onExperienceDetails( const LLSD& experience_details )
+{
+ callObservers(experience_details[LLExperienceCache::EXPERIENCE_ID].asString(), experience_details[LLExperienceCache::NAME].asString(), LLStringUtil::null);
+}
+
+
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index d4684e2e1e..85fef2c18b 100755
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -259,6 +259,20 @@ private:
};
///
+/// LLUrlEntryExperienceProfile Describes a Second Life experience profile Url, e.g.,
+/// secondlife:///app/experience/0e346d8b-4433-4d66-a6b0-fd37083abc4c/profile
+/// that displays the experience name
+class LLUrlEntryExperienceProfile : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryExperienceProfile();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+private:
+ void onExperienceDetails(const LLSD& experience_details);
+};
+
+
+///
/// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
/// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
///
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index bccc646821..4590f0ec28 100755
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -61,6 +61,7 @@ LLUrlRegistry::LLUrlRegistry()
registerUrl(new LLUrlEntryPlace());
registerUrl(new LLUrlEntryInventory());
registerUrl(new LLUrlEntryObjectIM());
+ registerUrl(new LLUrlEntryExperienceProfile());
//LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern,
//so it should be registered in the end of list
registerUrl(new LLUrlEntrySL());
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index c3f0e92cb0..15f2354552 100755
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -32,9 +32,23 @@
#include "lltut.h"
#include "../lluicolortable.h"
#include "../llrender/lluiimage.h"
+#include "../llmessage/llexperiencecache.h"
#include <boost/regex.hpp>
+
+namespace LLExperienceCache
+{
+ const LLSD& get( const LLUUID& key)
+ {
+ static LLSD boo;
+ return boo;
+ }
+
+ void get( const LLUUID& key, callback_slot_t slot ){}
+
+}
+
typedef std::map<std::string, LLControlGroup*> settings_map_t;
settings_map_t LLUI::sSettingGroups;
diff --git a/indra/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l
index 7772c95609..aad7c317d2 100755
--- a/indra/lscript/lscript_compile/indra.l
+++ b/indra/lscript/lscript_compile/indra.l
@@ -737,6 +737,23 @@ int yyerror(const char *fmt, ...);
"STATUS_INTERNAL_ERROR" { count(); yylval.ival = LSL_STATUS_INTERNAL_ERROR; return(INTEGER_CONSTANT); }
"STATUS_WHITELIST_FAILED" { count(); yylval.ival = LSL_STATUS_WHITELIST_FAILED; return(INTEGER_CONSTANT); }
+"XP_ERROR_NONE" { const char* sval= "no error"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_THROTTLED" { const char* sval= "exceeded throttle"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_EXPERIENCES_DISABLED" { const char* sval= "experiences are disabled"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_INVALID_PARAMETERS" { const char* sval= "invalid parameters"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_NOT_PERMITTED" { const char* sval= "operation not permitted"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_NO_EXPERIENCE" { const char* sval= "script not associated with an experience";yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_NOT_FOUND" { const char* sval= "not found"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_INVALID_EXPERIENCE" { const char* sval= "invalid experience"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_EXPERIENCE_DISABLED" { const char* sval= "experience is disabled"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_EXPERIENCE_SUSPENDED" { const char* sval= "experience is suspended"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_UNKNOWN_ERROR" { const char* sval= "unknown error"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_QUOTA_EXCEEDED" { const char* sval= "experience data quota exceeded"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_STORE_DISABLED" { const char* sval= "key-value store is disabled"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_STORAGE_EXCEPTION" { const char* sval= "key-value store communication failed"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_KEY_NOT_FOUND" { const char* sval= "key doesn't exist"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_RETRY_UPDATE" { const char* sval= "retry update"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+
{L}({L}|{N})* { count(); yylval.sval = new char[strlen(yytext) + 1]; strcpy(yylval.sval, yytext); return(IDENTIFIER); }
{N}+{E} { count(); yylval.fval = (F32)atof(yytext); return(FP_CONSTANT); }
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 3e296c8d9f..752508ea5e 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -189,6 +189,8 @@ set(viewer_SOURCE_FILES
lleventnotifier.cpp
lleventpoll.cpp
llexpandabletextbox.cpp
+ llexperienceassociationresponder.cpp
+ llexperiencelog.cpp
llexternaleditor.cpp
llface.cpp
llfacebookconnect.cpp
@@ -230,6 +232,9 @@ set(viewer_SOURCE_FILES
llfloatereditwater.cpp
llfloaterenvironmentsettings.cpp
llfloaterevent.cpp
+ llfloaterexperiencepicker.cpp
+ llfloaterexperienceprofile.cpp
+ llfloaterexperiences.cpp
llfloaterfacebook.cpp
llfloaterflickr.cpp
llfloaterfonttest.cpp
@@ -408,11 +413,16 @@ set(viewer_SOURCE_FILES
llpanelclassified.cpp
llpanelcontents.cpp
llpaneleditwearable.cpp
+ llpanelexperiencelisteditor.cpp
+ llpanelexperiencelog.cpp
+ llpanelexperiencepicker.cpp
+ llpanelexperiences.cpp
llpanelface.cpp
llpanelgenerictip.cpp
llpanelgroup.cpp
llpanelgroupbulk.cpp
llpanelgroupbulkban.cpp
+ llpanelgroupexperiences.cpp
llpanelgroupgeneral.cpp
llpanelgroupinvite.cpp
llpanelgrouplandmoney.cpp
@@ -793,6 +803,8 @@ set(viewer_HEADER_FILES
lleventnotifier.h
lleventpoll.h
llexpandabletextbox.h
+ llexperienceassociationresponder.h
+ llexperiencelog.h
llexternaleditor.h
llface.h
llfacebookconnect.h
@@ -834,6 +846,9 @@ set(viewer_HEADER_FILES
llfloatereditwater.h
llfloaterenvironmentsettings.h
llfloaterevent.h
+ llfloaterexperiencepicker.h
+ llfloaterexperienceprofile.h
+ llfloaterexperiences.h
llfloaterfacebook.h
llfloaterflickr.h
llfloaterfonttest.h
@@ -1005,12 +1020,17 @@ set(viewer_HEADER_FILES
llpanelclassified.h
llpanelcontents.h
llpaneleditwearable.h
+ llpanelexperiencelisteditor.h
+ llpanelexperiencelog.h
+ llpanelexperiencepicker.h
+ llpanelexperiences.h
llpanelface.h
llpanelgenerictip.h
llpanelgroup.h
llpanelgroupbulk.h
llpanelgroupbulkimpl.h
llpanelgroupbulkban.h
+ llpanelgroupexperiences.h
llpanelgroupgeneral.h
llpanelgroupinvite.h
llpanelgrouplandmoney.h
diff --git a/indra/newview/app_settings/keywords.ini b/indra/newview/app_settings/keywords.ini
index 17c70ef1c5..a070c8bcd0 100755
--- a/indra/newview/app_settings/keywords.ini
+++ b/indra/newview/app_settings/keywords.ini
@@ -52,6 +52,7 @@ remote_data remote_data(integer event_type, key channel, key message_id, str
http_response http_response(key request_id, integer status, list metadata, string body):Triggered when task receives a response to one of its llHTTPRequests
http_request http_request(key id, string method, string body):Triggered when task receives an http request against a public URL
+
# integer constants
[word .1, .1, .5]
TRUE Integer constant for Boolean operations
@@ -714,6 +715,25 @@ TEXTURE_TRANSPARENT UUID for the "White - Transparent" texture
URL_REQUEST_GRANTED Used with http_request when a public URL is successfully granted
URL_REQUEST_DENIED Used with http_request when a public URL is not available
+XP_ERROR_NONE No error was detected
+XP_ERROR_THROTTLED The call failed due to too many recent calls.
+XP_ERROR_EXPERIENCES_DISABLED The region currently has experiences disabled.
+XP_ERROR_INVALID_PARAMETERS One of the string arguments was too big to fit in the key-value store.
+XP_ERROR_NOT_PERMITTED This experience is not allowed to run on the current region.
+XP_ERROR_NO_EXPERIENCE This script is not associated with an experience.
+XP_ERROR_NOT_FOUND The sim was unable to verify the validity of the experience. Retrying after a short wait is advised.
+XP_ERROR_INVALID_EXPERIENCE The script is associated with an experience that no longer exists.
+XP_ERROR_EXPERIENCE_DISABLED The experience owner has temporarily disabled the experience.
+XP_ERROR_EXPERIENCE_SUSPENDED The experience has been suspended by Linden Customer Support.
+XP_ERROR_QUOTA_EXCEEDED An attempted write data to the key-value store failed due to the data quota being met.
+XP_ERROR_STORE_DISABLED The key-value store is currently disabled on this region.
+XP_ERROR_STORAGE_EXCEPTION Unable to communicate with the key-value store.
+XP_ERROR_KEY_NOT_FOUND The requested key does not exist.
+XP_ERROR_RETRY_UPDATE A checked update failed due to an out of date request.
+XP_ERROR_MATURITY_EXCEEDED The request failed due to agent content preferences.
+XP_ERROR_UNKNOWN_ERROR Other unknown error.
+
+
# float constants
[word .3, .1, .5]
PI 3.1415926535897932384626433832795
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 24150daea4..cd9e5da7a7 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -105,6 +105,7 @@
// Linden library includes
#include "llavatarnamecache.h"
#include "lldiriterator.h"
+#include "llexperiencecache.h"
#include "llimagej2c.h"
#include "llmemory.h"
#include "llprimitive.h"
@@ -4666,7 +4667,7 @@ void LLAppViewer::loadNameCache()
}
void LLAppViewer::saveNameCache()
- {
+{
// display names cache
std::string filename =
gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml");
@@ -4674,7 +4675,7 @@ void LLAppViewer::saveNameCache()
if(name_cache_stream.is_open())
{
LLAvatarNameCache::exportFile(name_cache_stream);
-}
+ }
if (!gCacheName) return;
@@ -4687,6 +4688,32 @@ void LLAppViewer::saveNameCache()
}
}
+
+void LLAppViewer::saveExperienceCache()
+{
+ std::string filename =
+ gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml");
+ LL_INFOS("ExperienceCache") << "Saving " << filename << LL_ENDL;
+ llofstream cache_stream(filename);
+ if(cache_stream.is_open())
+ {
+ LLExperienceCache::exportFile(cache_stream);
+ }
+}
+
+void LLAppViewer::loadExperienceCache()
+{
+ std::string filename =
+ gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml");
+ LL_INFOS("ExperienceCache") << "Loading " << filename << LL_ENDL;
+ llifstream cache_stream(filename);
+ if(cache_stream.is_open())
+ {
+ LLExperienceCache::importFile(cache_stream);
+ }
+}
+
+
/*! @brief This class is an LLFrameTimer that can be created with
an elapsed time that starts counting up from the given value
rather than 0.0.
@@ -4880,7 +4907,7 @@ void LLAppViewer::idle()
// floating throughout the various object lists.
//
idleNameCache();
-
+ idleExperienceCache();
idleNetwork();
@@ -5307,6 +5334,22 @@ void LLAppViewer::idleNameCache()
LLAvatarNameCache::idle();
}
+void LLAppViewer::idleExperienceCache()
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region) return;
+
+ std::string lookup_url=region->getCapability("GetExperienceInfo");
+ if(!lookup_url.empty() && *lookup_url.rbegin() != '/')
+ {
+ lookup_url += '/';
+ }
+
+ LLExperienceCache::setLookupURL(lookup_url);
+
+ LLExperienceCache::idle();
+}
+
//
// Handle messages, and all message related stuff
//
@@ -5469,6 +5512,7 @@ void LLAppViewer::disconnectViewer()
}
saveNameCache();
+ saveExperienceCache();
// close inventory interface, close all windows
LLFloaterInventory::cleanup();
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index e0f3f326c7..95b31cb94e 100755
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -122,6 +122,9 @@ public:
void loadNameCache();
void saveNameCache();
+ void loadExperienceCache();
+ void saveExperienceCache();
+
void removeMarkerFiles();
void removeDumpDir();
@@ -230,6 +233,7 @@ private:
void idle();
void idleShutdown();
// update avatar SLID and display name caches
+ void idleExperienceCache();
void idleNameCache();
void idleNetwork();
diff --git a/indra/newview/llassetuploadqueue.cpp b/indra/newview/llassetuploadqueue.cpp
index 8833c57948..359ee1e221 100755
--- a/indra/newview/llassetuploadqueue.cpp
+++ b/indra/newview/llassetuploadqueue.cpp
@@ -168,6 +168,7 @@ void LLAssetUploadQueue::request(LLAssetUploadQueueSupplier** supplier)
body["item_id"] = data.mItemId;
body["is_script_running"] = data.mIsRunning;
body["target"] = data.mIsTargetMono? "mono" : "lsl2";
+ body["experience"] = data.mExperienceId;
std::string url = "";
LLViewerObject* object = gObjectList.findObject(data.mTaskId);
@@ -191,7 +192,8 @@ void LLAssetUploadQueue::queue(const std::string& filename,
const LLUUID& queue_id,
U8* script_data,
U32 data_size,
- std::string script_name)
+ std::string script_name,
+ const LLUUID& experience_id)
{
UploadData data;
data.mTaskId = task_id;
@@ -203,6 +205,7 @@ void LLAssetUploadQueue::queue(const std::string& filename,
data.mData = script_data;
data.mDataSize = data_size;
data.mScriptName = script_name;
+ data.mExperienceId = experience_id;
mQueue.push_back(data);
diff --git a/indra/newview/llassetuploadqueue.h b/indra/newview/llassetuploadqueue.h
index 434f3e5c03..2ceee8f700 100755
--- a/indra/newview/llassetuploadqueue.h
+++ b/indra/newview/llassetuploadqueue.h
@@ -50,7 +50,8 @@ public:
const LLUUID& queue_id,
U8* data,
U32 data_size,
- std::string script_name);
+ std::string script_name,
+ const LLUUID& experience_id);
bool isEmpty() const {return mQueue.empty();}
@@ -69,6 +70,7 @@ private:
U8* mData;
U32 mDataSize;
std::string mScriptName;
+ LLUUID mExperienceId;
};
// Ownership of mSupplier passed to currently waiting responder
diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp
index b0916d769a..d9fd4509a5 100755
--- a/indra/newview/llcompilequeue.cpp
+++ b/indra/newview/llcompilequeue.cpp
@@ -59,6 +59,8 @@
#include "lltrans.h"
#include "llselectmgr.h"
+#include "llexperienceassociationresponder.h"
+#include "llexperiencecache.h"
// *TODO: This should be separated into the script queue, and the floater views of that queue.
// There should only be one floater class that can view any queue type
@@ -70,11 +72,13 @@
struct LLScriptQueueData
{
LLUUID mQueueID;
- std::string mScriptName;
LLUUID mTaskId;
- LLUUID mItemId;
- LLScriptQueueData(const LLUUID& q_id, const std::string& name, const LLUUID& task_id, const LLUUID& item_id) :
- mQueueID(q_id), mScriptName(name), mTaskId(task_id), mItemId(item_id) {}
+ LLPointer<LLInventoryItem> mItem;
+ LLHost mHost;
+ LLUUID mExperienceId;
+ std::string mExperiencename;
+ LLScriptQueueData(const LLUUID& q_id, const LLUUID& task_id, LLInventoryItem* item) :
+ mQueueID(q_id), mTaskId(task_id), mItem(new LLInventoryItem(item)) {}
};
@@ -88,6 +92,7 @@ LLFloaterScriptQueue::LLFloaterScriptQueue(const LLSD& key) :
mDone(false),
mMono(false)
{
+
}
// Destroys the object
@@ -167,7 +172,7 @@ BOOL LLFloaterScriptQueue::start()
getChild<LLScrollListCtrl>("queue output")->addSimpleElement(buffer, ADD_BOTTOM);
- return nextObject();
+ return startQueue();
}
BOOL LLFloaterScriptQueue::isDone() const
@@ -232,6 +237,40 @@ BOOL LLFloaterScriptQueue::popNext()
return rv;
}
+BOOL LLFloaterScriptQueue::startQueue()
+{
+ return nextObject();
+}
+
+class CompileQueueExperienceResponder : public LLHTTPClient::Responder
+{
+public:
+ CompileQueueExperienceResponder(const LLUUID& parent):mParent(parent)
+ {
+ }
+
+ LLUUID mParent;
+
+ /*virtual*/ void httpSuccess()
+ {
+ sendResult(getContent());
+ }
+ /*virtual*/ void httpFailure()
+ {
+ sendResult(LLSD());
+ }
+ void sendResult(const LLSD& content)
+ {
+ LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", mParent);
+ if(!queue)
+ return;
+
+ queue->experienceIdsReceived(content["experience_ids"]);
+ }
+};
+
+
+
///----------------------------------------------------------------------------
/// Class LLFloaterCompileQueue
@@ -284,6 +323,21 @@ LLFloaterCompileQueue::~LLFloaterCompileQueue()
{
}
+void LLFloaterCompileQueue::experienceIdsReceived( const LLSD& content )
+{
+ for(LLSD::array_const_iterator it = content.beginArray(); it != content.endArray(); ++it)
+ {
+ mExperienceIds.insert(it->asUUID());
+ }
+ nextObject();
+}
+
+BOOL LLFloaterCompileQueue::hasExperience( const LLUUID& id ) const
+{
+ return mExperienceIds.find(id) != mExperienceIds.end();
+}
+
+
void LLFloaterCompileQueue::handleInventory(LLViewerObject *viewer_object,
LLInventoryObject::object_list_t* inv)
{
@@ -324,25 +378,52 @@ void LLFloaterCompileQueue::handleInventory(LLViewerObject *viewer_object,
{
LLInventoryItem *itemp = iter->second;
LLScriptQueueData* datap = new LLScriptQueueData(getKey().asUUID(),
- itemp->getName(),
- viewer_object->getID(),
- itemp->getUUID());
-
- //LL_INFOS() << "ITEM NAME 2: " << names.get(i) << LL_ENDL;
- gAssetStorage->getInvItemAsset(viewer_object->getRegion()->getHost(),
- gAgent.getID(),
- gAgent.getSessionID(),
- itemp->getPermissions().getOwner(),
- viewer_object->getID(),
- itemp->getUUID(),
- itemp->getAssetUUID(),
- itemp->getType(),
- LLFloaterCompileQueue::scriptArrived,
- (void*)datap);
+ viewer_object->getID(), itemp);
+
+ ExperienceAssociationResponder::fetchAssociatedExperience(itemp->getParentUUID(), itemp->getUUID(),
+ boost::bind(LLFloaterCompileQueue::requestAsset, datap, _1));
+ }
+ }
+}
+
+
+void LLFloaterCompileQueue::requestAsset( LLScriptQueueData* datap, const LLSD& experience )
+{
+ LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance<LLFloaterCompileQueue>("compile_queue", datap->mQueueID);
+ if(!queue)
+ {
+ delete datap;
+ return;
+ }
+ if(experience.has(LLExperienceCache::EXPERIENCE_ID))
+ {
+ datap->mExperienceId=experience[LLExperienceCache::EXPERIENCE_ID].asUUID();
+ if(!queue->hasExperience(datap->mExperienceId))
+ {
+ std::string buffer = LLTrans::getString("CompileNoExperiencePerm", LLSD::emptyMap()
+ .with("SCRIPT", datap->mItem->getName())
+ .with("EXPERIENCE", experience[LLExperienceCache::NAME].asString()));
+
+ queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(buffer, ADD_BOTTOM);
+ queue->removeItemByItemID(datap->mItem->getUUID());
+ delete datap;
+ return;
}
}
+ //LL_INFOS() << "ITEM NAME 2: " << names.get(i) << LL_ENDL;
+ gAssetStorage->getInvItemAsset(datap->mHost,
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ datap->mItem->getPermissions().getOwner(),
+ datap->mTaskId,
+ datap->mItem->getUUID(),
+ datap->mItem->getAssetUUID(),
+ datap->mItem->getType(),
+ LLFloaterCompileQueue::scriptArrived,
+ (void*)datap);
}
+
// This is the callback for when each script arrives
// static
void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
@@ -382,12 +463,12 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
file.read(script_data, script_size);
queue->mUploadQueue->queue(filename, data->mTaskId,
- data->mItemId, is_running, queue->mMono, queue->getKey().asUUID(),
- script_data, script_size, data->mScriptName);
+ data->mItem->getUUID(), is_running, queue->mMono, queue->getKey().asUUID(),
+ script_data, script_size, data->mItem->getName(), data->mExperienceId);
}
else
{
- buffer = LLTrans::getString("CompileQueueServiceUnavailable") + (": ") + data->mScriptName;
+ buffer = LLTrans::getString("CompileQueueServiceUnavailable") + (": ") + data->mItem->getName();
}
}
}
@@ -399,7 +480,7 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
args["MESSAGE"] = LLTrans::getString("CompileQueueScriptNotFound");
LLNotificationsUtil::add("SystemMessage", args);
- buffer = LLTrans::getString("CompileQueueProblemDownloading") + (": ") + data->mScriptName;
+ buffer = LLTrans::getString("CompileQueueProblemDownloading") + (": ") + data->mItem->getName();
}
else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
{
@@ -407,15 +488,15 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
args["MESSAGE"] = LLTrans::getString("CompileQueueInsufficientPermDownload");
LLNotificationsUtil::add("SystemMessage", args);
- buffer = LLTrans::getString("CompileQueueInsufficientPermFor") + (": ") + data->mScriptName;
+ buffer = LLTrans::getString("CompileQueueInsufficientPermFor") + (": ") + data->mItem->getName();
}
else
{
- buffer = LLTrans::getString("CompileQueueUnknownFailure") + (" ") + data->mScriptName;
+ buffer = LLTrans::getString("CompileQueueUnknownFailure") + (" ") + data->mItem->getName();
}
LL_WARNS() << "Problem downloading script asset." << LL_ENDL;
- if(queue) queue->removeItemByItemID(data->mItemId);
+ if(queue) queue->removeItemByItemID(data->mItem->getUUID());
}
if(queue && (buffer.size() > 0))
{
@@ -564,6 +645,23 @@ void LLFloaterCompileQueue::removeItemByItemID(const LLUUID& asset_id)
}
}
+BOOL LLFloaterCompileQueue::startQueue()
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string lookup_url=region->getCapability("GetCreatorExperiences");
+ if(!lookup_url.empty())
+ {
+ LLHTTPClient::get(lookup_url, new CompileQueueExperienceResponder(getKey().asUUID()));
+ return TRUE;
+ }
+ }
+ return nextObject();
+}
+
+
+
void LLFloaterNotRunQueue::handleInventory(LLViewerObject* viewer_obj,
LLInventoryObject::object_list_t* inv)
{
diff --git a/indra/newview/llcompilequeue.h b/indra/newview/llcompilequeue.h
index 28f4625de8..54842bb302 100755
--- a/indra/newview/llcompilequeue.h
+++ b/indra/newview/llcompilequeue.h
@@ -81,13 +81,15 @@ protected:
// returns true if this is done
BOOL isDone() const;
+ virtual BOOL startQueue();
+
// go to the next object. If no objects left, it falls out
// silently and waits to be killed by the deleteIfDone() callback.
BOOL nextObject();
BOOL popNext();
void setStartString(const std::string& s) { mStartString = s; }
-
+
protected:
// UI
LLScrollListCtrl* mMessages;
@@ -131,6 +133,9 @@ public:
LLAssetUploadQueue* getUploadQueue() { return mUploadQueue; }
+ void experienceIdsReceived( const LLSD& content );
+ BOOL hasExperience(const LLUUID& id)const;
+
protected:
LLFloaterCompileQueue(const LLSD& key);
virtual ~LLFloaterCompileQueue();
@@ -139,16 +144,21 @@ protected:
virtual void handleInventory(LLViewerObject* viewer_obj,
LLInventoryObject::object_list_t* inv);
+ static void requestAsset(struct LLScriptQueueData* datap, const LLSD& experience);
+
+
// This is the callback for when each script arrives
static void scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
LLAssetType::EType type,
void* user_data, S32 status, LLExtStat ext_status);
+ virtual BOOL startQueue();
protected:
LLViewerInventoryItem::item_array_t mCurrentScripts;
private:
LLAssetUploadQueue* mUploadQueue;
+ uuid_list_t mExperienceIds;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/indra/newview/llexperienceassociationresponder.cpp b/indra/newview/llexperienceassociationresponder.cpp
new file mode 100644
index 0000000000..b50c81eedc
--- /dev/null
+++ b/indra/newview/llexperienceassociationresponder.cpp
@@ -0,0 +1,97 @@
+/**
+ * @file llexperienceassociationresponder.cpp
+ * @brief llexperienceassociationresponder implementation. This class combines
+ * a lookup for a script association and an experience details request. The first
+ * is always async, but the second may be cached locally.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llexperienceassociationresponder.h"
+#include "llexperiencecache.h"
+#include "llviewerregion.h"
+#include "llagent.h"
+
+ExperienceAssociationResponder::ExperienceAssociationResponder(ExperienceAssociationResponder::callback_t callback):mCallback(callback)
+{
+ ref();
+}
+
+void ExperienceAssociationResponder::fetchAssociatedExperience( const LLUUID& object_id, const LLUUID& item_id, callback_t callback )
+{
+ LLSD request;
+ request["object-id"]=object_id;
+ request["item-id"]=item_id;
+ fetchAssociatedExperience(request, callback);
+}
+
+void ExperienceAssociationResponder::fetchAssociatedExperience(LLSD& request, callback_t callback)
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string lookup_url=region->getCapability("GetMetadata");
+ if(!lookup_url.empty())
+ {
+ LLSD fields;
+ fields.append("experience");
+ request["fields"] = fields;
+ LLHTTPClient::post(lookup_url, request, new ExperienceAssociationResponder(callback));
+ }
+ }
+}
+
+void ExperienceAssociationResponder::httpFailure()
+{
+ LLSD msg;
+ msg["error"]=(LLSD::Integer)getStatus();
+ msg["message"]=getReason();
+ LL_INFOS("ExperienceAssociation") << "Failed to look up associated experience: " << getStatus() << ": " << getReason() << LL_ENDL;
+
+ sendResult(msg);
+
+}
+void ExperienceAssociationResponder::httpSuccess()
+{
+ if(!getContent().has("experience"))
+ {
+
+ LLSD msg;
+ msg["message"]="no experience";
+ msg["error"]=-1;
+ sendResult(msg);
+ return;
+ }
+
+ LLExperienceCache::get(getContent()["experience"].asUUID(), boost::bind(&ExperienceAssociationResponder::sendResult, this, _1));
+
+}
+
+void ExperienceAssociationResponder::sendResult( const LLSD& experience )
+{
+ mCallback(experience);
+ unref();
+}
+
+
+
diff --git a/indra/newview/llexperienceassociationresponder.h b/indra/newview/llexperienceassociationresponder.h
new file mode 100644
index 0000000000..2bdc3d251b
--- /dev/null
+++ b/indra/newview/llexperienceassociationresponder.h
@@ -0,0 +1,58 @@
+#include "llhttpclient.h"
+#include "llsd.h"
+/**
+ * @file llexperienceassociationresponder.h
+ * @brief llexperienceassociationresponder and related class definitions
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+
+#ifndef LL_LLEXPERIENCEASSOCIATIONRESPONDER_H
+#define LL_LLEXPERIENCEASSOCIATIONRESPONDER_H
+
+#include "llhttpclient.h"
+#include "llsd.h"
+
+class ExperienceAssociationResponder : public LLHTTPClient::Responder
+{
+public:
+ typedef boost::function<void(const LLSD& experience)> callback_t;
+
+ ExperienceAssociationResponder(callback_t callback);
+
+ /*virtual*/ void httpSuccess();
+ /*virtual*/ void httpFailure();
+
+ static void fetchAssociatedExperience(const LLUUID& object_it, const LLUUID& item_id, callback_t callback);
+
+private:
+ static void fetchAssociatedExperience(LLSD& request, callback_t callback);
+
+ void sendResult(const LLSD& experience);
+
+ callback_t mCallback;
+
+};
+
+#endif // LL_LLEXPERIENCEASSOCIATIONRESPONDER_H
diff --git a/indra/newview/llexperiencelog.cpp b/indra/newview/llexperiencelog.cpp
new file mode 100644
index 0000000000..7b594577db
--- /dev/null
+++ b/indra/newview/llexperiencelog.cpp
@@ -0,0 +1,273 @@
+/**
+ * @file llexperiencelog.cpp
+ * @brief llexperiencelog implementation
+ *
+ * $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llexperiencelog.h"
+
+#include "lldispatcher.h"
+#include "llsdserialize.h"
+#include "llviewergenericmessage.h"
+#include "llnotificationsutil.h"
+#include "lltrans.h"
+#include "llerror.h"
+#include "lldate.h"
+
+
+class LLExperienceLogDispatchHandler : public LLDispatchHandler
+{
+public:
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+ {
+ LLSD message;
+
+ sparam_t::const_iterator it = strings.begin();
+ if(it != strings.end()){
+ const std::string& llsdRaw = *it++;
+ std::istringstream llsdData(llsdRaw);
+ if (!LLSDSerialize::deserialize(message, llsdData, llsdRaw.length()))
+ {
+ LL_WARNS() << "LLExperienceLogDispatchHandler: Attempted to read parameter data into LLSD but failed:" << llsdRaw << LL_ENDL;
+ }
+ }
+ message["public_id"] = invoice;
+
+ // Object Name
+ if(it != strings.end())
+ {
+ message["ObjectName"] = *it++;
+ }
+
+ // parcel Name
+ if(it != strings.end())
+ {
+ message["ParcelName"] = *it++;
+ }
+ message["Count"] = 1;
+
+ LLExperienceLog::instance().handleExperienceMessage(message);
+ return true;
+ }
+};
+
+static LLExperienceLogDispatchHandler experience_log_dispatch_handler;
+
+void LLExperienceLog::handleExperienceMessage(LLSD& message)
+{
+ time_t now;
+ time(&now);
+ char daybuf[16];/* Flawfinder: ignore */
+ char time_of_day[16];/* Flawfinder: ignore */
+ strftime(daybuf, 16, "%Y-%m-%d", localtime(&now));
+ strftime(time_of_day, 16, " %H:%M:%S", localtime(&now));
+ message["Time"] = time_of_day;
+
+ std::string day = daybuf;
+
+ if(!mEvents.has(day))
+ {
+ mEvents[day] = LLSD::emptyArray();
+ }
+ LLSD& dayEvents = mEvents[day];
+ if(dayEvents.size() > 0)
+ {
+ LLSD& last = *(dayEvents.rbeginArray());
+ if( last["public_id"].asUUID() == message["public_id"].asUUID()
+ && last["ObjectName"].asString() == message["ObjectName"].asString()
+ && last["OwnerID"].asUUID() == message["OwnerID"].asUUID()
+ && last["ParcelName"].asString() == message["ParcelName"].asString()
+ && last["Permission"].asInteger() == message["Permission"].asInteger())
+ {
+ last["Count"] = last["Count"].asInteger() + 1;
+ last["Time"] = time_of_day;
+ mSignals(last);
+ return;
+ }
+ }
+ message["Time"] = time_of_day;
+ mEvents[day].append(message);
+ mSignals(message);
+}
+
+LLExperienceLog::LLExperienceLog()
+ : mMaxDays(7)
+ , mPageSize(25)
+ , mNotifyNewEvent(false)
+{
+}
+
+void LLExperienceLog::initialize()
+{
+ loadEvents();
+ if(!gGenericDispatcher.isHandlerPresent("ExperienceEvent"))
+ {
+ gGenericDispatcher.addHandler("ExperienceEvent", &experience_log_dispatch_handler);
+ }
+}
+
+std::string LLExperienceLog::getFilename()
+{
+ return gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "experience_events.xml");
+}
+
+
+std::string LLExperienceLog::getPermissionString( const LLSD& message, const std::string& base )
+{
+ std::ostringstream buf;
+ if(message.has("Permission"))
+ {
+ buf << base << message["Permission"].asInteger();
+ std::string entry;
+ if(LLTrans::findString(entry, buf.str()))
+ {
+ buf.str(entry);
+ }
+ else
+ {
+ buf.str();
+ }
+ }
+
+ if(buf.str().empty())
+ {
+ buf << base << "Unknown";
+
+ buf.str(LLTrans::getString(buf.str(), message));
+ }
+
+ return buf.str();
+}
+
+void LLExperienceLog::notify( LLSD& message )
+{
+ message["EventType"] = getPermissionString(message, "ExperiencePermission");
+ if(message.has("IsAttachment") && message["IsAttachment"].asBoolean())
+ {
+ LLNotificationsUtil::add("ExperienceEventAttachment", message);
+ }
+ else
+ {
+ LLNotificationsUtil::add("ExperienceEvent", message);
+ }
+ message.erase("EventType");
+}
+
+void LLExperienceLog::saveEvents()
+{
+ eraseExpired();
+ std::string filename = getFilename();
+ LLSD settings = LLSD::emptyMap().with("Events", mEvents);
+
+ settings["MaxDays"] = (int)mMaxDays;
+ settings["Notify"] = mNotifyNewEvent;
+ settings["PageSize"] = (int)mPageSize;
+
+ llofstream stream(filename);
+ LLSDSerialize::toPrettyXML(settings, stream);
+}
+
+
+void LLExperienceLog::loadEvents()
+{
+ LLSD settings = LLSD::emptyMap();
+
+ std::string filename = getFilename();
+ llifstream stream(filename);
+ LLSDSerialize::fromXMLDocument(settings, stream);
+
+ if(settings.has("MaxDays"))
+ {
+ setMaxDays((U32)settings["MaxDays"].asInteger());
+ }
+ if(settings.has("Notify"))
+ {
+ setNotifyNewEvent(settings["Notify"].asBoolean());
+ }
+ if(settings.has("PageSize"))
+ {
+ setPageSize((U32)settings["PageSize"].asInteger());
+ }
+ mEvents.clear();
+ if(mMaxDays > 0 && settings.has("Events"))
+ {
+ mEvents = settings["Events"];
+ }
+
+ eraseExpired();
+}
+
+LLExperienceLog::~LLExperienceLog()
+{
+ saveEvents();
+}
+
+void LLExperienceLog::eraseExpired()
+{
+ while(mEvents.size() > mMaxDays && mMaxDays > 0)
+ {
+ mEvents.erase(mEvents.beginMap()->first);
+ }
+}
+
+const LLSD& LLExperienceLog::getEvents() const
+{
+ return mEvents;
+}
+
+void LLExperienceLog::clear()
+{
+ mEvents.clear();
+}
+
+void LLExperienceLog::setMaxDays( U32 val )
+{
+ mMaxDays = val;
+ if(mMaxDays > 0)
+ {
+ eraseExpired();
+ }
+}
+
+LLExperienceLog::callback_connection_t LLExperienceLog::addUpdateSignal( const callback_slot_t& cb )
+{
+ return mSignals.connect(cb);
+}
+
+void LLExperienceLog::setNotifyNewEvent( bool val )
+{
+ mNotifyNewEvent = val;
+ if(!val && mNotifyConnection.connected())
+ {
+ mNotifyConnection.disconnect();
+ }
+ else if( val && !mNotifyConnection.connected())
+ {
+ mNotifyConnection = addUpdateSignal(boost::function<void(LLSD&)>(LLExperienceLog::notify));
+ }
+}
diff --git a/indra/newview/llexperiencelog.h b/indra/newview/llexperiencelog.h
new file mode 100644
index 0000000000..1e473e27d5
--- /dev/null
+++ b/indra/newview/llexperiencelog.h
@@ -0,0 +1,85 @@
+/**
+ * @file llexperiencelog.h
+ * @brief llexperiencelog and related class definitions
+ *
+ * $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+
+#ifndef LL_LLEXPERIENCELOG_H
+#define LL_LLEXPERIENCELOG_H
+
+#include "llsingleton.h"
+
+class LLExperienceLog : public LLSingleton<LLExperienceLog>
+{
+public:
+ typedef boost::signals2::signal<void(LLSD&)>
+ callback_signal_t;
+ typedef callback_signal_t::slot_type callback_slot_t;
+ typedef boost::signals2::connection callback_connection_t;
+ callback_connection_t addUpdateSignal(const callback_slot_t& cb);
+
+ void initialize();
+
+ U32 getMaxDays() const { return mMaxDays; }
+ void setMaxDays(U32 val);
+
+ bool getNotifyNewEvent() const { return mNotifyNewEvent; }
+ void setNotifyNewEvent(bool val);
+
+ U32 getPageSize() const { return mPageSize; }
+ void setPageSize(U32 val) { mPageSize = val; }
+
+ const LLSD& getEvents()const;
+ void clear();
+
+ virtual ~LLExperienceLog();
+
+ static void notify(LLSD& message);
+ static std::string getFilename();
+ static std::string getPermissionString(const LLSD& message, const std::string& base);
+protected:
+ LLExperienceLog();
+ void handleExperienceMessage(LLSD& message);
+
+
+ void loadEvents();
+ void saveEvents();
+ void eraseExpired();
+
+ LLSD mEvents;
+ callback_signal_t mSignals;
+ callback_connection_t mNotifyConnection;
+ U32 mMaxDays;
+ U32 mPageSize;
+ bool mNotifyNewEvent;
+
+ friend class LLExperienceLogDispatchHandler;
+ friend class LLSingleton<LLExperienceLog>;
+};
+
+
+
+
+#endif // LL_LLEXPERIENCELOG_H
diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp
index 51b59a7a74..b661fed276 100755
--- a/indra/newview/llfloaterauction.cpp
+++ b/indra/newview/llfloaterauction.cpp
@@ -376,13 +376,16 @@ void LLFloaterAuction::doResetParcel()
msg->sendReliable(region->getHost());
// Clear the access lists
- clearParcelAccessLists(parcelp, region);
+ clearParcelAccessList(parcelp, region, AL_ACCESS);
+ clearParcelAccessList(parcelp, region, AL_BAN);
+ clearParcelAccessList(parcelp, region, AL_ALLOW_EXPERIENCE);
+ clearParcelAccessList(parcelp, region, AL_BLOCK_EXPERIENCE);
}
}
-void LLFloaterAuction::clearParcelAccessLists(LLParcel* parcel, LLViewerRegion* region)
+void LLFloaterAuction::clearParcelAccessList(LLParcel* parcel, LLViewerRegion* region, U32 list)
{
if (!region || !parcel) return;
@@ -391,15 +394,12 @@ void LLFloaterAuction::clearParcelAccessLists(LLParcel* parcel, LLViewerRegion*
LLMessageSystem* msg = gMessageSystem;
- // Clear access list
- // parcel->mAccessList.clear();
-
msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
msg->nextBlockFast(_PREHASH_Data);
- msg->addU32Fast(_PREHASH_Flags, AL_ACCESS);
+ msg->addU32Fast(_PREHASH_Flags, list);
msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
msg->addS32Fast(_PREHASH_SequenceID, 1); // sequence_id
@@ -412,27 +412,6 @@ void LLFloaterAuction::clearParcelAccessLists(LLParcel* parcel, LLViewerRegion*
msg->addU32Fast(_PREHASH_Flags, 0 );
msg->sendReliable( region->getHost() );
-
- // Send message for empty ban list
- //parcel->mBanList.clear();
- msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
- msg->nextBlockFast(_PREHASH_Data);
- msg->addU32Fast(_PREHASH_Flags, AL_BAN);
- msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
- msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
- msg->addS32Fast(_PREHASH_SequenceID, 1); // sequence_id
- msg->addS32Fast(_PREHASH_Sections, 0); // num_sections
-
- // pack an empty block since there will be no data
- msg->nextBlockFast(_PREHASH_List);
- msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
- msg->addS32Fast(_PREHASH_Time, 0 );
- msg->addU32Fast(_PREHASH_Flags, 0 );
-
- msg->sendReliable( region->getHost() );
}
diff --git a/indra/newview/llfloaterauction.h b/indra/newview/llfloaterauction.h
index 9c0c0f7775..c83a11ba8b 100755
--- a/indra/newview/llfloaterauction.h
+++ b/indra/newview/llfloaterauction.h
@@ -67,7 +67,7 @@ private:
void doResetParcel();
void doSellToAnyone();
- void clearParcelAccessLists( LLParcel* parcel, LLViewerRegion* region );
+ void clearParcelAccessList( LLParcel* parcel, LLViewerRegion* region, U32 list);
void cleanupAndClose();
private:
diff --git a/indra/newview/llfloaterexperiencepicker.cpp b/indra/newview/llfloaterexperiencepicker.cpp
new file mode 100644
index 0000000000..bb54c57baf
--- /dev/null
+++ b/indra/newview/llfloaterexperiencepicker.cpp
@@ -0,0 +1,162 @@
+/**
+* @file llfloaterexperiencepicker.cpp
+* @brief Implementation of llfloaterexperiencepicker
+* @author dolphin@lindenlab.com
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterexperiencepicker.h"
+
+
+#include "lllineeditor.h"
+#include "llfloaterreg.h"
+#include "llscrolllistctrl.h"
+#include "llviewerregion.h"
+#include "llagent.h"
+#include "llexperiencecache.h"
+#include "llslurl.h"
+#include "llavatarnamecache.h"
+#include "llfloaterexperienceprofile.h"
+#include "llcombobox.h"
+#include "llviewercontrol.h"
+#include "lldraghandle.h"
+#include "llpanelexperiencepicker.h"
+
+LLFloaterExperiencePicker* LLFloaterExperiencePicker::show( select_callback_t callback, const LLUUID& key, BOOL allow_multiple, BOOL close_on_select, filter_list filters, LLView * frustumOrigin )
+{
+ LLFloaterExperiencePicker* floater =
+ LLFloaterReg::showTypedInstance<LLFloaterExperiencePicker>("experience_search", key);
+ if (!floater)
+ {
+ LL_WARNS() << "Cannot instantiate experience picker" << LL_ENDL;
+ return NULL;
+ }
+
+ if (floater->mSearchPanel)
+ {
+ floater->mSearchPanel->mSelectionCallback = callback;
+ floater->mSearchPanel->mCloseOnSelect = close_on_select;
+ floater->mSearchPanel->setAllowMultiple(allow_multiple);
+ floater->mSearchPanel->setDefaultFilters();
+ floater->mSearchPanel->addFilters(filters.begin(), filters.end());
+ floater->mSearchPanel->filterContent();
+ }
+
+ if(frustumOrigin)
+ {
+ floater->mFrustumOrigin = frustumOrigin->getHandle();
+ }
+
+ return floater;
+}
+
+void LLFloaterExperiencePicker::drawFrustum()
+{
+ if(mFrustumOrigin.get())
+ {
+ LLView * frustumOrigin = mFrustumOrigin.get();
+ LLRect origin_rect;
+ frustumOrigin->localRectToOtherView(frustumOrigin->getLocalRect(), &origin_rect, this);
+ // draw context cone connecting color picker with color swatch in parent floater
+ LLRect local_rect = getLocalRect();
+ if (hasFocus() && frustumOrigin->isInVisibleChain() && mContextConeOpacity > 0.001f)
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ LLGLEnable(GL_CULL_FACE);
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity);
+ gGL.vertex2i(origin_rect.mLeft, origin_rect.mTop);
+ gGL.vertex2i(origin_rect.mRight, origin_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity);
+ gGL.vertex2i(local_rect.mRight, local_rect.mTop);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
+
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity);
+ gGL.vertex2i(origin_rect.mLeft, origin_rect.mBottom);
+ gGL.vertex2i(origin_rect.mLeft, origin_rect.mTop);
+
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity);
+ gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
+ gGL.vertex2i(local_rect.mRight, local_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity);
+ gGL.vertex2i(origin_rect.mRight, origin_rect.mTop);
+ gGL.vertex2i(origin_rect.mRight, origin_rect.mBottom);
+
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
+ gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity);
+ gGL.vertex2i(origin_rect.mRight, origin_rect.mBottom);
+ gGL.vertex2i(origin_rect.mLeft, origin_rect.mBottom);
+ }
+ gGL.end();
+ }
+
+ if (gFocusMgr.childHasMouseCapture(getDragHandle()))
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, gSavedSettings.getF32("PickerContextOpacity"), LLCriticalDamp::getInterpolant(mContextConeFadeTime));
+ }
+ else
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(mContextConeFadeTime));
+ }
+ }
+}
+
+void LLFloaterExperiencePicker::draw()
+{
+ drawFrustum();
+ LLFloater::draw();
+}
+
+LLFloaterExperiencePicker::LLFloaterExperiencePicker( const LLSD& key )
+ :LLFloater(key)
+ ,mSearchPanel(NULL)
+ ,mContextConeOpacity(0.f)
+ ,mContextConeInAlpha(0.f)
+ ,mContextConeOutAlpha(0.f)
+ ,mContextConeFadeTime(0.f)
+{
+ mContextConeInAlpha = gSavedSettings.getF32("ContextConeInAlpha");
+ mContextConeOutAlpha = gSavedSettings.getF32("ContextConeOutAlpha");
+ mContextConeFadeTime = gSavedSettings.getF32("ContextConeFadeTime");
+}
+
+LLFloaterExperiencePicker::~LLFloaterExperiencePicker()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+}
+
+BOOL LLFloaterExperiencePicker::postBuild()
+{
+ mSearchPanel = new LLPanelExperiencePicker();
+ addChild(mSearchPanel);
+ mSearchPanel->setOrigin(0, 0);
+ return LLFloater::postBuild();
+}
diff --git a/indra/newview/llfloaterexperiencepicker.h b/indra/newview/llfloaterexperiencepicker.h
new file mode 100644
index 0000000000..29054a57db
--- /dev/null
+++ b/indra/newview/llfloaterexperiencepicker.h
@@ -0,0 +1,67 @@
+/**
+* @file llfloaterexperiencepicker.h
+* @brief Header file for llfloaterexperiencepicker
+* @author dolphin@lindenlab.com
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LL_LLFLOATEREXPERIENCEPICKER_H
+#define LL_LLFLOATEREXPERIENCEPICKER_H
+
+#include "llfloater.h"
+
+class LLScrollListCtrl;
+class LLLineEditor;
+class LLPanelExperiencePicker;
+
+
+class LLFloaterExperiencePicker : public LLFloater
+{
+public:
+
+ typedef boost::function<void (const uuid_vec_t&)> select_callback_t;
+ // filter function for experiences, return true if the experience should be hidden.
+ typedef boost::function<bool (const LLSD&)> filter_function;
+ typedef std::vector<filter_function> filter_list;
+
+ static LLFloaterExperiencePicker* show( select_callback_t callback, const LLUUID& key, BOOL allow_multiple, BOOL close_on_select, filter_list filters, LLView * frustumOrigin);
+
+ LLFloaterExperiencePicker(const LLSD& key);
+ virtual ~LLFloaterExperiencePicker();
+
+ BOOL postBuild();
+
+ virtual void draw();
+private:
+
+ LLPanelExperiencePicker* mSearchPanel;
+
+ void drawFrustum();
+ LLHandle <LLView> mFrustumOrigin;
+ F32 mContextConeOpacity;
+ F32 mContextConeInAlpha;
+ F32 mContextConeOutAlpha;
+ F32 mContextConeFadeTime;
+};
+
+#endif // LL_LLFLOATEREXPERIENCEPICKER_H
+
diff --git a/indra/newview/llfloaterexperienceprofile.cpp b/indra/newview/llfloaterexperienceprofile.cpp
new file mode 100644
index 0000000000..4ee31f5df1
--- /dev/null
+++ b/indra/newview/llfloaterexperienceprofile.cpp
@@ -0,0 +1,998 @@
+/**
+ * @file llfloaterexperienceprofile.cpp
+ * @brief llfloaterexperienceprofile and related class definitions
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloaterexperienceprofile.h"
+
+#include "llagent.h"
+#include "llappviewer.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llcommandhandler.h"
+#include "llexpandabletextbox.h"
+#include "llexperiencecache.h"
+#include "llfloaterreg.h"
+#include "llhttpclient.h"
+#include "lllayoutstack.h"
+#include "lllineeditor.h"
+#include "llnotificationsutil.h"
+#include "llsdserialize.h"
+#include "llslurl.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltexturectrl.h"
+#include "lltrans.h"
+#include "llviewerregion.h"
+#include "llevents.h"
+#include "llfloatergroups.h"
+#include "llnotifications.h"
+#include "llfloaterreporter.h"
+
+#define XML_PANEL_EXPERIENCE_PROFILE "floater_experienceprofile.xml"
+#define TF_NAME "experience_title"
+#define TF_DESC "experience_description"
+#define TF_SLURL "LocationTextText"
+#define TF_MRKT "marketplace"
+#define TF_MATURITY "ContentRatingText"
+#define TF_OWNER "OwnerText"
+#define TF_GROUP "GroupText"
+#define TF_GRID_WIDE "grid_wide"
+#define TF_PRIVILEGED "privileged"
+#define EDIT "edit_"
+
+#define IMG_LOGO "logo"
+
+#define PNL_TOP "top panel"
+#define PNL_IMAGE "image_panel"
+#define PNL_DESC "description panel"
+#define PNL_LOC "location panel"
+#define PNL_MRKT "marketplace panel"
+#define PNL_GROUP "group_panel"
+#define PNL_PERMS "perm panel"
+
+#define BTN_ALLOW "allow_btn"
+#define BTN_BLOCK "block_btn"
+#define BTN_CANCEL "cancel_btn"
+#define BTN_CLEAR_LOCATION "clear_btn"
+#define BTN_EDIT "edit_btn"
+#define BTN_ENABLE "enable_btn"
+#define BTN_FORGET "forget_btn"
+#define BTN_PRIVATE "private_btn"
+#define BTN_REPORT "report_btn"
+#define BTN_SAVE "save_btn"
+#define BTN_SET_GROUP "Group_btn"
+#define BTN_SET_LOCATION "location_btn"
+
+
+class LLExperienceHandler : public LLCommandHandler
+{
+public:
+ LLExperienceHandler() : LLCommandHandler("experience", UNTRUSTED_THROTTLE) { }
+
+ bool handle(const LLSD& params, const LLSD& query_map,
+ LLMediaCtrl* web)
+ {
+ if(params.size() != 2 || params[1].asString() != "profile")
+ return false;
+
+ LLExperienceCache::get(params[0].asUUID(), boost::bind(&LLExperienceHandler::experienceCallback, this, _1));
+ return true;
+ }
+
+ void experienceCallback(const LLSD& experienceDetails)
+ {
+ if(!experienceDetails.has(LLExperienceCache::MISSING))
+ {
+ LLFloaterReg::showInstance("experience_profile", experienceDetails[LLExperienceCache::EXPERIENCE_ID].asUUID(), true);
+ }
+ }
+};
+
+LLExperienceHandler gExperienceHandler;
+
+
+LLFloaterExperienceProfile::LLFloaterExperienceProfile(const LLSD& data)
+ : LLFloater(data)
+ , mExperienceId(data.asUUID())
+ , mSaveCompleteAction(NOTHING)
+ , mDirty(false)
+ , mForceClose(false)
+{
+
+}
+
+
+LLFloaterExperienceProfile::~LLFloaterExperienceProfile()
+{
+
+}
+
+template<class T>
+class HandleResponder : public LLHTTPClient::Responder
+{
+public:
+ HandleResponder(const LLHandle<T>& parent):mParent(parent){}
+ LLHandle<T> mParent;
+
+ virtual void httpFailure()
+ {
+ LL_WARNS() << "HandleResponder failed with code: " << getStatus() << ", reason: " << getReason() << LL_ENDL;
+ }
+};
+
+class ExperienceUpdateResponder : public HandleResponder<LLFloaterExperienceProfile>
+{
+public:
+ ExperienceUpdateResponder(const LLHandle<LLFloaterExperienceProfile>& parent):HandleResponder<LLFloaterExperienceProfile>(parent)
+ {
+ }
+
+ virtual void httpSuccess()
+ {
+ LLFloaterExperienceProfile* parent=mParent.get();
+ if(parent)
+ {
+ parent->onSaveComplete(getContent());
+ }
+ }
+};
+
+
+
+class ExperiencePreferencesResponder : public LLHTTPClient::Responder
+{
+public:
+ ExperiencePreferencesResponder(const LLUUID& single = LLUUID::null):mId(single)
+ {
+ }
+
+ bool sendSingle(const LLSD& content, const LLSD& permission, const char* name)
+ {
+ if(!content.has(name))
+ return false;
+
+ LLEventPump& pump = LLEventPumps::instance().obtain("experience_permission");
+ const LLSD& list = content[name];
+ LLSD::array_const_iterator it = list.beginArray();
+ while(it != list.endArray())
+ {
+ if(it->asUUID() == mId)
+ {
+ LLSD message;
+ message[it->asString()] = permission;
+ message["experience"] = mId;
+ pump.post(message);
+ return true;
+ }
+ ++it;
+ }
+ return false;
+ }
+
+ bool hasPermission(const LLSD& content, const char* name)
+ {
+ if(!content.has(name))
+ return false;
+
+ const LLSD& list = content[name];
+ LLSD::array_const_iterator it = list.beginArray();
+ while(it != list.endArray())
+ {
+ if(it->asUUID() == mId)
+ {
+ return true;
+ }
+ ++it;
+ }
+ return false;
+ }
+
+ const char* getPermission(const LLSD& content)
+ {
+ if(hasPermission(content, "experiences"))
+ {
+ return "Allow";
+ }
+ else if(hasPermission(content, "blocked"))
+ {
+ return "Block";
+ }
+ return "Forget";
+ }
+
+
+ virtual void httpSuccess()
+ {
+ if(mId.notNull())
+ {
+ post(getPermission(getContent()));
+ return;
+ }
+ LLEventPumps::instance().obtain("experience_permission").post(getContent());
+ }
+
+ void post( const char* perm )
+ {
+ LLSD experience;
+ LLSD message;
+ experience["permission"]=perm;
+ message["experience"] = mId;
+ message[mId.asString()] = experience;
+ LLEventPumps::instance().obtain("experience_permission").post(message);
+ }
+
+private:
+ LLUUID mId;
+};
+
+
+class IsAdminResponder : public HandleResponder<LLFloaterExperienceProfile>
+{
+public:
+ IsAdminResponder(const LLHandle<LLFloaterExperienceProfile>& parent):HandleResponder<LLFloaterExperienceProfile>(parent)
+ {
+ }
+
+ virtual void httpSuccess()
+ {
+ LLFloaterExperienceProfile* parent = mParent.get();
+ if(!parent)
+ return;
+
+ bool enabled = true;
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ {
+ enabled = false;
+ }
+ else
+ {
+ std::string url=region->getCapability("UpdateExperience");
+ if(url.empty())
+ enabled = false;
+ }
+ if(enabled && getContent()["status"].asBoolean())
+ {
+ parent->getChild<LLLayoutPanel>(PNL_TOP)->setVisible(TRUE);
+ parent->getChild<LLButton>(BTN_EDIT)->setVisible(TRUE);
+ }
+ }
+};
+
+BOOL LLFloaterExperienceProfile::postBuild()
+{
+
+ if (mExperienceId.notNull())
+ {
+ LLExperienceCache::fetch(mExperienceId, true);
+ LLExperienceCache::get(mExperienceId, boost::bind(&LLFloaterExperienceProfile::experienceCallback,
+ getDerivedHandle<LLFloaterExperienceProfile>(), _1));
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string lookup_url=region->getCapability("IsExperienceAdmin");
+ if(!lookup_url.empty())
+ {
+ LLHTTPClient::get(lookup_url+"?experience_id="+mExperienceId.asString(), new IsAdminResponder(getDerivedHandle<LLFloaterExperienceProfile>()));
+ }
+ }
+ }
+
+ childSetAction(BTN_EDIT, boost::bind(&LLFloaterExperienceProfile::onClickEdit, this));
+ childSetAction(BTN_ALLOW, boost::bind(&LLFloaterExperienceProfile::onClickPermission, this, "Allow"));
+ childSetAction(BTN_FORGET, boost::bind(&LLFloaterExperienceProfile::onClickForget, this));
+ childSetAction(BTN_BLOCK, boost::bind(&LLFloaterExperienceProfile::onClickPermission, this, "Block"));
+ childSetAction(BTN_CANCEL, boost::bind(&LLFloaterExperienceProfile::onClickCancel, this));
+ childSetAction(BTN_SAVE, boost::bind(&LLFloaterExperienceProfile::onClickSave, this));
+ childSetAction(BTN_SET_LOCATION, boost::bind(&LLFloaterExperienceProfile::onClickLocation, this));
+ childSetAction(BTN_CLEAR_LOCATION, boost::bind(&LLFloaterExperienceProfile::onClickClear, this));
+ childSetAction(BTN_SET_GROUP, boost::bind(&LLFloaterExperienceProfile::onPickGroup, this));
+ childSetAction(BTN_REPORT, boost::bind(&LLFloaterExperienceProfile::onReportExperience, this));
+
+ getChild<LLTextEditor>(EDIT TF_DESC)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this));
+ getChild<LLUICtrl>(EDIT TF_MATURITY)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this));
+ getChild<LLLineEditor>(EDIT TF_MRKT)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL);
+ getChild<LLLineEditor>(EDIT TF_NAME)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL);
+
+ childSetCommitCallback(EDIT BTN_ENABLE, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL);
+ childSetCommitCallback(EDIT BTN_PRIVATE, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL);
+
+ childSetCommitCallback(EDIT IMG_LOGO, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL);
+
+ getChild<LLTextEditor>(EDIT TF_DESC)->setCommitOnFocusLost(TRUE);
+
+
+ LLEventPumps::instance().obtain("experience_permission").listen(mExperienceId.asString()+"-profile",
+ boost::bind(&LLFloaterExperienceProfile::experiencePermission, getDerivedHandle<LLFloaterExperienceProfile>(this), _1));
+
+ return TRUE;
+}
+
+void LLFloaterExperienceProfile::experienceCallback(LLHandle<LLFloaterExperienceProfile> handle, const LLSD& experience )
+{
+ LLFloaterExperienceProfile* pllpep = handle.get();
+ if(pllpep)
+ {
+ pllpep->refreshExperience(experience);
+ }
+}
+
+
+bool LLFloaterExperienceProfile::experiencePermission( LLHandle<LLFloaterExperienceProfile> handle, const LLSD& permission )
+{
+ LLFloaterExperienceProfile* pllpep = handle.get();
+ if(pllpep)
+ {
+ pllpep->updatePermission(permission);
+ }
+ return false;
+}
+
+
+void LLFloaterExperienceProfile::onClickEdit()
+{
+ LLTabContainer* tabs = getChild<LLTabContainer>("tab_container");
+
+ tabs->selectTabByName("edit_panel_experience_info");
+}
+
+
+void LLFloaterExperienceProfile::onClickCancel()
+{
+ changeToView();
+}
+
+void LLFloaterExperienceProfile::onClickSave()
+{
+ doSave(NOTHING);
+}
+
+
+void LLFloaterExperienceProfile::onClickPermission(const char* perm)
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ return;
+
+ std::string lookup_url=region->getCapability("ExperiencePreferences");
+ if(lookup_url.empty())
+ return;
+ LLSD permission;
+ LLSD data;
+ permission["permission"]=perm;
+
+ data[mExperienceId.asString()]=permission;
+ LLHTTPClient::put(lookup_url, data, new ExperiencePreferencesResponder(mExperienceId));
+
+}
+
+
+void LLFloaterExperienceProfile::onClickForget()
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ return;
+
+ std::string lookup_url=region->getCapability("ExperiencePreferences");
+ if(lookup_url.empty())
+ return;
+
+ LLHTTPClient::del(lookup_url+"?"+mExperienceId.asString(), new ExperiencePreferencesResponder(mExperienceId));
+}
+
+bool LLFloaterExperienceProfile::setMaturityString( U8 maturity, LLTextBox* child, LLComboBox* combo )
+{
+ LLStyle::Params style;
+ std::string access;
+ if(maturity <= SIM_ACCESS_PG)
+ {
+ style.image(LLUI::getUIImage(getString("maturity_icon_general")));
+ access = LLTrans::getString("SIM_ACCESS_PG");
+ combo->setCurrentByIndex(2);
+ }
+ else if(maturity <= SIM_ACCESS_MATURE)
+ {
+ style.image(LLUI::getUIImage(getString("maturity_icon_moderate")));
+ access = LLTrans::getString("SIM_ACCESS_MATURE");
+ combo->setCurrentByIndex(1);
+ }
+ else if(maturity <= SIM_ACCESS_ADULT)
+ {
+ style.image(LLUI::getUIImage(getString("maturity_icon_adult")));
+ access = LLTrans::getString("SIM_ACCESS_ADULT");
+ combo->setCurrentByIndex(0);
+ }
+ else
+ {
+ return false;
+ }
+
+ child->setText(LLStringUtil::null);
+
+ child->appendImageSegment(style);
+
+ child->appendText(access, false);
+
+ return true;
+}
+
+
+void LLFloaterExperienceProfile::refreshExperience( const LLSD& experience )
+{
+ mExperienceDetails = experience;
+ mPackage = experience;
+
+
+ LLLayoutPanel* imagePanel = getChild<LLLayoutPanel>(PNL_IMAGE);
+ LLLayoutPanel* descriptionPanel = getChild<LLLayoutPanel>(PNL_DESC);
+ LLLayoutPanel* locationPanel = getChild<LLLayoutPanel>(PNL_LOC);
+ LLLayoutPanel* marketplacePanel = getChild<LLLayoutPanel>(PNL_MRKT);
+ LLLayoutPanel* topPanel = getChild<LLLayoutPanel>(PNL_TOP);
+
+
+ imagePanel->setVisible(FALSE);
+ descriptionPanel->setVisible(FALSE);
+ locationPanel->setVisible(FALSE);
+ marketplacePanel->setVisible(FALSE);
+ topPanel->setVisible(FALSE);
+
+
+ LLTextBox* child = getChild<LLTextBox>(TF_NAME);
+ //child->setText(experience[LLExperienceCache::NAME].asString());
+ child->setText(LLSLURL("experience", experience[LLExperienceCache::EXPERIENCE_ID], "profile").getSLURLString());
+
+ LLLineEditor* linechild = getChild<LLLineEditor>(EDIT TF_NAME);
+ linechild->setText(experience[LLExperienceCache::NAME].asString());
+
+ std::string value = experience[LLExperienceCache::DESCRIPTION].asString();
+ LLExpandableTextBox* exchild = getChild<LLExpandableTextBox>(TF_DESC);
+ exchild->setText(value);
+ descriptionPanel->setVisible(value.length()>0);
+
+ LLTextEditor* edit_child = getChild<LLTextEditor>(EDIT TF_DESC);
+ edit_child->setText(value);
+
+ mLocationSLURL = experience[LLExperienceCache::SLURL].asString();
+ child = getChild<LLTextBox>(TF_SLURL);
+ bool has_slurl = mLocationSLURL.length()>0;
+ locationPanel->setVisible(has_slurl);
+ mLocationSLURL = LLSLURL(mLocationSLURL).getSLURLString();
+ child->setText(mLocationSLURL);
+
+
+ child = getChild<LLTextBox>(EDIT TF_SLURL);
+ if(has_slurl)
+ {
+ child->setText(mLocationSLURL);
+ }
+ else
+ {
+ child->setText(getString("empty_slurl"));
+ }
+
+ setMaturityString((U8)(experience[LLExperienceCache::MATURITY].asInteger()), getChild<LLTextBox>(TF_MATURITY), getChild<LLComboBox>(EDIT TF_MATURITY));
+
+ LLUUID id = experience[LLExperienceCache::AGENT_ID].asUUID();
+ child = getChild<LLTextBox>(TF_OWNER);
+ value = LLSLURL("agent", id, "inspect").getSLURLString();
+ child->setText(value);
+
+
+ id = experience[LLExperienceCache::GROUP_ID].asUUID();
+ bool id_null = id.isNull();
+ child = getChild<LLTextBox>(TF_GROUP);
+ value = LLSLURL("group", id, "inspect").getSLURLString();
+ child->setText(value);
+ getChild<LLLayoutPanel>(PNL_GROUP)->setVisible(!id_null);
+
+ setEditGroup(id);
+
+ getChild<LLButton>(BTN_SET_GROUP)->setEnabled(experience[LLExperienceCache::AGENT_ID].asUUID() == gAgent.getID());
+
+ LLCheckBoxCtrl* enable = getChild<LLCheckBoxCtrl>(EDIT BTN_ENABLE);
+ S32 properties = mExperienceDetails[LLExperienceCache::PROPERTIES].asInteger();
+ enable->set(!(properties & LLExperienceCache::PROPERTY_DISABLED));
+
+ enable = getChild<LLCheckBoxCtrl>(EDIT BTN_PRIVATE);
+ enable->set(properties & LLExperienceCache::PROPERTY_PRIVATE);
+ if(properties & LLExperienceCache::PROPERTY_GRID)
+ {
+ topPanel->setVisible(TRUE);
+ child=getChild<LLTextBox>(TF_GRID_WIDE);
+ child->setVisible(TRUE);
+ child->setText(LLTrans::getString("GRID_WIDE"));
+ }
+
+ if(getChild<LLButton>(BTN_EDIT)->getVisible())
+ {
+ topPanel->setVisible(TRUE);
+ }
+
+ if(properties & LLExperienceCache::PROPERTY_PRIVILEGED)
+ {
+ child = getChild<LLTextBox>(TF_PRIVILEGED);
+ child->setVisible(TRUE);
+ }
+ else
+ {
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string lookup_url=region->getCapability("ExperiencePreferences");
+ if(!lookup_url.empty())
+ {
+ LLHTTPClient::get(lookup_url+"?"+mExperienceId.asString(), new ExperiencePreferencesResponder(mExperienceId));
+ }
+ }
+ }
+
+ value=experience[LLExperienceCache::METADATA].asString();
+ if(value.empty())
+ return;
+
+ LLPointer<LLSDParser> parser = new LLSDXMLParser();
+
+ LLSD data;
+
+ std::istringstream is(value);
+ if(LLSDParser::PARSE_FAILURE != parser->parse(is, data, value.size()))
+ {
+ value="";
+ if(data.has(TF_MRKT))
+ {
+ value=data[TF_MRKT].asString();
+
+ child = getChild<LLTextBox>(TF_MRKT);
+ child->setText(value);
+ if(value.size())
+ {
+ marketplacePanel->setVisible(TRUE);
+ }
+ else
+ {
+ marketplacePanel->setVisible(FALSE);
+ }
+ }
+ else
+ {
+ marketplacePanel->setVisible(FALSE);
+ }
+
+ linechild = getChild<LLLineEditor>(EDIT TF_MRKT);
+ linechild->setText(value);
+
+ if(data.has(IMG_LOGO))
+ {
+ LLTextureCtrl* logo = getChild<LLTextureCtrl>(IMG_LOGO);
+
+ LLUUID id = data[IMG_LOGO].asUUID();
+ logo->setImageAssetID(id);
+ imagePanel->setVisible(TRUE);
+
+ logo = getChild<LLTextureCtrl>(EDIT IMG_LOGO);
+ logo->setImageAssetID(data[IMG_LOGO].asUUID());
+
+ imagePanel->setVisible(id.notNull());
+ }
+ }
+ else
+ {
+ marketplacePanel->setVisible(FALSE);
+ imagePanel->setVisible(FALSE);
+ }
+
+ mDirty=false;
+ mForceClose = false;
+ getChild<LLButton>(BTN_SAVE)->setEnabled(mDirty);
+}
+
+void LLFloaterExperienceProfile::setPreferences( const LLSD& content )
+{
+ S32 properties = mExperienceDetails[LLExperienceCache::PROPERTIES].asInteger();
+ if(properties & LLExperienceCache::PROPERTY_PRIVILEGED)
+ {
+ return;
+ }
+
+ const LLSD& experiences = content["experiences"];
+ const LLSD& blocked = content["blocked"];
+
+
+ for(LLSD::array_const_iterator it = experiences.beginArray(); it != experiences.endArray() ; ++it)
+ {
+ if(it->asUUID()==mExperienceId)
+ {
+ experienceAllowed();
+ return;
+ }
+ }
+
+ for(LLSD::array_const_iterator it = blocked.beginArray(); it != blocked.endArray() ; ++it)
+ {
+ if(it->asUUID()==mExperienceId)
+ {
+ experienceBlocked();
+ return;
+ }
+ }
+
+ experienceForgotten();
+}
+
+void LLFloaterExperienceProfile::onFieldChanged()
+{
+ updatePackage();
+
+ if(!getChild<LLButton>(BTN_EDIT)->getVisible())
+ {
+ return;
+ }
+ LLSD::map_const_iterator st = mExperienceDetails.beginMap();
+ LLSD::map_const_iterator dt = mPackage.beginMap();
+
+ mDirty = false;
+ while( !mDirty && st != mExperienceDetails.endMap() && dt != mPackage.endMap())
+ {
+ mDirty = st->first != dt->first || st->second.asString() != dt->second.asString();
+ ++st;++dt;
+ }
+
+ if(!mDirty && (st != mExperienceDetails.endMap() || dt != mPackage.endMap()))
+ {
+ mDirty = true;
+ }
+
+ getChild<LLButton>(BTN_SAVE)->setEnabled(mDirty);
+}
+
+
+BOOL LLFloaterExperienceProfile::canClose()
+{
+ if(mForceClose || !mDirty)
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLFloaterExperienceProfile::handleSaveChangesDialog, this, _1, _2, CLOSE));
+ return FALSE;
+ }
+}
+
+bool LLFloaterExperienceProfile::handleSaveChangesDialog( const LLSD& notification, const LLSD& response, PostSaveAction action )
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ switch( option )
+ {
+ case 0: // "Yes"
+ // close after saving
+ doSave( action );
+ break;
+
+ case 1: // "No"
+ if(action != NOTHING)
+ {
+ mForceClose = TRUE;
+ if(action==CLOSE)
+ {
+ closeFloater();
+ }
+ else
+ {
+ changeToView();
+ }
+ }
+ break;
+
+ case 2: // "Cancel"
+ default:
+ // If we were quitting, we didn't really mean it.
+ LLAppViewer::instance()->abortQuit();
+ break;
+ }
+ return false;
+}
+
+void LLFloaterExperienceProfile::doSave( int success_action )
+{
+ mSaveCompleteAction=success_action;
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ return;
+
+ std::string url=region->getCapability("UpdateExperience");
+ if(url.empty())
+ return;
+
+ mPackage.erase(LLExperienceCache::QUOTA);
+ mPackage.erase(LLExperienceCache::EXPIRES);
+ mPackage.erase(LLExperienceCache::AGENT_ID);
+
+ LLHTTPClient::post(url, mPackage, new ExperienceUpdateResponder(getDerivedHandle<LLFloaterExperienceProfile>()));
+}
+
+void LLFloaterExperienceProfile::onSaveComplete( const LLSD& content )
+{
+ LLUUID id = getExperienceId();
+
+ if(content.has("removed"))
+ {
+ const LLSD& removed = content["removed"];
+ LLSD::map_const_iterator it = removed.beginMap();
+ for(/**/; it != removed.endMap(); ++it)
+ {
+ const std::string& field = it->first;
+ if(field == LLExperienceCache::EXPERIENCE_ID)
+ {
+ //this message should be removed by the experience api
+ continue;
+ }
+ const LLSD& data = it->second;
+ std::string error_tag = data["error_tag"].asString()+ "ExperienceProfileMessage";
+ LLSD fields;
+ if( LLNotifications::instance().getTemplate(error_tag))
+ {
+ fields["field"] = field;
+ fields["extra_info"] = data["extra_info"];
+ LLNotificationsUtil::add(error_tag, fields);
+ }
+ else
+ {
+ fields["MESSAGE"]=data["en"];
+ LLNotificationsUtil::add("GenericAlert", fields);
+ }
+ }
+ }
+
+ if(!content.has("experience_keys"))
+ {
+ LL_WARNS() << "LLFloaterExperienceProfile::onSaveComplete called with bad content" << LL_ENDL;
+ return;
+ }
+
+ const LLSD& experiences = content["experience_keys"];
+
+ LLSD::array_const_iterator it = experiences.beginArray();
+ if(it == experiences.endArray())
+ {
+ LL_WARNS() << "LLFloaterExperienceProfile::onSaveComplete called with empty content" << LL_ENDL;
+ return;
+ }
+
+ if(!it->has(LLExperienceCache::EXPERIENCE_ID) || ((*it)[LLExperienceCache::EXPERIENCE_ID].asUUID() != id))
+ {
+ LL_WARNS() << "LLFloaterExperienceProfile::onSaveComplete called with unexpected experience id" << LL_ENDL;
+ return;
+ }
+
+ refreshExperience(*it);
+ LLExperienceCache::insert(*it);
+ LLExperienceCache::fetch(id, true);
+
+ if(mSaveCompleteAction==VIEW)
+ {
+ LLTabContainer* tabs = getChild<LLTabContainer>("tab_container");
+ tabs->selectTabByName("panel_experience_info");
+ }
+ else if(mSaveCompleteAction == CLOSE)
+ {
+ closeFloater();
+ }
+}
+
+void LLFloaterExperienceProfile::changeToView()
+{
+ if(mForceClose || !mDirty)
+ {
+ refreshExperience(mExperienceDetails);
+ LLTabContainer* tabs = getChild<LLTabContainer>("tab_container");
+
+ tabs->selectTabByName("panel_experience_info");
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLFloaterExperienceProfile::handleSaveChangesDialog, this, _1, _2, VIEW));
+ }
+}
+
+void LLFloaterExperienceProfile::onClickLocation()
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if(region)
+ {
+ LLTextBox* child = getChild<LLTextBox>(EDIT TF_SLURL);
+ mLocationSLURL = LLSLURL(region->getName(), gAgent.getPositionGlobal()).getSLURLString();
+ child->setText(mLocationSLURL);
+ onFieldChanged();
+ }
+}
+
+void LLFloaterExperienceProfile::onClickClear()
+{
+ LLTextBox* child = getChild<LLTextBox>(EDIT TF_SLURL);
+ mLocationSLURL = "";
+ child->setText(getString("empty_slurl"));
+ onFieldChanged();
+}
+
+void LLFloaterExperienceProfile::updatePermission( const LLSD& permission )
+{
+ if(permission.has("experience"))
+ {
+ if(permission["experience"].asUUID() != mExperienceId)
+ {
+ return;
+ }
+
+ std::string str = permission[mExperienceId.asString()]["permission"].asString();
+ if(str == "Allow")
+ {
+ experienceAllowed();
+ }
+ else if(str == "Block")
+ {
+ experienceBlocked();
+ }
+ else if(str == "Forget")
+ {
+ experienceForgotten();
+ }
+ }
+ else
+ {
+ setPreferences(permission);
+ }
+}
+
+void LLFloaterExperienceProfile::experienceAllowed()
+{
+ LLButton* button=getChild<LLButton>(BTN_ALLOW);
+ button->setEnabled(FALSE);
+
+ button=getChild<LLButton>(BTN_FORGET);
+ button->setEnabled(TRUE);
+
+ button=getChild<LLButton>(BTN_BLOCK);
+ button->setEnabled(TRUE);
+}
+
+void LLFloaterExperienceProfile::experienceForgotten()
+{
+ LLButton* button=getChild<LLButton>(BTN_ALLOW);
+ button->setEnabled(TRUE);
+
+ button=getChild<LLButton>(BTN_FORGET);
+ button->setEnabled(FALSE);
+
+ button=getChild<LLButton>(BTN_BLOCK);
+ button->setEnabled(TRUE);
+}
+
+void LLFloaterExperienceProfile::experienceBlocked()
+{
+ LLButton* button=getChild<LLButton>(BTN_ALLOW);
+ button->setEnabled(TRUE);
+
+ button=getChild<LLButton>(BTN_FORGET);
+ button->setEnabled(TRUE);
+
+ button=getChild<LLButton>(BTN_BLOCK);
+ button->setEnabled(FALSE);
+}
+
+void LLFloaterExperienceProfile::onClose( bool app_quitting )
+{
+ LLEventPumps::instance().obtain("experience_permission").stopListening(mExperienceId.asString()+"-profile");
+ LLFloater::onClose(app_quitting);
+}
+
+void LLFloaterExperienceProfile::updatePackage()
+{
+ mPackage[LLExperienceCache::NAME] = getChild<LLLineEditor>(EDIT TF_NAME)->getText();
+ mPackage[LLExperienceCache::DESCRIPTION] = getChild<LLTextEditor>(EDIT TF_DESC)->getText();
+ if(mLocationSLURL.empty())
+ {
+ mPackage[LLExperienceCache::SLURL] = LLStringUtil::null;
+ }
+ else
+ {
+ mPackage[LLExperienceCache::SLURL] = mLocationSLURL;
+ }
+
+ mPackage[LLExperienceCache::MATURITY] = getChild<LLComboBox>(EDIT TF_MATURITY)->getSelectedValue().asInteger();
+
+ LLSD metadata;
+
+ metadata[TF_MRKT] = getChild<LLLineEditor>(EDIT TF_MRKT)->getText();
+ metadata[IMG_LOGO] = getChild<LLTextureCtrl>(EDIT IMG_LOGO)->getImageAssetID();
+
+ LLPointer<LLSDXMLFormatter> formatter = new LLSDXMLFormatter();
+
+ std::ostringstream os;
+ if(formatter->format(metadata, os))
+ {
+ mPackage[LLExperienceCache::METADATA]=os.str();
+ }
+
+ int properties = mPackage[LLExperienceCache::PROPERTIES].asInteger();
+ LLCheckBoxCtrl* enable = getChild<LLCheckBoxCtrl>(EDIT BTN_ENABLE);
+ if(enable->get())
+ {
+ properties &= ~LLExperienceCache::PROPERTY_DISABLED;
+ }
+ else
+ {
+ properties |= LLExperienceCache::PROPERTY_DISABLED;
+ }
+
+ enable = getChild<LLCheckBoxCtrl>(EDIT BTN_PRIVATE);
+ if(enable->get())
+ {
+ properties |= LLExperienceCache::PROPERTY_PRIVATE;
+ }
+ else
+ {
+ properties &= ~LLExperienceCache::PROPERTY_PRIVATE;
+ }
+
+ mPackage[LLExperienceCache::PROPERTIES] = properties;
+}
+
+void LLFloaterExperienceProfile::onPickGroup()
+{
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+
+ LLFloaterGroupPicker* widget = LLFloaterReg::showTypedInstance<LLFloaterGroupPicker>("group_picker", LLSD(gAgent.getID()));
+ if (widget)
+ {
+ widget->setSelectGroupCallback(boost::bind(&LLFloaterExperienceProfile::setEditGroup, this, _1));
+ if (parent_floater)
+ {
+ LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, widget);
+ widget->setOrigin(new_rect.mLeft, new_rect.mBottom);
+ parent_floater->addDependentFloater(widget);
+ }
+ }
+}
+
+void LLFloaterExperienceProfile::setEditGroup( LLUUID group_id )
+{
+ LLTextBox* child = getChild<LLTextBox>(EDIT TF_GROUP);
+ std::string value = LLSLURL("group", group_id, "inspect").getSLURLString();
+ child->setText(value);
+ mPackage[LLExperienceCache::GROUP_ID] = group_id;
+ onFieldChanged();
+}
+
+void LLFloaterExperienceProfile::onReportExperience()
+{
+ LLFloaterReporter::showFromExperience(mExperienceId);
+}
diff --git a/indra/newview/llfloaterexperienceprofile.h b/indra/newview/llfloaterexperienceprofile.h
new file mode 100644
index 0000000000..78d54eb447
--- /dev/null
+++ b/indra/newview/llfloaterexperienceprofile.h
@@ -0,0 +1,104 @@
+/**
+ * @file llfloaterexperienceprofile.h
+ * @brief llfloaterexperienceprofile and related class definitions
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+
+#ifndef LL_LLFLOATEREXPERIENCEPROFILE_H
+#define LL_LLFLOATEREXPERIENCEPROFILE_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "llsd.h"
+
+class LLLayoutPanel;
+class LLTextBox;
+class LLComboBox;
+
+class LLFloaterExperienceProfile : public LLFloater
+{
+ LOG_CLASS(LLFloaterExperienceProfile);
+public:
+ enum PostSaveAction
+ {
+ NOTHING,
+ CLOSE,
+ VIEW,
+ };
+
+
+ LLFloaterExperienceProfile(const LLSD& data);
+ virtual ~LLFloaterExperienceProfile();
+
+ LLUUID getExperienceId() const { return mExperienceId; }
+ void setPreferences( const LLSD& content );
+
+
+ void refreshExperience(const LLSD& experience);
+ void onSaveComplete( const LLSD& content );
+ virtual BOOL canClose();
+
+ virtual void onClose(bool app_quitting);
+protected:
+ void onClickEdit();
+ void onClickPermission(const char* permission);
+ void onClickForget();
+ void onClickCancel();
+ void onClickSave();
+ void onClickLocation();
+ void onClickClear();
+ void onPickGroup();
+ void onFieldChanged();
+ void onReportExperience();
+
+ void setEditGroup(LLUUID group_id);
+
+ void changeToView();
+
+ void experienceForgotten();
+ void experienceBlocked();
+ void experienceAllowed();
+
+ static void experienceCallback(LLHandle<LLFloaterExperienceProfile> handle, const LLSD& experience);
+ static bool experiencePermission(LLHandle<LLFloaterExperienceProfile> handle, const LLSD& permission);
+
+ BOOL postBuild();
+ bool setMaturityString(U8 maturity, LLTextBox* child, LLComboBox* combo);
+ bool handleSaveChangesDialog(const LLSD& notification, const LLSD& response, PostSaveAction action);
+ void doSave( int success_action );
+
+ void updatePackage();
+
+ void updatePermission( const LLSD& permission );
+ LLUUID mExperienceId;
+ LLSD mExperienceDetails;
+ LLSD mPackage;
+ std::string mLocationSLURL;
+ int mSaveCompleteAction;
+ bool mDirty;
+ bool mForceClose;
+};
+
+#endif // LL_LLFLOATEREXPERIENCEPROFILE_H
diff --git a/indra/newview/llfloaterexperiences.cpp b/indra/newview/llfloaterexperiences.cpp
new file mode 100644
index 0000000000..ac79c9ab43
--- /dev/null
+++ b/indra/newview/llfloaterexperiences.cpp
@@ -0,0 +1,325 @@
+/**
+ * @file llfloaterexperiences.cpp
+ * @brief LLFloaterExperiences class implementation
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloaterexperiences.h"
+
+#include "llagent.h"
+#include "llevents.h"
+#include "llexperiencecache.h"
+#include "llfloaterregioninfo.h"
+#include "llhttpclient.h"
+#include "llnotificationsutil.h"
+#include "llpanelexperiencelog.h"
+#include "llpanelexperiencepicker.h"
+#include "llpanelexperiences.h"
+#include "lltabcontainer.h"
+#include "lltrans.h"
+#include "llviewerregion.h"
+
+
+#define SHOW_RECENT_TAB (0)
+
+class LLExperienceListResponder : public LLHTTPClient::Responder
+{
+public:
+ typedef std::map<std::string, std::string> NameMap;
+ typedef boost::function<void(LLPanelExperiences*, const LLSD&)> Callback;
+ LLExperienceListResponder(const LLHandle<LLFloaterExperiences>& parent, NameMap& nameMap, const std::string& errorMessage="ErrorMessage"):mParent(parent),mErrorMessage(errorMessage)
+ {
+ mNameMap.swap(nameMap);
+ }
+
+ Callback mCallback;
+ LLHandle<LLFloaterExperiences> mParent;
+ NameMap mNameMap;
+ const std::string mErrorMessage;
+ /*virtual*/ void httpSuccess()
+ {
+ if(mParent.isDead())
+ return;
+
+ LLFloaterExperiences* parent=mParent.get();
+ LLTabContainer* tabs = parent->getChild<LLTabContainer>("xp_tabs");
+
+ NameMap::iterator it = mNameMap.begin();
+ while(it != mNameMap.end())
+ {
+ if(getContent().has(it->first))
+ {
+ LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName(it->second);
+ if(tab)
+ {
+ const LLSD& ids = getContent()[it->first];
+ tab->setExperienceList(ids);
+ if(!mCallback.empty())
+ {
+ mCallback(tab, getContent());
+ }
+ }
+ }
+ ++it;
+ }
+ }
+
+ /*virtual*/ void httpFailure()
+ {
+ LLSD subs;
+ subs["ERROR_MESSAGE"] = getReason();
+ LLNotificationsUtil::add(mErrorMessage, subs);
+ }
+};
+
+
+
+LLFloaterExperiences::LLFloaterExperiences(const LLSD& data)
+ :LLFloater(data)
+{
+}
+
+LLPanelExperiences* LLFloaterExperiences::addTab(const std::string& name, bool select)
+{
+ LLPanelExperiences* newPanel = LLPanelExperiences::create(name);
+ getChild<LLTabContainer>("xp_tabs")->addTabPanel(LLTabContainer::TabPanelParams().
+ panel(newPanel).
+ label(LLTrans::getString(name)).
+ select_tab(select));
+
+ return newPanel;
+}
+
+BOOL LLFloaterExperiences::postBuild()
+{
+ getChild<LLTabContainer>("xp_tabs")->addTabPanel(new LLPanelExperiencePicker());
+ addTab("Allowed_Experiences_Tab", true);
+ addTab("Blocked_Experiences_Tab", false);
+ addTab("Admin_Experiences_Tab", false);
+ addTab("Contrib_Experiences_Tab", false);
+ LLPanelExperiences* owned = addTab("Owned_Experiences_Tab", false);
+ owned->setButtonAction("acquire", boost::bind(&LLFloaterExperiences::sendPurchaseRequest, this));
+ owned->enableButton(false);
+#if SHOW_RECENT_TAB
+ addTab("Recent_Experiences_Tab", false);
+#endif //SHOW_RECENT_TAB
+ getChild<LLTabContainer>("xp_tabs")->addTabPanel(new LLPanelExperienceLog());
+ resizeToTabs();
+
+
+ LLEventPumps::instance().obtain("experience_permission").listen("LLFloaterExperiences",
+ boost::bind(&LLFloaterExperiences::updatePermissions, this, _1));
+
+ return TRUE;
+}
+
+
+void LLFloaterExperiences::clearFromRecent(const LLSD& ids)
+{
+#if SHOW_RECENT_TAB
+ LLTabContainer* tabs = getChild<LLTabContainer>("xp_tabs");
+
+ LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName("Recent_Experiences_Tab");
+ if(!tab)
+ return;
+
+ tab->removeExperiences(ids);
+#endif // SHOW_RECENT_TAB
+}
+
+void LLFloaterExperiences::setupRecentTabs()
+{
+#if SHOW_RECENT_TAB
+ LLTabContainer* tabs = getChild<LLTabContainer>("xp_tabs");
+
+ LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName("Recent_Experiences_Tab");
+ if(!tab)
+ return;
+
+ LLSD recent;
+
+ const LLExperienceCache::cache_t& experiences = LLExperienceCache::getCached();
+
+ LLExperienceCache::cache_t::const_iterator it = experiences.begin();
+ while( it != experiences.end() )
+ {
+ if(!it->second.has(LLExperienceCache::MISSING))
+ {
+ recent.append(it->first);
+ }
+ ++it;
+ }
+
+ tab->setExperienceList(recent);
+#endif // SHOW_RECENT_TAB
+}
+
+
+void LLFloaterExperiences::resizeToTabs()
+{
+ const S32 TAB_WIDTH_PADDING = 16;
+
+ LLTabContainer* tabs = getChild<LLTabContainer>("xp_tabs");
+ LLRect rect = getRect();
+ if(rect.getWidth() < tabs->getTotalTabWidth() + TAB_WIDTH_PADDING)
+ {
+ rect.mRight = rect.mLeft + tabs->getTotalTabWidth() + TAB_WIDTH_PADDING;
+ }
+ reshape(rect.getWidth(), rect.getHeight(), FALSE);
+}
+
+void LLFloaterExperiences::refreshContents()
+{
+ setupRecentTabs();
+
+ LLViewerRegion* region = gAgent.getRegion();
+
+ if (region)
+ {
+ LLExperienceListResponder::NameMap nameMap;
+ std::string lookup_url=region->getCapability("GetExperiences");
+ if(!lookup_url.empty())
+ {
+ nameMap["experiences"]="Allowed_Experiences_Tab";
+ nameMap["blocked"]="Blocked_Experiences_Tab";
+ LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap));
+ }
+
+ lookup_url = region->getCapability("GetAdminExperiences");
+ if(!lookup_url.empty())
+ {
+ nameMap["experience_ids"]="Admin_Experiences_Tab";
+ LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap));
+ }
+
+ lookup_url = region->getCapability("GetCreatorExperiences");
+ if(!lookup_url.empty())
+ {
+ nameMap["experience_ids"]="Contrib_Experiences_Tab";
+ LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap));
+ }
+
+ lookup_url = region->getCapability("AgentExperiences");
+ if(!lookup_url.empty())
+ {
+ nameMap["experience_ids"]="Owned_Experiences_Tab";
+ LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap));
+ }
+ }
+}
+
+void LLFloaterExperiences::onOpen( const LLSD& key )
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if(region)
+ {
+ if(region->capabilitiesReceived())
+ {
+ refreshContents();
+ return;
+ }
+ region->setCapabilitiesReceivedCallback(boost::bind(&LLFloaterExperiences::refreshContents, this));
+ return;
+ }
+}
+
+bool LLFloaterExperiences::updatePermissions( const LLSD& permission )
+{
+ LLTabContainer* tabs = getChild<LLTabContainer>("xp_tabs");
+ LLUUID experience;
+ std::string permission_string;
+ if(permission.has("experience"))
+ {
+ experience = permission["experience"].asUUID();
+ permission_string = permission[experience.asString()]["permission"].asString();
+
+ }
+ LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName("Allowed_Experiences_Tab");
+ if(tab)
+ {
+ if(permission.has("experiences"))
+ {
+ tab->setExperienceList(permission["experiences"]);
+ }
+ else if(experience.notNull())
+ {
+ if(permission_string != "Allow")
+ {
+ tab->removeExperience(experience);
+ }
+ else
+ {
+ tab->addExperience(experience);
+ }
+ }
+ }
+
+ tab = (LLPanelExperiences*)tabs->getPanelByName("Blocked_Experiences_Tab");
+ if(tab)
+ {
+ if(permission.has("blocked"))
+ {
+ tab->setExperienceList(permission["blocked"]);
+ }
+ else if(experience.notNull())
+ {
+ if(permission_string != "Block")
+ {
+ tab->removeExperience(experience);
+ }
+ else
+ {
+ tab->addExperience(experience);
+ }
+ }
+ }
+ return false;
+}
+
+void LLFloaterExperiences::onClose( bool app_quitting )
+{
+ LLEventPumps::instance().obtain("experience_permission").stopListening("LLFloaterExperiences");
+ LLFloater::onClose(app_quitting);
+}
+
+void LLFloaterExperiences::checkPurchaseInfo(LLPanelExperiences* panel, const LLSD& content) const
+{
+ panel->enableButton(content.has("purchase"));
+}
+
+void LLFloaterExperiences::sendPurchaseRequest() const
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ std::string url = region->getCapability("AgentExperiences");
+ if(!url.empty())
+ {
+ LLSD content;
+
+ LLExperienceListResponder::NameMap nameMap;
+ nameMap["experience_ids"]="Owned_Experiences_Tab";
+ LLExperienceListResponder* responder = new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap, "ExperienceAcquireFailed");
+ responder->mCallback = boost::bind(&LLFloaterExperiences::checkPurchaseInfo, this, _1, _2);
+ LLHTTPClient::post(url, content, responder);
+ }
+}
diff --git a/indra/newview/llfloaterexperiences.h b/indra/newview/llfloaterexperiences.h
new file mode 100644
index 0000000000..f1a0f49245
--- /dev/null
+++ b/indra/newview/llfloaterexperiences.h
@@ -0,0 +1,58 @@
+/**
+ * @file llfloaterexperiences.h
+ * @brief LLFloaterExperiences class definition
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATEREXPERIENCES_H
+#define LL_LLFLOATEREXPERIENCES_H
+
+#include "llfloater.h"
+
+class LLPanelExperiences;
+
+class LLFloaterExperiences :
+ public LLFloater
+{
+public:
+ LLFloaterExperiences(const LLSD& data);
+ virtual void onClose(bool app_quitting);
+
+ virtual void onOpen(const LLSD& key);
+protected:
+ void clearFromRecent(const LLSD& ids);
+ void resizeToTabs();
+ /*virtual*/ BOOL postBuild();
+ void refreshContents();
+ void setupRecentTabs();
+ LLPanelExperiences* addTab(const std::string& name, bool select);
+
+ bool updatePermissions(const LLSD& permission);
+ void sendPurchaseRequest() const;
+ void checkPurchaseInfo(LLPanelExperiences* panel, const LLSD& content)const;
+
+private:
+
+};
+
+#endif //LL_LLFLOATEREXPERIENCES_H
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index 7213064746..4fda1ebb7d 100755
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -75,6 +75,9 @@
#include "llviewercontrol.h"
#include "roles_constants.h"
#include "lltrans.h"
+#include "llpanelexperiencelisteditor.h"
+#include "llpanelexperiencepicker.h"
+#include "llexperiencecache.h"
#include "llgroupactions.h"
@@ -86,7 +89,6 @@ static std::string MATURITY = "[MATURITY]";
// constants used in callbacks below - syntactic sugar.
static const BOOL BUY_GROUP_LAND = TRUE;
static const BOOL BUY_PERSONAL_LAND = FALSE;
-LLPointer<LLParcelSelection> LLPanelLandGeneral::sSelectionForBuyPass = NULL;
// Statics
LLParcelSelectionObserver* LLFloaterLand::sObserver = NULL;
@@ -162,191 +164,117 @@ void send_parcel_select_objects(S32 parcel_local_id, U32 return_type,
msg->sendReliable(region->getHost());
}
-LLParcel* LLFloaterLand::getCurrentSelectedParcel()
+void send_other_clean_time_message(S32 parcel_local_id, S32 other_clean_time)
{
- return mParcel->getParcel();
-};
+ LLMessageSystem *msg = gMessageSystem;
-//static
-LLPanelLandObjects* LLFloaterLand::getCurrentPanelLandObjects()
-{
- LLFloaterLand* land_instance = LLFloaterReg::getTypedInstance<LLFloaterLand>("about_land");
- if(land_instance)
- {
- return land_instance->mPanelObjects;
- }
- else
- {
- return NULL;
- }
-}
+ LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
+ if (!region) return;
-//static
-LLPanelLandCovenant* LLFloaterLand::getCurrentPanelLandCovenant()
-{
- LLFloaterLand* land_instance = LLFloaterReg::getTypedInstance<LLFloaterLand>("about_land");
- if(land_instance)
- {
- return land_instance->mPanelCovenant;
- }
- else
- {
- return NULL;
- }
-}
+ msg->newMessageFast(_PREHASH_ParcelSetOtherCleanTime);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
+ msg->addS32Fast(_PREHASH_OtherCleanTime, other_clean_time);
-// static
-void LLFloaterLand::refreshAll()
-{
- LLFloaterLand* land_instance = LLFloaterReg::getTypedInstance<LLFloaterLand>("about_land");
- if(land_instance)
- {
- land_instance->refresh();
- }
+ msg->sendReliable(region->getHost());
}
-void LLFloaterLand::onOpen(const LLSD& key)
+// inserts maturity info(icon and text) into target textbox
+// names_floater - pointer to floater which contains strings with maturity icons filenames
+// str_to_parse is string in format "txt1[MATURITY]txt2" where maturity icon and text will be inserted instead of [MATURITY]
+void insert_maturity_into_textbox(LLTextBox* target_textbox, LLFloater* names_floater, std::string str_to_parse)
{
- // moved from triggering show instance in llviwermenu.cpp
-
- if (LLViewerParcelMgr::getInstance()->selectionEmpty())
- {
- LLViewerParcelMgr::getInstance()->selectParcelAt(gAgent.getPositionGlobal());
- }
-
- // Done automatically when the selected parcel's properties arrive
- // (and hence we have the local id).
- // LLViewerParcelMgr::getInstance()->sendParcelAccessListRequest(AL_ACCESS | AL_BAN | AL_RENTER);
+ LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
+ if (!region)
+ return;
- mParcel = LLViewerParcelMgr::getInstance()->getFloatingParcelSelection();
-
- // Refresh even if not over a region so we don't get an
- // uninitialized dialog. The dialog is 0-region aware.
- refresh();
-}
+ LLStyle::Params style;
-void LLFloaterLand::onVisibilityChanged(const LLSD& visible)
-{
- if (!visible.asBoolean())
- {
- // Might have been showing owned objects
- LLSelectMgr::getInstance()->unhighlightAll();
+ U8 sim_access = region->getSimAccess();
- // Save which panel we had open
- sLastTab = mTabLand->getCurrentPanelIndex();
- }
-}
+ switch(sim_access)
+ {
+ case SIM_ACCESS_PG:
+ style.image(LLUI::getUIImage(names_floater->getString("maturity_icon_general")));
+ break;
+ case SIM_ACCESS_ADULT:
+ style.image(LLUI::getUIImage(names_floater->getString("maturity_icon_adult")));
+ break;
-LLFloaterLand::LLFloaterLand(const LLSD& seed)
-: LLFloater(seed)
-{
- mFactoryMap["land_general_panel"] = LLCallbackMap(createPanelLandGeneral, this);
- mFactoryMap["land_covenant_panel"] = LLCallbackMap(createPanelLandCovenant, this);
- mFactoryMap["land_objects_panel"] = LLCallbackMap(createPanelLandObjects, this);
- mFactoryMap["land_options_panel"] = LLCallbackMap(createPanelLandOptions, this);
- mFactoryMap["land_audio_panel"] = LLCallbackMap(createPanelLandAudio, this);
- mFactoryMap["land_media_panel"] = LLCallbackMap(createPanelLandMedia, this);
- mFactoryMap["land_access_panel"] = LLCallbackMap(createPanelLandAccess, this);
+ case SIM_ACCESS_MATURE:
+ style.image(LLUI::getUIImage(names_floater->getString("maturity_icon_moderate")));
+ break;
- sObserver = new LLParcelSelectionObserver();
- LLViewerParcelMgr::getInstance()->addObserver( sObserver );
-}
+ default:
+ break;
+ }
-BOOL LLFloaterLand::postBuild()
-{
- setVisibleCallback(boost::bind(&LLFloaterLand::onVisibilityChanged, this, _2));
+ size_t maturity_pos = str_to_parse.find(MATURITY);
- LLTabContainer* tab = getChild<LLTabContainer>("landtab");
-
- mTabLand = (LLTabContainer*) tab;
-
- if (tab)
+ if (maturity_pos == std::string::npos)
{
- tab->selectTab(sLastTab);
+ return;
}
- return TRUE;
-}
+ std::string text_before_rating = str_to_parse.substr(0, maturity_pos);
+ std::string text_after_rating = str_to_parse.substr(maturity_pos + MATURITY.length());
+ target_textbox->setText(text_before_rating);
-// virtual
-LLFloaterLand::~LLFloaterLand()
-{
- LLViewerParcelMgr::getInstance()->removeObserver( sObserver );
- delete sObserver;
- sObserver = NULL;
-}
+ target_textbox->appendImageSegment(style);
-// public
-void LLFloaterLand::refresh()
-{
- mPanelGeneral->refresh();
- mPanelObjects->refresh();
- mPanelOptions->refresh();
- mPanelAudio->refresh();
- mPanelMedia->refresh();
- mPanelAccess->refresh();
- mPanelCovenant->refresh();
+ target_textbox->appendText(LLViewerParcelMgr::getInstance()->getSelectionRegion()->getSimAccessString(), false);
+ target_textbox->appendText(text_after_rating, false);
}
-
-
-void* LLFloaterLand::createPanelLandGeneral(void* data)
+void send_return_objects_message(S32 parcel_local_id, S32 return_type,
+ uuid_list_t* owner_ids = NULL)
{
- LLFloaterLand* self = (LLFloaterLand*)data;
- self->mPanelGeneral = new LLPanelLandGeneral(self->mParcel);
- return self->mPanelGeneral;
-}
+ LLMessageSystem *msg = gMessageSystem;
-// static
-void* LLFloaterLand::createPanelLandCovenant(void* data)
-{
- LLFloaterLand* self = (LLFloaterLand*)data;
- self->mPanelCovenant = new LLPanelLandCovenant(self->mParcel);
- return self->mPanelCovenant;
-}
+ LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
+ if (!region) return;
+ msg->newMessageFast(_PREHASH_ParcelReturnObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
+ msg->addU32Fast(_PREHASH_ReturnType, (U32) return_type);
-// static
-void* LLFloaterLand::createPanelLandObjects(void* data)
-{
- LLFloaterLand* self = (LLFloaterLand*)data;
- self->mPanelObjects = new LLPanelLandObjects(self->mParcel);
- return self->mPanelObjects;
-}
+ // Dummy task id, not used
+ msg->nextBlock("TaskIDs");
+ msg->addUUID("TaskID", LLUUID::null);
-// static
-void* LLFloaterLand::createPanelLandOptions(void* data)
-{
- LLFloaterLand* self = (LLFloaterLand*)data;
- self->mPanelOptions = new LLPanelLandOptions(self->mParcel);
- return self->mPanelOptions;
-}
+ // Throw all return ids into the packet.
+ // TODO: Check for too many ids.
+ if (owner_ids)
+ {
+ uuid_list_t::iterator end = owner_ids->end();
+ for (uuid_list_t::iterator it = owner_ids->begin();
+ it != end;
+ ++it)
+ {
+ msg->nextBlockFast(_PREHASH_OwnerIDs);
+ msg->addUUIDFast(_PREHASH_OwnerID, (*it));
+ }
+ }
+ else
+ {
+ msg->nextBlockFast(_PREHASH_OwnerIDs);
+ msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null);
+ }
-// static
-void* LLFloaterLand::createPanelLandAudio(void* data)
-{
- LLFloaterLand* self = (LLFloaterLand*)data;
- self->mPanelAudio = new LLPanelLandAudio(self->mParcel);
- return self->mPanelAudio;
+ msg->sendReliable(region->getHost());
}
-// static
-void* LLFloaterLand::createPanelLandMedia(void* data)
-{
- LLFloaterLand* self = (LLFloaterLand*)data;
- self->mPanelMedia = new LLPanelLandMedia(self->mParcel);
- return self->mPanelMedia;
-}
-// static
-void* LLFloaterLand::createPanelLandAccess(void* data)
-{
- LLFloaterLand* self = (LLFloaterLand*)data;
- self->mPanelAccess = new LLPanelLandAccess(self->mParcel);
- return self->mPanelAccess;
-}
+LLPointer<LLParcelSelection> LLPanelLandGeneral::sSelectionForBuyPass = NULL;
//---------------------------------------------------------------------------
// LLPanelLandGeneral
@@ -1063,6 +991,8 @@ void LLPanelLandGeneral::onClickStopSellLand(void* data)
LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate(parcel);
}
+
+
//---------------------------------------------------------------------------
// LLPanelLandObjects
//---------------------------------------------------------------------------
@@ -1308,66 +1238,6 @@ void LLPanelLandObjects::draw()
LLPanel::draw();
}
-void send_other_clean_time_message(S32 parcel_local_id, S32 other_clean_time)
-{
- LLMessageSystem *msg = gMessageSystem;
-
- LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
- if (!region) return;
-
- msg->newMessageFast(_PREHASH_ParcelSetOtherCleanTime);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_ParcelData);
- msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
- msg->addS32Fast(_PREHASH_OtherCleanTime, other_clean_time);
-
- msg->sendReliable(region->getHost());
-}
-
-void send_return_objects_message(S32 parcel_local_id, S32 return_type,
- uuid_list_t* owner_ids = NULL)
-{
- LLMessageSystem *msg = gMessageSystem;
-
- LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
- if (!region) return;
-
- msg->newMessageFast(_PREHASH_ParcelReturnObjects);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_ParcelData);
- msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
- msg->addU32Fast(_PREHASH_ReturnType, (U32) return_type);
-
- // Dummy task id, not used
- msg->nextBlock("TaskIDs");
- msg->addUUID("TaskID", LLUUID::null);
-
- // Throw all return ids into the packet.
- // TODO: Check for too many ids.
- if (owner_ids)
- {
- uuid_list_t::iterator end = owner_ids->end();
- for (uuid_list_t::iterator it = owner_ids->begin();
- it != end;
- ++it)
- {
- msg->nextBlockFast(_PREHASH_OwnerIDs);
- msg->addUUIDFast(_PREHASH_OwnerID, (*it));
- }
- }
- else
- {
- msg->nextBlockFast(_PREHASH_OwnerIDs);
- msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null);
- }
-
- msg->sendReliable(region->getHost());
-}
-
bool LLPanelLandObjects::callbackReturnOwnerObjects(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
@@ -1804,6 +1674,52 @@ void LLPanelLandObjects::onCommitClean(LLUICtrl *caller, void* user_data)
}
+class LLPanelLandOptions
+ : public LLPanel
+{
+public:
+ LLPanelLandOptions(LLSafeHandle<LLParcelSelection>& parcelp);
+ virtual ~LLPanelLandOptions();
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void draw();
+ /*virtual*/ void refresh();
+
+private:
+ // Refresh the "show in search" checkbox and category selector.
+ void refreshSearch();
+
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onClickSet(void* userdata);
+ static void onClickClear(void* userdata);
+
+private:
+ LLCheckBoxCtrl* mCheckEditObjects;
+ LLCheckBoxCtrl* mCheckEditGroupObjects;
+ LLCheckBoxCtrl* mCheckAllObjectEntry;
+ LLCheckBoxCtrl* mCheckGroupObjectEntry;
+ LLCheckBoxCtrl* mCheckSafe;
+ LLCheckBoxCtrl* mCheckFly;
+ LLCheckBoxCtrl* mCheckGroupScripts;
+ LLCheckBoxCtrl* mCheckOtherScripts;
+
+ LLCheckBoxCtrl* mCheckShowDirectory;
+ LLComboBox* mCategoryCombo;
+ LLComboBox* mLandingTypeCombo;
+
+ LLTextureCtrl* mSnapshotCtrl;
+
+ LLTextBox* mLocationText;
+ LLButton* mSetBtn;
+ LLButton* mClearBtn;
+
+ LLCheckBoxCtrl *mMatureCtrl;
+ LLCheckBoxCtrl *mPushRestrictionCtrl;
+ LLCheckBoxCtrl *mSeeAvatarsCtrl;
+
+ LLSafeHandle<LLParcelSelection>& mParcel;
+};
+
+
//---------------------------------------------------------------------------
// LLPanelLandOptions
//---------------------------------------------------------------------------
@@ -2320,6 +2236,38 @@ void LLPanelLandOptions::onClickClear(void* userdata)
}
+class LLPanelLandAccess
+ : public LLPanel
+{
+public:
+ LLPanelLandAccess(LLSafeHandle<LLParcelSelection>& parcelp);
+ virtual ~LLPanelLandAccess();
+ void refresh();
+ void refresh_ui();
+ void refreshNames();
+ virtual void draw();
+
+ static void onCommitPublicAccess(LLUICtrl* ctrl, void *userdata);
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onCommitGroupCheck(LLUICtrl* ctrl, void *userdata);
+ static void onClickRemoveAccess(void*);
+ static void onClickRemoveBanned(void*);
+
+ virtual BOOL postBuild();
+
+ void onClickAddAccess();
+ void onClickAddBanned();
+ void callbackAvatarCBBanned(const uuid_vec_t& ids);
+ void callbackAvatarCBAccess(const uuid_vec_t& ids);
+
+protected:
+ LLNameListCtrl* mListAccess;
+ LLNameListCtrl* mListBanned;
+
+ LLSafeHandle<LLParcelSelection>& mParcel;
+};
+
+
//---------------------------------------------------------------------------
// LLPanelLandAccess
//---------------------------------------------------------------------------
@@ -2398,7 +2346,7 @@ void LLPanelLandAccess::refresh()
getChild<LLUICtrl>("AccessList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count));
getChild<LLUICtrl>("AccessList")->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",PARCEL_MAX_ACCESS_LIST));
- for (access_map_const_iterator cit = parcel->mAccessList.begin();
+ for (LLAccessEntry::map::const_iterator cit = parcel->mAccessList.begin();
cit != parcel->mAccessList.end(); ++cit)
{
const LLAccessEntry& entry = (*cit).second;
@@ -2444,7 +2392,7 @@ void LLPanelLandAccess::refresh()
getChild<LLUICtrl>("BannedList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count));
getChild<LLUICtrl>("BannedList")->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",PARCEL_MAX_ACCESS_LIST));
- for (access_map_const_iterator cit = parcel->mBanList.begin();
+ for (LLAccessEntry::map::const_iterator cit = parcel->mBanList.begin();
cit != parcel->mBanList.end(); ++cit)
{
const LLAccessEntry& entry = (*cit).second;
@@ -2777,8 +2725,8 @@ void LLPanelLandAccess::callbackAvatarCBAccess(const uuid_vec_t& ids)
LLUUID id = ids[0];
LLParcel* parcel = mParcel->getParcel();
if (parcel)
- {
- parcel->addToAccessList(id, 0);
+ {
+ parcel->addToAccessList(id, 0);
LLViewerParcelMgr::getInstance()->sendParcelAccessListUpdate(AL_ACCESS);
refresh();
}
@@ -2860,6 +2808,7 @@ void LLPanelLandAccess::onClickRemoveBanned(void* data)
}
}
+
//---------------------------------------------------------------------------
// LLPanelLandCovenant
//---------------------------------------------------------------------------
@@ -2973,51 +2922,321 @@ void LLPanelLandCovenant::updateEstateOwnerName(const std::string& name)
}
}
-// inserts maturity info(icon and text) into target textbox
-// names_floater - pointer to floater which contains strings with maturity icons filenames
-// str_to_parse is string in format "txt1[MATURITY]txt2" where maturity icon and text will be inserted instead of [MATURITY]
-void insert_maturity_into_textbox(LLTextBox* target_textbox, LLFloater* names_floater, std::string str_to_parse)
+class LLPanelLandExperiences
+ : public LLPanel
{
- LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
- if (!region)
+public:
+ LLPanelLandExperiences(LLSafeHandle<LLParcelSelection>& parcelp);
+ virtual BOOL postBuild();
+ void refresh();
+
+ void experienceAdded(const LLUUID& id, U32 xp_type, U32 access_type);
+ void experienceRemoved(const LLUUID& id, U32 access_type);
+protected:
+ LLPanelExperienceListEditor* setupList( const char* control_name, U32 xp_type, U32 access_type );
+ void refreshPanel(LLPanelExperienceListEditor* panel, U32 xp_type);
+
+ LLSafeHandle<LLParcelSelection>& mParcel;
+
+
+ LLPanelExperienceListEditor* mAllowed;
+ LLPanelExperienceListEditor* mBlocked;
+};
+
+LLPanelLandExperiences::LLPanelLandExperiences( LLSafeHandle<LLParcelSelection>& parcelp )
+ : mParcel(parcelp)
+{
+
+}
+
+
+BOOL LLPanelLandExperiences::postBuild()
+{
+ mAllowed = setupList("panel_allowed", EXPERIENCE_KEY_TYPE_ALLOWED, AL_ALLOW_EXPERIENCE);
+ mBlocked = setupList("panel_blocked", EXPERIENCE_KEY_TYPE_BLOCKED, AL_BLOCK_EXPERIENCE);
+
+ // only non-grid-wide experiences
+ mAllowed->addFilter(boost::bind(LLPanelExperiencePicker::FilterWithProperty, _1, LLExperienceCache::PROPERTY_GRID));
+
+ // only grid-wide experiences
+ mBlocked->addFilter(boost::bind(LLPanelExperiencePicker::FilterWithoutProperty, _1, LLExperienceCache::PROPERTY_GRID));
+ // but not privileged ones
+ mBlocked->addFilter(boost::bind(LLPanelExperiencePicker::FilterWithProperty, _1, LLExperienceCache::PROPERTY_PRIVILEGED));
+
+ getChild<LLLayoutPanel>("trusted_layout_panel")->setVisible(FALSE);
+ getChild<LLTextBox>("experiences_help_text")->setVisible(FALSE);
+ getChild<LLTextBox>("allowed_text_help")->setText(getString("allowed_parcel_text"));
+ getChild<LLTextBox>("blocked_text_help")->setText(getString("blocked_parcel_text"));
+
+ return LLPanel::postBuild();
+}
+
+LLPanelExperienceListEditor* LLPanelLandExperiences::setupList( const char* control_name, U32 xp_type, U32 access_type )
+{
+ LLPanelExperienceListEditor* child = findChild<LLPanelExperienceListEditor>(control_name);
+ if(child)
+ {
+ child->getChild<LLTextBox>("text_name")->setText(child->getString(control_name));
+ child->setMaxExperienceIDs(PARCEL_MAX_EXPERIENCE_LIST);
+ child->setAddedCallback(boost::bind(&LLPanelLandExperiences::experienceAdded, this, _1, xp_type, access_type));
+ child->setRemovedCallback(boost::bind(&LLPanelLandExperiences::experienceRemoved, this, _1, access_type));
+ }
+
+ return child;
+}
+
+void LLPanelLandExperiences::experienceAdded( const LLUUID& id, U32 xp_type, U32 access_type )
+{
+ LLParcel* parcel = mParcel->getParcel();
+ if (parcel)
+ {
+ parcel->setExperienceKeyType(id, xp_type);
+ LLViewerParcelMgr::getInstance()->sendParcelAccessListUpdate(access_type);
+ refresh();
+ }
+}
+
+void LLPanelLandExperiences::experienceRemoved( const LLUUID& id, U32 access_type )
+{
+ LLParcel* parcel = mParcel->getParcel();
+ if (parcel)
+ {
+ parcel->setExperienceKeyType(id, EXPERIENCE_KEY_TYPE_NONE);
+ LLViewerParcelMgr::getInstance()->sendParcelAccessListUpdate(access_type);
+ refresh();
+ }
+}
+
+void LLPanelLandExperiences::refreshPanel(LLPanelExperienceListEditor* panel, U32 xp_type)
+{
+ LLParcel *parcel = mParcel->getParcel();
+
+ // Display options
+ if (panel == NULL)
+ {
return;
+ }
+ if (parcel == NULL)
+ {
+ // disable the panel
+ panel->setEnabled(FALSE);
+ panel->setExperienceIds(LLSD::emptyArray());
+ }
+ else
+ {
+ // enable the panel
+ panel->setEnabled(TRUE);
+ LLAccessEntry::map entries = parcel->getExperienceKeysByType(xp_type);
+ LLAccessEntry::map::iterator it = entries.begin();
+ LLSD ids = LLSD::emptyArray();
+ for (/**/; it != entries.end(); ++it)
+ {
+ ids.append(it->second.mID);
+ }
+ panel->setExperienceIds(ids);
+ panel->setReadonly(!LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_OPTIONS));
+ panel->refreshExperienceCounter();
+ }
+}
- LLStyle::Params style;
+void LLPanelLandExperiences::refresh()
+{
+ refreshPanel(mAllowed, EXPERIENCE_KEY_TYPE_ALLOWED);
+ refreshPanel(mBlocked, EXPERIENCE_KEY_TYPE_BLOCKED);
+}
- U8 sim_access = region->getSimAccess();
+LLParcel* LLFloaterLand::getCurrentSelectedParcel()
+{
+ return mParcel->getParcel();
+};
- switch(sim_access)
+//static
+LLPanelLandObjects* LLFloaterLand::getCurrentPanelLandObjects()
+{
+ LLFloaterLand* land_instance = LLFloaterReg::getTypedInstance<LLFloaterLand>("about_land");
+ if(land_instance)
{
- case SIM_ACCESS_PG:
- style.image(LLUI::getUIImage(names_floater->getString("maturity_icon_general")));
- break;
+ return land_instance->mPanelObjects;
+ }
+ else
+ {
+ return NULL;
+ }
+}
- case SIM_ACCESS_ADULT:
- style.image(LLUI::getUIImage(names_floater->getString("maturity_icon_adult")));
- break;
+//static
+LLPanelLandCovenant* LLFloaterLand::getCurrentPanelLandCovenant()
+{
+ LLFloaterLand* land_instance = LLFloaterReg::getTypedInstance<LLFloaterLand>("about_land");
+ if(land_instance)
+ {
+ return land_instance->mPanelCovenant;
+ }
+ else
+ {
+ return NULL;
+ }
+}
- case SIM_ACCESS_MATURE:
- style.image(LLUI::getUIImage(names_floater->getString("maturity_icon_moderate")));
- break;
+// static
+void LLFloaterLand::refreshAll()
+{
+ LLFloaterLand* land_instance = LLFloaterReg::getTypedInstance<LLFloaterLand>("about_land");
+ if(land_instance)
+ {
+ land_instance->refresh();
+ }
+}
- default:
- break;
+void LLFloaterLand::onOpen(const LLSD& key)
+{
+ // moved from triggering show instance in llviwermenu.cpp
+
+ if (LLViewerParcelMgr::getInstance()->selectionEmpty())
+ {
+ LLViewerParcelMgr::getInstance()->selectParcelAt(gAgent.getPositionGlobal());
}
+
+ // Done automatically when the selected parcel's properties arrive
+ // (and hence we have the local id).
+ // LLViewerParcelMgr::getInstance()->sendParcelAccessListRequest(AL_ACCESS | AL_BAN | AL_RENTER);
- size_t maturity_pos = str_to_parse.find(MATURITY);
+ mParcel = LLViewerParcelMgr::getInstance()->getFloatingParcelSelection();
- if (maturity_pos == std::string::npos)
+ // Refresh even if not over a region so we don't get an
+ // uninitialized dialog. The dialog is 0-region aware.
+ refresh();
+}
+
+void LLFloaterLand::onVisibilityChanged(const LLSD& visible)
+{
+ if (!visible.asBoolean())
{
- return;
+ // Might have been showing owned objects
+ LLSelectMgr::getInstance()->unhighlightAll();
+
+ // Save which panel we had open
+ sLastTab = mTabLand->getCurrentPanelIndex();
}
+}
- std::string text_before_rating = str_to_parse.substr(0, maturity_pos);
- std::string text_after_rating = str_to_parse.substr(maturity_pos + MATURITY.length());
- target_textbox->setText(text_before_rating);
+LLFloaterLand::LLFloaterLand(const LLSD& seed)
+: LLFloater(seed)
+{
+ mFactoryMap["land_general_panel"] = LLCallbackMap(createPanelLandGeneral, this);
+ mFactoryMap["land_covenant_panel"] = LLCallbackMap(createPanelLandCovenant, this);
+ mFactoryMap["land_objects_panel"] = LLCallbackMap(createPanelLandObjects, this);
+ mFactoryMap["land_options_panel"] = LLCallbackMap(createPanelLandOptions, this);
+ mFactoryMap["land_audio_panel"] = LLCallbackMap(createPanelLandAudio, this);
+ mFactoryMap["land_media_panel"] = LLCallbackMap(createPanelLandMedia, this);
+ mFactoryMap["land_access_panel"] = LLCallbackMap(createPanelLandAccess, this);
+ mFactoryMap["land_experiences_panel"] = LLCallbackMap(createPanelLandExperiences, this);
- target_textbox->appendImageSegment(style);
+ sObserver = new LLParcelSelectionObserver();
+ LLViewerParcelMgr::getInstance()->addObserver( sObserver );
+}
- target_textbox->appendText(LLViewerParcelMgr::getInstance()->getSelectionRegion()->getSimAccessString(), false);
- target_textbox->appendText(text_after_rating, false);
+BOOL LLFloaterLand::postBuild()
+{
+ setVisibleCallback(boost::bind(&LLFloaterLand::onVisibilityChanged, this, _2));
+
+ LLTabContainer* tab = getChild<LLTabContainer>("landtab");
+
+ mTabLand = (LLTabContainer*) tab;
+
+ if (tab)
+ {
+ tab->selectTab(sLastTab);
+ }
+
+ return TRUE;
+}
+
+
+// virtual
+LLFloaterLand::~LLFloaterLand()
+{
+ LLViewerParcelMgr::getInstance()->removeObserver( sObserver );
+ delete sObserver;
+ sObserver = NULL;
+}
+
+// public
+void LLFloaterLand::refresh()
+{
+ mPanelGeneral->refresh();
+ mPanelObjects->refresh();
+ mPanelOptions->refresh();
+ mPanelAudio->refresh();
+ mPanelMedia->refresh();
+ mPanelAccess->refresh();
+ mPanelCovenant->refresh();
+ mPanelExperiences->refresh();
+}
+
+
+
+void* LLFloaterLand::createPanelLandGeneral(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelGeneral = new LLPanelLandGeneral(self->mParcel);
+ return self->mPanelGeneral;
+}
+
+// static
+void* LLFloaterLand::createPanelLandCovenant(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelCovenant = new LLPanelLandCovenant(self->mParcel);
+ return self->mPanelCovenant;
+}
+
+
+// static
+void* LLFloaterLand::createPanelLandObjects(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelObjects = new LLPanelLandObjects(self->mParcel);
+ return self->mPanelObjects;
+}
+
+// static
+void* LLFloaterLand::createPanelLandOptions(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelOptions = new LLPanelLandOptions(self->mParcel);
+ return self->mPanelOptions;
+}
+
+// static
+void* LLFloaterLand::createPanelLandAudio(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelAudio = new LLPanelLandAudio(self->mParcel);
+ return self->mPanelAudio;
+}
+
+// static
+void* LLFloaterLand::createPanelLandMedia(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelMedia = new LLPanelLandMedia(self->mParcel);
+ return self->mPanelMedia;
+}
+
+// static
+void* LLFloaterLand::createPanelLandAccess(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelAccess = new LLPanelLandAccess(self->mParcel);
+ return self->mPanelAccess;
+}
+
+// static
+void* LLFloaterLand::createPanelLandExperiences(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelExperiences = new LLPanelLandExperiences(self->mParcel);
+ return self->mPanelExperiences;
}
diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h
index 1d9bd33720..ae9eecd374 100755
--- a/indra/newview/llfloaterland.h
+++ b/indra/newview/llfloaterland.h
@@ -66,6 +66,7 @@ class LLPanelLandBan;
class LLPanelLandRenters;
class LLPanelLandCovenant;
class LLParcel;
+class LLPanelLandExperiences;
class LLFloaterLand
: public LLFloater
@@ -101,6 +102,7 @@ protected:
static void* createPanelLandAudio(void* data);
static void* createPanelLandMedia(void* data);
static void* createPanelLandAccess(void* data);
+ static void* createPanelLandExperiences(void* data);
static void* createPanelLandBan(void* data);
@@ -116,6 +118,7 @@ protected:
LLPanelLandMedia* mPanelMedia;
LLPanelLandAccess* mPanelAccess;
LLPanelLandCovenant* mPanelCovenant;
+ LLPanelLandExperiences* mPanelExperiences;
LLSafeHandle<LLParcelSelection> mParcel;
@@ -306,85 +309,6 @@ protected:
LLSafeHandle<LLParcelSelection>& mParcel;
};
-
-class LLPanelLandOptions
-: public LLPanel
-{
-public:
- LLPanelLandOptions(LLSafeHandle<LLParcelSelection>& parcelp);
- virtual ~LLPanelLandOptions();
- /*virtual*/ BOOL postBuild();
- /*virtual*/ void draw();
- /*virtual*/ void refresh();
-
-private:
- // Refresh the "show in search" checkbox and category selector.
- void refreshSearch();
-
- static void onCommitAny(LLUICtrl* ctrl, void *userdata);
- static void onClickSet(void* userdata);
- static void onClickClear(void* userdata);
-
-private:
- LLCheckBoxCtrl* mCheckEditObjects;
- LLCheckBoxCtrl* mCheckEditGroupObjects;
- LLCheckBoxCtrl* mCheckAllObjectEntry;
- LLCheckBoxCtrl* mCheckGroupObjectEntry;
- LLCheckBoxCtrl* mCheckSafe;
- LLCheckBoxCtrl* mCheckFly;
- LLCheckBoxCtrl* mCheckGroupScripts;
- LLCheckBoxCtrl* mCheckOtherScripts;
-
- LLCheckBoxCtrl* mCheckShowDirectory;
- LLComboBox* mCategoryCombo;
- LLComboBox* mLandingTypeCombo;
-
- LLTextureCtrl* mSnapshotCtrl;
-
- LLTextBox* mLocationText;
- LLButton* mSetBtn;
- LLButton* mClearBtn;
-
- LLCheckBoxCtrl *mMatureCtrl;
- LLCheckBoxCtrl *mPushRestrictionCtrl;
- LLCheckBoxCtrl *mSeeAvatarsCtrl;
-
- LLSafeHandle<LLParcelSelection>& mParcel;
-};
-
-
-class LLPanelLandAccess
-: public LLPanel
-{
-public:
- LLPanelLandAccess(LLSafeHandle<LLParcelSelection>& parcelp);
- virtual ~LLPanelLandAccess();
- void refresh();
- void refresh_ui();
- void refreshNames();
- virtual void draw();
-
- static void onCommitPublicAccess(LLUICtrl* ctrl, void *userdata);
- static void onCommitAny(LLUICtrl* ctrl, void *userdata);
- static void onCommitGroupCheck(LLUICtrl* ctrl, void *userdata);
- static void onClickRemoveAccess(void*);
- static void onClickRemoveBanned(void*);
-
- virtual BOOL postBuild();
-
- void onClickAddAccess();
- void onClickAddBanned();
- void callbackAvatarCBBanned(const uuid_vec_t& ids);
- void callbackAvatarCBAccess(const uuid_vec_t& ids);
-
-protected:
- LLNameListCtrl* mListAccess;
- LLNameListCtrl* mListBanned;
-
- LLSafeHandle<LLParcelSelection>& mParcel;
-};
-
-
class LLPanelLandCovenant
: public LLPanel
{
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 5e9b25b474..f70152fc59 100755
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -92,6 +92,11 @@
#include "llagentui.h"
#include "llmeshrepository.h"
#include "llfloaterregionrestarting.h"
+#include "llpanelexperiencelisteditor.h"
+#include <boost/function.hpp>
+#include "llpanelexperiencepicker.h"
+#include "llexperiencecache.h"
+#include "llpanelexperiences.h"
const S32 TERRAIN_TEXTURE_COUNT = 4;
const S32 CORNER_COUNT = 4;
@@ -126,6 +131,18 @@ public:
const sparam_t& strings);
};
+class LLDispatchSetEstateExperience : public LLDispatchHandler
+{
+public:
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings);
+
+ LLSD getIDs( sparam_t::const_iterator it, sparam_t::const_iterator end, S32 count );
+};
+
/*
void unpack_request_params(
@@ -215,6 +232,14 @@ BOOL LLFloaterRegionInfo::postBuild()
panel->buildFromFile("panel_region_debug.xml");
mTab->addTabPanel(panel);
+ if(!gAgent.getRegion()->getCapability("RegionExperiences").empty())
+ {
+ panel = new LLPanelRegionExperiences;
+ mInfoPanels.push_back(panel);
+ panel->buildFromFile("panel_region_experiences.xml");
+ mTab->addTabPanel(panel);
+ }
+
gMessageSystem->setHandlerFunc(
"EstateOwnerMessage",
&processEstateOwnerRequest);
@@ -445,6 +470,16 @@ LLPanelRegionTerrainInfo* LLFloaterRegionInfo::getPanelRegionTerrain()
return panel;
}
+LLPanelRegionExperiences* LLFloaterRegionInfo::getPanelExperiences()
+{
+ LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info");
+ if (!floater) return NULL;
+ LLTabContainer* tab = floater->getChild<LLTabContainer>("region_panels");
+ return (LLPanelRegionExperiences*)tab->getChild<LLPanel>("Experiences");
+}
+
+
+
void LLFloaterRegionInfo::onTabSelected(const LLSD& param)
{
LLPanel* active_panel = getChild<LLPanel>(param.asString());
@@ -1373,6 +1408,11 @@ void LLPanelEstateInfo::initDispatch(LLDispatcher& dispatch)
static LLDispatchSetEstateAccess set_access;
dispatch.addHandler(name, &set_access);
+
+ name.assign("setexperience");
+ static LLDispatchSetEstateExperience set_experience;
+ dispatch.addHandler(name, &set_experience);
+
estate_dispatch_initialized = true;
}
@@ -2882,6 +2922,56 @@ bool LLDispatchSetEstateAccess::operator()(
return true;
}
+LLSD LLDispatchSetEstateExperience::getIDs( sparam_t::const_iterator it, sparam_t::const_iterator end, S32 count )
+{
+ LLSD idList = LLSD::emptyArray();
+ LLUUID id;
+ while(count--> 0)
+ {
+ memcpy(id.mData, (*(it++)).data(), UUID_BYTES);
+ idList.append(id);
+ }
+ return idList;
+}
+
+// key = "setexperience"
+// strings[0] = str(estate_id)
+// strings[1] = str(send_to_agent_only)
+// strings[2] = str(num blocked)
+// strings[3] = str(num trusted)
+// strings[4] = str(num allowed)
+// strings[8] = bin(uuid) ...
+// ...
+bool LLDispatchSetEstateExperience::operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+{
+ LLPanelRegionExperiences* panel = LLFloaterRegionInfo::getPanelExperiences();
+ if (!panel) return true;
+
+ sparam_t::const_iterator it = strings.begin();
+ ++it; // U32 estate_id = strtol((*it).c_str(), NULL, 10);
+ ++it; // U32 send_to_agent_only = strtoul((*(++it)).c_str(), NULL, 10);
+
+ LLUUID id;
+ S32 num_blocked = strtol((*(it++)).c_str(), NULL, 10);
+ S32 num_trusted = strtol((*(it++)).c_str(), NULL, 10);
+ S32 num_allowed = strtol((*(it++)).c_str(), NULL, 10);
+
+ LLSD ids = LLSD::emptyMap()
+ .with("blocked", getIDs(it, strings.end(), num_blocked))
+ .with("trusted", getIDs(it + (num_blocked), strings.end(), num_trusted))
+ .with("allowed", getIDs(it + (num_blocked+num_trusted), strings.end(), num_allowed));
+
+ panel->processResponse(ids);
+
+ return true;
+}
+
+
+
LLPanelEnvironmentInfo::LLPanelEnvironmentInfo()
: mEnableEditing(false),
mRegionSettingsRadioGroup(NULL),
@@ -3473,3 +3563,284 @@ void LLPanelEnvironmentInfo::onRegionSettingsApplied(bool ok)
LLEnvManagerNew::instance().requestRegionSettings();
}
}
+
+BOOL LLPanelRegionExperiences::postBuild()
+{
+ mAllowed = setupList("panel_allowed", ESTATE_EXPERIENCE_ALLOWED_ADD, ESTATE_EXPERIENCE_ALLOWED_REMOVE);
+ mTrusted = setupList("panel_trusted", ESTATE_EXPERIENCE_TRUSTED_ADD, ESTATE_EXPERIENCE_TRUSTED_REMOVE);
+ mBlocked = setupList("panel_blocked", ESTATE_EXPERIENCE_BLOCKED_ADD, ESTATE_EXPERIENCE_BLOCKED_REMOVE);
+
+ getChild<LLLayoutPanel>("trusted_layout_panel")->setVisible(TRUE);
+ getChild<LLTextBox>("experiences_help_text")->setText(getString("estate_caption"));
+ getChild<LLTextBox>("trusted_text_help")->setText(getString("trusted_estate_text"));
+ getChild<LLTextBox>("allowed_text_help")->setText(getString("allowed_estate_text"));
+ getChild<LLTextBox>("blocked_text_help")->setText(getString("blocked_estate_text"));
+
+ return LLPanelRegionInfo::postBuild();
+}
+
+LLPanelExperienceListEditor* LLPanelRegionExperiences::setupList( const char* control_name, U32 add_id, U32 remove_id )
+{
+ LLPanelExperienceListEditor* child = findChild<LLPanelExperienceListEditor>(control_name);
+ if(child)
+ {
+ child->getChild<LLTextBox>("text_name")->setText(child->getString(control_name));
+ child->setMaxExperienceIDs(ESTATE_MAX_EXPERIENCE_IDS);
+ child->setAddedCallback( boost::bind(&LLPanelRegionExperiences::itemChanged, this, add_id, _1));
+ child->setRemovedCallback(boost::bind(&LLPanelRegionExperiences::itemChanged, this, remove_id, _1));
+ }
+
+ return child;
+}
+
+
+void LLPanelRegionExperiences::processResponse( const LLSD& content )
+{
+ if(content.has("default"))
+ {
+ mDefaultExperience = content["default"].asUUID();
+ }
+
+ mAllowed->setExperienceIds(content["allowed"]);
+ mBlocked->setExperienceIds(content["blocked"]);
+
+ LLSD trusted = content["trusted"];
+ if(mDefaultExperience.notNull())
+ {
+ mTrusted->setStickyFunction(boost::bind(LLPanelExperiencePicker::FilterMatching, _1, mDefaultExperience));
+ trusted.append(mDefaultExperience);
+ }
+
+ mTrusted->setExperienceIds(trusted);
+
+ mAllowed->refreshExperienceCounter();
+ mBlocked->refreshExperienceCounter();
+ mTrusted->refreshExperienceCounter();
+
+}
+
+
+class LLRegionExperienceResponder : public LLHTTPClient::Responder
+{
+public:
+ typedef boost::function<void (const LLSD&)> callback_t;
+
+ callback_t mCallback;
+
+ LLRegionExperienceResponder(callback_t callback) : mCallback(callback) { }
+
+protected:
+ /*virtual*/ void httpSuccess()
+ {
+ mCallback(getContent());
+ }
+
+ /*virtual*/ void httpFailure()
+ {
+ LL_WARNS() << "experience responder failed [status:" << getStatus() << "]: " << getContent() << LL_ENDL;
+ }
+};
+
+
+// Used for both access add and remove operations, depending on the flag
+// passed in (ESTATE_EXPERIENCE_ALLOWED_ADD, ESTATE_EXPERIENCE_ALLOWED_REMOVE, etc.)
+// static
+bool LLPanelRegionExperiences::experienceCoreConfirm(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ const U32 originalFlags = (U32)notification["payload"]["operation"].asInteger();
+
+ LLViewerRegion* region = gAgent.getRegion();
+
+ LLSD::array_const_iterator end_it = notification["payload"]["allowed_ids"].endArray();
+
+ for (LLSD::array_const_iterator iter = notification["payload"]["allowed_ids"].beginArray();
+ iter != end_it;
+ iter++)
+ {
+ U32 flags = originalFlags;
+ if (iter + 1 != end_it)
+ flags |= ESTATE_ACCESS_NO_REPLY;
+
+ const LLUUID id = iter->asUUID();
+ switch(option)
+ {
+ case 0:
+ // This estate
+ sendEstateExperienceDelta(flags, id);
+ break;
+ case 1:
+ {
+ // All estates, either than I own or manage for this owner.
+ // This will be verified on simulator. JC
+ if (!region) break;
+ if (region->getOwner() == gAgent.getID()
+ || gAgent.isGodlike())
+ {
+ flags |= ESTATE_ACCESS_APPLY_TO_ALL_ESTATES;
+ sendEstateExperienceDelta(flags, id);
+ }
+ else if (region->isEstateManager())
+ {
+ flags |= ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES;
+ sendEstateExperienceDelta(flags, id);
+ }
+ break;
+ }
+ case 2:
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+
+// Send the actual "estateexperiencedelta" message
+void LLPanelRegionExperiences::sendEstateExperienceDelta(U32 flags, const LLUUID& experience_id)
+{
+ strings_t str(3, std::string());
+ gAgent.getID().toString(str[0]);
+ str[1] = llformat("%u", flags);
+ experience_id.toString(str[2]);
+
+ LLPanelRegionExperiences* panel = LLFloaterRegionInfo::getPanelExperiences();
+ if (panel)
+ {
+ panel->sendEstateOwnerMessage(gMessageSystem, "estateexperiencedelta", LLFloaterRegionInfo::getLastInvoice(), str);
+ }
+}
+
+
+void LLPanelRegionExperiences::infoCallback(LLHandle<LLPanelRegionExperiences> handle, const LLSD& content)
+{
+ if(handle.isDead())
+ return;
+
+ LLPanelRegionExperiences* floater = handle.get();
+ if (floater)
+ {
+ floater->processResponse(content);
+ }
+}
+
+
+bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region)
+{
+ BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate());
+
+ mAllowed->loading();
+ mAllowed->setReadonly(!allow_modify);
+ // remove grid-wide experiences
+ mAllowed->addFilter(boost::bind(LLPanelExperiencePicker::FilterWithProperty, _1, LLExperienceCache::PROPERTY_GRID));
+ // remove default experience
+ mAllowed->addFilter(boost::bind(LLPanelExperiencePicker::FilterMatching, _1, mDefaultExperience));
+
+ mBlocked->loading();
+ mBlocked->setReadonly(!allow_modify);
+ // only grid-wide experiences
+ mBlocked->addFilter(boost::bind(LLPanelExperiencePicker::FilterWithoutProperty, _1, LLExperienceCache::PROPERTY_GRID));
+ // but not privileged ones
+ mBlocked->addFilter(boost::bind(LLPanelExperiencePicker::FilterWithProperty, _1, LLExperienceCache::PROPERTY_PRIVILEGED));
+ // remove default experience
+ mBlocked->addFilter(boost::bind(LLPanelExperiencePicker::FilterMatching, _1, mDefaultExperience));
+
+ mTrusted->loading();
+ mTrusted->setReadonly(!allow_modify);
+
+ std::string url = region->getCapability("RegionExperiences");
+ if (!url.empty())
+ {
+ LLHTTPClient::get(url, new LLRegionExperienceResponder(boost::bind(&LLPanelRegionExperiences::infoCallback,
+ getDerivedHandle<LLPanelRegionExperiences>(), _1)));
+ }
+ return LLPanelRegionInfo::refreshFromRegion(region);
+}
+
+LLSD LLPanelRegionExperiences::addIds(LLPanelExperienceListEditor* panel)
+{
+ LLSD ids;
+ const uuid_list_t& id_list = panel->getExperienceIds();
+ for(uuid_list_t::const_iterator it = id_list.begin(); it != id_list.end(); ++it)
+ {
+ ids.append(*it);
+ }
+ return ids;
+}
+
+
+BOOL LLPanelRegionExperiences::sendUpdate()
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ std::string url = region->getCapability("RegionExperiences");
+ if (!url.empty())
+ {
+ LLSD content;
+
+ content["allowed"]=addIds(mAllowed);
+ content["blocked"]=addIds(mBlocked);
+ content["trusted"]=addIds(mTrusted);
+
+ LLHTTPClient::post(url, content, new LLRegionExperienceResponder(boost::bind(&LLPanelRegionExperiences::infoCallback,
+ getDerivedHandle<LLPanelRegionExperiences>(), _1)));
+ }
+
+ return TRUE;
+}
+
+void LLPanelRegionExperiences::itemChanged( U32 event_type, const LLUUID& id )
+{
+ std::string dialog_name;
+ switch (event_type)
+ {
+ case ESTATE_EXPERIENCE_ALLOWED_ADD:
+ dialog_name = "EstateAllowedExperienceAdd";
+ break;
+
+ case ESTATE_EXPERIENCE_ALLOWED_REMOVE:
+ dialog_name = "EstateAllowedExperienceRemove";
+ break;
+
+ case ESTATE_EXPERIENCE_TRUSTED_ADD:
+ dialog_name = "EstateTrustedExperienceAdd";
+ break;
+
+ case ESTATE_EXPERIENCE_TRUSTED_REMOVE:
+ dialog_name = "EstateTrustedExperienceRemove";
+ break;
+
+ case ESTATE_EXPERIENCE_BLOCKED_ADD:
+ dialog_name = "EstateBlockedExperienceAdd";
+ break;
+
+ case ESTATE_EXPERIENCE_BLOCKED_REMOVE:
+ dialog_name = "EstateBlockedExperienceRemove";
+ break;
+
+ default:
+ return;
+ }
+
+ LLSD payload;
+ payload["operation"] = (S32)event_type;
+ payload["dialog_name"] = dialog_name;
+ payload["allowed_ids"].append(id);
+
+ LLSD args;
+ args["ALL_ESTATES"] = all_estates_text();
+
+ LLNotification::Params params(dialog_name);
+ params.payload(payload)
+ .substitutions(args)
+ .functor.function(LLPanelRegionExperiences::experienceCoreConfirm);
+ if (LLPanelEstateInfo::isLindenEstate())
+ {
+ LLNotifications::instance().forceResponse(params, 0);
+ }
+ else
+ {
+ LLNotifications::instance().add(params);
+ }
+
+ onChangeAnything();
+}
diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h
index 5bc4273d5f..15eeb5ed7e 100755
--- a/indra/newview/llfloaterregioninfo.h
+++ b/indra/newview/llfloaterregioninfo.h
@@ -60,6 +60,9 @@ class LLPanelRegionDebugInfo;
class LLPanelRegionTerrainInfo;
class LLPanelEstateInfo;
class LLPanelEstateCovenant;
+class LLPanelExperienceListEditor;
+class LLPanelExperiences;
+class LLPanelRegionExperiences;
class LLEventTimer;
class LLEnvironmentSettings;
@@ -90,6 +93,7 @@ public:
static LLPanelEstateInfo* getPanelEstate();
static LLPanelEstateCovenant* getPanelCovenant();
static LLPanelRegionTerrainInfo* getPanelRegionTerrain();
+ static LLPanelRegionExperiences* getPanelExperiences();
// from LLPanel
virtual void refresh();
@@ -451,4 +455,34 @@ private:
LLComboBox* mDayCyclePresetCombo;
};
+class LLPanelRegionExperiences : public LLPanelRegionInfo
+{
+ LOG_CLASS(LLPanelEnvironmentInfo);
+
+public:
+ LLPanelRegionExperiences(){}
+ /*virtual*/ BOOL postBuild();
+ virtual BOOL sendUpdate();
+
+ static bool experienceCoreConfirm(const LLSD& notification, const LLSD& response);
+ static void sendEstateExperienceDelta(U32 flags, const LLUUID& agent_id);
+
+ static void infoCallback(LLHandle<LLPanelRegionExperiences> handle, const LLSD& content);
+ bool refreshFromRegion(LLViewerRegion* region);
+ void sendPurchaseRequest()const;
+ void processResponse( const LLSD& content );
+private:
+ void refreshRegionExperiences();
+
+ LLPanelExperienceListEditor* setupList(const char* control_name, U32 add_id, U32 remove_id);
+ static LLSD addIds( LLPanelExperienceListEditor* panel );
+
+ void itemChanged(U32 event_type, const LLUUID& id);
+
+ LLPanelExperienceListEditor* mTrusted;
+ LLPanelExperienceListEditor* mAllowed;
+ LLPanelExperienceListEditor* mBlocked;
+ LLUUID mDefaultExperience;
+};
+
#endif
diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp
index a3b9713e3e..7bba67627e 100755
--- a/indra/newview/llfloaterreporter.cpp
+++ b/indra/newview/llfloaterreporter.cpp
@@ -81,6 +81,7 @@
#include "llagentui.h"
#include "lltrans.h"
+#include "llexperiencecache.h"
const U32 INCLUDE_SCREENSHOT = 0x01 << 0;
@@ -227,6 +228,30 @@ void LLFloaterReporter::enableControls(BOOL enable)
getChildView("cancel_btn")->setEnabled(enable);
}
+void LLFloaterReporter::getExperienceInfo(const LLUUID& experience_id)
+{
+ mExperienceID = experience_id;
+
+ if (LLUUID::null != mExperienceID)
+ {
+ const LLSD& experience = LLExperienceCache::get(mExperienceID);
+ std::stringstream desc;
+
+ if(experience.isDefined())
+ {
+ setFromAvatarID(experience[LLExperienceCache::AGENT_ID]);
+ desc << "Experience id: " << mExperienceID;
+ }
+ else
+ {
+ desc << "Unable to retrieve details for id: "<< mExperienceID;
+ }
+
+ LLUICtrl* details = getChild<LLUICtrl>("details_edit");
+ details->setValue(desc.str());
+ }
+}
+
void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)
{
// TODO --
@@ -472,7 +497,7 @@ void LLFloaterReporter::showFromMenu(EReportType report_type)
}
// static
-void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_name)
+void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_name, const LLUUID& experience_id)
{
LLFloaterReporter* f = LLFloaterReg::showTypedInstance<LLFloaterReporter>("reporter");
@@ -485,6 +510,23 @@ void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_
{
f->setFromAvatarID(object_id);
}
+ if(experience_id.notNull())
+ {
+ f->getExperienceInfo(experience_id);
+ }
+
+ // Need to deselect on close
+ f->mDeselectOnClose = TRUE;
+
+ f->openFloater();
+}
+
+
+
+void LLFloaterReporter::showFromExperience( const LLUUID& experience_id )
+{
+ LLFloaterReporter* f = LLFloaterReg::showTypedInstance<LLFloaterReporter>("reporter");
+ f->getExperienceInfo(experience_id);
// Need to deselect on close
f->mDeselectOnClose = TRUE;
@@ -494,9 +536,9 @@ void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_
// static
-void LLFloaterReporter::showFromObject(const LLUUID& object_id)
+void LLFloaterReporter::showFromObject(const LLUUID& object_id, const LLUUID& experience_id)
{
- show(object_id);
+ show(object_id, LLStringUtil::null, experience_id);
}
// static
@@ -856,6 +898,7 @@ void LLFloaterReporter::setPosBox(const LLVector3d &pos)
getChild<LLUICtrl>("pos_field")->setValue(pos_string);
}
+
// void LLFloaterReporter::setDescription(const std::string& description, LLMeanCollisionData *mcd)
// {
// LLFloaterReporter *self = LLFloaterReg::findTypedInstance<LLFloaterReporter>("reporter");
diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h
index d54e7f6ab0..5eb5c20665 100755
--- a/indra/newview/llfloaterreporter.h
+++ b/indra/newview/llfloaterreporter.h
@@ -88,8 +88,9 @@ public:
// Enables all buttons
static void showFromMenu(EReportType report_type);
- static void showFromObject(const LLUUID& object_id);
+ static void showFromObject(const LLUUID& object_id, const LLUUID& experience_id = LLUUID::null);
static void showFromAvatar(const LLUUID& avatar_id, const std::string avatar_name);
+ static void showFromExperience(const LLUUID& experience_id);
static void onClickSend (void *userdata);
static void onClickCancel (void *userdata);
@@ -106,7 +107,7 @@ public:
void setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id);
private:
- static void show(const LLUUID& object_id, const std::string& avatar_name = LLStringUtil::null);
+ static void show(const LLUUID& object_id, const std::string& avatar_name = LLStringUtil::null, const LLUUID& experience_id = LLUUID::null);
void takeScreenshot();
void sendReportViaCaps(std::string url);
@@ -118,6 +119,7 @@ private:
void sendReportViaCaps(std::string url, std::string sshot_url, const LLSD & report);
void setPosBox(const LLVector3d &pos);
void enableControls(BOOL own_avatar);
+ void getExperienceInfo(const LLUUID& object_id);
void getObjectInfo(const LLUUID& object_id);
void callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names);
void setFromAvatarID(const LLUUID& avatar_id);
@@ -128,6 +130,7 @@ private:
LLUUID mObjectID;
LLUUID mScreenID;
LLUUID mAbuserID;
+ LLUUID mExperienceID;
// Store the real name, not the link, for upstream reporting
std::string mOwnerName;
BOOL mDeselectOnClose;
diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h
index bff4efa9ea..3e7f05b5e1 100755
--- a/indra/newview/llnotificationhandler.h
+++ b/indra/newview/llnotificationhandler.h
@@ -169,7 +169,9 @@ public:
virtual ~LLScriptHandler();
virtual void onDelete(LLNotificationPtr p);
+ virtual void onChange(LLNotificationPtr p);
virtual bool processNotification(const LLNotificationPtr& p);
+ virtual void addToastWithNotification(const LLNotificationPtr& p);
protected:
virtual void onDeleteToast(LLToast* toast);
diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp
index a3b15931c6..7acb2f9e90 100755
--- a/indra/newview/llnotificationscripthandler.cpp
+++ b/indra/newview/llnotificationscripthandler.cpp
@@ -68,6 +68,30 @@ void LLScriptHandler::initChannel()
}
//--------------------------------------------------------------------------
+void LLScriptHandler::addToastWithNotification(const LLNotificationPtr& notification)
+{
+ LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification);
+
+ LLToast::Params p;
+ p.notif_id = notification->getID();
+ p.notification = notification;
+ p.panel = notify_box;
+ p.on_delete_toast = boost::bind(&LLScriptHandler::onDeleteToast, this, _1);
+ if(gAgent.isDoNotDisturb())
+ {
+ p.force_show = notification->getName() == "SystemMessage"
+ || notification->getName() == "GodMessage"
+ || notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH;
+ }
+
+ LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get());
+ if(channel)
+ {
+ channel->addToast(p);
+ }
+}
+
+//--------------------------------------------------------------------------
bool LLScriptHandler::processNotification(const LLNotificationPtr& notification)
{
if(mChannel.isDead())
@@ -92,42 +116,33 @@ bool LLScriptHandler::processNotification(const LLNotificationPtr& notification)
}
else if (notification->canShowToast())
{
- LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification);
-
- LLToast::Params p;
- p.notif_id = notification->getID();
- p.notification = notification;
- p.panel = notify_box;
- p.on_delete_toast = boost::bind(&LLScriptHandler::onDeleteToast, this, _1);
- if(gAgent.isDoNotDisturb())
- {
- p.force_show = notification->getName() == "SystemMessage"
- || notification->getName() == "GodMessage"
- || notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH;
- }
-
- LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get());
- if(channel)
- {
- channel->addToast(p);
- }
+ addToastWithNotification(notification);
}
return false;
}
+void LLScriptHandler::onChange( LLNotificationPtr notification )
+{
+ LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get());
+ if (channel)
+ {
+ channel->removeToastByNotificationID(notification->getID());
+ addToastWithNotification(notification);
+ }
+}
void LLScriptHandler::onDelete( LLNotificationPtr notification )
- {
+{
if(notification->hasFormElements() && !notification->canShowToast())
- {
- LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID());
- }
- else
- {
- mChannel.get()->removeToastByNotificationID(notification->getID());
- }
+ {
+ LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID());
}
+ else
+ {
+ mChannel.get()->removeToastByNotificationID(notification->getID());
+ }
+}
//--------------------------------------------------------------------------
diff --git a/indra/newview/llpanelexperiencelisteditor.cpp b/indra/newview/llpanelexperiencelisteditor.cpp
new file mode 100644
index 0000000000..3561268063
--- /dev/null
+++ b/indra/newview/llpanelexperiencelisteditor.cpp
@@ -0,0 +1,254 @@
+/**
+ * @file llpanelexperiencelisteditor.cpp
+ * @brief Editor for building a list of experiences
+ *
+ * $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelexperiencelisteditor.h"
+
+#include "llbutton.h"
+#include "llexperiencecache.h"
+#include "llfloaterexperiencepicker.h"
+#include "llfloaterreg.h"
+#include "llhandle.h"
+#include "llscrolllistctrl.h"
+#include "llviewerregion.h"
+#include "llagent.h"
+#include "lltextbox.h"
+#include "lltrans.h"
+
+
+static LLPanelInjector<LLPanelExperienceListEditor> t_panel_experience_list_editor("panel_experience_list_editor");
+
+
+LLPanelExperienceListEditor::LLPanelExperienceListEditor()
+ :mItems(NULL)
+ ,mProfile(NULL)
+ ,mRemove(NULL)
+ ,mReadonly(false)
+ ,mMaxExperienceIDs(0)
+{
+}
+
+BOOL LLPanelExperienceListEditor::postBuild()
+{
+ mItems = getChild<LLScrollListCtrl>("experience_list");
+ mAdd = getChild<LLButton>("btn_add");
+ mRemove = getChild<LLButton>("btn_remove");
+ mProfile = getChild<LLButton>("btn_profile");
+
+ childSetAction("btn_add", boost::bind(&LLPanelExperienceListEditor::onAdd, this));
+ childSetAction("btn_remove", boost::bind(&LLPanelExperienceListEditor::onRemove, this));
+ childSetAction("btn_profile", boost::bind(&LLPanelExperienceListEditor::onProfile, this));
+
+ mItems->setCommitCallback(boost::bind(&LLPanelExperienceListEditor::checkButtonsEnabled, this));
+
+ checkButtonsEnabled();
+ return TRUE;
+}
+
+const uuid_list_t& LLPanelExperienceListEditor::getExperienceIds() const
+{
+ return mExperienceIds;
+}
+
+void LLPanelExperienceListEditor::addExperienceIds( const uuid_vec_t& experience_ids )
+{
+ // the commented out code in this function is handled by the callback and no longer necessary!
+
+ //mExperienceIds.insert(experience_ids.begin(), experience_ids.end());
+ //onItems();
+ if(!mAddedCallback.empty())
+ {
+ for(uuid_vec_t::const_iterator it = experience_ids.begin(); it != experience_ids.end(); ++it)
+ {
+ mAddedCallback(*it);
+ }
+ }
+}
+
+
+void LLPanelExperienceListEditor::setExperienceIds( const LLSD& experience_ids )
+{
+ mExperienceIds.clear();
+ mExperienceIds.insert(experience_ids.beginArray(), experience_ids.endArray());
+ onItems();
+}
+
+void LLPanelExperienceListEditor::addExperience( const LLUUID& id )
+{
+ mExperienceIds.insert(id);
+ onItems();
+}
+void LLPanelExperienceListEditor::onAdd()
+{
+ if(!mPicker.isDead())
+ {
+ mPicker.markDead();
+ }
+
+ mKey.generateNewID();
+
+ LLFloaterExperiencePicker* picker=LLFloaterExperiencePicker::show(boost::bind(&LLPanelExperienceListEditor::addExperienceIds, this, _1), mKey, FALSE, TRUE, mFilters, mAdd);
+ mPicker = picker->getDerivedHandle<LLFloaterExperiencePicker>();
+}
+
+
+void LLPanelExperienceListEditor::onRemove()
+{
+ // the commented out code in this function is handled by the callback and no longer necessary!
+
+ std::vector<LLScrollListItem*> items= mItems->getAllSelected();
+ std::vector<LLScrollListItem*>::iterator it = items.begin();
+ for(/**/; it != items.end(); ++it)
+ {
+ if((*it) != NULL)
+ {
+ //mExperienceIds.erase((*it)->getValue());
+ mRemovedCallback((*it)->getValue());
+ }
+ }
+ //onItems();
+}
+
+void LLPanelExperienceListEditor::onProfile()
+{
+ LLScrollListItem* item = mItems->getFirstSelected();
+ if(item)
+ {
+ LLFloaterReg::showInstance("experience_profile", item->getUUID(), true);
+ }
+}
+
+void LLPanelExperienceListEditor::checkButtonsEnabled()
+{
+ mAdd->setEnabled(!mReadonly);
+ int selected = mItems->getNumSelected();
+
+ bool remove_enabled = !mReadonly && selected>0;
+ if(remove_enabled && mSticky)
+ {
+ std::vector<LLScrollListItem*> items= mItems->getAllSelected();
+ std::vector<LLScrollListItem*>::iterator it = items.begin();
+ for(/**/; it != items.end() && remove_enabled; ++it)
+ {
+ if((*it) != NULL)
+ {
+ remove_enabled = !mSticky((*it)->getValue());
+ }
+ }
+
+
+ }
+ mRemove->setEnabled(remove_enabled);
+ mProfile->setEnabled(selected==1);
+}
+
+void LLPanelExperienceListEditor::onItems()
+{
+ mItems->deleteAllItems();
+
+ LLSD item;
+ uuid_list_t::iterator it = mExperienceIds.begin();
+ for(/**/; it != mExperienceIds.end(); ++it)
+ {
+ const LLUUID& experience = *it;
+ item["id"]=experience;
+ LLSD& columns = item["columns"];
+ columns[0]["column"] = "experience_name";
+ columns[0]["value"] = getString("loading");
+ mItems->addElement(item);
+
+ LLExperienceCache::get(experience, boost::bind(&LLPanelExperienceListEditor::experienceDetailsCallback,
+ getDerivedHandle<LLPanelExperienceListEditor>(), _1));
+ }
+
+
+ if(mItems->getItemCount() == 0)
+ {
+ mItems->setCommentText(getString("no_results"));
+ }
+
+
+ checkButtonsEnabled();
+}
+
+void LLPanelExperienceListEditor::experienceDetailsCallback( LLHandle<LLPanelExperienceListEditor> panel, const LLSD& experience )
+{
+ if(!panel.isDead())
+ {
+ panel.get()->onExperienceDetails(experience);
+ }
+}
+
+void LLPanelExperienceListEditor::onExperienceDetails( const LLSD& experience )
+{
+ LLScrollListItem* item = mItems->getItem(experience[LLExperienceCache::EXPERIENCE_ID]);
+ if(!item)
+ return;
+
+ item->getColumn(0)->setValue(experience[LLExperienceCache::NAME]);
+}
+
+LLPanelExperienceListEditor::~LLPanelExperienceListEditor()
+{
+ if(!mPicker.isDead())
+ {
+ mPicker.get()->closeFloater();
+ }
+}
+
+void LLPanelExperienceListEditor::loading()
+{
+ mItems->clear();
+ mItems->setCommentText( getString("loading"));
+}
+
+void LLPanelExperienceListEditor::setReadonly( bool val )
+{
+ mReadonly = val;
+ checkButtonsEnabled();
+}
+
+void LLPanelExperienceListEditor::refreshExperienceCounter()
+{
+ if(mMaxExperienceIDs > 0)
+ {
+ LLStringUtil::format_map_t args;
+ args["[EXPERIENCES]"] = llformat("%d", mItems->getItemCount());
+ args["[MAXEXPERIENCES]"] = llformat("%d", mMaxExperienceIDs);
+ getChild<LLTextBox>("text_count")->setText(LLTrans::getString("ExperiencesCounter", args));
+ }
+}
+
+boost::signals2::connection LLPanelExperienceListEditor::setAddedCallback( list_changed_signal_t::slot_type cb )
+{
+ return mAddedCallback.connect(cb);
+}
+
+boost::signals2::connection LLPanelExperienceListEditor::setRemovedCallback( list_changed_signal_t::slot_type cb )
+{
+ return mRemovedCallback.connect(cb);
+}
diff --git a/indra/newview/llpanelexperiencelisteditor.h b/indra/newview/llpanelexperiencelisteditor.h
new file mode 100644
index 0000000000..f69f0509be
--- /dev/null
+++ b/indra/newview/llpanelexperiencelisteditor.h
@@ -0,0 +1,100 @@
+/**
+* @file llpanelexperiencelisteditor.cpp
+* @brief Editor for building a list of experiences
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPANELEXPERIENCELISTEDITOR_H
+#define LL_LLPANELEXPERIENCELISTEDITOR_H
+
+#include "llpanel.h"
+#include "lluuid.h"
+#include <set>
+
+class LLScrollListCtrl;
+class LLButton;
+class LLFloaterExperiencePicker;
+
+class LLPanelExperienceListEditor : public LLPanel
+{
+public:
+
+ typedef boost::signals2::signal<void (const LLUUID&) > list_changed_signal_t;
+ // filter function for experiences, return true if the experience should be hidden.
+ typedef boost::function<bool (const LLSD&)> experience_function;
+ typedef std::vector<experience_function> filter_list;
+ typedef LLHandle<LLFloaterExperiencePicker> PickerHandle;
+ LLPanelExperienceListEditor();
+ ~LLPanelExperienceListEditor();
+ BOOL postBuild();
+
+ void loading();
+
+ const uuid_list_t& getExperienceIds()const;
+ void setExperienceIds(const LLSD& experience_ids);
+ void addExperienceIds(const uuid_vec_t& experience_ids);
+
+ void addExperience(const LLUUID& id);
+
+ boost::signals2::connection setAddedCallback(list_changed_signal_t::slot_type cb );
+ boost::signals2::connection setRemovedCallback(list_changed_signal_t::slot_type cb );
+
+ bool getReadonly() const { return mReadonly; }
+ void setReadonly(bool val);
+
+ void refreshExperienceCounter();
+
+ void addFilter(experience_function func){mFilters.push_back(func);}
+ void setStickyFunction(experience_function func){mSticky = func;}
+ U32 getMaxExperienceIDs() const { return mMaxExperienceIDs; }
+ void setMaxExperienceIDs(U32 val) { mMaxExperienceIDs = val; }
+private:
+
+ void onItems();
+ void onRemove();
+ void onAdd();
+ void onProfile();
+
+ void checkButtonsEnabled();
+ static void experienceDetailsCallback( LLHandle<LLPanelExperienceListEditor> panel, const LLSD& experience );
+ void onExperienceDetails( const LLSD& experience );
+ void processResponse( const LLSD& content );
+ uuid_list_t mExperienceIds;
+
+
+ LLScrollListCtrl* mItems;
+ filter_list mFilters;
+ LLButton* mAdd;
+ LLButton* mRemove;
+ LLButton* mProfile;
+ PickerHandle mPicker;
+ list_changed_signal_t mAddedCallback;
+ list_changed_signal_t mRemovedCallback;
+ LLUUID mKey;
+ bool mReadonly;
+ experience_function mSticky;
+ U32 mMaxExperienceIDs;
+
+};
+
+#endif //LL_LLPANELEXPERIENCELISTEDITOR_H
diff --git a/indra/newview/llpanelexperiencelog.cpp b/indra/newview/llpanelexperiencelog.cpp
new file mode 100644
index 0000000000..df03ef7526
--- /dev/null
+++ b/indra/newview/llpanelexperiencelog.cpp
@@ -0,0 +1,264 @@
+/**
+ * @file llpanelexperiencelog.cpp
+ * @brief llpanelexperiencelog
+ *
+ * $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+#include "llpanelexperiencelog.h"
+
+#include "llexperiencelog.h"
+#include "llexperiencecache.h"
+#include "llbutton.h"
+#include "llscrolllistctrl.h"
+#include "llcombobox.h"
+#include "llspinctrl.h"
+#include "llcheckboxctrl.h"
+#include "llfloaterreg.h"
+#include "llfloaterreporter.h"
+#include "llinventoryfunctions.h"
+
+
+#define BTN_PROFILE_XP "btn_profile_xp"
+#define BTN_REPORT_XP "btn_report_xp"
+
+static LLPanelInjector<LLPanelExperienceLog> register_experiences_panel("experience_log");
+
+
+LLPanelExperienceLog::LLPanelExperienceLog( )
+ : mEventList(NULL)
+ , mPageSize(25)
+ , mCurrentPage(0)
+{
+ buildFromFile("panel_experience_log.xml");
+}
+
+
+BOOL LLPanelExperienceLog::postBuild( void )
+{
+ LLExperienceLog* log = LLExperienceLog::getInstance();
+ mEventList = getChild<LLScrollListCtrl>("experience_log_list");
+ mEventList->setCommitCallback(boost::bind(&LLPanelExperienceLog::onSelectionChanged, this));
+ mEventList->setDoubleClickCallback( boost::bind(&LLPanelExperienceLog::onProfileExperience, this));
+
+ getChild<LLButton>("btn_clear")->setCommitCallback(boost::bind(&LLExperienceLog::clear, log));
+ getChild<LLButton>("btn_clear")->setCommitCallback(boost::bind(&LLPanelExperienceLog::refresh, this));
+
+ getChild<LLButton>(BTN_PROFILE_XP)->setCommitCallback(boost::bind(&LLPanelExperienceLog::onProfileExperience, this));
+ getChild<LLButton>(BTN_REPORT_XP )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onReportExperience, this));
+ getChild<LLButton>("btn_notify" )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onNotify, this));
+ getChild<LLButton>("btn_next" )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onNext, this));
+ getChild<LLButton>("btn_prev" )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onPrev, this));
+
+ LLCheckBoxCtrl* check = getChild<LLCheckBoxCtrl>("notify_all");
+ check->set(log->getNotifyNewEvent());
+ check->setCommitCallback(boost::bind(&LLPanelExperienceLog::notifyChanged, this));
+
+
+ LLSpinCtrl* spin = getChild<LLSpinCtrl>("logsizespinner");
+ spin->set(log->getMaxDays());
+ spin->setCommitCallback(boost::bind(&LLPanelExperienceLog::logSizeChanged, this));
+
+ mPageSize = log->getPageSize();
+ refresh();
+ mNewEvent = LLExperienceLog::instance().addUpdateSignal(boost::bind(&LLPanelExperienceLog::refresh, this));
+ return TRUE;
+}
+
+LLPanelExperienceLog* LLPanelExperienceLog::create()
+{
+ return new LLPanelExperienceLog();
+}
+
+void LLPanelExperienceLog::refresh()
+{
+ S32 selected = mEventList->getFirstSelectedIndex();
+ mEventList->deleteAllItems();
+ const LLSD events = LLExperienceLog::instance().getEvents();
+
+ if(events.size() == 0)
+ {
+ mEventList->setCommentText(getString("no_events"));
+ return;
+ }
+
+ setAllChildrenEnabled(FALSE);
+
+ LLSD item;
+ bool waiting = false;
+ LLUUID waiting_id;
+
+ int itemsToSkip = mPageSize*mCurrentPage;
+ int items = 0;
+ bool moreItems = false;
+
+ if (!events.emptyMap())
+ {
+ LLSD::map_const_iterator day = events.endMap();
+ do
+ {
+ --day;
+ const LLSD& dayArray = day->second;
+ int size = dayArray.size();
+ if(itemsToSkip > size)
+ {
+ itemsToSkip -= size;
+ continue;
+ }
+ if(items >= mPageSize && size > 0)
+ {
+ moreItems = true;
+ break;
+ }
+ for(int i = dayArray.size() - itemsToSkip - 1; i >= 0; i--)
+ {
+ if(items >= mPageSize)
+ {
+ moreItems = true;
+ break;
+ }
+ const LLSD event = dayArray[i];
+ LLUUID id = event[LLExperienceCache::EXPERIENCE_ID].asUUID();
+ const LLSD& experience = LLExperienceCache::get(id);
+ if(experience.isUndefined()){
+ waiting = true;
+ waiting_id = id;
+ }
+ if(!waiting)
+ {
+ item["id"] = event;
+
+ LLSD& columns = item["columns"];
+ columns[0]["column"] = "time";
+ columns[0]["value"] = day->first+event["Time"].asString();
+ columns[1]["column"] = "event";
+ columns[1]["value"] = LLExperienceLog::getPermissionString(event, "ExperiencePermissionShort");
+ columns[2]["column"] = "experience_name";
+ columns[2]["value"] = experience[LLExperienceCache::NAME].asString();
+ columns[3]["column"] = "object_name";
+ columns[3]["value"] = event["ObjectName"].asString();
+ mEventList->addElement(item);
+ }
+ ++items;
+ }
+ } while (day != events.beginMap());
+ }
+ if(waiting)
+ {
+ mEventList->deleteAllItems();
+ mEventList->setCommentText(getString("loading"));
+ LLExperienceCache::get(waiting_id, boost::bind(&LLPanelExperienceLog::refresh, this));
+ }
+ else
+ {
+ setAllChildrenEnabled(TRUE);
+
+ mEventList->setEnabled(TRUE);
+ getChild<LLButton>("btn_next")->setEnabled(moreItems);
+ getChild<LLButton>("btn_prev")->setEnabled(mCurrentPage>0);
+ getChild<LLButton>("btn_clear")->setEnabled(mEventList->getItemCount()>0);
+ if(selected<0)
+ {
+ selected = 0;
+ }
+ mEventList->selectNthItem(selected);
+ onSelectionChanged();
+ }
+}
+
+void LLPanelExperienceLog::onProfileExperience()
+{
+ LLSD event = getSelectedEvent();
+ if(event.isDefined())
+ {
+ LLFloaterReg::showInstance("experience_profile", event[LLExperienceCache::EXPERIENCE_ID].asUUID(), true);
+ }
+}
+
+void LLPanelExperienceLog::onReportExperience()
+{
+ LLSD event = getSelectedEvent();
+ if(event.isDefined())
+ {
+ LLFloaterReporter::showFromExperience(event[LLExperienceCache::EXPERIENCE_ID].asUUID());
+ }
+}
+
+void LLPanelExperienceLog::onNotify()
+{
+ LLSD event = getSelectedEvent();
+ if(event.isDefined())
+ {
+ LLExperienceLog::instance().notify(event);
+ }
+}
+
+void LLPanelExperienceLog::onNext()
+{
+ mCurrentPage++;
+ refresh();
+}
+
+void LLPanelExperienceLog::onPrev()
+{
+ if(mCurrentPage>0)
+ {
+ mCurrentPage--;
+ refresh();
+ }
+}
+
+void LLPanelExperienceLog::notifyChanged()
+{
+ LLExperienceLog::instance().setNotifyNewEvent(getChild<LLCheckBoxCtrl>("notify_all")->get());
+}
+
+void LLPanelExperienceLog::logSizeChanged()
+{
+ int value = (int)(getChild<LLSpinCtrl>("logsizespinner")->get());
+ bool dirty = value > 0 && value < LLExperienceLog::instance().getMaxDays();
+ LLExperienceLog::instance().setMaxDays(value);
+ if(dirty)
+ {
+ refresh();
+ }
+}
+
+void LLPanelExperienceLog::onSelectionChanged()
+{
+ bool enabled = (1 == mEventList->getNumSelected());
+ getChild<LLButton>(BTN_REPORT_XP)->setEnabled(enabled);
+ getChild<LLButton>(BTN_PROFILE_XP)->setEnabled(enabled);
+ getChild<LLButton>("btn_notify")->setEnabled(enabled);
+}
+
+LLSD LLPanelExperienceLog::getSelectedEvent()
+{
+ LLScrollListItem* item = mEventList->getFirstSelected();
+ if(item)
+ {
+ return item->getValue();
+ }
+ return LLSD();
+}
diff --git a/indra/newview/llpanelexperiencelog.h b/indra/newview/llpanelexperiencelog.h
new file mode 100644
index 0000000000..4135d9cac9
--- /dev/null
+++ b/indra/newview/llpanelexperiencelog.h
@@ -0,0 +1,64 @@
+/**
+ * @file llpanelexperiencelog.h
+ * @brief llpanelexperiencelog and related class definitions
+ *
+ * $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#ifndef LL_LLPANELEXPERIENCELOG_H
+#define LL_LLPANELEXPERIENCELOG_H
+
+#include "llpanel.h"
+class LLScrollListCtrl;
+
+class LLPanelExperienceLog
+ : public LLPanel
+{
+public:
+
+ LLPanelExperienceLog();
+
+ static LLPanelExperienceLog* create();
+
+ /*virtual*/ BOOL postBuild(void);
+
+ void refresh();
+protected:
+ void logSizeChanged();
+ void notifyChanged();
+ void onNext();
+ void onNotify();
+ void onPrev();
+ void onProfileExperience();
+ void onReportExperience();
+ void onSelectionChanged();
+
+ LLSD getSelectedEvent();
+private:
+ LLScrollListCtrl* mEventList;
+ U32 mPageSize;
+ U32 mCurrentPage;
+ boost::signals2::scoped_connection mNewEvent;
+};
+
+#endif // LL_LLPANELEXPERIENCELOG_H
diff --git a/indra/newview/llpanelexperiencepicker.cpp b/indra/newview/llpanelexperiencepicker.cpp
new file mode 100644
index 0000000000..0a572a8a5c
--- /dev/null
+++ b/indra/newview/llpanelexperiencepicker.cpp
@@ -0,0 +1,431 @@
+/**
+* @file llpanelexperiencepicker.cpp
+* @brief Implementation of llpanelexperiencepicker
+* @author dolphin@lindenlab.com
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelexperiencepicker.h"
+
+
+#include "lllineeditor.h"
+#include "llfloaterreg.h"
+#include "llscrolllistctrl.h"
+#include "llviewerregion.h"
+#include "llagent.h"
+#include "llexperiencecache.h"
+#include "llslurl.h"
+#include "llavatarnamecache.h"
+#include "llcombobox.h"
+#include "llviewercontrol.h"
+#include "llfloater.h"
+
+#define BTN_FIND "find"
+#define BTN_OK "ok_btn"
+#define BTN_CANCEL "cancel_btn"
+#define BTN_PROFILE "profile_btn"
+#define BTN_LEFT "left_btn"
+#define BTN_RIGHT "right_btn"
+#define TEXT_EDIT "edit"
+#define TEXT_MATURITY "maturity"
+#define LIST_RESULTS "search_results"
+#define PANEL_SEARCH "search_panel"
+
+const static std::string columnSpace = " ";
+
+static LLPanelInjector<LLPanelExperiencePicker> t_panel_status("llpanelexperiencepicker");
+
+class LLExperienceSearchResponder : public LLHTTPClient::Responder
+{
+public:
+ LLUUID mQueryID;
+ LLHandle<LLPanelExperiencePicker> mParent;
+
+ LLExperienceSearchResponder(const LLUUID& id, const LLHandle<LLPanelExperiencePicker>& parent) : mQueryID(id), mParent(parent) { }
+
+protected:
+ /*virtual*/ void httpSuccess()
+ {
+ if(mParent.isDead())
+ return;
+
+ LLPanelExperiencePicker* panel =mParent.get();
+ if (panel)
+ {
+ panel->processResponse(mQueryID, getContent());
+ }
+ }
+
+ /*virtual*/ void httpFailure()
+ {
+ if(mParent.isDead())
+ return;
+
+ LLPanelExperiencePicker* panel =mParent.get();
+ if (panel)
+ {
+ panel->processResponse(mQueryID, LLSD());
+ }
+ LL_WARNS() << "experience picker failed [status:" << getStatus() << "]: " << getContent() << LL_ENDL;
+ }
+};
+
+LLPanelExperiencePicker::LLPanelExperiencePicker()
+ :LLPanel()
+{
+ buildFromFile("panel_experience_search.xml");
+ setDefaultFilters();
+}
+
+LLPanelExperiencePicker::~LLPanelExperiencePicker()
+{
+}
+
+BOOL LLPanelExperiencePicker::postBuild()
+{
+ getChild<LLLineEditor>(TEXT_EDIT)->setKeystrokeCallback( boost::bind(&LLPanelExperiencePicker::editKeystroke, this, _1, _2),NULL);
+
+ childSetAction(BTN_FIND, boost::bind(&LLPanelExperiencePicker::onBtnFind, this));
+ getChildView(BTN_FIND)->setEnabled(FALSE);
+
+ LLScrollListCtrl* searchresults = getChild<LLScrollListCtrl>(LIST_RESULTS);
+ searchresults->setDoubleClickCallback( boost::bind(&LLPanelExperiencePicker::onBtnSelect, this));
+ searchresults->setCommitCallback(boost::bind(&LLPanelExperiencePicker::onList, this));
+ getChildView(LIST_RESULTS)->setEnabled(FALSE);
+ getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("no_results"));
+
+ childSetAction(BTN_OK, boost::bind(&LLPanelExperiencePicker::onBtnSelect, this));
+ getChildView(BTN_OK)->setEnabled(FALSE);
+ childSetAction(BTN_CANCEL, boost::bind(&LLPanelExperiencePicker::onBtnClose, this));
+ childSetAction(BTN_PROFILE, boost::bind(&LLPanelExperiencePicker::onBtnProfile, this));
+ getChildView(BTN_PROFILE)->setEnabled(FALSE);
+
+ getChild<LLComboBox>(TEXT_MATURITY)->setCurrentByIndex(2);
+ getChild<LLComboBox>(TEXT_MATURITY)->setCommitCallback(boost::bind(&LLPanelExperiencePicker::onMaturity, this));
+ getChild<LLUICtrl>(TEXT_EDIT)->setFocus(TRUE);
+
+ childSetAction(BTN_LEFT, boost::bind(&LLPanelExperiencePicker::onPage, this, -1));
+ childSetAction(BTN_RIGHT, boost::bind(&LLPanelExperiencePicker::onPage, this, 1));
+
+ LLPanel* search_panel = getChild<LLPanel>(PANEL_SEARCH);
+ if (search_panel)
+ {
+ // Start searching when Return is pressed in the line editor.
+ search_panel->setDefaultBtn(BTN_FIND);
+ }
+ return TRUE;
+}
+
+void LLPanelExperiencePicker::editKeystroke( class LLLineEditor* caller, void* user_data )
+{
+ getChildView(BTN_FIND)->setEnabled(caller->getText().size() > 0);
+}
+
+void LLPanelExperiencePicker::onBtnFind()
+{
+ mCurrentPage=1;
+ find();
+}
+
+void LLPanelExperiencePicker::onList()
+{
+ bool enabled = isSelectButtonEnabled();
+ getChildView(BTN_OK)->setEnabled(enabled);
+
+ enabled = enabled && getChild<LLScrollListCtrl>(LIST_RESULTS)->getNumSelected() == 1;
+ getChildView(BTN_PROFILE)->setEnabled(enabled);
+}
+
+void LLPanelExperiencePicker::find()
+{
+ std::string text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString();
+ mQueryID.generate();
+ std::ostringstream url;
+
+ LLViewerRegion* region = gAgent.getRegion();
+ std::string cap = region->getCapability("FindExperienceByName");
+ if (!cap.empty())
+ {
+ url << cap << "?page=" << mCurrentPage << "&page_size=30&query=" << LLURI::escape(text);
+ LLHTTPClient::get(url.str(), new LLExperienceSearchResponder(mQueryID, getDerivedHandle<LLPanelExperiencePicker>()));
+
+ }
+ getChild<LLScrollListCtrl>(LIST_RESULTS)->deleteAllItems();
+ getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("searching"));
+
+ getChildView(BTN_OK)->setEnabled(FALSE);
+ getChildView(BTN_PROFILE)->setEnabled(FALSE);
+
+ getChildView(BTN_RIGHT)->setEnabled(FALSE);
+ getChildView(BTN_LEFT)->setEnabled(FALSE);
+}
+
+
+bool LLPanelExperiencePicker::isSelectButtonEnabled()
+{
+ LLScrollListCtrl* list=getChild<LLScrollListCtrl>(LIST_RESULTS);
+ return list->getFirstSelectedIndex() >=0;
+}
+
+void LLPanelExperiencePicker::getSelectedExperienceIds( const LLScrollListCtrl* results, uuid_vec_t &experience_ids )
+{
+ std::vector<LLScrollListItem*> items = results->getAllSelected();
+ for(std::vector<LLScrollListItem*>::iterator it = items.begin(); it != items.end(); ++it)
+ {
+ LLScrollListItem* item = *it;
+ if (item->getUUID().notNull())
+ {
+ experience_ids.push_back(item->getUUID());
+ }
+ }
+}
+
+void LLPanelExperiencePicker::setAllowMultiple( bool allow_multiple )
+{
+ getChild<LLScrollListCtrl>(LIST_RESULTS)->setAllowMultipleSelection(allow_multiple);
+}
+
+
+void name_callback(const LLHandle<LLPanelExperiencePicker>& floater, const LLUUID& experience_id, const LLUUID& agent_id, const LLAvatarName& av_name)
+{
+ if(floater.isDead())
+ return;
+ LLPanelExperiencePicker* picker = floater.get();
+ LLScrollListCtrl* search_results = picker->getChild<LLScrollListCtrl>(LIST_RESULTS);
+
+ LLScrollListItem* item = search_results->getItem(experience_id);
+ if(!item)
+ return;
+
+ item->getColumn(2)->setValue(columnSpace+av_name.getDisplayName());
+
+}
+
+void LLPanelExperiencePicker::processResponse( const LLUUID& query_id, const LLSD& content )
+{
+ if(query_id != mQueryID)
+ {
+ return;
+ }
+
+ mResponse = content;
+
+ const LLSD& experiences=mResponse["experience_keys"];
+ LLSD::array_const_iterator it = experiences.beginArray();
+ for ( ; it != experiences.endArray(); ++it)
+ {
+ LLExperienceCache::insert(*it);
+ }
+
+ getChildView(BTN_RIGHT)->setEnabled(content.has("next_page_url"));
+ getChildView(BTN_LEFT)->setEnabled(content.has("previous_page_url"));
+
+ filterContent();
+
+}
+
+void LLPanelExperiencePicker::onBtnSelect()
+{
+ if(!isSelectButtonEnabled())
+ {
+ return;
+ }
+
+ if(mSelectionCallback)
+ {
+ const LLScrollListCtrl* results = getChild<LLScrollListCtrl>(LIST_RESULTS);
+ uuid_vec_t experience_ids;
+
+ getSelectedExperienceIds(results, experience_ids);
+ mSelectionCallback(experience_ids);
+ getChild<LLScrollListCtrl>(LIST_RESULTS)->deselectAllItems(TRUE);
+ if(mCloseOnSelect)
+ {
+ mCloseOnSelect = FALSE;
+ onBtnClose();
+ }
+ }
+ else
+ {
+ onBtnProfile();
+ }
+}
+
+void LLPanelExperiencePicker::onBtnClose()
+{
+ LLFloater* floater = getParentByType<LLFloater>();
+ if (floater)
+ {
+ floater->closeFloater();
+ }
+}
+
+void LLPanelExperiencePicker::onBtnProfile()
+{
+ LLScrollListItem* item = getChild<LLScrollListCtrl>(LIST_RESULTS)->getFirstSelected();
+ if(item)
+ {
+ LLFloaterReg::showInstance("experience_profile", item->getUUID(), true);
+ }
+}
+
+std::string LLPanelExperiencePicker::getMaturityString(int maturity)
+{
+ if(maturity <= SIM_ACCESS_PG)
+ {
+ return getString("maturity_icon_general");
+ }
+ else if(maturity <= SIM_ACCESS_MATURE)
+ {
+ return getString("maturity_icon_moderate");
+ }
+ return getString("maturity_icon_adult");
+}
+
+void LLPanelExperiencePicker::filterContent()
+{
+ LLScrollListCtrl* search_results = getChild<LLScrollListCtrl>(LIST_RESULTS);
+
+ const LLSD& experiences=mResponse["experience_keys"];
+
+ search_results->deleteAllItems();
+
+ LLSD item;
+ LLSD::array_const_iterator it = experiences.beginArray();
+ for ( ; it != experiences.endArray(); ++it)
+ {
+ const LLSD& experience = *it;
+
+ if(isExperienceHidden(experience))
+ continue;
+
+ item["id"]=experience[LLExperienceCache::EXPERIENCE_ID];
+ LLSD& columns = item["columns"];
+ columns[0]["column"] = "maturity";
+ columns[0]["value"] = getMaturityString(experience[LLExperienceCache::MATURITY].asInteger());
+ columns[0]["type"]="icon";
+ columns[0]["halign"]="right";
+ columns[1]["column"] = "experience_name";
+ columns[1]["value"] = columnSpace+experience[LLExperienceCache::NAME].asString();
+ columns[2]["column"] = "owner";
+ columns[2]["value"] = columnSpace+getString("loading");
+ search_results->addElement(item);
+ LLAvatarNameCache::get(experience[LLExperienceCache::AGENT_ID], boost::bind(name_callback, getDerivedHandle<LLPanelExperiencePicker>(), experience[LLExperienceCache::EXPERIENCE_ID], _1, _2));
+ }
+
+ if (search_results->isEmpty())
+ {
+ LLStringUtil::format_map_t map;
+ std::string search_text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString();
+ map["[TEXT]"] = search_text;
+ if (search_text.empty())
+ {
+ getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("no_results"));
+ }
+ else
+ {
+ getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("not_found", map));
+ }
+ search_results->setEnabled(false);
+ getChildView(BTN_OK)->setEnabled(false);
+ getChildView(BTN_PROFILE)->setEnabled(false);
+ }
+ else
+ {
+ getChildView(BTN_OK)->setEnabled(true);
+ search_results->setEnabled(true);
+ search_results->sortByColumnIndex(1, TRUE);
+ std::string text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString();
+ if (!search_results->selectItemByLabel(text, TRUE, 1))
+ {
+ search_results->selectFirstItem();
+ }
+ onList();
+ search_results->setFocus(TRUE);
+ }
+}
+
+void LLPanelExperiencePicker::onMaturity()
+{
+ if(mResponse.has("experience_keys") && mResponse["experience_keys"].beginArray() != mResponse["experience_keys"].endArray())
+ {
+ filterContent();
+ }
+}
+
+bool LLPanelExperiencePicker::isExperienceHidden( const LLSD& experience) const
+{
+ bool hide=false;
+ filter_list::const_iterator it = mFilters.begin();
+ for(/**/;it != mFilters.end(); ++it)
+ {
+ if((*it)(experience)){
+ return true;
+ }
+ }
+
+ return hide;
+}
+
+bool LLPanelExperiencePicker::FilterOverRating( const LLSD& experience )
+{
+ int maturity = getChild<LLComboBox>(TEXT_MATURITY)->getSelectedValue().asInteger();
+ return experience[LLExperienceCache::MATURITY].asInteger() > maturity;
+}
+
+bool LLPanelExperiencePicker::FilterWithProperty( const LLSD& experience, S32 prop)
+{
+ return (experience[LLExperienceCache::PROPERTIES].asInteger() & prop) != 0;
+}
+
+bool LLPanelExperiencePicker::FilterWithoutProperty( const LLSD& experience, S32 prop )
+{
+ return (experience[LLExperienceCache::PROPERTIES].asInteger() & prop) == 0;
+}
+
+void LLPanelExperiencePicker::setDefaultFilters()
+{
+ mFilters.clear();
+ addFilter(boost::bind(&LLPanelExperiencePicker::FilterOverRating, this, _1));
+}
+
+bool LLPanelExperiencePicker::FilterMatching( const LLSD& experience, const LLUUID& id )
+{
+ if(experience.isUUID())
+ {
+ return experience.asUUID() == id;
+ }
+ return experience[LLExperienceCache::EXPERIENCE_ID].asUUID() == id;
+}
+
+void LLPanelExperiencePicker::onPage( S32 direction )
+{
+ mCurrentPage += direction;
+ if(mCurrentPage < 1)
+ {
+ mCurrentPage = 1;
+ }
+ find();
+}
diff --git a/indra/newview/llpanelexperiencepicker.h b/indra/newview/llpanelexperiencepicker.h
new file mode 100644
index 0000000000..b2ba7f26ee
--- /dev/null
+++ b/indra/newview/llpanelexperiencepicker.h
@@ -0,0 +1,94 @@
+/**
+* @file llpanelexperiencepicker.h
+* @brief Header file for llpanelexperiencepicker
+* @author dolphin@lindenlab.com
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LL_LLPANELEXPERIENCEPICKER_H
+#define LL_LLPANELEXPERIENCEPICKER_H
+
+#include "llpanel.h"
+
+class LLScrollListCtrl;
+class LLLineEditor;
+
+
+class LLPanelExperiencePicker : public LLPanel
+{
+public:
+ friend class LLExperienceSearchResponder;
+ friend class LLFloaterExperiencePicker;
+
+ typedef boost::function<void (const uuid_vec_t&)> select_callback_t;
+ // filter function for experiences, return true if the experience should be hidden.
+ typedef boost::function<bool (const LLSD&)> filter_function;
+ typedef std::vector<filter_function> filter_list;
+
+ LLPanelExperiencePicker();
+ virtual ~LLPanelExperiencePicker();
+
+ BOOL postBuild();
+
+ void addFilter(filter_function func){mFilters.push_back(func);}
+ template <class IT>
+ void addFilters(IT begin, IT end){mFilters.insert(mFilters.end(), begin, end);}
+ void setDefaultFilters();
+
+ static bool FilterWithProperty(const LLSD& experience, S32 prop);
+ static bool FilterWithoutProperty(const LLSD& experience, S32 prop);
+ static bool FilterMatching(const LLSD& experience, const LLUUID& id);
+ bool FilterOverRating(const LLSD& experience);
+
+private:
+ void editKeystroke(LLLineEditor* caller, void* user_data);
+
+ void onBtnFind();
+ void onBtnSelect();
+ void onBtnClose();
+ void onBtnProfile();
+ void onList();
+ void onMaturity();
+ void onPage(S32 direction);
+
+ void getSelectedExperienceIds( const LLScrollListCtrl* results, uuid_vec_t &experience_ids );
+ void setAllowMultiple(bool allow_multiple);
+
+
+ void find();
+ bool isSelectButtonEnabled();
+ void processResponse( const LLUUID& query_id, const LLSD& content );
+
+ void filterContent();
+ bool isExperienceHidden(const LLSD& experience) const ;
+ std::string getMaturityString(int maturity);
+
+
+ select_callback_t mSelectionCallback;
+ filter_list mFilters;
+ LLUUID mQueryID;
+ LLSD mResponse;
+ bool mCloseOnSelect;
+ S32 mCurrentPage;
+};
+
+#endif // LL_LLPANELEXPERIENCEPICKER_H
diff --git a/indra/newview/llpanelexperiences.cpp b/indra/newview/llpanelexperiences.cpp
new file mode 100644
index 0000000000..3ee4b5e968
--- /dev/null
+++ b/indra/newview/llpanelexperiences.cpp
@@ -0,0 +1,218 @@
+/**
+ * @file llpanelexperiences.cpp
+ * @brief LLPanelExperiences class implementation
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+
+
+#include "llpanelprofile.h"
+#include "lluictrlfactory.h"
+#include "llexperiencecache.h"
+#include "llagent.h"
+
+#include "llpanelexperiences.h"
+#include "llslurl.h"
+#include "lllayoutstack.h"
+
+
+
+static LLPanelInjector<LLPanelExperiences> register_experiences_panel("experiences_panel");
+
+
+//comparators
+static const LLExperienceItemComparator NAME_COMPARATOR;
+
+LLPanelExperiences::LLPanelExperiences( )
+ : mExperiencesList(NULL)
+{
+ buildFromFile("panel_experiences.xml");
+}
+
+BOOL LLPanelExperiences::postBuild( void )
+{
+ mExperiencesList = getChild<LLFlatListView>("experiences_list");
+ if (hasString("loading_experiences"))
+ {
+ mExperiencesList->setNoItemsCommentText(getString("loading_experiences"));
+ }
+ else if (hasString("no_experiences"))
+ {
+ mExperiencesList->setNoItemsCommentText(getString("no_experiences"));
+ }
+ mExperiencesList->setComparator(&NAME_COMPARATOR);
+
+ return TRUE;
+}
+
+
+
+LLExperienceItem* LLPanelExperiences::getSelectedExperienceItem()
+{
+ LLPanel* selected_item = mExperiencesList->getSelectedItem();
+ if (!selected_item) return NULL;
+
+ return dynamic_cast<LLExperienceItem*>(selected_item);
+}
+
+void LLPanelExperiences::setExperienceList( const LLSD& experiences )
+{
+ if (hasString("no_experiences"))
+ {
+ mExperiencesList->setNoItemsCommentText(getString("no_experiences"));
+ }
+ mExperiencesList->clear();
+
+ LLSD::array_const_iterator it = experiences.beginArray();
+ for( /**/ ; it != experiences.endArray(); ++it)
+ {
+ LLUUID public_key = it->asUUID();
+ LLExperienceItem* item = new LLExperienceItem();
+
+ item->init(public_key);
+ mExperiencesList->addItem(item, public_key);
+ }
+
+ mExperiencesList->sort();
+}
+
+LLPanelExperiences* LLPanelExperiences::create(const std::string& name)
+{
+ LLPanelExperiences* panel= new LLPanelExperiences();
+ panel->setName(name);
+ return panel;
+}
+
+void LLPanelExperiences::removeExperiences( const LLSD& ids )
+{
+ LLSD::array_const_iterator it = ids.beginArray();
+ for( /**/ ; it != ids.endArray(); ++it)
+ {
+ removeExperience(it->asUUID());
+ }
+}
+
+void LLPanelExperiences::removeExperience( const LLUUID& id )
+{
+ mExperiencesList->removeItemByUUID(id);
+}
+
+void LLPanelExperiences::addExperience( const LLUUID& id )
+{
+ if(!mExperiencesList->getItemByValue(id))
+ {
+ LLExperienceItem* item = new LLExperienceItem();
+
+ item->init(id);
+ mExperiencesList->addItem(item, id);
+ mExperiencesList->sort();
+ }
+}
+
+void LLPanelExperiences::setButtonAction(const std::string& label, const commit_signal_t::slot_type& cb )
+{
+ if(label.empty())
+ {
+ getChild<LLLayoutPanel>("button_panel")->setVisible(false);
+ }
+ else
+ {
+ getChild<LLLayoutPanel>("button_panel")->setVisible(true);
+ LLButton* child = getChild<LLButton>("btn_action");
+ child->setCommitCallback(cb);
+ child->setLabel(getString(label));
+ }
+}
+
+void LLPanelExperiences::enableButton( bool enable )
+{
+ getChild<LLButton>("btn_action")->setEnabled(enable);
+}
+
+
+LLExperienceItem::LLExperienceItem()
+ : mName(NULL)
+{
+ buildFromFile("panel_experience_list_item.xml");
+}
+
+void LLExperienceItem::init( const LLUUID& id)
+{
+ mName = getChild<LLUICtrl>("experience_name");
+ mName->setValue(LLSLURL("experience", id, "profile").getSLURLString());
+}
+
+LLExperienceItem::~LLExperienceItem()
+{
+
+}
+
+std::string LLExperienceItem::getExperienceName() const
+{
+ if (mName)
+ {
+ return mName->getValue();
+ }
+
+ return "";
+}
+
+void LLPanelSearchExperiences::doSearch()
+{
+
+}
+
+LLPanelSearchExperiences* LLPanelSearchExperiences::create( const std::string& name )
+{
+ LLPanelSearchExperiences* panel= new LLPanelSearchExperiences();
+ panel->getChild<LLPanel>("results")->addChild(LLPanelExperiences::create(name));
+ return panel;
+}
+
+BOOL LLPanelSearchExperiences::postBuild( void )
+{
+ childSetAction("search_button", boost::bind(&LLPanelSearchExperiences::doSearch, this));
+ return TRUE;
+}
+
+bool LLExperienceItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const
+{
+ const LLExperienceItem* experience_item1 = dynamic_cast<const LLExperienceItem*>(item1);
+ const LLExperienceItem* experience_item2 = dynamic_cast<const LLExperienceItem*>(item2);
+
+ if (!experience_item1 || !experience_item2)
+ {
+ LL_ERRS() << "item1 and item2 cannot be null" << LL_ENDL;
+ return true;
+ }
+
+ std::string name1 = experience_item1->getExperienceName();
+ std::string name2 = experience_item2->getExperienceName();
+
+ LLStringUtil::toUpper(name1);
+ LLStringUtil::toUpper(name2);
+
+ return name1 < name2;
+}
diff --git a/indra/newview/llpanelexperiences.h b/indra/newview/llpanelexperiences.h
new file mode 100644
index 0000000000..0370499583
--- /dev/null
+++ b/indra/newview/llpanelexperiences.h
@@ -0,0 +1,97 @@
+/**
+ * @file llpanelexperiences.h
+ * @brief LLPanelExperiences class definition
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPANELEXPERIENCES_H
+#define LL_LLPANELEXPERIENCES_H
+
+#include "llaccordionctrltab.h"
+#include "llflatlistview.h"
+#include "llpanelavatar.h"
+
+class LLExperienceItem;
+class LLPanelProfile;
+
+
+class LLPanelSearchExperiences
+ : public LLPanel
+{
+public:
+ LLPanelSearchExperiences(){}
+ static LLPanelSearchExperiences* create(const std::string& name);
+ /*virtual*/ BOOL postBuild(void);
+
+ void doSearch();
+};
+
+class LLPanelExperiences
+ : public LLPanel
+{
+public:
+ LLPanelExperiences();
+
+ static LLPanelExperiences* create(const std::string& name);
+
+ /*virtual*/ BOOL postBuild(void);
+ /*virtual*/ void onClosePanel();
+
+ void setExperienceList(const LLSD& experiences);
+
+ LLExperienceItem* getSelectedExperienceItem();
+ void removeExperiences( const LLSD& ids );
+ void removeExperience( const LLUUID& id);
+ void addExperience( const LLUUID& id);
+ void setButtonAction(const std::string& label, const commit_signal_t::slot_type& cb);
+ void enableButton(bool enable);
+protected:
+
+private:
+ LLFlatListView* mExperiencesList;
+};
+
+class LLExperienceItemComparator : public LLFlatListView::ItemComparator
+{
+ LOG_CLASS(LLExperienceItemComparator);
+
+public:
+ LLExperienceItemComparator() {};
+ virtual ~LLExperienceItemComparator() {};
+
+ virtual bool compare(const LLPanel* item1, const LLPanel* item2) const;
+};
+
+class LLExperienceItem
+ : public LLPanel
+{
+public:
+ LLExperienceItem();
+ ~LLExperienceItem();
+
+ void init(const LLUUID& experience_id);
+ std::string getExperienceName() const;
+protected:
+ LLUICtrl* mName;
+};
+#endif // LL_LLPANELEXPERIENCES_H
diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp
index f4aab6bd4e..f67a90fd01 100755
--- a/indra/newview/llpanelgroup.cpp
+++ b/indra/newview/llpanelgroup.cpp
@@ -177,11 +177,13 @@ BOOL LLPanelGroup::postBuild()
LLPanelGroupTab* panel_roles = findChild<LLPanelGroupTab>("group_roles_tab_panel");
LLPanelGroupTab* panel_notices = findChild<LLPanelGroupTab>("group_notices_tab_panel");
LLPanelGroupTab* panel_land = findChild<LLPanelGroupTab>("group_land_tab_panel");
+ LLPanelGroupTab* panel_experiences = findChild<LLPanelGroupTab>("group_experiences_tab_panel");
if(panel_general) mTabs.push_back(panel_general);
if(panel_roles) mTabs.push_back(panel_roles);
if(panel_notices) mTabs.push_back(panel_notices);
if(panel_land) mTabs.push_back(panel_land);
+ if(panel_experiences) mTabs.push_back(panel_experiences);
if(panel_general)
{
@@ -418,6 +420,7 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id)
LLAccordionCtrlTab* tab_roles = getChild<LLAccordionCtrlTab>("group_roles_tab");
LLAccordionCtrlTab* tab_notices = getChild<LLAccordionCtrlTab>("group_notices_tab");
LLAccordionCtrlTab* tab_land = getChild<LLAccordionCtrlTab>("group_land_tab");
+ LLAccordionCtrlTab* tab_experiences = getChild<LLAccordionCtrlTab>("group_experiences_tab");
if(mButtonJoin)
mButtonJoin->setVisible(false);
@@ -434,10 +437,13 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id)
tab_notices->changeOpenClose(tab_notices->getDisplayChildren());
if(tab_land->getDisplayChildren())
tab_land->changeOpenClose(tab_land->getDisplayChildren());
+ if(tab_experiences->getDisplayChildren())
+ tab_experiences->changeOpenClose(tab_land->getDisplayChildren());
tab_roles->setVisible(false);
tab_notices->setVisible(false);
tab_land->setVisible(false);
+ tab_experiences->setVisible(false);
getChild<LLUICtrl>("group_name")->setVisible(false);
getChild<LLUICtrl>("group_name_editor")->setVisible(true);
@@ -459,6 +465,8 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id)
tab_notices->changeOpenClose(tab_notices->getDisplayChildren());
if(tab_land->getDisplayChildren())
tab_land->changeOpenClose(tab_land->getDisplayChildren());
+ if(tab_experiences->getDisplayChildren())
+ tab_experiences->changeOpenClose(tab_land->getDisplayChildren());
}
LLGroupData agent_gdatap;
@@ -467,6 +475,7 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id)
tab_roles->setVisible(is_member);
tab_notices->setVisible(is_member);
tab_land->setVisible(is_member);
+ tab_experiences->setVisible(is_member);
getChild<LLUICtrl>("group_name")->setVisible(true);
getChild<LLUICtrl>("group_name_editor")->setVisible(false);
@@ -534,6 +543,7 @@ bool LLPanelGroup::apply()
&& apply(findChild<LLPanelGroupTab>("group_roles_tab_panel"))
&& apply(findChild<LLPanelGroupTab>("group_notices_tab_panel"))
&& apply(findChild<LLPanelGroupTab>("group_land_tab_panel"))
+ && apply(findChild<LLPanelGroupTab>("group_experiences_tab_panel"))
;
}
diff --git a/indra/newview/llpanelgroupexperiences.cpp b/indra/newview/llpanelgroupexperiences.cpp
new file mode 100644
index 0000000000..76b68122fb
--- /dev/null
+++ b/indra/newview/llpanelgroupexperiences.cpp
@@ -0,0 +1,143 @@
+/**
+ * @file llpanelgroupexperiences.cpp
+ * @brief List of experiences owned by a group.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroupexperiences.h"
+
+#include "lluictrlfactory.h"
+#include "roles_constants.h"
+
+#include "llhttpclient.h"
+#include "llagent.h"
+#include "llviewerregion.h"
+#include "llflatlistview.h"
+#include "llpanelexperiences.h"
+#include "llsd.h"
+
+
+static LLPanelInjector<LLPanelGroupExperiences> t_panel_group_experiences("panel_group_experiences");
+
+
+class LLGroupExperienceResponder : public LLHTTPClient::Responder
+{
+public:
+ LLHandle<LLPanelGroupExperiences> mHandle;
+
+ LLGroupExperienceResponder(LLHandle<LLPanelGroupExperiences> handle) : mHandle(handle) { }
+
+protected:
+ /*virtual*/ void httpSuccess()
+ {
+ if (mHandle.isDead())
+ {
+ return;
+ }
+
+ LLPanelGroupExperiences* panel = mHandle.get();
+ if (panel)
+ {
+ panel->setExperienceList(getContent().get("experience_ids"));
+ }
+ }
+
+ /*virtual*/ void httpFailure()
+ {
+ LL_WARNS() << "experience responder failed [status:" << getStatus() << "]: " << getContent() << LL_ENDL;
+ }
+};
+
+LLPanelGroupExperiences::LLPanelGroupExperiences()
+: LLPanelGroupTab(), mExperiencesList(NULL)
+{
+}
+
+LLPanelGroupExperiences::~LLPanelGroupExperiences()
+{
+}
+
+BOOL LLPanelGroupExperiences::postBuild()
+{
+ mExperiencesList = getChild<LLFlatListView>("experiences_list");
+ if (hasString("loading_experiences"))
+ {
+ mExperiencesList->setNoItemsCommentText(getString("loading_experiences"));
+ }
+ else if (hasString("no_experiences"))
+ {
+ mExperiencesList->setNoItemsCommentText(getString("no_experiences"));
+ }
+
+ return LLPanelGroupTab::postBuild();
+}
+
+void LLPanelGroupExperiences::activate()
+{
+ if (getGroupID() == LLUUID::null)
+ {
+ return;
+ }
+
+ // search for experiences owned by the current group
+ std::string url = gAgent.getRegion()->getCapability("GroupExperiences");
+ if (!url.empty())
+ {
+ url += "?" + getGroupID().asString();
+
+ LLHTTPClient::get(url, new LLGroupExperienceResponder(getDerivedHandle<LLPanelGroupExperiences>()));
+ }
+}
+
+void LLPanelGroupExperiences::setGroupID(const LLUUID& id)
+{
+ LLPanelGroupTab::setGroupID(id);
+
+ if(id == LLUUID::null)
+ {
+ return;
+ }
+
+ activate();
+}
+
+void LLPanelGroupExperiences::setExperienceList(const LLSD& experiences)
+{
+ if (hasString("no_experiences"))
+ {
+ mExperiencesList->setNoItemsCommentText(getString("no_experiences"));
+ }
+ mExperiencesList->clear();
+
+ LLSD::array_const_iterator it = experiences.beginArray();
+ for ( /**/ ; it != experiences.endArray(); ++it)
+ {
+ LLUUID public_key = it->asUUID();
+ LLExperienceItem* item = new LLExperienceItem();
+
+ item->init(public_key);
+ mExperiencesList->addItem(item, public_key);
+ }
+}
diff --git a/indra/newview/llpanelgroupexperiences.h b/indra/newview/llpanelgroupexperiences.h
new file mode 100644
index 0000000000..ae1ecc1ac5
--- /dev/null
+++ b/indra/newview/llpanelgroupexperiences.h
@@ -0,0 +1,53 @@
+/**
+ * @file llpanelgroupexperiences.h
+ * @brief List of experiences owned by a group.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPANELGROUPEXPERIENCES_H
+#define LL_LLPANELGROUPEXPERIENCES_H
+
+#include "llpanelgroup.h"
+
+class LLFlatListView;
+
+class LLPanelGroupExperiences : public LLPanelGroupTab
+{
+public:
+ LLPanelGroupExperiences();
+ virtual ~LLPanelGroupExperiences();
+
+ // LLPanelGroupTab
+ virtual void activate();
+
+ virtual BOOL postBuild();
+
+ virtual void setGroupID(const LLUUID& id);
+
+ void setExperienceList(const LLSD& experiences);
+
+protected:
+ LLFlatListView* mExperiencesList;
+};
+
+#endif
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index 9411b8265b..932aa503e9 100755
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -86,19 +86,22 @@
#include "llviewercontrol.h"
#include "llappviewer.h"
#include "llfloatergotoline.h"
+#include "llexperiencecache.h"
+#include "llfloaterexperienceprofile.h"
+#include "llexperienceassociationresponder.h"
const std::string HELLO_LSL =
"default\n"
"{\n"
- " state_entry()\n"
- " {\n"
- " llSay(0, \"Hello, Avatar!\");\n"
- " }\n"
+ " state_entry()\n"
+ " {\n"
+ " llSay(0, \"Hello, Avatar!\");\n"
+ " }\n"
"\n"
- " touch_start(integer total_number)\n"
- " {\n"
- " llSay(0, \"Touched.\");\n"
- " }\n"
+ " touch_start(integer total_number)\n"
+ " {\n"
+ " llSay(0, \"Touched.\");\n"
+ " }\n"
"}\n";
const std::string HELP_LSL_PORTAL_TOPIC = "LSL_Portal";
@@ -118,6 +121,26 @@ static bool have_script_upload_cap(LLUUID& object_id)
return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty());
}
+
+class ExperienceResponder : public LLHTTPClient::Responder
+{
+public:
+ ExperienceResponder(const LLHandle<LLLiveLSLEditor>& parent):mParent(parent)
+ {
+ }
+
+ LLHandle<LLLiveLSLEditor> mParent;
+
+ /*virtual*/ void httpSuccess()
+ {
+ LLLiveLSLEditor* parent = mParent.get();
+ if(!parent)
+ return;
+
+ parent->setExperienceIds(getContent()["experience_ids"]);
+ }
+};
+
/// ---------------------------------------------------------------------------
/// LLLiveLSLFile
/// ---------------------------------------------------------------------------
@@ -196,7 +219,7 @@ private:
protected:
LLLineEditor* mSearchBox;
LLLineEditor* mReplaceBox;
- void onSearchBoxCommit();
+ void onSearchBoxCommit();
};
LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL;
@@ -405,6 +428,55 @@ LLScriptEdCore::~LLScriptEdCore()
}
}
+void LLLiveLSLEditor::experienceChanged()
+{
+ if(mScriptEd->getAssociatedExperience() != mExperiences->getSelectedValue().asUUID())
+ {
+ mScriptEd->enableSave(getIsModifiable());
+ //getChildView("Save_btn")->setEnabled(TRUE);
+ mScriptEd->setAssociatedExperience(mExperiences->getSelectedValue().asUUID());
+ updateExperiencePanel();
+ }
+}
+
+void LLLiveLSLEditor::onViewProfile( LLUICtrl *ui, void* userdata )
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
+
+ LLUUID id;
+ if(self->mExperienceEnabled->get())
+ {
+ id=self->mScriptEd->getAssociatedExperience();
+ if(id.notNull())
+ {
+ LLFloaterReg::showInstance("experience_profile", id, true);
+ }
+ }
+
+}
+
+void LLLiveLSLEditor::onToggleExperience( LLUICtrl *ui, void* userdata )
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
+
+ LLUUID id;
+ if(self->mExperienceEnabled->get())
+ {
+ if(self->mScriptEd->getAssociatedExperience().isNull())
+ {
+ id=self->mExperienceIds.beginArray()->asUUID();
+ }
+ }
+
+ if(id != self->mScriptEd->getAssociatedExperience())
+ {
+ self->mScriptEd->enableSave(self->getIsModifiable());
+ }
+ self->mScriptEd->setAssociatedExperience(id);
+
+ self->updateExperiencePanel();
+}
+
BOOL LLScriptEdCore::postBuild()
{
mErrorList = getChild<LLScrollListCtrl>("lsl errors");
@@ -811,7 +883,7 @@ bool LLScriptEdCore::handleSaveChangesDialog(const LLSD& notification, const LLS
case 2: // "Cancel"
default:
// If we were quitting, we didn't really mean it.
- LLAppViewer::instance()->abortQuit();
+ LLAppViewer::instance()->abortQuit();
break;
}
return false;
@@ -846,8 +918,8 @@ void LLScriptEdCore::onBtnDynamicHelp()
LLKeywordToken *token;
LLKeywords::keyword_iterator_t token_it;
for (token_it = mEditor->keywordsBegin();
- token_it != mEditor->keywordsEnd();
- ++token_it)
+ token_it != mEditor->keywordsEnd();
+ ++token_it)
{
token = token_it->second;
help_combo->add(wstring_to_utf8str(token->getToken()));
@@ -1199,6 +1271,130 @@ bool LLScriptEdCore::enableLoadFromFileMenu(void* userdata)
return (self && self->mEditor) ? self->mEditor->canLoadOrSaveToFile() : FALSE;
}
+LLUUID LLScriptEdCore::getAssociatedExperience()const
+{
+ return mAssociatedExperience;
+}
+
+void LLLiveLSLEditor::setExperienceIds( const LLSD& experience_ids )
+{
+ mExperienceIds=experience_ids;
+ updateExperiencePanel();
+}
+
+
+void LLLiveLSLEditor::updateExperiencePanel()
+{
+ if(mScriptEd->getAssociatedExperience().isNull())
+ {
+ mExperienceEnabled->set(FALSE);
+ mExperiences->setVisible(FALSE);
+ if(mExperienceIds.size()>0)
+ {
+ mExperienceEnabled->setEnabled(TRUE);
+ mExperienceEnabled->setToolTip(getString("add_experiences"));
+ }
+ else
+ {
+ mExperienceEnabled->setEnabled(FALSE);
+ mExperienceEnabled->setToolTip(getString("no_experiences"));
+ }
+ getChild<LLButton>("view_profile")->setVisible(FALSE);
+ }
+ else
+ {
+ mExperienceEnabled->setToolTip(getString("experience_enabled"));
+ mExperienceEnabled->setEnabled(getIsModifiable());
+ mExperiences->setVisible(TRUE);
+ mExperienceEnabled->set(TRUE);
+ buildExperienceList();
+ }
+}
+
+void LLLiveLSLEditor::buildExperienceList()
+{
+ mExperiences->clearRows();
+ bool foundAssociated=false;
+ const LLUUID& associated = mScriptEd->getAssociatedExperience();
+ LLUUID last;
+ LLScrollListItem* item;
+ for(LLSD::array_const_iterator it = mExperienceIds.beginArray(); it != mExperienceIds.endArray(); ++it)
+ {
+ LLUUID id = it->asUUID();
+ EAddPosition position = ADD_BOTTOM;
+ if(id == associated)
+ {
+ foundAssociated = true;
+ position = ADD_TOP;
+ }
+
+ const LLSD& experience = LLExperienceCache::get(id);
+ if(experience.isUndefined())
+ {
+ mExperiences->add(getString("loading"), id, position);
+ last = id;
+ }
+ else
+ {
+ mExperiences->add(experience[LLExperienceCache::NAME].asString(), id, position);
+ }
+ }
+
+ if(!foundAssociated )
+ {
+ const LLSD& experience = LLExperienceCache::get(associated);
+ if(experience.isDefined())
+ {
+ item=mExperiences->add(experience[LLExperienceCache::NAME].asString(), associated, ADD_TOP);
+ }
+ else
+ {
+ item=mExperiences->add(getString("loading"), associated, ADD_TOP);
+ last = associated;
+ }
+ item->setEnabled(FALSE);
+ }
+
+ if(last.notNull())
+ {
+ mExperiences->setEnabled(FALSE);
+ LLExperienceCache::get(last, boost::bind(&LLLiveLSLEditor::buildExperienceList, this));
+ }
+ else
+ {
+ mExperiences->setEnabled(TRUE);
+ getChild<LLButton>("view_profile")->setVisible(TRUE);
+ }
+}
+
+
+void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id )
+{
+ mAssociatedExperience = experience_id;
+}
+
+
+
+void LLLiveLSLEditor::requestExperiences()
+{
+ if (!getIsModifiable())
+ {
+ return;
+ }
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string lookup_url=region->getCapability("GetCreatorExperiences");
+ if(!lookup_url.empty())
+ {
+ LLHTTPClient::get(lookup_url, new ExperienceResponder(getDerivedHandle<LLLiveLSLEditor>()));
+ }
+ }
+}
+
+
+
/// ---------------------------------------------------------------------------
/// LLScriptEdContainer
/// ---------------------------------------------------------------------------
@@ -1217,7 +1413,7 @@ std::string LLScriptEdContainer::getTmpFileName()
std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString();
// Use MD5 sum to make the file name shorter and not exceed maximum path length.
- char script_id_hash_str[33]; /* Flawfinder: ignore */
+ char script_id_hash_str[33]; /* Flawfinder: ignore */
LLMD5 script_id_hash((const U8 *)script_id.c_str());
script_id_hash.hex_digest(script_id_hash_str);
@@ -1755,6 +1951,16 @@ BOOL LLLiveLSLEditor::postBuild()
mScriptEd->mEditor->makePristine();
mScriptEd->mEditor->setFocus(TRUE);
+
+ mExperiences = getChild<LLComboBox>("Experiences...");
+ mExperiences->setCommitCallback(boost::bind(&LLLiveLSLEditor::experienceChanged, this));
+
+ mExperienceEnabled = getChild<LLCheckBoxCtrl>("enable_xp");
+
+ childSetCommitCallback("enable_xp", onToggleExperience, this);
+ childSetCommitCallback("view_profile", onViewProfile, this);
+
+
return LLPreview::postBuild();
}
@@ -1798,60 +2004,59 @@ void LLLiveLSLEditor::loadAsset()
if(object)
{
LLViewerInventoryItem* item = dynamic_cast<LLViewerInventoryItem*>(object->getInventoryObject(mItemUUID));
- if(item
- && (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE)
- || gAgent.isGodlike()))
- {
- mItem = new LLViewerInventoryItem(item);
- //LL_INFOS() << "asset id " << mItem->getAssetUUID() << LL_ENDL;
- }
- if(!gAgent.isGodlike()
- && (item
- && (!gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE)
- || !gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE))))
- {
- mItem = new LLViewerInventoryItem(item);
- mScriptEd->setScriptText(getString("not_allowed"), FALSE);
- mScriptEd->mEditor->makePristine();
- mScriptEd->enableSave(FALSE);
- mAssetStatus = PREVIEW_ASSET_LOADED;
- }
- else if(item && mItem.notNull())
+ if(item)
{
- // request the text from the object
- LLUUID* user_data = new LLUUID(mItemUUID); // ^ mObjectUUID
- gAssetStorage->getInvItemAsset(object->getRegion()->getHost(),
- gAgent.getID(),
- gAgent.getSessionID(),
- item->getPermissions().getOwner(),
- object->getID(),
- item->getUUID(),
- item->getAssetUUID(),
- item->getType(),
- &LLLiveLSLEditor::onLoadComplete,
- (void*)user_data,
- TRUE);
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_GetScriptRunning);
- msg->nextBlockFast(_PREHASH_Script);
- msg->addUUIDFast(_PREHASH_ObjectID, mObjectUUID);
- msg->addUUIDFast(_PREHASH_ItemID, mItemUUID);
- msg->sendReliable(object->getRegion()->getHost());
- mAskedForRunningInfo = TRUE;
- mAssetStatus = PREVIEW_ASSET_LOADING;
+ ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLLiveLSLEditor::setAssociatedExperience, getDerivedHandle<LLLiveLSLEditor>(), _1));
+
+ bool isGodlike = gAgent.isGodlike();
+ bool copyManipulate = gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE);
+ mIsModifiable = gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE);
+
+ if(!isGodlike && (!copyManipulate || !mIsModifiable))
+ {
+ mItem = new LLViewerInventoryItem(item);
+ mScriptEd->setScriptText(getString("not_allowed"), FALSE);
+ mScriptEd->mEditor->makePristine();
+ mScriptEd->enableSave(FALSE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ else if(copyManipulate || isGodlike)
+ {
+ mItem = new LLViewerInventoryItem(item);
+ // request the text from the object
+ LLUUID* user_data = new LLUUID(mItemUUID); // ^ mObjectUUID
+ gAssetStorage->getInvItemAsset(object->getRegion()->getHost(),
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getPermissions().getOwner(),
+ object->getID(),
+ item->getUUID(),
+ item->getAssetUUID(),
+ item->getType(),
+ &LLLiveLSLEditor::onLoadComplete,
+ (void*)user_data,
+ TRUE);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_GetScriptRunning);
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, mObjectUUID);
+ msg->addUUIDFast(_PREHASH_ItemID, mItemUUID);
+ msg->sendReliable(object->getRegion()->getHost());
+ mAskedForRunningInfo = TRUE;
+ mAssetStatus = PREVIEW_ASSET_LOADING;
+ }
}
- else
+
+ if(mItem.isNull())
{
mScriptEd->setScriptText(LLStringUtil::null, FALSE);
mScriptEd->mEditor->makePristine();
mAssetStatus = PREVIEW_ASSET_LOADED;
+ mIsModifiable = FALSE;
}
- mIsModifiable = item && gAgent.allowOperation(PERM_MODIFY,
- item->getPermissions(),
- GP_OBJECT_MANIPULATE);
-
+ refreshFromItem();
// This is commented out, because we don't completely
// handle script exports yet.
/*
@@ -1888,6 +2093,8 @@ void LLLiveLSLEditor::loadAsset()
time_corrected());
mAssetStatus = PREVIEW_ASSET_LOADED;
}
+
+ requestExperiences();
}
// static
@@ -2150,7 +2357,7 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/)
BOOL is_running = getChild<LLCheckBoxCtrl>( "running")->get();
if (!url.empty())
{
- uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running);
+ uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running, mScriptEd->getAssociatedExperience());
}
else if (gAssetStorage)
{
@@ -2162,7 +2369,8 @@ void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url,
const std::string& filename,
const LLUUID& task_id,
const LLUUID& item_id,
- BOOL is_running)
+ BOOL is_running,
+ const LLUUID& experience_public_id )
{
LL_INFOS() << "Update Task Inventory via capability " << url << LL_ENDL;
LLSD body;
@@ -2170,6 +2378,7 @@ void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url,
body["item_id"] = item_id;
body["is_script_running"] = is_running;
body["target"] = monoChecked() ? "mono" : "lsl2";
+ body["experience"] = experience_public_id;
LLHTTPClient::post(url, body,
new LLUpdateTaskInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT));
}
@@ -2421,3 +2630,18 @@ BOOL LLLiveLSLEditor::monoChecked() const
}
return FALSE;
}
+
+void LLLiveLSLEditor::setAssociatedExperience( LLHandle<LLLiveLSLEditor> editor, const LLSD& experience )
+{
+ LLLiveLSLEditor* scriptEd = editor.get();
+ if(scriptEd)
+ {
+ LLUUID id;
+ if(experience.has(LLExperienceCache::EXPERIENCE_ID))
+ {
+ id=experience[LLExperienceCache::EXPERIENCE_ID].asUUID();
+ }
+ scriptEd->mScriptEd->setAssociatedExperience(id);
+ scriptEd->updateExperiencePanel();
+ }
+}
diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h
index 9ea191e928..fd94f32c72 100755
--- a/indra/newview/llpreviewscript.h
+++ b/indra/newview/llpreviewscript.h
@@ -51,6 +51,7 @@ class LLVFS;
class LLViewerInventoryItem;
class LLScriptEdContainer;
class LLFloaterGotoLine;
+class LLFloaterExperienceProfile;
// Inner, implementation class. LLPreviewScript and LLLiveLSLEditor each own one of these.
class LLScriptEdCore : public LLPanel
@@ -107,12 +108,14 @@ public:
static void onBtnInsertSample(void*);
static void onBtnInsertFunction(LLUICtrl*, void*);
static void onBtnLoadFromFile(void*);
- static void onBtnSaveToFile(void*);
+ static void onBtnSaveToFile(void*);
static bool enableSaveToFileMenu(void* userdata);
static bool enableLoadFromFileMenu(void* userdata);
- virtual bool hasAccelerators() const { return true; }
+ virtual bool hasAccelerators() const { return true; }
+ LLUUID getAssociatedExperience()const;
+ void setAssociatedExperience( const LLUUID& experience_id );
private:
void onBtnHelp();
@@ -142,8 +145,8 @@ private:
void (*mLoadCallback)(void* userdata);
void (*mSaveCallback)(void* userdata, BOOL close_after_save);
void (*mSearchReplaceCallback) (void* userdata);
- void* mUserdata;
- LLComboBox *mFunctions;
+ void* mUserdata;
+ LLComboBox *mFunctions;
BOOL mForceClose;
LLPanel* mCodePanel;
LLScrollListCtrl* mErrorList;
@@ -155,6 +158,7 @@ private:
BOOL mEnableSave;
BOOL mHasScriptData;
LLLiveLSLFile* mLiveFile;
+ LLUUID mAssociatedExperience;
LLScriptEdContainer* mContainer; // parent view
@@ -241,7 +245,18 @@ public:
/*virtual*/ BOOL postBuild();
- void setIsNew() { mIsNew = TRUE; }
+ void setIsNew() { mIsNew = TRUE; }
+
+ static void setAssociatedExperience( LLHandle<LLLiveLSLEditor> editor, const LLSD& experience );
+ static void onToggleExperience(LLUICtrl *ui, void* userdata);
+ static void onViewProfile(LLUICtrl *ui, void* userdata);
+
+ void setExperienceIds(const LLSD& experience_ids);
+ void buildExperienceList();
+ void updateExperiencePanel();
+ void requestExperiences();
+ void experienceChanged();
+ void addAssociatedExperience(const LLSD& experience);
private:
virtual BOOL canClose();
@@ -252,10 +267,11 @@ private:
void loadAsset(BOOL is_new);
/*virtual*/ void saveIfNeeded(bool sync = true);
void uploadAssetViaCaps(const std::string& url,
- const std::string& filename,
+ const std::string& filename,
const LLUUID& task_id,
const LLUUID& item_id,
- BOOL is_running);
+ BOOL is_running,
+ const LLUUID& experience_public_id);
void uploadAssetLegacy(const std::string& filename,
LLViewerObject* object,
const LLTransactionID& tid,
@@ -296,9 +312,16 @@ private:
S32 mPendingUploads;
BOOL getIsModifiable() const { return mIsModifiable; } // Evaluated on load assert
-
+
LLCheckBoxCtrl* mMonoCheckbox;
BOOL mIsModifiable;
+
+
+ LLComboBox* mExperiences;
+ LLCheckBoxCtrl* mExperienceEnabled;
+ LLSD mExperienceIds;
+
+ LLHandle<LLFloater> mExperienceProfile;
};
#endif // LL_LLPREVIEWSCRIPT_H
diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp
index 8708fb87ee..6d94b178dd 100755
--- a/indra/newview/llscreenchannel.cpp
+++ b/indra/newview/llscreenchannel.cpp
@@ -541,19 +541,23 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel)
{
std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id);
+ LLPanel* panel_to_delete = panel;
+
if( it != mToastList.end() && panel)
{
LLToast* toast = it->getToast();
if (toast)
{
- LLPanel* old_panel = toast->getPanel();
- toast->removeChild(old_panel);
- delete old_panel;
- toast->insertPanel(panel);
- toast->startTimer();
+ LLPanel* old_panel = toast->getPanel();
+ toast->removeChild(old_panel);
+ panel_to_delete = old_panel;
+ toast->insertPanel(panel);
+ toast->startTimer();
}
redrawToasts();
}
+
+ delete panel_to_delete;
}
//--------------------------------------------------------------------------
diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp
index 1d20b7bed5..d75d4ae951 100755
--- a/indra/newview/llsidepaneliteminfo.cpp
+++ b/indra/newview/llsidepaneliteminfo.cpp
@@ -43,6 +43,9 @@
#include "llviewercontrol.h"
#include "llviewerinventory.h"
#include "llviewerobjectlist.h"
+#include "llexperienceassociationresponder.h"
+#include "llexperiencecache.h"
+#include "lltrans.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -316,6 +319,15 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
is_obj_modify = object->permOwnerModify();
}
+ if(item->getInventoryType() == LLInventoryType::IT_LSL)
+ {
+ getChildView("LabelItemExperienceTitle")->setVisible(TRUE);
+ LLTextBox* tb = getChild<LLTextBox>("LabelItemExperience");
+ tb->setText(getString("loading_experience"));
+ tb->setVisible(TRUE);
+ ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLSidepanelItemInfo::setAssociatedExperience, getDerivedHandle<LLSidepanelItemInfo>(), _1));
+ }
+
//////////////////////
// ITEM NAME & DESC //
//////////////////////
@@ -666,6 +678,29 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)
}
}
+
+void LLSidepanelItemInfo::setAssociatedExperience( LLHandle<LLSidepanelItemInfo> hInfo, const LLSD& experience )
+{
+ LLSidepanelItemInfo* info = hInfo.get();
+ if(info)
+ {
+ LLUUID id;
+ if(experience.has(LLExperienceCache::EXPERIENCE_ID))
+ {
+ id=experience[LLExperienceCache::EXPERIENCE_ID].asUUID();
+ }
+ if(id.notNull())
+ {
+ info->getChild<LLTextBox>("LabelItemExperience")->setText(LLSLURL("experience", id, "profile").getSLURLString());
+ }
+ else
+ {
+ info->getChild<LLTextBox>("LabelItemExperience")->setText(LLTrans::getString("ExperienceNameNull"));
+ }
+ }
+}
+
+
void LLSidepanelItemInfo::startObjectInventoryObserver()
{
if (!mObjectInventoryObserver)
diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h
index 12aaca923e..2e24e58a2a 100755
--- a/indra/newview/llsidepaneliteminfo.h
+++ b/indra/newview/llsidepaneliteminfo.h
@@ -67,6 +67,8 @@ protected:
void refreshFromItem(LLViewerInventoryItem* item);
private:
+ static void setAssociatedExperience( LLHandle<LLSidepanelItemInfo> hInfo, const LLSD& experience );
+
void startObjectInventoryObserver();
void stopObjectInventoryObserver();
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 0c282a19a5..884d0cc8d2 100755
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -48,6 +48,7 @@
#include "llares.h"
#include "llavatarnamecache.h"
+#include "llexperiencecache.h"
#include "lllandmark.h"
#include "llcachename.h"
#include "lldir.h"
@@ -194,6 +195,7 @@
#include "llevents.h"
#include "llstartuplistener.h"
#include "lltoolbarview.h"
+#include "llexperiencelog.h"
#if LL_WINDOWS
#include "lldxhardware.h"
@@ -1306,6 +1308,9 @@ bool idle_startup()
gAgent.setPositionAgent(agent_start_position_region);
display_startup();
+ LLStartUp::initExperiences();
+
+ display_startup();
LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT );
LLConversationLog::getInstance();
@@ -2836,6 +2841,14 @@ void LLStartUp::initNameCache()
LLAvatarNameCache::setUseUsernames(gSavedSettings.getBOOL("NameTagShowUsernames"));
}
+
+void LLStartUp::initExperiences()
+{
+ LLAppViewer::instance()->loadExperienceCache();
+ LLExperienceCache::initClass();
+ LLExperienceLog::instance().initialize();
+}
+
void LLStartUp::cleanupNameCache()
{
LLAvatarNameCache::cleanupClass();
@@ -3537,3 +3550,4 @@ void transition_back_to_login_panel(const std::string& emsg)
reset_login(); // calls LLStartUp::setStartupState( STATE_LOGIN_SHOW );
gSavedSettings.setBOOL("AutoLogin", FALSE);
}
+
diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h
index e39810713a..d434e86f82 100755
--- a/indra/newview/llstartup.h
+++ b/indra/newview/llstartup.h
@@ -103,6 +103,7 @@ public:
static void fontInit();
static void initNameCache();
+ static void initExperiences();
static void cleanupNameCache();
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 4e491f257d..7f497aed4a 100755
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -61,6 +61,9 @@
#include "llfloatereditsky.h"
#include "llfloatereditwater.h"
#include "llfloaterenvironmentsettings.h"
+#include "llfloaterexperienceprofile.h"
+#include "llfloaterexperiences.h"
+#include "llfloaterexperiencepicker.h"
#include "llfloaterevent.h"
#include "llfloaterfacebook.h"
#include "llfloaterflickr.h"
@@ -212,8 +215,11 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("env_edit_water", "floater_edit_water_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEditWater>);
LLFloaterReg::add("env_edit_day_cycle", "floater_edit_day_cycle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEditDayCycle>);
- LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEvent>);
-
+ LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEvent>);
+ LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiences>);
+ LLFloaterReg::add("experience_profile", "floater_experienceprofile.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperienceProfile>);
+ LLFloaterReg::add("experience_search", "floater_experience_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiencePicker>);
+
LLFloaterReg::add("font_test", "floater_font_test.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFontTest>);
LLFloaterReg::add("gestures", "floater_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGesture>);
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 9d5c3c4d4a..929affe311 100755
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -117,12 +117,15 @@
#include <boost/regex.hpp>
#include "llnotificationmanager.h" //
+#include "llexperiencecache.h"
#if LL_MSVC
// disable boost::lexical_cast warning
#pragma warning (disable:4702)
#endif
+
+
extern void on_new_message(const LLSD& msg);
//
@@ -145,6 +148,7 @@ extern bool gShiftFrame;
bool check_offer_throttle(const std::string& from_name, bool check_only);
bool check_asset_previewable(const LLAssetType::EType asset_type);
static void process_money_balance_reply_extended(LLMessageSystem* msg);
+bool handle_trusted_experiences_notification(const LLSD&);
//inventory offer throttle globals
LLFrameTimer gThrottleTimer;
@@ -5687,146 +5691,174 @@ bool handle_prompt_for_maturity_level_change_and_reteleport_callback(const LLSD&
// some of the server notifications need special handling. This is where we do that.
bool handle_special_notification(std::string notificationID, LLSD& llsdBlock)
{
- U8 regionAccess = static_cast<U8>(llsdBlock["_region_access"].asInteger());
- std::string regionMaturity = LLViewerRegion::accessToString(regionAccess);
- LLStringUtil::toLower(regionMaturity);
- llsdBlock["REGIONMATURITY"] = regionMaturity;
bool returnValue = false;
- LLNotificationPtr maturityLevelNotification;
- std::string notifySuffix = "_Notify";
- if (regionAccess == SIM_ACCESS_MATURE)
+ if(llsdBlock.has("_region_access"))
{
- if (gAgent.isTeen())
+ U8 regionAccess = static_cast<U8>(llsdBlock["_region_access"].asInteger());
+ std::string regionMaturity = LLViewerRegion::accessToString(regionAccess);
+ LLStringUtil::toLower(regionMaturity);
+ llsdBlock["REGIONMATURITY"] = regionMaturity;
+ LLNotificationPtr maturityLevelNotification;
+ std::string notifySuffix = "_Notify";
+ if (regionAccess == SIM_ACCESS_MATURE)
{
- gAgent.clearTeleportRequest();
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock);
- returnValue = true;
+ if (gAgent.isTeen())
+ {
+ gAgent.clearTeleportRequest();
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock);
+ returnValue = true;
- notifySuffix = "_NotifyAdultsOnly";
+ notifySuffix = "_NotifyAdultsOnly";
+ }
+ else if (gAgent.prefersPG())
+ {
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
+ returnValue = true;
+ }
+ else if (LLStringUtil::compareStrings(notificationID, "RegionEntryAccessBlocked") == 0)
+ {
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock);
+ returnValue = true;
+ }
}
- else if (gAgent.prefersPG())
+ else if (regionAccess == SIM_ACCESS_ADULT)
{
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
- returnValue = true;
+ if (!gAgent.isAdult())
+ {
+ gAgent.clearTeleportRequest();
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock);
+ returnValue = true;
+
+ notifySuffix = "_NotifyAdultsOnly";
+ }
+ else if (gAgent.prefersPG() || gAgent.prefersMature())
+ {
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
+ returnValue = true;
+ }
+ else if (LLStringUtil::compareStrings(notificationID, "RegionEntryAccessBlocked") == 0)
+ {
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock);
+ returnValue = true;
+ }
}
- else if (LLStringUtil::compareStrings(notificationID, "RegionEntryAccessBlocked") == 0)
+
+ if ((maturityLevelNotification == NULL) || maturityLevelNotification->isIgnored())
{
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock);
- returnValue = true;
+ // Given a simple notification if no maturityLevelNotification is set or it is ignore
+ LLNotificationsUtil::add(notificationID + notifySuffix, llsdBlock);
}
}
- else if (regionAccess == SIM_ACCESS_ADULT)
- {
- if (!gAgent.isAdult())
- {
- gAgent.clearTeleportRequest();
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock);
- returnValue = true;
- notifySuffix = "_NotifyAdultsOnly";
- }
- else if (gAgent.prefersPG() || gAgent.prefersMature())
+ return returnValue;
+}
+
+bool handle_trusted_experiences_notification(const LLSD& llsdBlock)
+{
+ if(llsdBlock.has("trusted_experiences"))
+ {
+ std::ostringstream str;
+ const LLSD& experiences = llsdBlock["trusted_experiences"];
+ LLSD::array_const_iterator it = experiences.beginArray();
+ for(/**/; it != experiences.endArray(); ++it)
{
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
- returnValue = true;
+ str<<LLSLURL("experience", it->asUUID(), "profile").getSLURLString() << "\n";
}
- else if (LLStringUtil::compareStrings(notificationID, "RegionEntryAccessBlocked") == 0)
+ std::string str_list = str.str();
+ if(!str_list.empty())
{
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock);
- returnValue = true;
+ LLNotificationsUtil::add("TrustedExperiencesAvailable", LLSD::emptyMap().with("EXPERIENCE_LIST", (LLSD)str_list));
+ return true;
}
}
-
- if ((maturityLevelNotification == NULL) || maturityLevelNotification->isIgnored())
- {
- // Given a simple notification if no maturityLevelNotification is set or it is ignore
- LLNotificationsUtil::add(notificationID + notifySuffix, llsdBlock);
- }
-
- return returnValue;
+ return false;
}
// some of the server notifications need special handling. This is where we do that.
bool handle_teleport_access_blocked(LLSD& llsdBlock)
{
- std::string notificationID("TeleportEntryAccessBlocked");
- U8 regionAccess = static_cast<U8>(llsdBlock["_region_access"].asInteger());
- std::string regionMaturity = LLViewerRegion::accessToString(regionAccess);
- LLStringUtil::toLower(regionMaturity);
- llsdBlock["REGIONMATURITY"] = regionMaturity;
-
bool returnValue = false;
- LLNotificationPtr maturityLevelNotification;
- std::string notifySuffix = "_Notify";
- if (regionAccess == SIM_ACCESS_MATURE)
+ if(llsdBlock.has("_region_access"))
{
- if (gAgent.isTeen())
- {
- gAgent.clearTeleportRequest();
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock);
- returnValue = true;
-
- notifySuffix = "_NotifyAdultsOnly";
- }
- else if (gAgent.prefersPG())
+ std::string notificationID("TeleportEntryAccessBlocked");
+ U8 regionAccess = static_cast<U8>(llsdBlock["_region_access"].asInteger());
+ std::string regionMaturity = LLViewerRegion::accessToString(regionAccess);
+ LLStringUtil::toLower(regionMaturity);
+ llsdBlock["REGIONMATURITY"] = regionMaturity;
+
+ LLNotificationPtr maturityLevelNotification;
+ std::string notifySuffix = "_Notify";
+ if (regionAccess == SIM_ACCESS_MATURE)
{
- if (gAgent.hasRestartableFailedTeleportRequest())
+ if (gAgent.isTeen())
{
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_ChangeAndReTeleport", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_and_reteleport_callback);
+ gAgent.clearTeleportRequest();
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock);
returnValue = true;
+
+ notifySuffix = "_NotifyAdultsOnly";
+ }
+ else if (gAgent.prefersPG())
+ {
+ if (gAgent.hasRestartableFailedTeleportRequest())
+ {
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_ChangeAndReTeleport", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_and_reteleport_callback);
+ returnValue = true;
+ }
+ else
+ {
+ gAgent.clearTeleportRequest();
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
+ returnValue = true;
+ }
}
else
{
gAgent.clearTeleportRequest();
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
returnValue = true;
}
}
- else
+ else if (regionAccess == SIM_ACCESS_ADULT)
{
- gAgent.clearTeleportRequest();
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
- returnValue = true;
- }
- }
- else if (regionAccess == SIM_ACCESS_ADULT)
- {
- if (!gAgent.isAdult())
- {
- gAgent.clearTeleportRequest();
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock);
- returnValue = true;
-
- notifySuffix = "_NotifyAdultsOnly";
- }
- else if (gAgent.prefersPG() || gAgent.prefersMature())
- {
- if (gAgent.hasRestartableFailedTeleportRequest())
+ if (!gAgent.isAdult())
{
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_ChangeAndReTeleport", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_and_reteleport_callback);
+ gAgent.clearTeleportRequest();
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock);
returnValue = true;
+
+ notifySuffix = "_NotifyAdultsOnly";
+ }
+ else if (gAgent.prefersPG() || gAgent.prefersMature())
+ {
+ if (gAgent.hasRestartableFailedTeleportRequest())
+ {
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_ChangeAndReTeleport", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_and_reteleport_callback);
+ returnValue = true;
+ }
+ else
+ {
+ gAgent.clearTeleportRequest();
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
+ returnValue = true;
+ }
}
else
{
gAgent.clearTeleportRequest();
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
+ maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
returnValue = true;
}
- }
- else
+ }
+
+ if ((maturityLevelNotification == NULL) || maturityLevelNotification->isIgnored())
{
- gAgent.clearTeleportRequest();
- maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback);
- returnValue = true;
- }
+ // Given a simple notification if no maturityLevelNotification is set or it is ignore
+ LLNotificationsUtil::add(notificationID + notifySuffix, llsdBlock);
}
-
- if ((maturityLevelNotification == NULL) || maturityLevelNotification->isIgnored())
- {
- // Given a simple notification if no maturityLevelNotification is set or it is ignore
- LLNotificationsUtil::add(notificationID + notifySuffix, llsdBlock);
}
+ handle_trusted_experiences_notification(llsdBlock);
return returnValue;
}
@@ -5855,6 +5887,9 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem)
LL_WARNS() << "attempt_standard_notification: Attempted to read notification parameter data into LLSD but failed:" << llsdRaw << LL_ENDL;
}
}
+
+
+ handle_trusted_experiences_notification(llsdBlock);
if (
(notificationID == "RegionEntryAccessBlocked") ||
@@ -5938,6 +5973,7 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem)
}
LLNotificationsUtil::add(notificationID, llsdBlock);
+
return true;
}
return false;
@@ -6320,6 +6356,12 @@ bool script_question_cb(const LLSD& notification, const LLSD& response)
return false;
}
+ LLUUID experience;
+ if(notification["payload"].has("experience"))
+ {
+ experience = notification["payload"]["experience"].asUUID();
+ }
+
// check whether permissions were granted or denied
BOOL allowed = TRUE;
// the "yes/accept" button is the first button in the template, making it button 0
@@ -6328,7 +6370,17 @@ bool script_question_cb(const LLSD& notification, const LLSD& response)
{
new_questions = 0;
allowed = FALSE;
- }
+ }
+ else if(experience.notNull())
+ {
+ LLSD permission;
+ LLSD data;
+ permission["permission"]="Allow";
+
+ data[experience.asString()]=permission;
+ data["experience"]=experience;
+ LLEventPumps::instance().obtain("experience_permission").post(data);
+ }
LLUUID task_id = notification["payload"]["task_id"].asUUID();
LLUUID item_id = notification["payload"]["item_id"].asUUID();
@@ -6355,7 +6407,27 @@ bool script_question_cb(const LLSD& notification, const LLSD& response)
{
script_question_mute(task_id,notification["payload"]["object_name"].asString());
}
+ if ( response["BlockExperience"] )
+ {
+ if(experience.notNull())
+ {
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ return false;
+
+ std::string lookup_url=region->getCapability("ExperiencePreferences");
+ if(lookup_url.empty())
+ return false;
+ LLSD permission;
+ LLSD data;
+ permission["permission"]="Block";
+ data[experience.asString()]=permission;
+ LLHTTPClient::put(lookup_url, data, NULL);
+ data["experience"]=experience;
+ LLEventPumps::instance().obtain("experience_permission").post(data);
+ }
+ }
return false;
}
@@ -6388,8 +6460,24 @@ void script_question_mute(const LLUUID& task_id, const std::string& object_name)
static LLNotificationFunctorRegistration script_question_cb_reg_1("ScriptQuestion", script_question_cb);
static LLNotificationFunctorRegistration script_question_cb_reg_2("ScriptQuestionCaution", script_question_cb);
+static LLNotificationFunctorRegistration script_question_cb_reg_3("ScriptQuestionExperience", script_question_cb);
static LLNotificationFunctorRegistration unknown_script_question_cb_reg("UnknownScriptQuestion", unknown_script_question_cb);
+void process_script_experience_details(const LLSD& experience_details, LLSD args, LLSD payload)
+{
+ if(experience_details[LLExperienceCache::PROPERTIES].asInteger() & LLExperienceCache::PROPERTY_GRID)
+ {
+ args["GRID_WIDE"] = LLTrans::getString("GRID_WIDE")+ " ";
+ }
+ else
+ {
+ args["GRID_WIDE"] = "";
+ }
+ args["EXPERIENCE"] = LLSLURL("experience", experience_details[LLExperienceCache::EXPERIENCE_ID].asUUID(), "profile").getSLURLString();
+
+ LLNotificationsUtil::add("ScriptQuestionExperience", args, payload);
+}
+
void process_script_question(LLMessageSystem *msg, void **user_data)
{
// *TODO: Translate owner name -> [FIRST] [LAST]
@@ -6401,6 +6489,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data)
S32 questions;
std::string object_name;
std::string owner_name;
+ LLUUID experienceid;
// taskid -> object key of object requesting permissions
msg->getUUIDFast(_PREHASH_Data, _PREHASH_TaskID, taskid );
@@ -6410,6 +6499,11 @@ void process_script_question(LLMessageSystem *msg, void **user_data)
msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectOwner, owner_name);
msg->getS32Fast(_PREHASH_Data, _PREHASH_Questions, questions );
+ if(msg->has(_PREHASH_Experience))
+ {
+ msg->getUUIDFast(_PREHASH_Experience, _PREHASH_ExperienceID, experienceid);
+ }
+
// Special case. If the objects are owned by this agent, throttle per-object instead
// of per-owner. It's common for residents to reset a ton of scripts that re-request
// permissions, as with tier boxes. UUIDs can't be valid agent names and vice-versa,
@@ -6495,21 +6589,21 @@ void process_script_question(LLMessageSystem *msg, void **user_data)
payload["object_name"] = object_name;
payload["owner_name"] = owner_name;
- // check whether cautions are even enabled or not
- if (gSavedSettings.getBOOL("PermissionsCautionEnabled"))
+ const char* notification = "ScriptQuestion";
+
+ if(caution && gSavedSettings.getBOOL("PermissionsCautionEnabled"))
{
- if (caution)
- {
- args["FOOTERTEXT"] = (count > 1) ? LLTrans::getString("AdditionalPermissionsRequestHeader") + "\n\n" + script_question : "";
- }
- // display the caution permissions prompt
- LLNotificationsUtil::add(caution ? "ScriptQuestionCaution" : "ScriptQuestion", args, payload);
+ args["FOOTERTEXT"] = (count > 1) ? LLTrans::getString("AdditionalPermissionsRequestHeader") + "\n\n" + script_question : "";
+ notification = "ScriptQuestionCaution";
}
- else
+ else if(experienceid.notNull())
{
- // fall back to default behavior if cautions are entirely disabled
- LLNotificationsUtil::add("ScriptQuestion", args, payload);
+ payload["experience"]=experienceid;
+ LLExperienceCache::get(experienceid, boost::bind(process_script_experience_details, _1, args, payload));
+ return;
}
+
+ LLNotificationsUtil::add(notification, args, payload);
}
}
}
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
index cdb08f4707..30d743b409 100755
--- a/indra/newview/llviewerparcelmgr.cpp
+++ b/indra/newview/llviewerparcelmgr.cpp
@@ -204,8 +204,8 @@ void LLViewerParcelMgr::dump()
mCurrentParcel->dump();
LL_INFOS() << "banning " << mCurrentParcel->mBanList.size() << LL_ENDL;
- access_map_const_iterator cit = mCurrentParcel->mBanList.begin();
- access_map_const_iterator end = mCurrentParcel->mBanList.end();
+ LLAccessEntry::map::const_iterator cit = mCurrentParcel->mBanList.begin();
+ LLAccessEntry::map::const_iterator end = mCurrentParcel->mBanList.end();
for ( ; cit != end; ++cit)
{
LL_INFOS() << "ban id " << (*cit).first << LL_ENDL;
@@ -891,7 +891,7 @@ void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags)
if (!region) return;
LLMessageSystem *msg = gMessageSystem;
-
+
if (flags & AL_BAN)
{
@@ -901,6 +901,14 @@ void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags)
{
mCurrentParcel->mAccessList.clear();
}
+ if (flags & AL_ALLOW_EXPERIENCE)
+ {
+ mCurrentParcel->clearExperienceKeysByType(EXPERIENCE_KEY_TYPE_ALLOWED);
+ }
+ if (flags & AL_BLOCK_EXPERIENCE)
+ {
+ mCurrentParcel->clearExperienceKeysByType(EXPERIENCE_KEY_TYPE_BLOCKED);
+ }
// Only the headers differ
msg->newMessageFast(_PREHASH_ParcelAccessListRequest);
@@ -1656,7 +1664,7 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use
}
// Request access list information for this land
- parcel_mgr.sendParcelAccessListRequest(AL_ACCESS | AL_BAN);
+ parcel_mgr.sendParcelAccessListRequest(AL_ACCESS | AL_BAN | AL_ALLOW_EXPERIENCE | AL_BLOCK_EXPERIENCE);
// Request dwell for this land, if it's not public land.
parcel_mgr.mSelectedDwell = DWELL_NAN;
@@ -1821,6 +1829,14 @@ void LLViewerParcelMgr::processParcelAccessListReply(LLMessageSystem *msg, void
{
parcel->unpackAccessEntries(msg, &(parcel->mBanList) );
}
+ else if (message_flags & AL_ALLOW_EXPERIENCE)
+ {
+ parcel->unpackExperienceEntries(msg, EXPERIENCE_KEY_TYPE_ALLOWED);
+ }
+ else if (message_flags & AL_BLOCK_EXPERIENCE)
+ {
+ parcel->unpackExperienceEntries(msg, EXPERIENCE_KEY_TYPE_BLOCKED);
+ }
/*else if (message_flags & AL_RENTER)
{
parcel->unpackAccessEntries(msg, &(parcel->mRenterList) );
@@ -1855,10 +1871,6 @@ void LLViewerParcelMgr::processParcelDwellReply(LLMessageSystem* msg, void**)
void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which)
{
-
- LLUUID transactionUUID;
- transactionUUID.generate();
-
if (!mSelected)
{
return;
@@ -1867,125 +1879,92 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which)
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
if (!region) return;
- LLMessageSystem* msg = gMessageSystem;
-
LLParcel* parcel = mCurrentParcel;
if (!parcel) return;
if (which & AL_ACCESS)
{
- S32 count = parcel->mAccessList.size();
- S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
- S32 sequence_id = 1;
- BOOL start_message = TRUE;
- BOOL initial = TRUE;
-
- access_map_const_iterator cit = parcel->mAccessList.begin();
- access_map_const_iterator end = parcel->mAccessList.end();
- while ( (cit != end) || initial )
- {
- if (start_message)
- {
- msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
- msg->nextBlockFast(_PREHASH_Data);
- msg->addU32Fast(_PREHASH_Flags, AL_ACCESS);
- msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
- msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
- msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
- msg->addS32Fast(_PREHASH_Sections, num_sections);
- start_message = FALSE;
-
- if (initial && (cit == end))
- {
- // pack an empty block if there will be no data
- msg->nextBlockFast(_PREHASH_List);
- msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
- msg->addS32Fast(_PREHASH_Time, 0 );
- msg->addU32Fast(_PREHASH_Flags, 0 );
- }
+ sendParcelAccessListUpdate(AL_ACCESS, parcel->mAccessList, region, parcel->getLocalID());
+ }
- initial = FALSE;
- sequence_id++;
+ if (which & AL_BAN)
+ {
+ sendParcelAccessListUpdate(AL_BAN, parcel->mBanList, region, parcel->getLocalID());
+ }
- }
-
- while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES))
- {
+ if(which & AL_ALLOW_EXPERIENCE)
+ {
+ sendParcelAccessListUpdate(AL_ALLOW_EXPERIENCE, parcel->getExperienceKeysByType(EXPERIENCE_KEY_TYPE_ALLOWED), region, parcel->getLocalID());
+ }
+ if(which & AL_BLOCK_EXPERIENCE)
+ {
+ sendParcelAccessListUpdate(AL_BLOCK_EXPERIENCE, parcel->getExperienceKeysByType(EXPERIENCE_KEY_TYPE_BLOCKED), region, parcel->getLocalID());
+ }
+}
- const LLAccessEntry& entry = (*cit).second;
-
- msg->nextBlockFast(_PREHASH_List);
- msg->addUUIDFast(_PREHASH_ID, entry.mID );
- msg->addS32Fast(_PREHASH_Time, entry.mTime );
- msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
- ++cit;
- }
+void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 flags, const LLAccessEntry::map& entries, LLViewerRegion* region, S32 parcel_local_id)
+{
+ S32 count = entries.size();
+ S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
+ S32 sequence_id = 1;
+ BOOL start_message = TRUE;
+ BOOL initial = TRUE;
- start_message = TRUE;
- msg->sendReliable( region->getHost() );
- }
- }
+ LLUUID transactionUUID;
+ transactionUUID.generate();
- if (which & AL_BAN)
- {
- S32 count = parcel->mBanList.size();
- S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
- S32 sequence_id = 1;
- BOOL start_message = TRUE;
- BOOL initial = TRUE;
-
- access_map_const_iterator cit = parcel->mBanList.begin();
- access_map_const_iterator end = parcel->mBanList.end();
- while ( (cit != end) || initial )
- {
- if (start_message)
- {
- msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
- msg->nextBlockFast(_PREHASH_Data);
- msg->addU32Fast(_PREHASH_Flags, AL_BAN);
- msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
- msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
- msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
- msg->addS32Fast(_PREHASH_Sections, num_sections);
- start_message = FALSE;
-
- if (initial && (cit == end))
- {
- // pack an empty block if there will be no data
- msg->nextBlockFast(_PREHASH_List);
- msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
- msg->addS32Fast(_PREHASH_Time, 0 );
- msg->addU32Fast(_PREHASH_Flags, 0 );
- }
- initial = FALSE;
- sequence_id++;
+ LLMessageSystem* msg = gMessageSystem;
- }
-
- while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES))
+ LLAccessEntry::map::const_iterator cit = entries.begin();
+ LLAccessEntry::map::const_iterator end = entries.end();
+ while ( (cit != end) || initial )
+ {
+ if (start_message)
+ {
+ msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addU32Fast(_PREHASH_Flags, flags);
+ msg->addS32(_PREHASH_LocalID, parcel_local_id);
+ msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
+ msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
+ msg->addS32Fast(_PREHASH_Sections, num_sections);
+ start_message = FALSE;
+
+ if (initial && (cit == end))
{
- const LLAccessEntry& entry = (*cit).second;
-
+ // pack an empty block if there will be no data
msg->nextBlockFast(_PREHASH_List);
- msg->addUUIDFast(_PREHASH_ID, entry.mID );
- msg->addS32Fast(_PREHASH_Time, entry.mTime );
- msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
- ++cit;
+ msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
+ msg->addS32Fast(_PREHASH_Time, 0 );
+ msg->addU32Fast(_PREHASH_Flags, 0 );
}
- start_message = TRUE;
- msg->sendReliable( region->getHost() );
+ initial = FALSE;
+ sequence_id++;
+
}
+
+ while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES))
+ {
+ const LLAccessEntry& entry = (*cit).second;
+
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, entry.mID );
+ msg->addS32Fast(_PREHASH_Time, entry.mTime );
+ msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
+ ++cit;
+ }
+
+ start_message = TRUE;
+ msg->sendReliable( region->getHost() );
}
}
+
void LLViewerParcelMgr::deedLandToGroup()
{
std::string group_name;
diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h
index 58d398e141..e2c4bad313 100755
--- a/indra/newview/llviewerparcelmgr.h
+++ b/indra/newview/llviewerparcelmgr.h
@@ -218,7 +218,7 @@ public:
// Takes an Access List flag, like AL_ACCESS or AL_BAN
void sendParcelAccessListUpdate(U32 which);
-
+
// Takes an Access List flag, like AL_ACCESS or AL_BAN
void sendParcelAccessListRequest(U32 flags);
@@ -291,6 +291,8 @@ public:
static BOOL isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power);
private:
+ static void sendParcelAccessListUpdate(U32 flags, const std::map<LLUUID, class LLAccessEntry>& entries, LLViewerRegion* region, S32 parcel_local_id);
+ static void sendParcelExperienceUpdate( const U32 flags, uuid_vec_t experience_ids, LLViewerRegion* region, S32 parcel_local_id );
static bool releaseAlertCB(const LLSD& notification, const LLSD& response);
// If the user is claiming land and the current selection
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index cd5f64b9ca..f70ed68e49 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -2703,8 +2703,21 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
}
capabilityNames.append("GetDisplayNames");
+ capabilityNames.append("GetExperiences");
+ capabilityNames.append("AgentExperiences");
+ capabilityNames.append("FindExperienceByName");
+ capabilityNames.append("GetExperienceInfo");
+ capabilityNames.append("GetAdminExperiences");
+ capabilityNames.append("GetCreatorExperiences");
+ capabilityNames.append("ExperiencePreferences");
+ capabilityNames.append("GroupExperiences");
+ capabilityNames.append("UpdateExperience");
+ capabilityNames.append("IsExperienceAdmin");
+ capabilityNames.append("IsExperienceContributor");
+ capabilityNames.append("RegionExperiences");
capabilityNames.append("GetMesh");
capabilityNames.append("GetMesh2");
+ capabilityNames.append("GetMetadata");
capabilityNames.append("GetObjectCost");
capabilityNames.append("GetObjectPhysicsData");
capabilityNames.append("GetTexture");
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 9f42776d78..9e66086f6d 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -43,6 +43,7 @@
#include "llanimationstates.h"
#include "llavatarnamecache.h"
#include "llavatarpropertiesprocessor.h"
+#include "llexperiencecache.h"
#include "llphysicsmotion.h"
#include "llviewercontrol.h"
#include "llcallingcard.h" // IDEVO for LLAvatarTracker
@@ -2150,7 +2151,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)
idleUpdateBelowWater(); // wind effect uses this
idleUpdateWindEffect();
}
-
+
idleUpdateNameTag( root_pos_last );
idleUpdateRenderCost();
}
diff --git a/indra/newview/roles_constants.h b/indra/newview/roles_constants.h
index 8fd7978fa1..24792dd731 100644
--- a/indra/newview/roles_constants.h
+++ b/indra/newview/roles_constants.h
@@ -145,6 +145,9 @@ const U64 GP_SESSION_JOIN = 0x1LL << 16; //can join session
const U64 GP_SESSION_VOICE = 0x1LL << 27; //can hear/talk
const U64 GP_SESSION_MODERATOR = 0x1LL << 37; //can mute people's session
+const U64 GP_EXPERIENCE_ADMIN = 0x1LL << 49; // has admin rights to any experiences owned by this group
+const U64 GP_EXPERIENCE_CREATOR = 0x1LL << 50; // can sign scripts for experiences owned by this group
+
// Group Banning
const U64 GP_GROUP_BAN_ACCESS = 0x1LL << 51; // Allows access to ban / un-ban agents from a group.
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 46698b3949..393c8314b2 100755
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -222,6 +222,7 @@ with the same filename but different name
<texture name="ForwardArrow_Off" file_name="icons/ForwardArrow_Off.png" preload="false" />
<texture name="ForwardArrow_Press" file_name="icons/ForwardArrow_Press.png" preload="false" />
+ <texture name="Generic_Experience" file_name="Blank.png" preload="false" />
<texture name="Generic_Group" file_name="icons/Generic_Group.png" preload="false" />
<texture name="Generic_Group_Large" file_name="icons/Generic_Group_Large.png" preload="false" />
<texture name="icon_group.tga" file_name="icons/Generic_Group.png" preload="false" />
diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml
index a660e812cc..62ab8ed193 100755
--- a/indra/newview/skins/default/xui/en/floater_about_land.xml
+++ b/indra/newview/skins/default/xui/en/floater_about_land.xml
@@ -2096,5 +2096,17 @@ Only large parcels can be listed in search.
width="100" />
</panel>
</panel>
+ <panel
+ border="true"
+ follows="all"
+ label="EXPERIENCES"
+ layout="topleft"
+ left="0"
+ top="0"
+ help_topic="land_experiences_tab"
+ name="land_experiences_panel"
+ class="land_experiences_panel"
+ filename="panel_region_experiences.xml">
+ </panel>
</tab_container>
</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_experience_search.xml b/indra/newview/skins/default/xui/en/floater_experience_search.xml
new file mode 100644
index 0000000000..15a4b5665e
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_experience_search.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ positioning="cascading"
+ legacy_header_height="18"
+ can_resize="true"
+ height="350"
+ layout="topleft"
+ min_height="200"
+ min_width="400"
+ name="experiencepicker"
+ help_topic="experiencepicker"
+ title="CHOOSE EXPERIENCE"
+ width="350">
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_experienceprofile.xml b/indra/newview/skins/default/xui/en/floater_experienceprofile.xml
new file mode 100644
index 0000000000..d146d1306f
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_experienceprofile.xml
@@ -0,0 +1,687 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<floater
+ positioning="cascading"
+ can_close="true"
+ enabled="true"
+ can_resize="true"
+ help_topic="floater_experienceprofile"
+ title="EXPERIENCE PROFILE"
+ save_rect="true"
+ min_width="325"
+ min_height="325"
+ width="358"
+ height="650">
+ <floater.string
+ name="empty_slurl">
+ (none)
+ </floater.string>
+ <floater.string
+ name="maturity_icon_general">
+ "Parcel_PG_Light"
+ </floater.string>
+ <floater.string
+ name="maturity_icon_moderate">
+ "Parcel_M_Light"
+ </floater.string>
+ <floater.string
+ name="maturity_icon_adult">
+ "Parcel_R_Light"
+ </floater.string>
+ <text
+ follows="top|left|right"
+ font="SansSerifHugeBold"
+ height="26"
+ layout="topleft"
+ left_pad="4"
+ name="edit_title"
+ top="2"
+ value="Experience Profile"
+ use_ellipses="true"
+ left="6"
+ right="-3"/>
+ <tab_container
+ hide_tabs="true"
+ follows="all"
+ height="615"
+ layout="topleft"
+ left="5"
+ min_height="250"
+ top_pad="3"
+ width="348"
+ name="tab_container">
+ <panel
+ background_visible="true"
+ follows="all"
+ height="540"
+ layout="topleft"
+ left="0"
+ min_height="250"
+ top="0"
+ width="348"
+ name="panel_experience_info">
+ <scroll_container
+ color="DkGray2"
+ follows="all"
+ height="520"
+ layout="topleft"
+ left="9"
+ name="xp_scroll"
+ opaque="true"
+ top_pad="10"
+ width="330">
+ <panel
+ bg_alpha_color="DkGray2"
+ follows="top|left|right"
+ height="550"
+ layout="topleft"
+ left="0"
+ name="scrolling_panel"
+ top="0"
+ width="315"
+ min_width="315">
+ <layout_stack
+ follows="all"
+ height="550"
+ layout="topleft"
+ left="0"
+ animate="false"
+ top="0"
+ orientation="vertical"
+ width="315">
+ <layout_panel
+ follows="all"
+ height="29"
+ layout="topleft"
+ left="0"
+ top="0"
+ auto_resize="false"
+ visible="false"
+ width="315"
+ name="top panel">
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ font="SansSerif"
+ height="19"
+ top="10"
+ layout="topleft"
+ left="10"
+ right="-123"
+ visible="false"
+ name="grid_wide"/>
+ <button
+ follows="top|right"
+ height="23"
+ label="Edit"
+ layout="topleft"
+ name="edit_btn"
+ width="100"
+ visible="false"
+ top_pad="-23"
+ right="-14"/>
+ </layout_panel>
+ <layout_panel
+ follows="all"
+ height="197"
+ layout="topleft"
+ left="0"
+ top="0"
+ auto_resize="false"
+ visible="false"
+ width="315"
+ name="image_panel">
+ <texture_picker
+ enabled="false"
+ fallback_image="default_land_picture.j2c"
+ follows="left|top"
+ height="197"
+ layout="topleft"
+ left="10"
+ name="logo"
+ top="10"
+ width="290" />
+ </layout_panel>
+ <layout_panel
+ follows="all"
+ height="19"
+ layout="topleft"
+ left="0"
+ top="5"
+ width="313"
+ auto_resize="false">
+ <text
+ follows="left|top|right"
+ font="SansSerifLarge"
+ height="14"
+ layout="topleft"
+ left="10"
+ name="experience_title"
+ top="0"
+ use_ellipses="true"
+ value=""
+ width="288"/>
+ </layout_panel>
+ <layout_panel
+ follows=""
+ height="50"
+ layout="topleft"
+ left="0"
+ top="0"
+ auto_resize="false"
+ width="315"
+ visible="false"
+ name="description panel">
+ <expandable_text
+ follows="left|top|right"
+ font="SansSerif"
+ height="50"
+ layout="topleft"
+ left="7"
+ name="experience_description"
+ top="0"
+ value=""
+ width="293"/>
+ </layout_panel>
+ <layout_panel
+ follows=""
+ height="18"
+ layout="topleft"
+ left="0"
+ top="0"
+ auto_resize="false"
+ width="315"
+ visible="true"
+ name="maturity panel">
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="ContentRating"
+ width="75">
+ Rating:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left_pad="2"
+ valign="center"
+ name="ContentRatingText"
+ top_delta="-3"
+ width="188">
+ </text>
+ </layout_panel>
+ <layout_panel
+ follows="all"
+ height="46"
+ layout="topleft"
+ left="0"
+ top="5"
+ width="313"
+ visible="false"
+ auto_resize="false"
+ name="location panel">
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="Location"
+ width="290">
+ Location:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left="10"
+ valign="center"
+ use_ellipses="true"
+ name="LocationTextText"
+ width="288">
+ </text>
+ </layout_panel>
+ <layout_panel
+ follows="all"
+ height="53"
+ layout="topleft"
+ left="0"
+ top="5"
+ width="313"
+ visible="false"
+ auto_resize="false"
+ name="marketplace panel">
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ width="290">
+ Marketplace store:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left="10"
+ valign="center"
+ use_ellipses="true"
+ name="marketplace"
+ width="288">
+ </text>
+ </layout_panel>
+ <layout_panel
+ follows="left|top|right"
+ height="18"
+ left="0"
+ top="0"
+ auto_resize="false"
+ width="315">
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="Owner"
+ width="75">
+ Owner:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left_pad="2"
+ valign="center"
+ name="OwnerText"
+ use_ellipses="true"
+ top_delta="-2"
+ width="188">
+ </text>
+ </layout_panel>
+ <layout_panel
+ follows="all"
+ height="18"
+ layout="topleft"
+ left="0"
+ top="5"
+ width="313"
+ visible="false"
+ auto_resize="false"
+ name="group_panel">
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="Group"
+ width="75">
+ Group:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left_pad="2"
+ valign="center"
+ name="GroupText"
+ use_ellipses="true"
+ top_delta="-2"
+ width="188">
+ </text>
+ </layout_panel>
+ <layout_panel
+ follows="all"
+ height="75"
+ layout="topleft"
+ left="0"
+ top="5"
+ width="313"
+ auto_resize="false"
+ visible="true"
+ name="perm panel">
+ <button
+ follows="bottom|left"
+ height="23"
+ label="Allow"
+ layout="topleft"
+ name="allow_btn"
+ width="94"
+ top_pad="3"
+ left="10"
+ enabled="false"/>
+ <button
+ follows="bottom|left"
+ height="23"
+ label="Forget"
+ layout="topleft"
+ name="forget_btn"
+ width="94"
+ top_pad="-23"
+ left_pad="3"
+ enabled="false"/>
+ <button
+ follows="bottom|left"
+ height="23"
+ label="Block"
+ layout="topleft"
+ name="block_btn"
+ width="94"
+ top_pad="-23"
+ left_pad="3"
+ enabled="false"/>
+ <text
+ type="string"
+ halign="center"
+ length="1"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="privileged"
+ visible="false"
+ width="288">
+ This experience is enabled for all residents.
+ </text>
+ <button
+ follows="bottom|left"
+ height="23"
+ label="Report Abuse"
+ layout="topleft"
+ name="report_btn"
+ width="94"
+ top_pad="3"
+ left="107"
+ enabled="true"/>
+ </layout_panel>
+ </layout_stack>
+ </panel>
+ </scroll_container>
+ </panel>
+ <panel
+ background_visible="true"
+ follows="all"
+ layout="topleft"
+ height="540"
+ left="0"
+ top="0"
+ width="348"
+ name="edit_panel_experience_info">
+ <scroll_container
+ color="DkGray2"
+ follows="all"
+ height="520"
+ layout="topleft"
+ left="9"
+ name="edit_xp_scroll"
+ opaque="true"
+ top_pad="10"
+ width="330">
+ <panel
+ bg_alpha_color="DkGray2"
+ follows="top|left|right"
+ height="590"
+ layout="topleft"
+ left="0"
+ name="edit_scrolling_panel"
+ top="0"
+ width="310">
+ <texture_picker
+ enabled="true"
+ fallback_image="default_land_picture.j2c"
+ follows="left|top"
+ height="197"
+ layout="topleft"
+ left="10"
+ name="edit_logo"
+ top="10"
+ width="290" />
+ <text
+ follows="left|top|right"
+ height="14"
+ layout="topleft"
+ left="10"
+ name="edit_experience_title_label"
+ use_ellipses="true"
+ value="Name:"
+ right="-10"/>
+ <line_editor
+ follows="left|top|right"
+ height="19"
+ layout="topleft"
+ left="10"
+ name="edit_experience_title"
+ max_length_bytes="63"
+ text_color="black"
+ right="-10"/>
+ <text
+ follows="left|top|right"
+ height="14"
+ layout="topleft"
+ left="10"
+ top_pad="10"
+ name="edit_experience_desc_label"
+ use_ellipses="true"
+ value="Description:"
+ right="-10"/>
+ <text_editor
+ follows="left|top|right"
+ height="57"
+ layout="topleft"
+ left="11"
+ name="edit_experience_description"
+ max_length="2048"
+ text_color="black"
+ right="-11"
+ word_wrap="true"/>
+ <button
+ top_pad="10"
+ left="10"
+ width="125"
+ height="23"
+ name="Group_btn"
+ label="Group"
+ />
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="14"
+ layout="topleft"
+ left_pad="10"
+ top_pad="-18"
+ use_ellipses="true"
+ name="edit_GroupText"
+ right="-10" />
+ <text
+ top_pad="10"
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="edit_ContentRating"
+ right="-10">
+ Rating:
+ </text>
+ <icons_combo_box
+ follows="right|top"
+ height="20"
+ label="Moderate"
+ layout="topleft"
+ right="-10"
+ top_pad="-19"
+ tool_tip="Increasing the maturity rating on an experience will reset permission for all residents which have allowed the experience."
+ name="edit_ContentRatingText"
+ width="105">
+ <icons_combo_box.drop_down_button
+ image_overlay="Parcel_M_Light"
+ image_overlay_alignment="left"
+ imgoverlay_label_space="3"
+ pad_left="3"/>
+ <icons_combo_box.item
+ label="Adult"
+ name="Adult"
+ value="42">
+ <item.columns
+ halign="center"
+ type="icon"
+ value="Parcel_R_Light"
+ width="20"/>
+ </icons_combo_box.item>
+ <icons_combo_box.item
+ label="Moderate"
+ name="Mature"
+ value="21">
+ <item.columns
+ halign="center"
+ type="icon"
+ value="Parcel_M_Light"
+ width="20"/>
+ </icons_combo_box.item>
+ <icons_combo_box.item
+ label="General"
+ name="PG"
+ value="13">
+ <item.columns
+ halign="center"
+ type="icon"
+ value="Parcel_PG_Light"
+ width="20"/>
+ </icons_combo_box.item>
+ </icons_combo_box>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="14"
+ layout="topleft"
+ left="10"
+ top_pad="10"
+ name="edit_Location"
+ right="90">
+ Location:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="14"
+ layout="topleft"
+ left_pad="5"
+ top_pad="-14"
+ use_ellipses="true"
+ name="edit_LocationTextText"
+ right="-10" />
+ <button
+ left="10"
+ width="125"
+ height="23"
+ name="location_btn"
+ label="Set to Current"
+ />
+ <button
+ top_pad="-23"
+ follows="top|right"
+ right="-10"
+ width="125"
+ name="clear_btn"
+ label="Clear Location"/>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="14"
+ top_pad="10"
+ layout="topleft"
+ left="10"
+ right="-10">
+ Marketplace store:
+ </text>
+ <line_editor
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="19"
+ layout="topleft"
+ left="10"
+ max_length_bytes="255"
+ valign="center"
+ name="edit_marketplace"
+ right="-10"/>
+ <check_box width="140"
+ height="21"
+ left="10"
+ layout="topleft"
+ follows="top|left"
+ tool_tip=""
+ label="Enable Experience"
+ name="edit_enable_btn"/>
+ <check_box width="125"
+ height="21"
+ top_pad="-21"
+ right="-10"
+ visible="false"
+ layout="topleft"
+ follows="top|left|right"
+ label="Hide In Search"
+ name="edit_private_btn"/>
+ <text
+ follows="left|top|right"
+ height="25"
+ layout="topleft"
+ left="10"
+ top_pad="10"
+ name="changes"
+ use_ellipses="true"
+ word_wrap="true"
+ value="Experience changes may take several minutes to be seen on all regions."
+ right="-10"/>
+ <button
+ follows="top|left"
+ height="23"
+ label="Back"
+ layout="topleft"
+ name="cancel_btn"
+ width="125"
+ top_pad="15"
+ left="10"
+ visible="true"/>
+ <button
+ follows="top|right"
+ height="23"
+ label="Save"
+ layout="topleft"
+ name="save_btn"
+ top_pad="-23"
+ width="125"
+ right="-10"
+ visible="true"/>
+ </panel>
+ </scroll_container>
+ </panel>
+ </tab_container>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_experiences.xml b/indra/newview/skins/default/xui/en/floater_experiences.xml
new file mode 100644
index 0000000000..70e7507907
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_experiences.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<floater
+ can_close="true"
+ can_resize="true"
+ height="400"
+ width="500"
+ min_height="300"
+ min_width="500"
+ layout="topleft"
+ help_topic="floater_experiences"
+ name="floater_experiences"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="false"
+ bg_opaque_color="0 0.5 0 0.3"
+ title="EXPERIENCES">
+ <tab_container
+ top="3"
+ left="3"
+ layout="topleft"
+ right="-3"
+ follows="all"
+ height="394"
+ name="xp_tabs">
+ </tab_container>
+
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml b/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml
index 5cd7cd196d..40b47dc1d7 100755
--- a/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml
+++ b/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml
@@ -1,71 +1,114 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
- legacy_header_height="18"
- bevel_style="none"
- border_style="line"
- can_resize="true"
- height="580"
- layout="topleft"
- min_height="271"
- min_width="290"
- name="script ed float"
- help_topic="script_ed_float"
- save_rect="true"
- title="SCRIPT: NEW SCRIPT"
- width="508">
- <floater.string
- name="not_allowed">
- You can not view or edit this script, since it has been set as &quot;no copy&quot;. You need full permissions to view or edit a script inside an object.
- </floater.string>
- <floater.string
- name="script_running">
- Running
- </floater.string>
- <floater.string
- name="Title">
- SCRIPT: [NAME]
- </floater.string>
- <panel
- bevel_style="none"
+ legacy_header_height="18"
+ bevel_style="none"
+ border_style="line"
+ can_resize="true"
+ height="582"
+ layout="topleft"
+ min_height="271"
+ min_width="328"
+ name="script ed float"
+ help_topic="script_ed_float"
+ save_rect="true"
+ title="SCRIPT: NEW SCRIPT"
+ width="508">
+ <floater.string
+ name="not_allowed">
+ You can not view or edit this script, since it has been set as &quot;no copy&quot;. You need full permissions to view or edit a script inside an object.
+ </floater.string>
+ <floater.string
+ name="script_running">
+ Running
+ </floater.string>
+ <floater.string
+ name="Title">
+ SCRIPT: [NAME]
+ </floater.string>
+ <floater.string
+ name="experience_enabled">
+ Uncheck to remove the current experience
+ </floater.string>
+ <floater.string
+ name="no_experiences">
+ You are not authorized for any experiences
+ </floater.string>
+ <floater.string
+ name="add_experiences">
+ Select to add an experience
+ </floater.string>
+ <floater.string
+ name="loading">
+ Loading...
+ </floater.string>
+ <panel
+ bevel_style="none"
- border_style="line"
- follows="left|top|right|bottom"
- height="522"
- layout="topleft"
- left="10"
- name="script ed panel"
- top="20"
- width="497" />
- <button
- follows="left|bottom"
- height="23"
- label="Reset"
- label_selected="Reset"
- layout="topleft"
- name="Reset"
- left="10"
- width="85" />
- <check_box
+ border_style="line"
+ follows="left|top|right|bottom"
+ height="499"
+ layout="topleft"
+ left="10"
+ name="script ed panel"
+ top="16"
+ width="501" />
+ <button
+ follows="left|bottom"
+ height="23"
+ label="Reset"
+ label_selected="Reset"
+ layout="topleft"
+ name="Reset"
+ left="10"
+ width="85" />
+ <check_box
left_delta="90"
top_delta="3"
- enabled="false"
- follows="left|bottom"
- font="SansSerif"
- height="18"
- initial_value="true"
- label="Running"
- layout="topleft"
- name="running"
- width="205" />
- <check_box
+ enabled="false"
+ follows="left|bottom"
+ font="SansSerif"
+ height="18"
+ initial_value="true"
+ label="Running"
+ layout="topleft"
+ name="running"
+ width="205" />
+ <check_box
left_delta="140"
- enabled="true"
- follows="left|bottom"
- font="SansSerif"
- height="18"
- initial_value="true"
- label="Mono"
- layout="topleft"
- name="mono"
- width="100" />
+ enabled="true"
+ follows="left|bottom"
+ font="SansSerif"
+ height="18"
+ initial_value="true"
+ label="Mono"
+ layout="topleft"
+ name="mono"
+ width="100" />
+ <check_box width="130"
+ height="21"
+ enabled="false"
+ left="9"
+ top_pad="10"
+ layout="topleft"
+ follows="bottom|left"
+ label="Use Experience:"
+ name="enable_xp"/>
+ <combo_box
+ label=""
+ top_pad="-21"
+ left="149"
+ right="467"
+ layout="topleft"
+ follows="left|bottom|right"
+ visible="false"
+ name="Experiences..."/>
+ <button label="&gt;"
+ name="view_profile"
+ height="23"
+ width="23"
+ right="496"
+ layout="topleft"
+ top_pad="-23"
+ follows="right"
+ visible="false"/>
</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_url_experience.xml b/indra/newview/skins/default/xui/en/menu_url_experience.xml
new file mode 100644
index 0000000000..f4d50e1603
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_url_experience.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<context_menu
+ layout="topleft"
+ name="Url Popup">
+ <menu_item_call
+ label="Copy SLurl to clipboard"
+ layout="topleft"
+ name="url_copy">
+ <menu_item_call.on_click
+ function="Url.CopyUrl" />
+ </menu_item_call>
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index c8fcda9858..e226a88b30 100755
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -66,6 +66,13 @@
function="Floater.ToggleOrBringToFront"
parameter="picks" />
</menu_item_call>
+ <menu_item_call
+ label="Experiences..."
+ name="Experiences">
+ <menu_item_call.on_click
+ function="Floater.ToggleOrBringToFront"
+ parameter="experiences"/>
+ </menu_item_call>
<menu_item_separator/>
<menu_item_call
label="Camera Controls..."
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 772d81c448..c910f2eae8 100755
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -4410,6 +4410,90 @@ Remove estate manager for this estate only or for [ALL_ESTATES]?
<notification
icon="alert.tga"
+ label="Select estate"
+ name="EstateAllowedExperienceAdd"
+ type="alert">
+ Add to allowed list for this estate only or for [ALL_ESTATES]?
+ <tag>confirm</tag>
+ <usetemplate
+ canceltext="Cancel"
+ name="yesnocancelbuttons"
+ notext="All Estates"
+ yestext="This Estate"/>
+ </notification>
+
+ <notification
+ icon="alert.tga"
+ label="Select estate"
+ name="EstateAllowedExperienceRemove"
+ type="alert">
+ Remove from allowed list for this estate only or for [ALL_ESTATES]?
+ <tag>confirm</tag>
+ <usetemplate
+ canceltext="Cancel"
+ name="yesnocancelbuttons"
+ notext="All Estates"
+ yestext="This Estate"/>
+ </notification>
+
+ <notification
+ icon="alert.tga"
+ label="Select estate"
+ name="EstateBlockedExperienceAdd"
+ type="alert">
+ Add to blocked list for this estate only or for [ALL_ESTATES]?
+ <tag>confirm</tag>
+ <usetemplate
+ canceltext="Cancel"
+ name="yesnocancelbuttons"
+ notext="All Estates"
+ yestext="This Estate"/>
+ </notification>
+
+ <notification
+ icon="alert.tga"
+ label="Select estate"
+ name="EstateBlockedExperienceRemove"
+ type="alert">
+ Remove from blocked list for this estate only or for [ALL_ESTATES]?
+ <tag>confirm</tag>
+ <usetemplate
+ canceltext="Cancel"
+ name="yesnocancelbuttons"
+ notext="All Estates"
+ yestext="This Estate"/>
+ </notification>
+
+ <notification
+ icon="alert.tga"
+ label="Select estate"
+ name="EstateTrustedExperienceAdd"
+ type="alert">
+ Add to trusted list for this estate only or for [ALL_ESTATES]?
+ <tag>confirm</tag>
+ <usetemplate
+ canceltext="Cancel"
+ name="yesnocancelbuttons"
+ notext="All Estates"
+ yestext="This Estate"/>
+ </notification>
+
+ <notification
+ icon="alert.tga"
+ label="Select estate"
+ name="EstateTrustedExperienceRemove"
+ type="alert">
+ Remove from trusted list for this estate only or for [ALL_ESTATES]?
+ <tag>confirm</tag>
+ <usetemplate
+ canceltext="Cancel"
+ name="yesnocancelbuttons"
+ notext="All Estates"
+ yestext="This Estate"/>
+ </notification>
+
+ <notification
+ icon="alert.tga"
label="Confirm Kick"
name="EstateKickUser"
type="alert">
@@ -7072,6 +7156,152 @@ Is this OK?
</notification>
<notification
+ icon="alertmodal.tga"
+ name="ExperienceAcquireFailed"
+ type="alertmodal">
+Unable to acquire a new experience:
+ [ERROR_MESSAGE]
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="NotInGroupExperienceProfileMessage"
+ persist="false"
+ type="notify">
+ A change to the experience group was ignored because the owner is not a member of the selected group.
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="UneditableExperienceProfileMessage"
+ persist="false"
+ type="notify">
+ The uneditable field '[field]' was ignored when updating the experience profile.
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="RestrictedToOwnerExperienceProfileMessage"
+ persist="false"
+ type="notify">
+ Ignored changes to the field '[field]' which can only be set by the experience owner.
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="MaturityRatingExceedsOwnerExperienceProfileMessage"
+ persist="false"
+ type="notify">
+ You may not set the maturity rating of an experience higher than that of the owner.
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="RestrictedTermExperienceProfileMessage"
+ persist="false"
+ type="notify">
+ The following terms prevented the update of the experience profile name and/or description: [extra_info]
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="TeleportedHomeExperienceRemoved"
+ persist="false"
+ type="notify">
+ You have been teleported from the region [region_name] for removing the experience secondlife:///app/experience/[public_id]/profile and are no longer permitted in the region.
+ <form name="form">
+ <ignore name="ignore"
+ text="Kicked from region for removing an experience"/>
+ </form>
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="TrustedExperienceEntry"
+ persist="false"
+ type="notify">
+ You have been allowed into the region [region_name] by participating in the trusted experience secondlife:///app/experience/[public_id]/profile removing this experience may kick you from the region.
+ <form name="form">
+ <ignore name="ignore"
+ text="Allowed into a region by an experience"/>
+ </form>
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="TrustedExperiencesAvailable"
+ persist="false"
+ type="notify">
+You do not have access to this destination. You may be allowed into the region by Accepting an experience below:
+
+[EXPERIENCE_LIST]
+ </notification>
+
+
+ <notification
+ icon="notify.tga"
+ name="ExperienceEvent"
+ persist="false"
+ type="notifytip">
+ An object was allowed to [EventType] by the secondlife:///app/experience/[public_id]/profile experience.
+ Owner: secondlife:///app/agent/[OwnerID]/inspect
+ Object Name: [ObjectName]
+ Parcel Name: [ParcelName]
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="ExperienceEventAttachment"
+ persist="false"
+ type="notifytip">
+ An attachment was allowed to [EventType] by the secondlife:///app/experience/[public_id]/profile experience.
+ Owner: secondlife:///app/agent/[OwnerID]/inspect
+ </notification>
+
+ <notification
+ icon="notify.tga"
+ name="ScriptQuestionExperience"
+ persist="false"
+ type="notify">
+&apos;&lt;nolink&gt;[OBJECTNAME]&lt;/nolink&gt;&apos;, an object owned by &apos;[NAME]&apos;, requests your participation in the [GRID_WIDE]experience:
+
+[EXPERIENCE]
+
+Once permission is granted you will not see this message again for this experience unless it is revoked from the experience profile.
+
+Scripts associated with this experience will be able to do the following on regions where the experience is active:
+
+[QUESTIONS]Is this OK?
+
+ <unique combine="combine_with_new">
+ <context>experience</context>
+ </unique>
+ <tag>confirm</tag>
+ <form name="form">
+ <button
+ index="3"
+ name="BlockExperience"
+ text="Block Experience"/>
+ <button
+ index="2"
+ name="Mute"
+ text="Block Object"/>
+ <button
+ index="0"
+ name="Yes"
+ text="Yes"/>
+ <button
+ index="1"
+ name="No"
+ text="No"/>
+ </form>
+ </notification>
+
+ <notification
icon="notify.tga"
name="ScriptQuestionCaution"
priority="critical"
diff --git a/indra/newview/skins/default/xui/en/panel_experience_info.xml b/indra/newview/skins/default/xui/en/panel_experience_info.xml
new file mode 100644
index 0000000000..70d8a4fc64
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_experience_info.xml
@@ -0,0 +1,377 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ background_visible="true"
+ follows="all"
+ height="570"
+ layout="topleft"
+ left="5"
+ min_height="350"
+ top="5"
+ width="348"
+ name="panel_experience_info">
+ <text
+ follows="top|left|right"
+ font="SansSerifHugeBold"
+ height="26"
+ layout="topleft"
+ left_pad="4"
+ name="title"
+ text_color="White"
+ top="2"
+ value="Experience Profile"
+ use_ellipses="true"
+ left="3"
+ right="-3"/>
+ <scroll_container
+ color="DkGray2"
+ follows="all"
+ height="532"
+ layout="topleft"
+ left="9"
+ name="xp_scroll"
+ opaque="true"
+ top_pad="10"
+ width="330">
+ <panel
+ bg_alpha_color="DkGray2"
+ follows="top|left"
+ height="480"
+ layout="topleft"
+ left="0"
+ min_height="480"
+ name="scrolling_panel"
+ top="0"
+ width="315"
+ min_width="315">
+ <layout_stack
+ follows="all"
+ height="480"
+ layout="topleft"
+ left="0"
+ top="0"
+ orientation="vertical"
+ width="315">
+ <layout_panel
+ follows="all"
+ height="197"
+ layout="topleft"
+ left="0"
+ top="0"
+ auto_resize="false"
+ visible="true"
+ width="315"
+ name="image_panel">
+ <texture_picker
+ enabled="false"
+ fallback_image="default_land_picture.j2c"
+ follows="left|top"
+ height="197"
+ layout="topleft"
+ left="10"
+ name="logo"
+ top="10"
+ width="290" />
+ </layout_panel>
+ <layout_panel
+ follows="all"
+ height="19"
+ layout="topleft"
+ left="0"
+ top="5"
+ width="313"
+ auto_resize="false"
+ >
+ <text
+ follows="left|top|right"
+ font="SansSerifLarge"
+ height="14"
+ layout="topleft"
+ left="10"
+ name="experience_title"
+ text_color="white"
+ top="0"
+ use_ellipses="true"
+ value="Kyle's Superhero RPG"
+ width="288"/>
+ </layout_panel>
+ <layout_panel
+ follows=""
+ height="50"
+ layout="topleft"
+ left="0"
+ top="0"
+ auto_resize="false"
+ width="315"
+ name="description panel">
+ <expandable_text
+ follows="left|top|right"
+ font="SansSerif"
+ height="50"
+ layout="topleft"
+ left="7"
+ name="experience_description"
+ top="0"
+ value=""
+ width="293"/>
+ </layout_panel>
+ <layout_panel
+ follows="all"
+ height="69"
+ layout="topleft"
+ left="0"
+ top="5"
+ width="313"
+ visible="true"
+ auto_resize="false"
+ name="location panel"
+ >
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="Location"
+ width="290">
+ Location:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left="10"
+ valign="center"
+ name="LocationTextText"
+ width="288">
+ someplace
+ </text>
+ <button
+ follows="bottom|left"
+ height="23"
+ label="Teleport"
+ layout="topleft"
+ name="teleport_btn"
+ width="151"
+ left="10"/>
+ <button
+ follows="bottom|left"
+ height="23"
+ label="Map"
+ layout="topleft"
+ name="map_btn"
+ top_pad="-23"
+ width="101"
+ left_pad="5"/>
+ </layout_panel>
+ <layout_panel
+ follows="all"
+ height="53"
+ layout="topleft"
+ left="0"
+ top="5"
+ width="313"
+ visible="true"
+ auto_resize="false"
+ name="marketplace panel"
+
+ >
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="Location"
+ width="290">
+ Marketplace store:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left="10"
+ valign="center"
+ name="LocationTextText"
+ width="288">
+ someplace
+ </text>
+ </layout_panel>
+ <layout_panel
+ follows="left|top|right"
+ height="69"
+ left="0"
+ top="0"
+ auto_resize="false"
+ width="315"
+ >
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="ContentRating"
+ width="100">
+ Rating:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left_pad="2"
+ valign="center"
+ name="ContentRatingText"
+ top_delta="-2"
+ width="188">
+ Adult
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="Owner"
+ width="100">
+ Owner:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="18"
+ layout="topleft"
+ left_pad="2"
+ valign="center"
+ name="OwnerText"
+ top_delta="-2"
+ width="188">
+ Kyle
+ </text>
+ <button
+ follows="bottom|left"
+ height="23"
+ label="Edit"
+ layout="topleft"
+ name="edit_btn"
+ top_pad="0"
+ width="151"
+ left="10"/>
+ <!--
+
+
+ <expandable_text
+ allow_scroll="false"
+ bg_visible="false"
+ follows="left|top|right"
+ h_pad="0"
+ height="35"
+ width="280"
+ layout="topleft"
+ font="SansSerifBig"
+ font.style="BOLD"
+ left="10"
+ top_pad="10"
+ name="pick_name"
+ read_only="false"
+ text_color="white"
+ v_pad="0"
+ use_ellipses="true"
+ value="It's an experience" />
+
+ <text
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ top_pad="10"
+ value="Maturity level:"
+ width="130" />
+ <icon
+ follows="top|left"
+ height="16"
+ image_name="unknown"
+ layout="topleft"
+ left_pad="10"
+ name="maturity_icon"
+ top_pad="-18"
+ width="18" />
+ <text
+ follows="top|left"
+ height="16"
+ layout="topleft"
+ left_pad="5"
+ name="maturity_value"
+ top_pad="-14"
+ value="unknown"
+ width="118" />
+ <panel
+ follows="left|top|right"
+ name="location_panel"
+ left="10"
+ width="280"
+ visible="false"
+ top_pad="10"
+ height="42">
+ <text
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left="00"
+ top="0"
+ value="Location:"
+ width="280" />
+
+ <text
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left="0"
+ top_pad="10"
+ value="Location:"
+ width="280" />
+ </panel>
+
+ <panel
+ follows="left|top|right"
+ name="location_panel"
+ left="10"
+ width="280"
+ top_pad="10"
+ height="42">
+ <text
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left="00"
+ top="0"
+ value="Location:"
+ width="280" />
+
+ <text
+ follows="left|top|right"
+ height="16"
+ layout="topleft"
+ left="0"
+ top_pad="10"
+ value="Location:"
+ width="280" />
+ </panel>
+
+-->
+ </layout_panel>
+ </layout_stack>
+ </panel>
+ </scroll_container>
+
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_experience_list_editor.xml b/indra/newview/skins/default/xui/en/panel_experience_list_editor.xml
new file mode 100644
index 0000000000..128c721992
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_experience_list_editor.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ layout="topleft"
+ left="0"
+ top="0"
+ width="300"
+ height="100"
+ min_height="100"
+ follows="all"
+ border="true"
+ name="experince_list_editor">
+ <panel.string
+ name="loading">
+ loading...
+ </panel.string>
+ <panel.string
+ name="panel_allowed">
+ Allowed Experiences:
+ </panel.string>
+ <panel.string
+ name="panel_blocked">
+ Blocked Experiences:
+ </panel.string>
+ <panel.string
+ name="panel_trusted">
+ Trusted Experiences:
+ </panel.string>
+ <panel.string
+ name="no_results">
+ (empty)
+ </panel.string>
+ <text
+ layout="topleft"
+ name="text_name"
+ left="3"
+ right="120"
+ height="12"
+ follows="left|top">
+ Experience List
+ </text>
+ <text
+ layout="topleft"
+ name="text_count"
+ left="125"
+ top_pad="-12"
+ right="-1"
+ height="12"
+ follows="top|left">
+ </text>
+ <scroll_list
+ draw_heading="false"
+ left="3"
+ width="225"
+ height="77"
+ follows="all"
+ name="experience_list">
+ <columns
+ width="225"
+ user_resize="false"
+ name="experience_name"
+ label="Name"/>
+ </scroll_list>
+ <button
+ layout="topleft"
+ follows="top|right"
+ top_pad="-77"
+ left_pad="3"
+ width="65"
+ name="btn_add"
+ label="Add..."/>
+ <button
+ layout="topleft"
+ follows="top|right"
+ width="64"
+ name="btn_remove"
+ label="Remove"/>
+ <button
+ layout="topleft"
+ follows="top|right"
+ width="64"
+ name="btn_profile"
+ label="Profile..."/>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_experience_list_item.xml b/indra/newview/skins/default/xui/en/panel_experience_list_item.xml
new file mode 100644
index 0000000000..a9777c51e7
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_experience_list_item.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+
+<panel
+ layout="topleft"
+ top="100"
+ left="5"
+ width="100"
+ height="19"
+ label="Experiences"
+ follows="top|right|left">
+ <text
+ left="3"
+ top="3"
+ height="16"
+ width="177"
+ follows="all"
+ name="experience_name"
+ >
+ Dummy Name
+ </text>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_experience_log.xml b/indra/newview/skins/default/xui/en/panel_experience_log.xml
new file mode 100644
index 0000000000..6869a135d8
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_experience_log.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+
+<panel
+ layout="topleft"
+ top="3"
+ left="3"
+ width="500"
+ height="300"
+ label="EVENTS"
+ bg_opaque_color="0 0.5 0 0.3"
+ follows="all">
+ <string
+ name="no_events"
+ value="No events."/>
+ <string
+ name="loading"
+ value="loading..."/>
+ <layout_stack
+ layout="topleft"
+ top="0"
+ left="4"
+ right="-1"
+ bottom="-4"
+ orientation="vertical"
+ follows="all">
+ <layout_panel
+ layout="topleft"
+ top="0"
+ left="1"
+ right="-1"
+ height="250"
+ follows="all">
+ <scroll_list
+ draw_heading="true"
+ left="1"
+ right="-86"
+ height="250"
+ follows="all"
+ name="experience_log_list">
+ <columns
+ width="100"
+ user_resize="true"
+ name="time"
+ label="Time"/>
+ <columns
+ width="100"
+ user_resize="true"
+ name="event"
+ label="Event"/>
+ <columns
+ width="100"
+ user_resize="true"
+ name="experience_name"
+ label="Experience"/>
+ <columns
+ width="100"
+ user_resize="true"
+ name="object_name"
+ label="Object"/>
+ </scroll_list>
+
+ <button
+ layout="topleft"
+ follows="top|right"
+ name="btn_notify"
+ label="Notify"
+ top_pad="-225"
+ left_pad="5"
+ right="-1"
+ enabled="false"/>
+
+ <button
+ layout="topleft"
+ follows="top|right"
+ name="btn_profile_xp"
+ label="Profile"
+ top_pad="5"
+ right="-1"
+ enabled="false"/>
+
+ <button
+ layout="topleft"
+ follows="top|right"
+ name="btn_report_xp"
+ label="Report"
+ top_pad="5"
+ right="-1"
+ enabled="false"/>
+ </layout_panel>
+ <layout_panel
+ layout="topleft"
+ top="0"
+ left="1"
+ right="-1"
+ height="30"
+ min_height="30"
+ follows="all"
+ name="button_panel"
+ visible="true">
+ <check_box
+ top="0"
+ follows="top|left"
+ height="26"
+ label="Notify All Events Days"
+ width="140"
+ name="notify_all" />
+
+ <spinner
+ top="5"
+ control_name="LogDays"
+ decimal_digits="0"
+ follows="left|top"
+ height="23"
+ increment="1"
+ initial_value="7"
+ label_width="30"
+ layout="topleft"
+ left_pad="5"
+ max_val="14"
+ min_val="0"
+ name="logsizespinner"
+ width="40" />
+ <button
+ top="5"
+ left="280"
+ layout="topleft"
+ follows="top|left"
+ name="btn_clear"
+ label="Clear"
+ width="80"
+ enabled="false"/>
+ <button
+ top="5"
+ layout="topleft"
+ left_pad="5"
+ width="20"
+ follows="top|left"
+ name="btn_prev"
+ label="&lt;"
+ enabled="false"/>
+ <button
+ top="5"
+ layout="topleft"
+ follows="top|left"
+ name="btn_next"
+ label="&gt;"
+ left_pad="5"
+ width="20"
+ enabled="false"/>
+ </layout_panel>
+ </layout_stack>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_experience_search.xml b/indra/newview/skins/default/xui/en/panel_experience_search.xml
new file mode 100644
index 0000000000..99ad3e7fe3
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_experience_search.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ layout="topleft"
+ top="18"
+ left="0"
+ width="350"
+ height="330"
+ label="SEARCH"
+ follows="all">
+
+ <string
+ name="not_found">
+ &apos;[TEXT]&apos; not found
+ </string>
+ <string
+ name="no_results">
+ No results
+ </string>
+ <string
+ name="searching">
+ Searching...
+ </string>
+ <string
+ name="loading">
+ Loading...
+ </string>
+ <string
+ name="maturity_icon_general">
+ "Parcel_PG_Light"
+ </string>
+ <string
+ name="maturity_icon_moderate">
+ "Parcel_M_Light"
+ </string>
+ <string
+ name="maturity_icon_adult">
+ "Parcel_R_Light"
+ </string>
+ <panel
+ follows="all"
+ left="5"
+ right="-3"
+ bottom="-2"
+ top="0"
+ name="search_panel">
+ <text
+ left="0"
+ follows="top|left|right"
+ right="-1">
+ Enter part of the name:
+ </text>
+ <line_editor
+ left="0"
+ follows="left|top|right"
+ name="edit"
+ height="18"
+ right="-60"/>
+ <button label="Go"
+ follows="top|right"
+ top_pad="-18"
+ left_pad="2"
+ right="-1"
+ height="18"
+ name="find"/>
+ <text
+ top_pad="6"
+ left="0"
+ follows="top|left"
+ width="180">
+ Max Content Rating:
+ </text>
+ <icons_combo_box
+ follows="left|top"
+ height="20"
+ label="Moderate"
+ layout="topleft"
+ name="maturity"
+ top_pad="-13"
+ left_pad="0"
+ right="-1">
+ <icons_combo_box.drop_down_button
+ image_overlay="Parcel_M_Light"
+ image_overlay_alignment="left"
+ imgoverlay_label_space="3"
+ pad_left="3"/>
+ <icons_combo_box.item
+ label="Adult"
+ name="Adult"
+ value="42">
+ <item.columns
+ halign="center"
+ type="icon"
+ value="Parcel_R_Light"
+ width="20"/>
+ </icons_combo_box.item>
+ <icons_combo_box.item
+ label="Moderate"
+ name="Mature"
+ value="21">
+ <item.columns
+ halign="center"
+ type="icon"
+ value="Parcel_M_Light"
+ width="20"/>
+ </icons_combo_box.item>
+ <icons_combo_box.item
+ label="General"
+ name="PG"
+ value="13">
+ <item.columns
+ halign="center"
+ type="icon"
+ value="Parcel_PG_Light"
+ width="20"/>
+ </icons_combo_box.item>
+ </icons_combo_box>
+ <scroll_list
+ draw_heading="true"
+ left="0"
+ right="-1"
+ height="239"
+ top_pad="4"
+ follows="all"
+ column_padding="5"
+ name="search_results">
+ <columns
+ halign="center"
+ width="24"
+ name="maturity"
+ label=""/>
+ <columns
+ user_resize="true"
+ name="experience_name"
+ label="Name"/>
+ <columns
+ user_resize="true"
+ name="owner"
+ label="Owner"/>
+ </scroll_list>
+
+ <button
+ follows="left|bottom"
+ height="23"
+ label="OK"
+ label_selected="OK"
+ name="ok_btn"
+ top_pad="3"
+ left="0"
+ width="95" />
+ <button
+ follows="left|bottom"
+ height="23"
+ label="Cancel"
+ name="cancel_btn"
+ width="95"
+ left_pad="3" />
+ <button
+ follows="left|bottom"
+ height="23"
+ label="View Profile"
+ name="profile_btn"
+ width="95"
+ left_pad="3" />
+ <button
+ follows="left|bottom"
+ height="23"
+ label="&lt;"
+ name="left_btn"
+ width="23"
+ left_pad="3"
+ enabled="false"/>
+ <button
+ follows="left|bottom"
+ height="23"
+ label="&gt;"
+ name="right_btn"
+ width="23"
+ left_pad="3"
+ enabled="false" />
+
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_experiences.xml b/indra/newview/skins/default/xui/en/panel_experiences.xml
new file mode 100644
index 0000000000..7691e684c0
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_experiences.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+
+<panel
+ layout="topleft"
+ top="3"
+ left="3"
+ width="200"
+ height="300"
+ label="Experiences"
+ bg_opaque_color="0 0.5 0 0.3"
+ follows="all">
+ <string
+ name="loading_experiences"
+ value="Loading experiences..."/>
+ <string
+ name="no_experiences"
+ value="No experiences."/>
+ <string
+ name="acquire"
+ value="Acquire an Experience" />
+ <layout_stack
+ layout="topleft"
+ top="0"
+ left="4"
+ right="-1"
+ bottom="-4"
+ orientation="vertical"
+ follows="all">
+ <layout_panel
+ layout="topleft"
+ top="0"
+ left="1"
+ right="-1"
+ height="250"
+ follows="all">
+ <flat_list_view
+ name="experiences_list"
+ layout="topleft"
+ top="0"
+ left="1"
+ right="-1"
+ bottom="-1"
+ follows="all">
+ </flat_list_view>
+ </layout_panel>
+ <layout_panel
+ layout="topleft"
+ top="0"
+ left="1"
+ right="-1"
+ height="24"
+ min_height="24"
+ follows="all"
+ name="button_panel"
+ visible="false">
+ <button
+ layout="topleft"
+ left="2"
+ top="0"
+ follows="top|left"
+ name="btn_action"
+ enabled="true"/>
+ </layout_panel>
+ </layout_stack>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml
index b3326d8da6..95312edfb9 100755
--- a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml
@@ -176,6 +176,22 @@ background_visible="true"
name="group_land_tab_panel"
top="0" />
</accordion_tab>
+ <accordion_tab
+ expanded="false"
+ layout="topleft"
+ name="group_experiences_tab"
+ title="Experiences"
+ fit_panel="false">
+ <panel
+ border="false"
+ class="panel_group_experiences"
+ filename="panel_experiences.xml"
+ follows="left|top|right"
+ layout="topleft"
+ left="0"
+ name="group_experiences_tab_panel"
+ top="0" />
+ </accordion_tab>
</accordion>
</layout_panel>
</layout_stack>
diff --git a/indra/newview/skins/default/xui/en/panel_region_experiences.xml b/indra/newview/skins/default/xui/en/panel_region_experiences.xml
new file mode 100644
index 0000000000..ea57bdd164
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_region_experiences.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ border="true"
+ follows="all"
+ height="320"
+ help_topic="panel_region_experience_tab"
+ label="Experiences"
+ name="Experiences"
+ layout="topleft"
+ left="0"
+ top="320"
+ width="480">
+ <panel.string
+ name="trusted_estate_text">
+Any Experience may be Trusted.
+
+Trusted Experiences have permission to run on this estate.
+
+Additionally, if the estate does not allow public access, Residents participating in any Trusted Experience may enter the estate and can remain as long as they are in the Experience.
+ </panel.string>
+ <panel.string
+ name="allowed_estate_text">
+Only Experiences that are not Grid-Wide may be Allowed.
+
+Allowed Experiences have permission to run on this estate.
+ </panel.string>
+ <panel.string
+ name="blocked_estate_text">
+Only Grid-Wide Experiences may be Blocked.
+
+Blocked Experiences may not run on this estate.
+ </panel.string>
+ <panel.string
+ name="estate_caption">
+ Changes to settings on this tab will affect all regions in the estate.
+ </panel.string>
+ <panel.string
+ name="allowed_parcel_text">Only Experiences that are not Grid-Wide may be Allowed.
+
+Allowed Experiences have permission to run on this parcel if they are not Blocked by the estate.
+ </panel.string>
+ <panel.string
+ name="blocked_parcel_text">Only Grid-Wide Experiences may be Blocked.
+
+Blocked Experiences may not run on this parcel.
+ </panel.string>
+ <panel.string
+ name="parcel_caption">
+ </panel.string>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="32"
+ layout="topleft"
+ left="10"
+ name="experiences_help_text"
+ top="14"
+ word_wrap="true"/>
+ <layout_stack
+ left="5"
+ layout="topleft"
+ follows="all"
+ right="-5"
+ height="260"
+ orientation="vertical">
+ <layout_panel
+ follows="all"
+ height="50"
+ min_height="50"
+ width="530"
+ visible="false"
+ name="trusted_layout_panel">
+ <panel follows="all"
+ width="330"
+ name="panel_trusted"
+ class="panel_experience_list_editor"
+ filename="panel_experience_list_editor.xml" />
+ <text
+ layout="topleft"
+ name="trusted_text_help"
+ length="1"
+ top="5"
+ left_pad="5"
+ width="200"
+ height="200"
+ follows="top|right"
+ word_wrap="true">
+ </text>
+ </layout_panel>
+ <layout_panel
+ height="50"
+ min_height="50"
+ follows="all"
+ width="530">
+ <panel
+ width="330"
+ name="panel_allowed"
+ class="panel_experience_list_editor"
+ filename="panel_experience_list_editor.xml"/>
+ <text
+ layout="topleft"
+ name="allowed_text_help"
+ length="1"
+ top="5"
+ left_pad="5"
+ width="200"
+ height="200"
+ follows="top|right"
+ word_wrap="true">
+ </text>
+ </layout_panel>
+ <layout_panel
+ height="50"
+ min_height="50"
+ follows="all"
+ width="530">
+ <panel
+ width="330"
+ name="panel_blocked"
+ class="panel_experience_list_editor"
+ filename="panel_experience_list_editor.xml"/>
+ <text
+ layout="topleft"
+ name="blocked_text_help"
+ length="1"
+ top="5"
+ left_pad="5"
+ width="200"
+ height="200"
+ follows="top|right"
+ word_wrap="true">
+ </text>
+ </layout_panel>
+ </layout_stack>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml
index eb1b954e61..c56a5e17cd 100755
--- a/indra/newview/skins/default/xui/en/panel_script_ed.xml
+++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml
@@ -1,75 +1,75 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
- bevel_style="none"
- border_style="line"
- follows="left|top|right|bottom"
+ bevel_style="none"
+ border_style="line"
+ follows="left|top|right|bottom"
height="522"
- layout="topleft"
- left="0"
- name="script panel"
- width="497">
- <panel.string
- name="loading">
- Loading...
- </panel.string>
- <panel.string
- name="can_not_view">
- You can not view or edit this script, since it has been set as &quot;no copy&quot;. You need full permissions to view or edit a script inside an object.
- </panel.string>
- <panel.string
- name="public_objects_can_not_run">
- Public Objects cannot run scripts
- </panel.string>
- <panel.string
- name="script_running">
- Running
- </panel.string>
- <panel.string
- name="Title">
- Script: [NAME]
- </panel.string>
- <panel.string
- name="external_editor_not_set">
- Select an editor by setting the environment variable LL_SCRIPT_EDITOR or the ExternalEditor setting.
- </panel.string>
- <menu_bar
- bg_visible="false"
- follows="left|top"
- height="18"
- layout="topleft"
- left="0"
- mouse_opaque="false"
- name="script_menu"
- width="476">
- <menu
- top="0"
- height="62"
- label="File"
- layout="topleft"
- left="0"
- mouse_opaque="false"
- name="File"
- width="138">
- <menu_item_call
- label="Save"
- layout="topleft"
- name="Save" />
- <menu_item_separator
- layout="topleft" />
- <menu_item_call
- label="Revert All Changes"
- layout="topleft"
- name="Revert All Changes" />
- <menu_item_separator
- layout="topleft" />
- <menu_item_call
- label="Load from file..."
- layout="topleft"
- name="LoadFromFile" />
- <menu_item_call
- label="Save to file..."
- layout="topleft"
- name="SaveToFile" />
+ layout="topleft"
+ left="0"
+ name="script panel"
+ width="497">
+ <panel.string
+ name="loading">
+ Loading...
+ </panel.string>
+ <panel.string
+ name="can_not_view">
+ You can not view or edit this script, since it has been set as &quot;no copy&quot;. You need full permissions to view or edit a script inside an object.
+ </panel.string>
+ <panel.string
+ name="public_objects_can_not_run">
+ Public Objects cannot run scripts
+ </panel.string>
+ <panel.string
+ name="script_running">
+ Running
+ </panel.string>
+ <panel.string
+ name="Title">
+ Script: [NAME]
+ </panel.string>
+ <panel.string
+ name="external_editor_not_set">
+ Select an editor by setting the environment variable LL_SCRIPT_EDITOR or the ExternalEditor setting.
+ </panel.string>
+ <menu_bar
+ bg_visible="false"
+ follows="left|top"
+ height="18"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="script_menu"
+ width="476">
+ <menu
+ top="0"
+ height="62"
+ label="File"
+ layout="topleft"
+ left="0"
+ mouse_opaque="false"
+ name="File"
+ width="138">
+ <menu_item_call
+ label="Save"
+ layout="topleft"
+ name="Save" />
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ label="Revert All Changes"
+ layout="topleft"
+ name="Revert All Changes" />
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ label="Load from file..."
+ layout="topleft"
+ name="LoadFromFile" />
+ <menu_item_call
+ label="Save to file..."
+ layout="topleft"
+ name="SaveToFile" />
<menu_item_separator
layout="topleft" />
<menu_item_call
@@ -80,95 +80,95 @@
function="Floater.Toggle"
parameter="script_colors"/>
</menu_item_call>
- </menu>
- <menu
- top="0"
- height="198"
- label="Edit"
- layout="topleft"
- mouse_opaque="false"
- name="Edit"
- width="139">
- <menu_item_call
- enabled="false"
- label="Undo"
- layout="topleft"
- name="Undo" />
- <menu_item_call
- enabled="false"
- label="Redo"
- layout="topleft"
- name="Redo" />
- <menu_item_separator
- layout="topleft" />
- <menu_item_call
- enabled="false"
- label="Cut"
- layout="topleft"
- name="Cut" />
- <menu_item_call
- enabled="false"
- label="Copy"
- layout="topleft"
- name="Copy" />
- <menu_item_call
- enabled="false"
- label="Paste"
- layout="topleft"
- name="Paste" />
- <menu_item_separator
- layout="topleft"
- name="separator2" />
- <menu_item_call
- label="Select All"
- layout="topleft"
- name="Select All" />
- <menu_item_call
- enabled="false"
- label="Deselect"
- layout="topleft"
- name="Deselect" />
- <menu_item_separator
- layout="topleft"
- name="separator3" />
- <menu_item_call
- label="Search / Replace..."
- layout="topleft"
- name="Search / Replace..." />
+ </menu>
+ <menu
+ top="0"
+ height="198"
+ label="Edit"
+ layout="topleft"
+ mouse_opaque="false"
+ name="Edit"
+ width="139">
+ <menu_item_call
+ enabled="false"
+ label="Undo"
+ layout="topleft"
+ name="Undo" />
+ <menu_item_call
+ enabled="false"
+ label="Redo"
+ layout="topleft"
+ name="Redo" />
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ enabled="false"
+ label="Cut"
+ layout="topleft"
+ name="Cut" />
+ <menu_item_call
+ enabled="false"
+ label="Copy"
+ layout="topleft"
+ name="Copy" />
+ <menu_item_call
+ enabled="false"
+ label="Paste"
+ layout="topleft"
+ name="Paste" />
+ <menu_item_separator
+ layout="topleft"
+ name="separator2" />
+ <menu_item_call
+ label="Select All"
+ layout="topleft"
+ name="Select All" />
+ <menu_item_call
+ enabled="false"
+ label="Deselect"
+ layout="topleft"
+ name="Deselect" />
+ <menu_item_separator
+ layout="topleft"
+ name="separator3" />
+ <menu_item_call
+ label="Search / Replace..."
+ layout="topleft"
+ name="Search / Replace..." />
<menu_item_call
label="Go to line..."
layout="topleft"
name="Go to line..." />
- </menu>
- <menu
- top="0"
- height="34"
- label="Help"
- layout="topleft"
- mouse_opaque="false"
- name="Help"
- width="112">
- <menu_item_call
- label="Help..."
- layout="topleft"
- name="Help..." />
- <menu_item_call
- label="Keyword Help..."
- layout="topleft"
- name="Keyword Help..." />
- </menu>
- </menu_bar>
+ </menu>
+ <menu
+ top="0"
+ height="34"
+ label="Help"
+ layout="topleft"
+ mouse_opaque="false"
+ name="Help"
+ width="112">
+ <menu_item_call
+ label="Help..."
+ layout="topleft"
+ name="Help..." />
+ <menu_item_call
+ label="Keyword Help..."
+ layout="topleft"
+ name="Keyword Help..." />
+ </menu>
+ </menu_bar>
<script_editor
- left="0"
- type="string"
- length="1"
- follows="left|top|right|bottom"
- font="Monospace"
+ left="0"
+ type="string"
+ length="1"
+ follows="left|top|right|bottom"
+ font="Monospace"
height="376"
- ignore_tab="false"
- layout="topleft"
+ ignore_tab="false"
+ layout="topleft"
max_length="262144"
- name="Script Editor"
+ name="Script Editor"
width="487"
text_color="ScriptText"
default_color="ScriptText"
@@ -177,51 +177,51 @@
text_readonly_color="ScriptText"
bg_readonly_color="ScriptBackground"
cursor_color="ScriptCursorColor"
- enable_tooltip_paste="true"
+ enable_tooltip_paste="true"
word_wrap="true"
show_context_menu="true">
- Loading...
+ Loading...
</script_editor>
- <scroll_list
+ <scroll_list
top_pad="10"
left="0"
- follows="left|right|bottom"
- height="60"
- layout="topleft"
- name="lsl errors"
+ follows="left|right|bottom"
+ height="60"
+ layout="topleft"
+ name="lsl errors"
width="487" />
- <text
- follows="left|bottom"
- height="12"
- layout="topleft"
+ <text
+ follows="left|bottom"
+ height="12"
+ layout="topleft"
left="0"
- name="line_col"
- width="128" />
- <combo_box
- follows="left|bottom"
- height="23"
- label="Insert..."
- layout="topleft"
- name="Insert..."
+ name="line_col"
+ width="128" />
+ <combo_box
+ follows="left|bottom"
+ height="23"
+ label="Insert..."
+ layout="topleft"
+ name="Insert..."
width="128" />
- <button
- follows="right|bottom"
- height="23"
- label="Save"
- label_selected="Save"
- layout="topleft"
- top_pad="-35"
+ <button
+ follows="right|bottom"
+ height="23"
+ label="Save"
+ label_selected="Save"
+ layout="topleft"
+ top_pad="-35"
right="487"
- name="Save_btn"
- width="81" />
- <button
- enabled="false"
- follows="right|bottom"
- height="23"
- label="Edit..."
- layout="topleft"
- top_pad="-23"
+ name="Save_btn"
+ width="81" />
+ <button
+ enabled="false"
+ follows="right|bottom"
+ height="23"
+ label="Edit..."
+ layout="topleft"
+ top_pad="-23"
right="400"
- name="Edit_btn"
- width="81" />
+ name="Edit_btn"
+ width="81" />
</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_script_experience.xml b/indra/newview/skins/default/xui/en/panel_script_experience.xml
new file mode 100644
index 0000000000..e798638751
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_script_experience.xml
@@ -0,0 +1,97 @@
+<panel
+ name="script_experience"
+ title="EXPERIENCE"
+ width="400"
+ follows="top|left|right"
+ top="0"
+ left="0"
+ layout="topleft">
+ <!-- <floater.string name="EXPERIENCE">EXPERIENCE!!!!</floater.string>
+
+ <floater.string name="Script:">Script:</floater.string>
+ <floater.string name="Associated with:">Associated with:</floater.string>
+ <floater.string name="You can contribute:">You can contribute:</floater.string>
+ <floater.string name="Associate with:">Associate with:</floater.string>
+ <floater.string name="Yes">Yes</floater.string>
+ <floater.string name="No">No</floater.string>
+ <floater.string name="(none)"></floater.string>
+ <floater.string name="Choose Experience...">Choose Experience...</floater.string>
+ <floater.string name="You are not a contributor to any experiences.">You are not a contributor to any experiences.</floater.string>
+ -->
+
+ <button name="Expand Experience" width="200"
+ height="35"
+ is_toggle="true"
+ tab_stop="false"
+ pad_left="35"
+ top="0"
+ left="3"
+ label="Experience"
+ halign="left"
+ handle_right_mouse="false"
+ follows="top|left|right"
+ image_unselected="MarketplaceBtn_Off"
+ image_selected="MarketplaceBtn_Selected">
+ </button>
+ <check_box
+ follows="top|right" height="25" label="Uses Experience" left="215" width="0" top="0" name="enable_xp"
+ />
+ <layout_stack
+ follows="top|left|right"
+
+ width="384"
+ height="140"
+ name="xp_details"
+ left="4"
+ top="45"
+ orientation="horizontal"
+ layout="topleft"
+ visible="false">
+ <layout_panel width="120"
+ height="140">
+ <text >
+ Script:
+ </text>
+ <text bottom_delta="25">
+ Associated with:
+ </text>
+ <text bottom_delta="25">
+ You can contribute:
+ </text>
+ <text bottom_delta="25">
+ Associate with:
+ </text>
+ </layout_panel>
+
+ <layout_panel width="250"
+ height="140">
+ <text >
+ EasySit Animator 1.2.4
+ </text>
+ <text bottom_delta="25"
+ text_color="HTMLLinkColor" font.style="UNDERLINE">
+ Kyle's Superhero RPG
+ </text>
+ <text bottom_delta="25">
+ Yes
+ </text>
+ <combo_box left="0" bottom_delta="33"
+ label="Choose Experience..."
+ name="Experiences..."
+ follows="top|left|right"
+ />
+ </layout_panel>
+ </layout_stack>
+ <text
+ follows="top|left|right"
+ width="400"
+ height="15"
+ bottom_delta="-12"
+ left="0"
+ halign="center"
+ name="No Experiences"
+ visible="false"
+ text_color="AlertCautionTextColor">
+ You are not a contributor to any experiences.
+ </text>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/role_actions.xml b/indra/newview/skins/default/xui/en/role_actions.xml
index 1044cbfd2e..c2e7c706d6 100755
--- a/indra/newview/skins/default/xui/en/role_actions.xml
+++ b/indra/newview/skins/default/xui/en/role_actions.xml
@@ -190,4 +190,16 @@
longdescription="Members in a Role with this Ability can control access and participation in group voice and text chat sessions."
name="moderate group chat" value="37" />
</action_set>
+ <action_set
+ description="These Abilities include power to modify experiences owned by this group."
+ name="experience_tools_experience">
+ <action description="Experience Admin"
+ longdescription="Members in a role with this ability can edit the meta-data for an experience."
+ name="experience admin"
+ value ="49" />
+ <action description="Experience Contributor"
+ longdescription="Members in a role with this ability can contribute scripts for an experience."
+ name="experience contributor"
+ value ="50" />
+ </action_set>
</role_actions>
diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
index c5dfb703e5..fc3fdbcfa5 100755
--- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
+++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml
@@ -1,440 +1,472 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
- follows="all"
- height="570"
- layout="topleft"
- name="item properties"
- help_topic="item_properties"
- title="Item Profile"
- width="333">
- <panel.string
- name="unknown">
- (unknown)
- </panel.string>
- <panel.string
- name="unknown_multiple">
- (unknown / multiple)
- </panel.string>
- <panel.string
- name="public">
- (public)
- </panel.string>
- <panel.string
- name="you_can">
- You can:
- </panel.string>
- <panel.string
- name="owner_can">
- Owner can:
- </panel.string>
- <panel.string
- name="acquiredDate">
- [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
- </panel.string>
- <panel.string
- name="origin_inventory">
- (Inventory)
- </panel.string>
- <panel.string
- name="origin_inworld">
- (Inworld)
- </panel.string>
- <icon
- follows="top|right"
- height="18"
- image_name="Lock"
- layout="topleft"
- right="-15"
- mouse_opaque="true"
- name="IconLocked"
- top="8"
- width="18" />
- <button
- follows="top|left"
- height="24"
- image_hover_unselected="BackButton_Over"
- image_pressed="BackButton_Press"
- image_unselected="BackButton_Off"
- layout="topleft"
- left="12"
- name="back_btn"
- tab_stop="false"
- top="2"
- width="30"
- use_draw_context_alpha="false" />
- <text
- follows="top|left|right"
- font="SansSerifHugeBold"
- height="26"
- layout="topleft"
- left_pad="3"
- name="title"
- text_color="LtGray"
- top="2"
- use_ellipses="true"
- value="Item Profile"
- width="275" />
- <text
- follows="top|left|right"
- height="13"
- layout="topleft"
- left="45"
- name="origin"
- text_color="LtGray_50"
- use_ellipses="true"
- value="(Inventory)"
- width="275" />
- <scroll_container
- color="DkGray2"
- follows="all"
- layout="topleft"
- left="9"
- name="item_profile_scroll"
- opaque="true"
- height="493"
- width="313"
- top="45">
- <panel
- follows="left|top|right"
- height="390"
- help_topic=""
- label=""
- layout="topleft"
- left="0"
- name="item_profile"
- top="0"
- width="295">
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left="5"
- name="LabelItemNameTitle"
- top="10"
- width="78">
- Name:
- </text>
- <line_editor
- border_style="line"
- border_thickness="1"
- follows="left|top|right"
- height="20"
- layout="topleft"
- left_delta="78"
- max_length_bytes="63"
- name="LabelItemName"
- top_delta="0"
- width="210" />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left="5"
- name="LabelItemDescTitle"
- top_pad="10"
- width="78">
- Description:
- </text>
- <line_editor
- border_style="line"
- border_thickness="1"
- follows="left|top|right"
- height="23"
- layout="topleft"
- left_delta="78"
- max_length_bytes="127"
- name="LabelItemDesc"
- top_delta="-5"
- width="210" />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="23"
- layout="topleft"
- left="5"
- name="LabelCreatorTitle"
- top_pad="10"
- width="78">
- Creator:
- </text>
- <avatar_icon
- follows="top|left"
- height="20"
- default_icon_name="Generic_Person"
- layout="topleft"
- left_pad="0"
- top_delta="-6"
- mouse_opaque="true"
- width="20" />
- <text
- type="string"
- follows="left|right|top"
- font="SansSerifSmall"
- height="15"
- layout="topleft"
- left_pad="5"
- name="LabelCreatorName"
- top_delta="6"
- use_ellipses="true"
- width="165">
- </text>
- <button
- follows="top|right"
- height="16"
+ follows="all"
+ height="570"
+ layout="topleft"
+ name="item properties"
+ help_topic="item_properties"
+ title="Item Profile"
+ width="333">
+ <panel.string
+ name="loading_experience">
+ (loading)
+ </panel.string>
+ <panel.string
+ name="unknown">
+ (unknown)
+ </panel.string>
+ <panel.string
+ name="unknown_multiple">
+ (unknown / multiple)
+ </panel.string>
+ <panel.string
+ name="public">
+ (public)
+ </panel.string>
+ <panel.string
+ name="you_can">
+ You can:
+ </panel.string>
+ <panel.string
+ name="owner_can">
+ Owner can:
+ </panel.string>
+ <panel.string
+ name="acquiredDate">
+ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]
+ </panel.string>
+ <panel.string
+ name="origin_inventory">
+ (Inventory)
+ </panel.string>
+ <panel.string
+ name="origin_inworld">
+ (Inworld)
+ </panel.string>
+ <icon
+ follows="top|right"
+ height="18"
+ image_name="Lock"
+ layout="topleft"
+ right="-15"
+ mouse_opaque="true"
+ name="IconLocked"
+ top="8"
+ width="18" />
+ <button
+ follows="top|left"
+ height="24"
+ image_hover_unselected="BackButton_Over"
+ image_pressed="BackButton_Press"
+ image_unselected="BackButton_Off"
+ layout="topleft"
+ left="12"
+ name="back_btn"
+ tab_stop="false"
+ top="2"
+ width="30"
+ use_draw_context_alpha="false" />
+ <text
+ follows="top|left|right"
+ font="SansSerifHugeBold"
+ height="26"
+ layout="topleft"
+ left_pad="3"
+ name="title"
+ text_color="LtGray"
+ top="2"
+ use_ellipses="true"
+ value="Item Profile"
+ width="275" />
+ <text
+ follows="top|left|right"
+ height="13"
+ layout="topleft"
+ left="45"
+ name="origin"
+ text_color="LtGray_50"
+ use_ellipses="true"
+ value="(Inventory)"
+ width="275" />
+ <scroll_container
+ color="DkGray2"
+ follows="all"
+ layout="topleft"
+ left="9"
+ name="item_profile_scroll"
+ opaque="true"
+ height="493"
+ width="313"
+ top="45">
+ <panel
+ follows="left|top|right"
+ height="390"
+ help_topic=""
+ label=""
+ layout="topleft"
+ left="0"
+ name="item_profile"
+ top="0"
+ width="295">
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="5"
+ name="LabelItemNameTitle"
+ top="10"
+ width="78">
+ Name:
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="left|top|right"
+ height="20"
+ layout="topleft"
+ left_delta="78"
+ max_length_bytes="63"
+ name="LabelItemName"
+ top_delta="0"
+ width="210" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="5"
+ name="LabelItemDescTitle"
+ top_pad="10"
+ width="78">
+ Description:
+ </text>
+ <line_editor
+ border_style="line"
+ border_thickness="1"
+ follows="left|top|right"
+ height="23"
+ layout="topleft"
+ left_delta="78"
+ max_length_bytes="127"
+ name="LabelItemDesc"
+ top_delta="-5"
+ width="210" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="23"
+ layout="topleft"
+ left="5"
+ name="LabelCreatorTitle"
+ top_pad="10"
+ width="78">
+ Creator:
+ </text>
+ <avatar_icon
+ follows="top|left"
+ height="20"
+ default_icon_name="Generic_Person"
+ layout="topleft"
+ left_pad="0"
+ top_delta="-6"
+ mouse_opaque="true"
+ width="20" />
+ <text
+ type="string"
+ follows="left|right|top"
+ font="SansSerifSmall"
+ height="15"
+ layout="topleft"
+ left_pad="5"
+ name="LabelCreatorName"
+ top_delta="6"
+ use_ellipses="true"
+ width="165">
+ </text>
+ <button
+ follows="top|right"
+ height="16"
+ image_selected="Inspector_I"
+ image_unselected="Inspector_I"
+ layout="topleft"
+ right="-5"
+ name="BtnCreator"
+ top_delta="-6"
+ width="16" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="23"
+ layout="topleft"
+ left="5"
+ name="LabelOwnerTitle"
+ top_pad="10"
+ width="78">
+ Owner:
+ </text>
+ <avatar_icon
+ follows="top|left"
+ height="20"
+ default_icon_name="Generic_Person"
+ layout="topleft"
+ left_pad="0"
+ top_delta="-6"
+ mouse_opaque="true"
+ width="20" />
+ <text
+ type="string"
+ follows="left|right|top"
+ font="SansSerifSmall"
+ height="15"
+ layout="topleft"
+ left_pad="5"
+ name="LabelOwnerName"
+ top_delta="6"
+ use_ellipses="true"
+ width="165">
+ </text>
+ <button
+ follows="top|right"
+ height="16"
image_selected="Inspector_I"
image_unselected="Inspector_I"
+ layout="topleft"
+ right="-5"
+ name="BtnOwner"
+ top_delta="-3"
+ width="16" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="23"
+ layout="topleft"
+ left="5"
+ name="LabelAcquiredTitle"
+ top_pad="10"
+ width="78">
+ Acquired:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="23"
+ layout="topleft"
+ left_delta="78"
+ name="LabelAcquiredDate"
+ top_delta="0"
+ width="210">
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left="5"
+ name="LabelItemExperienceTitle"
+ top_pad="0"
+ width="78"
+ visible="true">
+ Experience:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top|right"
+ height="10"
+ layout="topleft"
+ left_delta="78"
+ name="LabelItemExperience"
+ top_delta="0"
+ width="210"
+ visible="true"
+ />
+ <panel
+ border="false"
+ follows="left|top|right"
+ layout="topleft"
+ mouse_opaque="false"
+ name="perms_inv"
+ left="0"
+ top_pad="25"
+ height="155"
+ width="313">
+ <text
+ type="string"
+ length="1"
+ left="10"
+ top_pad="13"
+ text_color="EmphasisColor"
+ height="15"
+ follows="left|top|right"
+ layout="topleft"
+ name="perm_modify"
+ width="200">
+ You can:
+ </text>
+ <check_box
+ height="18"
+ label="Modify"
+ layout="topleft"
+ left="20"
+ name="CheckOwnerModify"
+ top_pad="0"
+ width="90" />
+ <check_box
+ height="18"
+ label="Copy"
+ layout="topleft"
+ left_pad="0"
+ name="CheckOwnerCopy"
+ width="90" />
+ <check_box
+ height="18"
+ label="Transfer"
+ layout="topleft"
+ left_pad="0"
+ name="CheckOwnerTransfer"
+ width="106" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="AnyoneLabel"
+ top_pad="8"
+ width="100">
+ Anyone:
+ </text>
+ <check_box
+ height="18"
+ label="Copy"
layout="topleft"
- right="-5"
- name="BtnCreator"
- top_delta="-6"
- width="16" />
- <text
+ left_pad="0"
+ name="CheckEveryoneCopy"
+ top_delta="-2"
+ width="150" />
+ <text
type="string"
length="1"
follows="left|top"
- height="23"
+ height="16"
layout="topleft"
- left="5"
- name="LabelOwnerTitle"
- top_pad="10"
- width="78">
- Owner:
- </text>
- <avatar_icon
- follows="top|left"
- height="20"
- default_icon_name="Generic_Person"
- layout="topleft"
- left_pad="0"
- top_delta="-6"
- mouse_opaque="true"
- width="20" />
- <text
+ left="10"
+ name="GroupLabel"
+ top_pad="8"
+ width="100">
+ Group:
+ </text>
+ <check_box
+ height="18"
+ label="Share"
+ layout="topleft"
+ left_pad="0"
+ top_delta="-2"
+ name="CheckShareWithGroup"
+ tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions."
+ width="150" />
+ <text
type="string"
- follows="left|right|top"
- font="SansSerifSmall"
- height="15"
- layout="topleft"
- left_pad="5"
- name="LabelOwnerName"
- top_delta="6"
- use_ellipses="true"
- width="165">
- </text>
- <button
- follows="top|right"
- height="16"
- image_selected="Inspector_I"
- image_unselected="Inspector_I"
- layout="topleft"
- right="-5"
- name="BtnOwner"
- top_delta="-3"
- width="16" />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="23"
- layout="topleft"
- left="5"
- name="LabelAcquiredTitle"
- top_pad="10"
- width="78">
- Acquired:
- </text>
- <text
- type="string"
- length="1"
- follows="left|top|right"
- height="23"
- layout="topleft"
- left_delta="78"
- name="LabelAcquiredDate"
- top_delta="0"
- width="210">
- </text>
- <panel
- border="false"
- follows="left|top|right"
- layout="topleft"
- mouse_opaque="false"
- name="perms_inv"
- left="0"
- top_pad="25"
- height="155"
- width="313">
- <text
- type="string"
- length="1"
- left="10"
- top_pad="13"
- text_color="EmphasisColor"
- height="15"
- follows="left|top|right"
- layout="topleft"
- name="perm_modify"
- width="200">
- You can:
- </text>
- <check_box
- height="18"
- label="Modify"
- layout="topleft"
- left="20"
- name="CheckOwnerModify"
- top_pad="0"
- width="90" />
- <check_box
- height="18"
- label="Copy"
- layout="topleft"
- left_pad="0"
- name="CheckOwnerCopy"
- width="90" />
- <check_box
- height="18"
- label="Transfer"
- layout="topleft"
- left_pad="0"
- name="CheckOwnerTransfer"
- width="106" />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="16"
- layout="topleft"
- left="10"
- name="AnyoneLabel"
- top_pad="8"
- width="100">
- Anyone:
- </text>
- <check_box
- height="18"
- label="Copy"
- layout="topleft"
- left_pad="0"
- name="CheckEveryoneCopy"
- top_delta="-2"
- width="150" />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="16"
- layout="topleft"
- left="10"
- name="GroupLabel"
- top_pad="8"
- width="100">
- Group:
- </text>
- <check_box
- height="18"
- label="Share"
- layout="topleft"
- left_pad="0"
- top_delta="-2"
- name="CheckShareWithGroup"
- tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions."
- width="150" />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="16"
- layout="topleft"
- left="10"
- name="NextOwnerLabel"
- top_pad="8"
- width="200"
- word_wrap="true">
- Next owner:
- </text>
- <check_box
- height="18"
- label="Modify"
- layout="topleft"
- left="20"
- top_pad="0"
- name="CheckNextOwnerModify"
- width="90" />
- <check_box
- height="18"
- label="Copy"
- layout="topleft"
- left_pad="0"
- name="CheckNextOwnerCopy"
- width="90" />
- <check_box
- height="18"
- label="Transfer"
- layout="topleft"
- left_pad="0"
- name="CheckNextOwnerTransfer"
- tool_tip="Next owner can give away or resell this object"
- width="106" />
- </panel>
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="10"
+ name="NextOwnerLabel"
+ top_pad="8"
+ width="200"
+ word_wrap="true">
+ Next owner:
+ </text>
<check_box
- height="18"
- label="For Sale"
- layout="topleft"
- left="20"
- name="CheckPurchase"
- top_pad="20"
- width="100" />
- <combo_box
- height="23"
- left_pad="0"
- layout="topleft"
- follows="left|top"
- name="combobox sale copy"
- width="170">
- <combo_box.item
- label="Copy"
- name="Copy"
- value="Copy" />
- <combo_box.item
- label="Original"
- name="Original"
- value="Original" />
- </combo_box>
- <spinner
- follows="left|top"
- decimal_digits="0"
- increment="1"
- control_name="Edit Cost"
- name="Edit Cost"
- label="Price: L$"
- label_width="75"
- left="120"
- width="170"
- min_val="0"
- height="23"
- max_val="999999999"
- top_pad="10"/>
- </panel>
- </scroll_container>
- <panel
- height="30"
- layout="topleft"
- name="button_panel"
- left="5"
- top_pad="0"
- width="313">
- <button
- height="23"
- label="Cancel"
- layout="topleft"
- name="cancel_btn"
- right="-1"
- width="100" />
- </panel>
- </panel>
+ height="18"
+ label="Modify"
+ layout="topleft"
+ left="20"
+ top_pad="0"
+ name="CheckNextOwnerModify"
+ width="90" />
+ <check_box
+ height="18"
+ label="Copy"
+ layout="topleft"
+ left_pad="0"
+ name="CheckNextOwnerCopy"
+ width="90" />
+ <check_box
+ height="18"
+ label="Transfer"
+ layout="topleft"
+ left_pad="0"
+ name="CheckNextOwnerTransfer"
+ tool_tip="Next owner can give away or resell this object"
+ width="106" />
+ </panel>
+ <check_box
+ height="18"
+ label="For Sale"
+ layout="topleft"
+ left="20"
+ name="CheckPurchase"
+ top_pad="20"
+ width="100" />
+ <combo_box
+ height="23"
+ left_pad="0"
+ layout="topleft"
+ follows="left|top"
+ name="combobox sale copy"
+ width="170">
+ <combo_box.item
+ label="Copy"
+ name="Copy"
+ value="Copy" />
+ <combo_box.item
+ label="Original"
+ name="Original"
+ value="Original" />
+ </combo_box>
+ <spinner
+ follows="left|top"
+ decimal_digits="0"
+ increment="1"
+ control_name="Edit Cost"
+ name="Edit Cost"
+ label="Price: L$"
+ label_width="75"
+ left="120"
+ width="170"
+ min_val="0"
+ height="23"
+ max_val="999999999"
+ top_pad="10"/>
+ </panel>
+
+ </scroll_container>
+ <panel
+ height="30"
+ layout="topleft"
+ name="button_panel"
+ left="5"
+ top_pad="0"
+ width="313"
+ follows="top|right|left">
+ <button
+ follows="top|right"
+ height="23"
+ label="Cancel"
+ layout="topleft"
+ name="cancel_btn"
+ right="-1"
+ width="100" />
+ </panel>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 5fbc539a06..e5ed49fd1b 100755
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -488,7 +488,7 @@ Please try logging in again in a minute.</string>
<string name="NotConnected">Not Connected</string>
<string name="AgentNameSubst">(You)</string> <!-- Substitution for agent name -->
- <string name="JoinAnExperience">Join an experience</string> <!-- not used -->
+ <string name="JoinAnExperience"></string> <!-- intentionally left blank -->
<string name="SilentlyManageEstateAccess">Suppress alerts when managing estate access lists</string>
<string name="OverrideYourAnimations">Replace your default animations</string>
<string name="ScriptReturnObjects">Return objects on your behalf</string>
@@ -519,8 +519,6 @@ Please try logging in again in a minute.</string>
<string name="load_file_verb">Load</string>
<string name="targa_image_files">Targa Images</string>
<string name="bitmap_image_files">Bitmap Images</string>
- <string name="png_image_files">PNG Images</string>
- <string name="save_texture_image_files">Targa or PNG Images</string>
<string name="avi_movie_file">AVI Movie File</string>
<string name="xaf_animation_file">XAF Anim File</string>
<string name="xml_file">XML File</string>
@@ -2494,7 +2492,8 @@ The [[MARKETPLACE_CREATE_STORE_URL] Marketplace store] is returning errors.
<string name="CompileQueueProblemDownloading">Problem downloading</string>
<string name="CompileQueueInsufficientPermDownload">Insufficient permissions to download a script.</string>
<string name="CompileQueueInsufficientPermFor">Insufficient permissions for</string>
- <string name="CompileQueueUnknownFailure">Unknown failure to download</string>
+ <string name="CompileQueueUnknownFailure">Unknown failure to download</string>
+ <string name="CompileNoExperiencePerm">Skipping script [SCRIPT] with Experience [EXPERIENCE].</string>
<string name="CompileQueueTitle">Recompilation Progress</string>
<string name="CompileQueueStart">recompile</string>
<string name="ResetQueueTitle">Reset Progress</string>
@@ -4027,6 +4026,34 @@ Try enclosing path to the editor with double quotes.
<!-- Spell check settings floater -->
<string name="UserDictionary">[User]</string>
+ <!-- Experience Tools strings -->
+ <string name="experience_tools_experience">Experience</string>
+ <string name="ExperienceNameNull">(no experience)</string>
+ <string name="GRID_WIDE">Grid-wide</string>
+ <string name="Allowed_Experiences_Tab">ALLOWED</string>
+ <string name="Blocked_Experiences_Tab">BLOCKED</string>
+ <string name="Contrib_Experiences_Tab">CONTRIBUTOR</string>
+ <string name="Admin_Experiences_Tab">ADMIN</string>
+ <string name="Recent_Experiences_Tab">RECENT</string>
+ <string name="Owned_Experiences_Tab">OWNED</string>
+ <string name="ExperiencesCounter">([EXPERIENCES], max [MAXEXPERIENCES])</string>
+ <string name="ExperiencePermission1">take over your controls</string>
+ <string name="ExperiencePermission3">trigger animations on your avatar</string>
+ <string name="ExperiencePermission4">attach to your avatar</string>
+ <string name="ExperiencePermission9">track your camera</string>
+ <string name="ExperiencePermission10">control your camera</string>
+ <string name="ExperiencePermission11">teleport you</string>
+ <string name="ExperiencePermission12">automatically accept experience permissions</string>
+ <string name="ExperiencePermissionShortUnknown">perform an unknown operation: [Permission]</string>
+ <string name="ExperiencePermissionShort1">Take Controls</string>
+ <string name="ExperiencePermissionShort3">Trigger Animations</string>
+ <string name="ExperiencePermissionShort4">Attach</string>
+ <string name="ExperiencePermissionShort9">Track Camera</string>
+ <string name="ExperiencePermissionShort10">Control Camera</string>
+ <string name="ExperiencePermissionShort11">Teleport</string>
+ <string name="ExperiencePermissionShort12">Permission</string>
+ <string name="ExperiencePermissionShortUnknown">Unknown: [Permission]</string>
+
<!-- Conversation log messages -->
<string name="logging_calls_disabled_log_empty">
Conversations are not being logged. To begin keeping a log, choose "Save: Log only" or "Save: Log and transcripts" under Preferences > Chat.
diff --git a/indra/tools/vstool/DispatchUtility.cs b/indra/tools/vstool/DispatchUtility.cs
new file mode 100644
index 0000000000..6056ac55a1
--- /dev/null
+++ b/indra/tools/vstool/DispatchUtility.cs
@@ -0,0 +1,271 @@
+#region Using Directives
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Reflection;
+using System.Security.Permissions;
+
+#endregion
+
+namespace TestDispatchUtility
+{
+ /// <summary>
+ /// Provides helper methods for working with COM IDispatch objects that have a registered type library.
+ /// </summary>
+ public static class DispatchUtility
+ {
+ #region Private Constants
+
+ private const int S_OK = 0; //From WinError.h
+ private const int LOCALE_SYSTEM_DEFAULT = 2 << 10; //From WinNT.h == 2048 == 0x800
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Gets whether the specified object implements IDispatch.
+ /// </summary>
+ /// <param name="obj">An object to check.</param>
+ /// <returns>True if the object implements IDispatch. False otherwise.</returns>
+ public static bool ImplementsIDispatch(object obj)
+ {
+ bool result = obj is IDispatchInfo;
+ return result;
+ }
+
+ /// <summary>
+ /// Gets a Type that can be used with reflection.
+ /// </summary>
+ /// <param name="obj">An object that implements IDispatch.</param>
+ /// <param name="throwIfNotFound">Whether an exception should be thrown if a Type can't be obtained.</param>
+ /// <returns>A .NET Type that can be used with reflection.</returns>
+ /// <exception cref="InvalidCastException">If <paramref name="obj"/> doesn't implement IDispatch.</exception>
+ [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ public static Type GetType(object obj, bool throwIfNotFound)
+ {
+ RequireReference(obj, "obj");
+ Type result = GetType((IDispatchInfo)obj, throwIfNotFound);
+ return result;
+ }
+
+ /// <summary>
+ /// Tries to get the DISPID for the requested member name.
+ /// </summary>
+ /// <param name="obj">An object that implements IDispatch.</param>
+ /// <param name="name">The name of a member to lookup.</param>
+ /// <param name="dispId">If the method returns true, this holds the DISPID on output.
+ /// If the method returns false, this value should be ignored.</param>
+ /// <returns>True if the member was found and resolved to a DISPID. False otherwise.</returns>
+ /// <exception cref="InvalidCastException">If <paramref name="obj"/> doesn't implement IDispatch.</exception>
+ [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ public static bool TryGetDispId(object obj, string name, out int dispId)
+ {
+ RequireReference(obj, "obj");
+ bool result = TryGetDispId((IDispatchInfo)obj, name, out dispId);
+ return result;
+ }
+
+ /// <summary>
+ /// Invokes a member by DISPID.
+ /// </summary>
+ /// <param name="obj">An object that implements IDispatch.</param>
+ /// <param name="dispId">The DISPID of a member. This can be obtained using
+ /// <see cref="TryGetDispId(object, string, out int)"/>.</param>
+ /// <param name="args">The arguments to pass to the member.</param>
+ /// <returns>The member's return value.</returns>
+ /// <remarks>
+ /// This can invoke a method or a property get accessor.
+ /// </remarks>
+ public static object Invoke(object obj, int dispId, object[] args)
+ {
+ string memberName = "[DispId=" + dispId + "]";
+ object result = Invoke(obj, memberName, args);
+ return result;
+ }
+
+ /// <summary>
+ /// Invokes a member by name.
+ /// </summary>
+ /// <param name="obj">An object.</param>
+ /// <param name="memberName">The name of the member to invoke.</param>
+ /// <param name="args">The arguments to pass to the member.</param>
+ /// <returns>The member's return value.</returns>
+ /// <remarks>
+ /// This can invoke a method or a property get accessor.
+ /// </remarks>
+ public static object Invoke(object obj, string memberName, object[] args)
+ {
+ RequireReference(obj, "obj");
+ Type type = obj.GetType();
+ object result = type.InvokeMember(memberName, BindingFlags.InvokeMethod | BindingFlags.GetProperty,
+ null, obj, args, null);
+ return result;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /// <summary>
+ /// Requires that the value is non-null.
+ /// </summary>
+ /// <typeparam name="T">The type of the value.</typeparam>
+ /// <param name="value">The value to check.</param>
+ /// <param name="name">The name of the value.</param>
+ private static void RequireReference<T>(T value, string name) where T : class
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(name);
+ }
+ }
+
+ /// <summary>
+ /// Gets a Type that can be used with reflection.
+ /// </summary>
+ /// <param name="dispatch">An object that implements IDispatch.</param>
+ /// <param name="throwIfNotFound">Whether an exception should be thrown if a Type can't be obtained.</param>
+ /// <returns>A .NET Type that can be used with reflection.</returns>
+ private static Type GetType(IDispatchInfo dispatch, bool throwIfNotFound)
+ {
+ RequireReference(dispatch, "dispatch");
+
+ Type result = null;
+ int typeInfoCount;
+ int hr = dispatch.GetTypeInfoCount(out typeInfoCount);
+ if (hr == S_OK && typeInfoCount > 0)
+ {
+ // Type info isn't usually culture-aware for IDispatch, so we might as well pass
+ // the default locale instead of looking up the current thread's LCID each time
+ // (via CultureInfo.CurrentCulture.LCID).
+ dispatch.GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, out result);
+ }
+
+ if (result == null && throwIfNotFound)
+ {
+ // If the GetTypeInfoCount called failed, throw an exception for that.
+ Marshal.ThrowExceptionForHR(hr);
+
+ // Otherwise, throw the same exception that Type.GetType would throw.
+ throw new TypeLoadException();
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Tries to get the DISPID for the requested member name.
+ /// </summary>
+ /// <param name="dispatch">An object that implements IDispatch.</param>
+ /// <param name="name">The name of a member to lookup.</param>
+ /// <param name="dispId">If the method returns true, this holds the DISPID on output.
+ /// If the method returns false, this value should be ignored.</param>
+ /// <returns>True if the member was found and resolved to a DISPID. False otherwise.</returns>
+ private static bool TryGetDispId(IDispatchInfo dispatch, string name, out int dispId)
+ {
+ RequireReference(dispatch, "dispatch");
+ RequireReference(name, "name");
+
+ bool result = false;
+
+ // Members names aren't usually culture-aware for IDispatch, so we might as well
+ // pass the default locale instead of looking up the current thread's LCID each time
+ // (via CultureInfo.CurrentCulture.LCID).
+ Guid iidNull = Guid.Empty;
+ int hr = dispatch.GetDispId(ref iidNull, ref name, 1, LOCALE_SYSTEM_DEFAULT, out dispId);
+
+ const int DISP_E_UNKNOWNNAME = unchecked((int)0x80020006); //From WinError.h
+ const int DISPID_UNKNOWN = -1; //From OAIdl.idl
+ if (hr == S_OK)
+ {
+ result = true;
+ }
+ else if (hr == DISP_E_UNKNOWNNAME && dispId == DISPID_UNKNOWN)
+ {
+ // This is the only supported "error" case because it means IDispatch
+ // is saying it doesn't know the member we asked about.
+ result = false;
+ }
+ else
+ {
+ // The other documented result codes are all errors.
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ #region Private Interfaces
+
+ /// <summary>
+ /// A partial declaration of IDispatch used to lookup Type information and DISPIDs.
+ /// </summary>
+ /// <remarks>
+ /// This interface only declares the first three methods of IDispatch. It omits the
+ /// fourth method (Invoke) because there are already plenty of ways to do dynamic
+ /// invocation in .NET. But the first three methods provide dynamic type metadata
+ /// discovery, which .NET doesn't provide normally if you have a System.__ComObject
+ /// RCW instead of a strongly-typed RCW.
+ /// <para/>
+ /// Note: The original declaration of IDispatch is in OAIdl.idl.
+ /// </remarks>
+ [ComImport]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ [Guid("00020400-0000-0000-C000-000000000046")]
+ private interface IDispatchInfo
+ {
+ /// <summary>
+ /// Gets the number of Types that the object provides (0 or 1).
+ /// </summary>
+ /// <param name="typeInfoCount">Returns 0 or 1 for the number of Types provided by <see cref="GetTypeInfo"/>.</param>
+ /// <remarks>
+ /// http://msdn.microsoft.com/en-us/library/da876d53-cb8a-465c-a43e-c0eb272e2a12(VS.85)
+ /// </remarks>
+ [PreserveSig]
+ int GetTypeInfoCount(out int typeInfoCount);
+
+ /// <summary>
+ /// Gets the Type information for an object if <see cref="GetTypeInfoCount"/> returned 1.
+ /// </summary>
+ /// <param name="typeInfoIndex">Must be 0.</param>
+ /// <param name="lcid">Typically, LOCALE_SYSTEM_DEFAULT (2048).</param>
+ /// <param name="typeInfo">Returns the object's Type information.</param>
+ /// <remarks>
+ /// http://msdn.microsoft.com/en-us/library/cc1ec9aa-6c40-4e70-819c-a7c6dd6b8c99(VS.85)
+ /// </remarks>
+ void GetTypeInfo(int typeInfoIndex, int lcid, [MarshalAs(UnmanagedType.CustomMarshaler,
+ MarshalTypeRef = typeof(System.Runtime.InteropServices.CustomMarshalers.TypeToTypeInfoMarshaler))] out Type typeInfo);
+
+ /// <summary>
+ /// Gets the DISPID of the specified member name.
+ /// </summary>
+ /// <param name="riid">Must be IID_NULL. Pass a copy of Guid.Empty.</param>
+ /// <param name="name">The name of the member to look up.</param>
+ /// <param name="nameCount">Must be 1.</param>
+ /// <param name="lcid">Typically, LOCALE_SYSTEM_DEFAULT (2048).</param>
+ /// <param name="dispId">If a member with the requested <paramref name="name"/>
+ /// is found, this returns its DISPID and the method's return value is 0.
+ /// If the method returns a non-zero value, then this parameter's output value is
+ /// undefined.</param>
+ /// <returns>Zero for success. Non-zero for failure.</returns>
+ /// <remarks>
+ /// http://msdn.microsoft.com/en-us/library/6f6cf233-3481-436e-8d6a-51f93bf91619(VS.85)
+ /// </remarks>
+ [PreserveSig]
+ int GetDispId(ref Guid riid, ref string name, int nameCount, int lcid, out int dispId);
+
+ // NOTE: The real IDispatch also has an Invoke method next, but we don't need it.
+ // We can invoke methods using .NET's Type.InvokeMember method with the special
+ // [DISPID=n] syntax for member "names", or we can get a .NET Type using GetTypeInfo
+ // and invoke methods on that through reflection.
+ // Type.InvokeMember: http://msdn.microsoft.com/en-us/library/de3dhzwy.aspx
+ }
+
+ #endregion
+ }
+}
diff --git a/indra/tools/vstool/app.config b/indra/tools/vstool/app.config
new file mode 100644
index 0000000000..8494f728ff
--- /dev/null
+++ b/indra/tools/vstool/app.config
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<configuration>
+<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>