diff --git a/.hgignore b/.hgignore index 183561545f..2e64f36909 100755 --- a/.hgignore +++ b/.hgignore @@ -1,6 +1,5 @@ syntax: glob - # WinMerge temp files *.bak # Compiled python bytecode diff --git a/.hgtags b/.hgtags index 9c532e81a1..fa08875947 100755 --- a/.hgtags +++ b/.hgtags @@ -533,3 +533,4 @@ afd8d4756e8eda3c8f760625d1c17a2ad40ad6c8 3.7.27-release 566874eb5ab26c003ef7fb0e22ce40c5fa0013f4 3.7.28-release d07f76c5b9860fb87924d00ca729f7d4532534d6 3.7.29-release 67edc442c80b8d2fadd2a6c4a7184b469906cdbf 3.7.30-release +797ed69e6134ef48bb922577ab2540fb2d964668 3.8.0-release diff --git a/BuildParams b/BuildParams index 74bd2b4923..aeea3b1246 100755 --- a/BuildParams +++ b/BuildParams @@ -70,6 +70,12 @@ additional_packages = "" # the viewer_channel_suffix is prefixed by a blank and then appended to the viewer_channel # for the package in a setting that overrides the compiled-in value ################################################################ +additional_packages = "EDU" + +# The EDU package allows us to create a separate release channel whose expirations +# are synchronized as much as possible with the academic year +EDU_sourceid = "" +EDU_viewer_channel_suffix = "edu" # Notifications - to configure email notices, add a setting like this: # _.email = diff --git a/indra/cmake/LLWindow.cmake b/indra/cmake/LLWindow.cmake index ba07a80f05..80af7ff2ab 100755 --- a/indra/cmake/LLWindow.cmake +++ b/indra/cmake/LLWindow.cmake @@ -18,7 +18,7 @@ else (USESYSTEMLIBS) use_prebuilt_binary(SDL) set (SDL_FOUND TRUE) set (SDL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/i686-linux) - set (SDL_LIBRARY SDL directfb fusion direct) + set (SDL_LIBRARY SDL directfb fusion direct X11) endif (LINUX) endif (USESYSTEMLIBS) diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp index 7ec7063cf1..5d17177d64 100755 --- a/indra/llimage/llimagetga.cpp +++ b/indra/llimage/llimagetga.cpp @@ -1163,8 +1163,17 @@ bool LLImageTGA::loadFile( const std::string& path ) { return false; } - + //< FS:ND> FIRE-16342 make sure no one overwrites this file while we load it + + // LLFILE* file = LLFile::fopen(path, "rb"); /* Flawfinder: ignore */ +#ifndef LL_WINDOWS LLFILE* file = LLFile::fopen(path, "rb"); /* Flawfinder: ignore */ +#else + LLFILE* file = LLFile::_fsopen(path, "rb", _SH_DENYWR);/* Flawfinder: ignore */ +#endif + + // 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& 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; @@ -814,7 +835,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; @@ -849,7 +870,7 @@ BOOL remove_from_access_array(std::map* 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; @@ -1192,3 +1213,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(), 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 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::iterator access_map_iterator; -typedef std::map::const_iterator access_map_const_iterator; class LLParcel { @@ -320,6 +323,9 @@ public: void unpackAccessEntries(LLMessageSystem* msg, std::map* 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 mTempBanList; std::map mTempAccessList; + typedef std::map 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 c9ed175ca9..a267e82ac4 100755 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -46,6 +46,7 @@ set(llmessage_SOURCE_FILES llcurl.cpp lldatapacker.cpp lldispatcher.cpp + llexperiencecache.cpp llfiltersd2xmlrpc.cpp llhost.cpp llhttpassetstorage.cpp @@ -137,6 +138,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 +#include +#include "boost/tokenizer.hpp" + + +namespace LLExperienceCache +{ + + typedef std::map KeyMap; + KeyMap privateToPublicKeyMap; + + void mapKeys(const LLSD& legacyKeys); + + std::string sLookupURL; + + typedef std::map ask_queue_t; + ask_queue_t sAskQueue; + + typedef std::map 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 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 EQUALS_SEPARATOR("="); + static const boost::char_separator 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 > 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 "<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 + +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 + callback_signal_t; + typedef callback_signal_t::slot_type callback_slot_t; + typedef std::map 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; +// 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 = 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; +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 4332106c91..e6a09a8a3b 100755 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -1385,6 +1385,8 @@ char const* const _PREHASH_AppearanceVersion = LLMessageStringTable::getInstance char const* const _PREHASH_CofVersion = LLMessageStringTable::getInstance()->getString("CofVersion"); char const* const _PREHASH_AppearanceHover = LLMessageStringTable::getInstance()->getString("AppearanceHover"); char const* const _PREHASH_HoverHeight = LLMessageStringTable::getInstance()->getString("HoverHeight"); +char const* const _PREHASH_Experience = LLMessageStringTable::getInstance()->getString("Experience"); +char const* const _PREHASH_ExperienceID = LLMessageStringTable::getInstance()->getString("ExperienceID"); // Aurora Sim char const* const _PREHASH_RegionSizeX = LLMessageStringTable::getInstance()->getString("RegionSizeX"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index bcaeb51d83..4d85bca3d4 100755 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -1385,6 +1385,8 @@ extern char const* const _PREHASH_AppearanceVersion; extern char const* const _PREHASH_CofVersion; extern char const* const _PREHASH_AppearanceHover; extern char const* const _PREHASH_HoverHeight; +extern char const* const _PREHASH_Experience; +extern char const* const _PREHASH_ExperienceID; // Aurora Sim extern char const* const _PREHASH_RegionSizeX; diff --git a/indra/llprimitive/lllslconstants.h b/indra/llprimitive/lllslconstants.h index 926ce32d75..b6baf98211 100644 --- a/indra/llprimitive/lllslconstants.h +++ b/indra/llprimitive/lllslconstants.h @@ -239,4 +239,23 @@ const U32 LSL_STATUS_INTERNAL_ERROR = 1999; // Start per-function errors below, starting at 2000: const U32 LSL_STATUS_WHITELIST_FAILED = 2001; + +const S32 LSL_XP_ERROR_NONE = 0; +const S32 LSL_XP_ERROR_THROTTLED = 1; +const S32 LSL_XP_ERROR_EXPERIENCES_DISABLED = 2; +const S32 LSL_XP_ERROR_INVALID_PARAMETERS = 3; +const S32 LSL_XP_ERROR_NOT_PERMITTED = 4; +const S32 LSL_XP_ERROR_NO_EXPERIENCE = 5; +const S32 LSL_XP_ERROR_NOT_FOUND = 6; +const S32 LSL_XP_ERROR_INVALID_EXPERIENCE = 7; +const S32 LSL_XP_ERROR_EXPERIENCE_DISABLED = 8; +const S32 LSL_XP_ERROR_EXPERIENCE_SUSPENDED = 9; +const S32 LSL_XP_ERROR_UNKNOWN_ERROR = 10; +const S32 LSL_XP_ERROR_QUOTA_EXCEEDED = 11; +const S32 LSL_XP_ERROR_STORE_DISABLED = 12; +const S32 LSL_XP_ERROR_STORAGE_EXCEPTION = 13; +const S32 LSL_XP_ERROR_KEY_NOT_FOUND = 14; +const S32 LSL_XP_ERROR_RETRY_UPDATE = 15; +const S32 LSL_XP_ERROR_MATURITY_EXCEEDED = 16; + #endif diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index ab56d7d48f..7446faf30b 100755 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -679,7 +679,7 @@ void LLNotification::respond(const LLSD& response) // and then call it functor(asLLSD(), response); } - else + else if (mCombinedNotifications.empty()) { // no registered responder return; @@ -701,6 +701,14 @@ void LLNotification::respond(const LLSD& response) } } + for (std::vector::const_iterator it = mCombinedNotifications.begin(); it != mCombinedNotifications.end(); ++it) + { + if ((*it)) + { + (*it)->respond(response); + } + } + update(); } @@ -1331,6 +1339,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 2f028b78a7..915e4750c5 100755 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -416,6 +416,9 @@ private: using the same mechanism. */ bool mTemporaryResponder; + + // keep track of other notifications combined with COMBINE_WITH_NEW + std::vector mCombinedNotifications; void init(const std::string& template_name, const LLSD& form_elements); @@ -566,6 +569,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 4c3f77f2c6..86df251e27 100755 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -2296,6 +2296,11 @@ void LLTabContainer::commitHoveredButton(S32 x, S32 y) } } +S32 LLTabContainer::getTotalTabWidth() const +{ + return mTotalTabWidth; +} + // Hide one tab. Will switch to the first visible tab if one exists. Otherwise the Tabcontainer is hidden void LLTabContainer::setTabVisibility( LLPanel const *aPanel, bool aVisible ) { diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 9b1a8a7c25..e27de68d8d 100755 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -194,7 +194,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(); @@ -302,7 +303,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 e7f88d5137..2beb8b65cf 100755 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -38,6 +38,7 @@ #include "lltrans.h" #include "lluicolortable.h" #include "message.h" +#include "llexperiencecache.h" // hop:// protocol> //#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" @@ -1540,3 +1541,57 @@ std::string LLUrlEntryJira::getUrl(const std::string &string) const return llformat("https://jira.secondlife.com/browse/%s", string.c_str()); } } + +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()) + { + std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString(); + return experience_name_string.empty() ? LLTrans::getString("ExperienceNameUntitled") : experience_name_string; + } + + 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 ) +{ + std::string name = experience_details[LLExperienceCache::NAME].asString(); + if(name.empty()) + { + name = LLTrans::getString("ExperienceNameUntitled"); + } + callObservers(experience_details[LLExperienceCache::EXPERIENCE_ID].asString(), name, LLStringUtil::null); +} + + diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 3e494e4c10..9b8d095b3d 100755 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -351,6 +351,20 @@ private: }; // [/RLVa:KB] +/// +/// 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 6a680dab33..b5350c97db 100755 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -41,7 +41,7 @@ LLUrlRegistry::LLUrlRegistry() { // mUrlEntry.reserve(20); // [RLVa:KB] - Checked: 2010-11-01 (RLVa-1.2.2a) | Added: RLVa-1.2.2a - mUrlEntry.reserve(24); + mUrlEntry.reserve(25); // [/RLVa:KB] // Urls are matched in the order that they were registered @@ -84,6 +84,7 @@ LLUrlRegistry::LLUrlRegistry() registerUrl(new LLUrlEntryObjectIM()); registerUrl(new LLUrlEntryPlace()); registerUrl(new LLUrlEntryInventory()); + 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 + +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 settings_map_t; settings_map_t LLUI::sSettingGroups; diff --git a/indra/lscript/lscript_byteformat.h b/indra/lscript/lscript_byteformat.h index 54031aaf05..a4703b7a61 100755 --- a/indra/lscript/lscript_byteformat.h +++ b/indra/lscript/lscript_byteformat.h @@ -355,6 +355,10 @@ typedef enum e_lscript_state_event_type LSTT_REMOTE_DATA, LSTT_HTTP_RESPONSE, LSTT_HTTP_REQUEST, + LSTT_EXPERMISSIONS, + LSTT_TRANSACTION_RESULT, + LSTT_PATH_UPDATE, + LSTT_EXPERMISSIONS_DENIED, LSTT_EOF, LSTT_STATE_BEGIN = LSTT_STATE_ENTRY, @@ -397,7 +401,11 @@ const U64 LSCRIPTStateBitField[LSTT_EOF] = 0x0000000040000000, // LSTT_OBJECT_REZ 0x0000000080000000, // LSTT_REMOTE_DATA 0x0000000100000000LL, // LSTT_HTTP_RESPOSE - 0x0000000200000000LL // LSTT_HTTP_REQUEST + 0x0000000200000000LL, // LSTT_HTTP_REQUEST + 0x0000000400000000LL, // LSTT_EXPERMISSIONS + 0x0000000800000000LL, // LSTT_TRANSACTION_RESULT + 0x0000001000000000LL, // LSTT_PATH_UPDATE + 0x0000002000000000LL, //LSTT_EXPERMISSIONS_DENIED }; inline S32 get_event_handler_jump_position(U64 bit_field, LSCRIPTStateEventType type) @@ -511,6 +519,7 @@ typedef enum e_lscript_runtime_faults LSRF_TOO_MANY_LISTENS, LSRF_NESTING_LISTS, LSRF_CLI, + LSRF_INVALID_STATE, LSRF_EOF } LSCRIPTRunTimeFaults; @@ -551,10 +560,10 @@ const U32 LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_EOF] = (0x1 << 10),// SCRIPT_PERMISSION_TRACK_CAMERA (0x1 << 11),// SCRIPT_PERMISSION_CONTROL_CAMERA (0x1 << 12),// SCRIPT_PERMISSION_TELEPORT - (0x1 << 13),// SCRIPT_PERMISSION_EXPERIENCE, - (0x1 << 14),// SCRIPT_PERMISSION_SILENT_ESTATE_MANAGEMENT, - (0x1 << 15),// SCRIPT_PERMISSION_OVERRIDE_ANIMATIONS, - (0x1 << 16),// SCRIPT_PERMISSION_RETURN_OBJECTS, + (0x1 << 13),// SCRIPT_PERMISSION_EXPERIENCE + (0x1 << 14),// SCRIPT_PERMISSION_SILENT_ESTATE_MANAGEMENT + (0x1 << 15),// SCRIPT_PERMISSION_OVERRIDE_ANIMATIONS + (0x1 << 16),// SCRIPT_PERMISSION_RETURN_OBJECTS }; // http_request string constants diff --git a/indra/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l index b40e03cb0e..8f55655cc6 100755 --- a/indra/lscript/lscript_compile/indra.l +++ b/indra/lscript/lscript_compile/indra.l @@ -132,6 +132,8 @@ int yyerror(const char *fmt, ...); "money" { count(); return(MONEY); } "email" { count(); return(EMAIL); } "run_time_permissions" { count(); return(RUN_TIME_PERMISSIONS); } +"experience_permissions" { count(); return(EXPERIENCE_PERMISSIONS); } +"experience_permissions_denied" { count(); return(EXPERIENCE_PERMISSIONS_DENIED); } "changed" { count(); return(INVENTORY); } "attach" { count(); return(ATTACH); } "dataserver" { count(); return(DATASERVER); } @@ -394,7 +396,6 @@ int yyerror(const char *fmt, ...); "PSYS_PART_END_ALPHA" { count(); yylval.ival = LLPS_PART_END_ALPHA; return (INTEGER_CONSTANT); } "PSYS_PART_END_SCALE" { count(); yylval.ival = LLPS_PART_END_SCALE; return (INTEGER_CONSTANT); } "PSYS_PART_MAX_AGE" { count(); yylval.ival = LLPS_PART_MAX_AGE; return (INTEGER_CONSTANT); } - "PSYS_PART_BLEND_FUNC_SOURCE" { count(); yylval.ival = LLPS_PART_BLEND_FUNC_SOURCE; return (INTEGER_CONSTANT); } "PSYS_PART_BLEND_FUNC_DEST" { count(); yylval.ival = LLPS_PART_BLEND_FUNC_DEST; return (INTEGER_CONSTANT); } "PSYS_PART_START_GLOW" { count(); yylval.ival = LLPS_PART_START_GLOW; return (INTEGER_CONSTANT); } @@ -420,7 +421,6 @@ int yyerror(const char *fmt, ...); "PSYS_PART_BF_SOURCE_ALPHA" { count(); yylval.ival = LLPartData::LL_PART_BF_SOURCE_ALPHA; return(INTEGER_CONSTANT); } "PSYS_PART_BF_ONE_MINUS_SOURCE_ALPHA" { count(); yylval.ival = LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA; return(INTEGER_CONSTANT); } - "PSYS_SRC_MAX_AGE" { count(); yylval.ival = LLPS_SRC_MAX_AGE; return(INTEGER_CONSTANT); } "PSYS_SRC_PATTERN" { count(); yylval.ival = LLPS_SRC_PATTERN; return(INTEGER_CONSTANT); } "PSYS_SRC_INNERANGLE" { count(); yylval.ival = LLPS_SRC_INNERANGLE; return(INTEGER_CONSTANT); } @@ -738,6 +738,24 @@ 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" { count(); yylval.ival = LSL_XP_ERROR_NONE ; return (INTEGER_CONSTANT); } +"XP_ERROR_THROTTLED" { count(); yylval.ival = LSL_XP_ERROR_THROTTLED ; return (INTEGER_CONSTANT); } +"XP_ERROR_EXPERIENCES_DISABLED" { count(); yylval.ival = LSL_XP_ERROR_EXPERIENCES_DISABLED; return (INTEGER_CONSTANT); } +"XP_ERROR_INVALID_PARAMETERS" { count(); yylval.ival = LSL_XP_ERROR_INVALID_PARAMETERS ; return (INTEGER_CONSTANT); } +"XP_ERROR_NOT_PERMITTED" { count(); yylval.ival = LSL_XP_ERROR_NOT_PERMITTED ; return (INTEGER_CONSTANT); } +"XP_ERROR_NO_EXPERIENCE" { count(); yylval.ival = LSL_XP_ERROR_NO_EXPERIENCE ; return (INTEGER_CONSTANT); } +"XP_ERROR_NOT_FOUND" { count(); yylval.ival = LSL_XP_ERROR_NOT_FOUND ; return (INTEGER_CONSTANT); } +"XP_ERROR_INVALID_EXPERIENCE" { count(); yylval.ival = LSL_XP_ERROR_INVALID_EXPERIENCE ; return (INTEGER_CONSTANT); } +"XP_ERROR_EXPERIENCE_DISABLED" { count(); yylval.ival = LSL_XP_ERROR_EXPERIENCE_DISABLED ; return (INTEGER_CONSTANT); } +"XP_ERROR_EXPERIENCE_SUSPENDED" { count(); yylval.ival = LSL_XP_ERROR_EXPERIENCE_SUSPENDED; return (INTEGER_CONSTANT); } +"XP_ERROR_UNKNOWN_ERROR" { count(); yylval.ival = LSL_XP_ERROR_UNKNOWN_ERROR ; return (INTEGER_CONSTANT); } +"XP_ERROR_QUOTA_EXCEEDED" { count(); yylval.ival = LSL_XP_ERROR_QUOTA_EXCEEDED ; return (INTEGER_CONSTANT); } +"XP_ERROR_STORE_DISABLED" { count(); yylval.ival = LSL_XP_ERROR_STORE_DISABLED ; return (INTEGER_CONSTANT); } +"XP_ERROR_STORAGE_EXCEPTION" { count(); yylval.ival = LSL_XP_ERROR_STORAGE_EXCEPTION ; return (INTEGER_CONSTANT); } +"XP_ERROR_KEY_NOT_FOUND" { count(); yylval.ival = LSL_XP_ERROR_KEY_NOT_FOUND ; return (INTEGER_CONSTANT); } +"XP_ERROR_RETRY_UPDATE" { count(); yylval.ival = LSL_XP_ERROR_RETRY_UPDATE ; return (INTEGER_CONSTANT); } +"XP_ERROR_MATURITY_EXCEEDED" { count(); yylval.ival = LSL_XP_ERROR_MATURITY_EXCEEDED ; return (INTEGER_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/lscript/lscript_compile/indra.y b/indra/lscript/lscript_compile/indra.y index a0a034d21c..c451eee3d8 100755 --- a/indra/lscript/lscript_compile/indra.y +++ b/indra/lscript/lscript_compile/indra.y @@ -15,7 +15,6 @@ #pragma warning (disable : 4702) // warning C4702: unreachable code #pragma warning( disable : 4065 ) // warning: switch statement contains 'default' but no 'case' labels #endif - %} %union @@ -75,6 +74,8 @@ %token MONEY %token EMAIL %token RUN_TIME_PERMISSIONS +%token EXPERIENCE_PERMISSIONS +%token EXPERIENCE_PERMISSIONS_DENIED %token INVENTORY %token ATTACH %token DATASERVER @@ -180,6 +181,8 @@ %type money %type email %type run_time_permissions +%type experience_permissions +%type experience_permissions_denied %type inventory %type attach %type dataserver @@ -788,6 +791,16 @@ event $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2); gAllocationManager->addAllocation($$); } + | experience_permissions compound_statement + { + $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2); + gAllocationManager->addAllocation($$); + } + | experience_permissions_denied compound_statement + { + $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2); + gAllocationManager->addAllocation($$); + } | inventory compound_statement { $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2); @@ -1040,6 +1053,28 @@ run_time_permissions } ; +experience_permissions + : EXPERIENCE_PERMISSIONS '(' LLKEY IDENTIFIER ')' + { + LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4); + gAllocationManager->addAllocation(id1); + $$ = new LLScriptEXPEvent(gLine, gColumn, id1); + gAllocationManager->addAllocation($$); + } + ; + +experience_permissions_denied + : EXPERIENCE_PERMISSIONS_DENIED '(' LLKEY IDENTIFIER ',' INTEGER IDENTIFIER ')' + { + LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4); + gAllocationManager->addAllocation(id1); + LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7); + gAllocationManager->addAllocation(id2); + $$ = new LLScriptEXPDeniedEvent(gLine, gColumn, id1, id2); + gAllocationManager->addAllocation($$); + } + ; + inventory : INVENTORY '(' INTEGER IDENTIFIER ')' { diff --git a/indra/lscript/lscript_compile/lscript_tree.cpp b/indra/lscript/lscript_compile/lscript_tree.cpp index 8a70dd9ac1..4215596c8b 100755 --- a/indra/lscript/lscript_compile/lscript_tree.cpp +++ b/indra/lscript/lscript_compile/lscript_tree.cpp @@ -3844,6 +3844,156 @@ S32 LLScriptNotAtTarget::getSize() return 0; } + +void LLScriptEXPEvent::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata) +{ + if (gErrorToText.getErrors()) + { + return; + } + switch(pass) + { + case LSCP_PRETTY_PRINT: + case LSCP_EMIT_ASSEMBLY: + fdotabs(fp, tabs, tabsize); + fprintf(fp, "experience_permissions( key "); + mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL); + fprintf(fp, " )\n"); + break; + case LSCP_SCOPE_PASS1: + checkForDuplicateHandler(fp, this, scope, "experience_permissions"); + if (scope->checkEntry(mName->mName)) + { + gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME); + } + else + { + mName->mScopeEntry = scope->addEntry(mName->mName, LIT_VARIABLE, LST_KEY); + } + break; + case LSCP_RESOURCE: + { + // we're just tryng to determine how much space the variable needs + if (mName->mScopeEntry) + { + mName->mScopeEntry->mOffset = (S32)count; + mName->mScopeEntry->mSize = 4; + count += mName->mScopeEntry->mSize; + } + } + break; + + case LSCP_EMIT_BYTE_CODE: + { +#ifdef LSL_INCLUDE_DEBUG_INFO + char name[] = "experience_permissions"; + chunk->addBytes(name, strlen(name) + 1); + chunk->addBytes(mName->mName, strlen(mName->mName) + 1); +#endif + } + break; + case LSCP_EMIT_CIL_ASSEMBLY: + fdotabs(fp, tabs, tabsize); + fprintf(fp, "experience_permissions( valuetype [ScriptTypes]LindenLab.SecondLife.Key "); + mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL); + fprintf(fp, " )"); + break; + default: + mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL); + break; + } +} + +S32 LLScriptEXPEvent::getSize() +{ + // key = 4 + return 4; +} + + +void LLScriptEXPDeniedEvent::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata) +{ + if (gErrorToText.getErrors()) + { + return; + } + switch(pass) + { + case LSCP_PRETTY_PRINT: + case LSCP_EMIT_ASSEMBLY: + fdotabs(fp, tabs, tabsize); + fprintf(fp, "experience_permissions_denied( key "); + mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL); + fprintf(fp, ", integer "); + mReason->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL); + fprintf(fp, " )\n"); + break; + case LSCP_SCOPE_PASS1: + checkForDuplicateHandler(fp, this, scope, "experience_permissions_denied"); + if (scope->checkEntry(mName->mName)) + { + gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME); + } + else + { + mName->mScopeEntry = scope->addEntry(mName->mName, LIT_VARIABLE, LST_KEY); + } + if (scope->checkEntry(mReason->mName)) + { + gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME); + } + else + { + mReason->mScopeEntry = scope->addEntry(mReason->mName, LIT_VARIABLE, LST_INTEGER); + } + break; + case LSCP_RESOURCE: + { + // we're just trying to determine how much space the variable needs + if (mName->mScopeEntry) + { + mName->mScopeEntry->mOffset = (S32)count; + mName->mScopeEntry->mSize = 4; + count += mName->mScopeEntry->mSize; + + mReason->mScopeEntry->mOffset = (S32)count; + mReason->mScopeEntry->mSize = 4; + count += mReason->mScopeEntry->mSize; + } + } + break; + + case LSCP_EMIT_BYTE_CODE: + { +#ifdef LSL_INCLUDE_DEBUG_INFO + char name[] = "experience_permissions_denied"; + chunk->addBytes(name, strlen(name) + 1); + chunk->addBytes(mName->mName, strlen(mName->mName) + 1); + chunk->addBytes(mReason->mName, strlen(mReason->mName) + 1); +#endif + } + break; + case LSCP_EMIT_CIL_ASSEMBLY: + fdotabs(fp, tabs, tabsize); + fprintf(fp, "experience_permissions_denied( valuetype [ScriptTypes]LindenLab.SecondLife.Key "); + mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL); + fprintf(fp, ", int32 "); + mReason->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL); + fprintf(fp, " )"); + break; + default: + mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL); + mReason->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL); + break; + } +} + +S32 LLScriptEXPDeniedEvent::getSize() +{ + // key = 4 + integer + return LSCRIPTDataSize[LST_KEY]+LSCRIPTDataSize[LST_INTEGER]; +} + void LLScriptAtRotTarget::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata) { if (gErrorToText.getErrors()) @@ -8569,6 +8719,7 @@ void LLScriptReturn::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePa } } prunearg = TRUE; + break; case LSCP_TYPE: // if there is a return expression, it must be promotable to the return type of the function if (mExpression) @@ -9767,7 +9918,13 @@ void LLScriptEventHandler::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCom mScopeEntry->mFunctionArgs.addType(LST_STRING); mScopeEntry->mFunctionArgs.addType(LST_STRING); break; - + case LSTT_EXPERMISSIONS: + mScopeEntry->mFunctionArgs.addType(LST_KEY); + break; + case LSTT_EXPERMISSIONS_DENIED: + mScopeEntry->mFunctionArgs.addType(LST_KEY); + mScopeEntry->mFunctionArgs.addType(LST_INTEGER); + break; default: break; } diff --git a/indra/lscript/lscript_compile/lscript_tree.h b/indra/lscript/lscript_compile/lscript_tree.h index 047c220b17..38a69cece8 100755 --- a/indra/lscript/lscript_compile/lscript_tree.h +++ b/indra/lscript/lscript_compile/lscript_tree.h @@ -627,6 +627,39 @@ public: LLScriptIdentifier *mRTPermissions; }; +class LLScriptEXPEvent : public LLScriptEvent +{ +public: + LLScriptEXPEvent(S32 line, S32 col, LLScriptIdentifier *name) + : LLScriptEvent(line, col, LSTT_EXPERMISSIONS), mName(name) + { + } + + ~LLScriptEXPEvent() {} + + void recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata); + S32 getSize(); + + LLScriptIdentifier *mName; +}; + +class LLScriptEXPDeniedEvent : public LLScriptEvent +{ +public: + LLScriptEXPDeniedEvent(S32 line, S32 col, LLScriptIdentifier *name, LLScriptIdentifier *reason) + : LLScriptEvent(line, col, LSTT_EXPERMISSIONS_DENIED), mName(name), mReason(reason) + { + } + + ~LLScriptEXPDeniedEvent() {} + + void recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata); + S32 getSize(); + + LLScriptIdentifier *mName; + LLScriptIdentifier *mReason; +}; + class LLScriptChatEvent : public LLScriptEvent { public: diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d03075fc9e..ef19a8b5b1 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -308,6 +308,8 @@ set(viewer_SOURCE_FILES lleventnotifier.cpp lleventpoll.cpp llexpandabletextbox.cpp + llexperienceassociationresponder.cpp + llexperiencelog.cpp llexternaleditor.cpp llface.cpp llfacebookconnect.cpp @@ -349,6 +351,9 @@ set(viewer_SOURCE_FILES llfloatereditwater.cpp llfloaterenvironmentsettings.cpp llfloaterevent.cpp + llfloaterexperiencepicker.cpp + llfloaterexperienceprofile.cpp + llfloaterexperiences.cpp llfloaterfacebook.cpp llfloaterflickr.cpp llfloaterfonttest.cpp @@ -531,11 +536,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 @@ -1038,6 +1048,8 @@ set(viewer_HEADER_FILES lleventnotifier.h lleventpoll.h llexpandabletextbox.h + llexperienceassociationresponder.h + llexperiencelog.h llexternaleditor.h llface.h llfacebookconnect.h @@ -1079,6 +1091,9 @@ set(viewer_HEADER_FILES llfloatereditwater.h llfloaterenvironmentsettings.h llfloaterevent.h + llfloaterexperiencepicker.h + llfloaterexperienceprofile.h + llfloaterexperiences.h llfloaterfacebook.h llfloaterflickr.h llfloaterfonttest.h @@ -1254,12 +1269,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 dfc06930a2..f998888054 100755 --- a/indra/newview/app_settings/keywords.ini +++ b/indra/newview/app_settings/keywords.ini @@ -55,6 +55,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 @@ -717,6 +718,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/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8cf2ee2ac0..1da4f9bbab 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3153,7 +3153,7 @@ Type S32 Value - 100 + 100 ChatLoadGroupTimeout @@ -6304,7 +6304,7 @@ Type F32 Value - 3 + 3.0 FullScreenAutoDetectAspectRatio @@ -8606,7 +8606,7 @@ Type Boolean Value - 0 + 0 Backup 0 @@ -8619,7 +8619,7 @@ Type Boolean Value - 1 + 1 MeshImportUseSLM @@ -8630,7 +8630,7 @@ Type Boolean Value - 0 + 0 MeshUploadLogXML @@ -8641,7 +8641,7 @@ Type Boolean Value - 0 + 0 Backup 0 @@ -8654,7 +8654,7 @@ Type S32 Value - 0 + 0 Backup 0 @@ -8667,7 +8667,7 @@ Type S32 Value - 600 + 600 Backup 0 @@ -11069,7 +11069,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type F32 Value - 64 + 64.0 Backup 0 @@ -11223,7 +11223,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 16 + 16 Backup 0 @@ -11277,7 +11277,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type F32 Value - 4 + 4.0 Backup 0 @@ -11356,7 +11356,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type F32 Value - 0 + 0.0 Backup 0 @@ -11580,7 +11580,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 0 + 0 RenderSpecularResX @@ -11592,7 +11592,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 1024 + 1024 Backup 0 @@ -11606,7 +11606,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 256 + 256 Backup 0 @@ -11786,7 +11786,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 4 + 4 Backup 0 @@ -11961,9 +11961,9 @@ Change of this parameter will affect the layout of buttons in notification toast Vector3 Value - 1 - 0 - 0 + 1.0 + 0.0 + 0.0 RenderGlowMaxExtractAlpha @@ -12603,7 +12603,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 0 + 0 RenderAutoMuteLogging @@ -12843,7 +12843,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 250000 + 250000 Backup 0 @@ -12856,7 +12856,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 384 + 384 Backup 0 @@ -12869,7 +12869,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 16 + 16 Backup 0 @@ -12882,7 +12882,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 16 + 16 Backup 0 @@ -13047,7 +13047,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 1024 + 1024 SceneLoadLowMemoryBound @@ -13058,7 +13058,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type U32 Value - 750 + 750 SceneLoadMinRadius @@ -14888,7 +14888,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 6 + 6 UICheckboxctrlBtnSize @@ -14899,7 +14899,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 13 + 13 UICheckboxctrlHeight @@ -14910,7 +14910,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 16 + 16 UICheckboxctrlHPad @@ -14921,7 +14921,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 2 + 2 UICheckboxctrlSpacing @@ -14932,7 +14932,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 5 + 5 UICheckboxctrlVPad @@ -14943,7 +14943,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 2 + 2 UICloseBoxFromTop @@ -14954,7 +14954,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 5 + 5 UIExtraTriangleHeight @@ -14965,7 +14965,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - -1 + -1 UIExtraTriangleWidth @@ -14976,7 +14976,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 4 + 4 UIFloaterCloseBoxSize @@ -14987,7 +14987,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 16 + 16 UIFloaterHPad @@ -14998,7 +14998,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 6 + 6 UIFloaterTestBool @@ -15242,7 +15242,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 500 + 500 UIMinimizedWidth @@ -15253,7 +15253,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 160 + 160 UIMultiSliderctrlSpacing @@ -15264,7 +15264,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 4 + 4 UIMultiTrackHeight @@ -16249,7 +16249,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type S32 Value - 3 + 3 UseCircuitCodeTimeout @@ -19135,9 +19135,9 @@ Change of this parameter will affect the layout of buttons in notification toast Color4 Value - 0 - 0 - 0 + 0.0 + 0.0 + 0.0 1.0 @@ -19454,7 +19454,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type F32 Value - 2 + 2.0 TeleportLocalDelay @@ -19465,7 +19465,7 @@ Change of this parameter will affect the layout of buttons in notification toast Type F32 Value - 1 + 1.0 FMODExProfilerEnable @@ -23400,6 +23400,28 @@ Change of this parameter will affect the layout of buttons in notification toast Value + FSEnablePerGroupSnoozeDuration + + Comment + Enables input of a snooze duration per group. + Persist + 1 + Type + Boolean + Value + 0 + + FSExperienceSearchMaturityRating + + Comment + Setting for the user's preferred maturity level for experiences search (consts in indra_constants.h) + Persist + 1 + Type + U32 + Value + 13 + diff --git a/indra/newview/fsfloaterim.cpp b/indra/newview/fsfloaterim.cpp index c20dbacf57..85f5ab65cf 100644 --- a/indra/newview/fsfloaterim.cpp +++ b/indra/newview/fsfloaterim.cpp @@ -49,6 +49,7 @@ #include "llchiclet.h" #include "llchicletbar.h" #include "llfloaterabout.h" // for sysinfo button -Zi +#include "llfloateravatarpicker.h" #include "llfloaterreg.h" #include "llgroupactions.h" #include "llhttpclient.h" @@ -105,13 +106,10 @@ FSFloaterIM::FSFloaterIM(const LLUUID& session_id) mUnreadMessagesNotificationPanel(NULL), mUnreadMessagesNotificationTextBox(NULL) { - LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionID); - if (im_session) + initIMSession(session_id); + + switch (mDialog) { - mSessionInitialized = im_session->mSessionInitialized; - - mDialog = im_session->mType; - switch(mDialog){ case IM_NOTHING_SPECIAL: case IM_SESSION_P2P_INVITE: mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); @@ -123,7 +121,7 @@ FSFloaterIM::FSFloaterIM(const LLUUID& session_id) setCanSnooze(TRUE); mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); break; - case IM_SESSION_INVITE: + case IM_SESSION_INVITE: if (gAgent.isInGroup(mSessionID)) { setCanSnooze(TRUE); @@ -135,9 +133,8 @@ FSFloaterIM::FSFloaterIM(const LLUUID& session_id) } break; default: break; - } } - + mCommitCallbackRegistrar.add("IMSession.Menu.Action", boost::bind(&FSFloaterIM::doToSelected, this, _2)); mEnableCallbackRegistrar.add("IMSession.Menu.Enable", boost::bind(&FSFloaterIM::checkEnabled, this, _2)); @@ -211,8 +208,7 @@ void FSFloaterIM::onClose(bool app_quitting) void FSFloaterIM::onSnooze() { - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession( - mSessionID); + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); if (session == NULL) { @@ -229,10 +225,57 @@ void FSFloaterIM::onSnooze() { LLSD payload; payload["session_id"] = mSessionID; + payload["snooze"] = true; LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); return; } + confirmSnooze(); +} + +void FSFloaterIM::confirmSnooze() +{ + if (gSavedSettings.getBOOL("FSEnablePerGroupSnoozeDuration")) + { + LLSD args; + args["DURATION"] = gSavedSettings.getS32("GroupSnoozeTime"); + + LLNotificationsUtil::add("SnoozeDuration", args, LLSD(), boost::bind(&FSFloaterIM::snoozeDurationCallback, this, _1, _2)); + return; + } + + snooze(); +} + +void FSFloaterIM::snoozeDurationCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + std::istringstream duration_str(response["duration"].asString()); + S32 duration(-1); + if (duration_str >> duration && duration >= 0) + { + snooze(duration); + } + else + { + LLNotificationsUtil::add("SnoozeDurationInvalidInput"); + } + } +} + +void FSFloaterIM::snooze(S32 duration /*= -1*/) +{ + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); + + if (session == NULL) + { + LL_WARNS("FSFloaterIM") << "Empty session." << LL_ENDL; + return; + } + + session->mSnoozeTime = duration; session->mCloseAction = LLIMModel::LLIMSession::CLOSE_SNOOZE; LLFloater::onClickCloseBtn(); @@ -477,6 +520,8 @@ void FSFloaterIM::doToSelected(const LLSD& userdata) LLAvatarActions::teleportRequest(mOtherParticipantUUID); else if (command == "share") LLAvatarActions::share(mOtherParticipantUUID); + else if (command == "add_participant") + onAddButtonClicked(); else if (command == "pay") LLAvatarActions::pay(mOtherParticipantUUID); else if (command == "show_profile") @@ -495,7 +540,7 @@ void FSFloaterIM::doToSelected(const LLSD& userdata) { if (gSavedSettings.getBOOL("FSUseBuiltInHistory")) { - LLFloaterReg::showInstance("preview_conversation", mSessionID, true); + LLFloaterReg::showInstance("preview_conversation", mSessionID, TRUE); } else { @@ -575,7 +620,7 @@ void FSFloaterIM::updateCallButton() if (!session) { - getChild("call_btn")->setEnabled(false); + getChild("call_btn")->setEnabled(FALSE); return; } @@ -632,7 +677,7 @@ BOOL FSFloaterIM::postBuild() // AO: always hide the control panel to start. LL_DEBUGS("FSFloaterIM") << "mControlPanel->getParent()" << mControlPanel->getParent() << LL_ENDL; - mControlPanel->getParent()->setVisible(false); + mControlPanel->getParent()->setVisible(FALSE); LL_DEBUGS("FSFloaterIM") << "buttons setup in IM start" << LL_ENDL; @@ -657,10 +702,10 @@ BOOL FSFloaterIM::postBuild() case LLIMModel::LLIMSession::P2P_SESSION: // One-on-one IM { LL_DEBUGS("FSFloaterIM") << "LLIMModel::LLIMSession::P2P_SESSION" << LL_ENDL; - getChild("slide_panel")->setVisible(false); - getChild("gprofile_panel")->setVisible(false); - getChild("end_call_btn_panel")->setVisible(false); - getChild("voice_ctrls_btn_panel")->setVisible(false); + getChild("slide_panel")->setVisible(FALSE); + getChild("gprofile_panel")->setVisible(FALSE); + getChild("end_call_btn_panel")->setVisible(FALSE); + getChild("voice_ctrls_btn_panel")->setVisible(FALSE); LL_DEBUGS("FSFloaterIM") << "adding FSFloaterIM removing/adding particularfriendobserver" << LL_ENDL; LLAvatarTracker::instance().removeParticularFriendObserver(mOtherParticipantUUID, this); @@ -690,13 +735,14 @@ BOOL FSFloaterIM::postBuild() case LLIMModel::LLIMSession::GROUP_SESSION: // Group chat { LL_DEBUGS("FSFloaterIM") << "LLIMModel::LLIMSession::GROUP_SESSION start" << LL_ENDL; - getChild("profile_panel")->setVisible(false); - getChild("friend_panel")->setVisible(false); - getChild("tp_panel")->setVisible(false); - getChild("share_panel")->setVisible(false); - getChild("pay_panel")->setVisible(false); - getChild("end_call_btn_panel")->setVisible(false); - getChild("voice_ctrls_btn_panel")->setVisible(false); + getChild("profile_panel")->setVisible(FALSE); + getChild("friend_panel")->setVisible(FALSE); + getChild("tp_panel")->setVisible(FALSE); + getChild("share_panel")->setVisible(FALSE); + getChild("pay_panel")->setVisible(FALSE); + getChild("end_call_btn_panel")->setVisible(FALSE); + getChild("voice_ctrls_btn_panel")->setVisible(FALSE); + getChild("add_participant_panel")->setVisible(FALSE); LL_DEBUGS("FSFloaterIM") << "LLIMModel::LLIMSession::GROUP_SESSION end" << LL_ENDL; break; @@ -704,21 +750,21 @@ BOOL FSFloaterIM::postBuild() case LLIMModel::LLIMSession::ADHOC_SESSION: // Conference chat { LL_DEBUGS("FSFloaterIM") << "LLIMModel::LLIMSession::ADHOC_SESSION start" << LL_ENDL; - getChild("profile_panel")->setVisible(false); - getChild("gprofile_panel")->setVisible(false); - getChild("friend_panel")->setVisible(false); - getChild("tp_panel")->setVisible(false); - getChild("share_panel")->setVisible(false); - getChild("pay_panel")->setVisible(false); - getChild("end_call_btn_panel")->setVisible(false); - getChild("voice_ctrls_btn_panel")->setVisible(false); + getChild("profile_panel")->setVisible(FALSE); + getChild("gprofile_panel")->setVisible(FALSE); + getChild("friend_panel")->setVisible(FALSE); + getChild("tp_panel")->setVisible(FALSE); + getChild("share_panel")->setVisible(FALSE); + getChild("pay_panel")->setVisible(FALSE); + getChild("end_call_btn_panel")->setVisible(FALSE); + getChild("voice_ctrls_btn_panel")->setVisible(FALSE); LL_DEBUGS("FSFloaterIM") << "LLIMModel::LLIMSession::ADHOC_SESSION end" << LL_ENDL; break; } default: LL_DEBUGS("FSFloaterIM") << "default buttons start" << LL_ENDL; - getChild("end_call_btn_panel")->setVisible(false); - getChild("voice_ctrls_btn_panel")->setVisible(false); + getChild("end_call_btn_panel")->setVisible(FALSE); + getChild("voice_ctrls_btn_panel")->setVisible(FALSE); LL_DEBUGS("FSFloaterIM") << "default buttons end" << LL_ENDL; break; } @@ -926,6 +972,13 @@ void FSFloaterIM::draw() mOtherTyping = false; } + // add people who were added via dropPerson() + if (!mPendingParticipants.empty()) + { + addSessionParticipants(mPendingParticipants); + mPendingParticipants.clear(); + } + LLTransientDockableFloater::draw(); } @@ -1214,14 +1267,17 @@ void FSFloaterIM::sessionInitReplyReceived(const LLUUID& im_session_id) //need to send delayed messaged collected while waiting for session initialization - if (!mQueuedMsgsForInit.size()) return; - LLSD::array_iterator iter; - for ( iter = mQueuedMsgsForInit.beginArray(); - iter != mQueuedMsgsForInit.endArray(); - ++iter) + if (mQueuedMsgsForInit.size()) { - LLIMModel::sendMessage(iter->asString(), mSessionID, - mOtherParticipantUUID, mDialog); + LLSD::array_iterator iter; + for ( iter = mQueuedMsgsForInit.beginArray(); + iter != mQueuedMsgsForInit.endArray(); ++iter) + { + LLIMModel::sendMessage(iter->asString(), mSessionID, + mOtherParticipantUUID, mDialog); + } + + mQueuedMsgsForInit.clear(); } } @@ -1443,31 +1499,68 @@ void FSFloaterIM::processIMTyping(const LLIMInfo* im_info, BOOL typing) void FSFloaterIM::processAgentListUpdates(const LLSD& body) { - if ( !body.isMap() ) return; + loadInitialInvitedIDs(); - if ( body.has("agent_updates") && body["agent_updates"].isMap() ) + uuid_vec_t joined_uuids; + + if (body.isMap() && body.has("agent_updates") && body["agent_updates"].isMap()) { - LLSD agent_data = body["agent_updates"].get(gAgentID.asString()); - if (agent_data.isMap() && agent_data.has("info")) + LLSD::map_const_iterator update_it; + for(update_it = body["agent_updates"].beginMap(); + update_it != body["agent_updates"].endMap(); + ++update_it) { - LLSD agent_info = agent_data["info"]; + LLUUID agent_id(update_it->first); + LLSD agent_data = update_it->second; - if (agent_info.has("mutes")) + if (agent_data.isMap()) { - BOOL moderator_muted_text = agent_info["mutes"]["text"].asBoolean(); - mInputEditor->setEnabled(!moderator_muted_text); - std::string label; - if (moderator_muted_text) - label = LLTrans::getString("IM_muted_text_label"); - else - label = LLTrans::getString("IM_to_label") + " " + LLIMModel::instance().getName(mSessionID); - mInputEditor->setLabel(label); + // store the new participants in joined_uuids + if (agent_data.has("transition") && agent_data["transition"].asString() == "ENTER") + { + joined_uuids.push_back(agent_id); + } - if (moderator_muted_text) - LLNotificationsUtil::add("TextChatIsMutedByModerator"); + // process the moderator mutes + if (agent_id == gAgentID && agent_data.has("info") && agent_data["info"].has("mutes")) + { + BOOL moderator_muted_text = agent_data["info"]["mutes"]["text"].asBoolean(); + mInputEditor->setEnabled(!moderator_muted_text); + std::string label; + if (moderator_muted_text) + label = LLTrans::getString("IM_muted_text_label"); + else + label = LLTrans::getString("IM_to_label") + " " + LLIMModel::instance().getName(mSessionID); + mInputEditor->setLabel(label); + + if (moderator_muted_text) + LLNotificationsUtil::add("TextChatIsMutedByModerator"); + } } } } + + // the vectors need to be sorted for computing the intersection and difference + std::sort(mInvitedParticipants.begin(), mInvitedParticipants.end()); + std::sort(joined_uuids.begin(), joined_uuids.end()); + + uuid_vec_t intersection; // uuids of invited residents who have joined the conversation + std::set_intersection(mInvitedParticipants.begin(), mInvitedParticipants.end(), + joined_uuids.begin(), joined_uuids.end(), + std::back_inserter(intersection)); + + if (intersection.size() > 0) + { + sendParticipantsAddedNotification(intersection); + } + + // Remove all joined participants from invited array. + // The difference between the two vectors (the elements in mInvitedParticipants which are not in joined_uuids) + // is placed at the beginning of mInvitedParticipants, then all other elements are erased. + mInvitedParticipants.erase(std::set_difference(mInvitedParticipants.begin(), mInvitedParticipants.end(), + joined_uuids.begin(), joined_uuids.end(), + mInvitedParticipants.begin()), + mInvitedParticipants.end()); } void FSFloaterIM::sendParticipantsAddedNotification(const uuid_vec_t& uuids) @@ -1537,15 +1630,26 @@ BOOL FSFloaterIM::handleDragAndDrop(S32 x, S32 y, MASK mask, void *cargo_data, EAcceptance *accept, std::string& tooltip_msg) { + if (cargo_type == DAD_PERSON) + { + if (dropPerson(static_cast(cargo_data), drop)) + { + *accept = ACCEPT_YES_MULTI; + } + else + { + *accept = ACCEPT_NO; + } + } - if (mDialog == IM_NOTHING_SPECIAL) + else if (mDialog == IM_NOTHING_SPECIAL) { LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, cargo_type, cargo_data, accept); } // handle case for dropping calling cards (and folders of calling cards) onto invitation panel for invites - else if (isInviteAllowed()) + else if (isInviteAllowed() && !mIsP2PChat) { *accept = ACCEPT_NO; @@ -1567,30 +1671,37 @@ BOOL FSFloaterIM::handleDragAndDrop(S32 x, S32 y, MASK mask, return TRUE; } -BOOL FSFloaterIM::dropCallingCard(LLInventoryItem* item, BOOL drop) +bool FSFloaterIM::dropCallingCard(LLInventoryItem* item, bool drop) { - BOOL rv = isInviteAllowed(); - if(rv && item && item->getCreatorUUID().notNull()) + bool rv = true; + if(item && item->getCreatorUUID().notNull()) { - if(drop) + uuid_vec_t ids; + ids.push_back(item->getCreatorUUID()); + if (canAddSelectedToChat(ids)) { - uuid_vec_t ids; - ids.push_back(item->getCreatorUUID()); - inviteToSession(ids); + if (drop) + { + mPendingParticipants.push_back(item->getCreatorUUID()); + } + } + else + { + rv = false; } } else { // set to false if creator uuid is null. - rv = FALSE; + rv = false; } return rv; } -BOOL FSFloaterIM::dropCategory(LLInventoryCategory* category, BOOL drop) +bool FSFloaterIM::dropCategory(LLInventoryCategory* category, bool drop) { - BOOL rv = isInviteAllowed(); - if(rv && category) + bool rv = true; + if(category) { LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; @@ -1603,9 +1714,9 @@ BOOL FSFloaterIM::dropCategory(LLInventoryCategory* category, BOOL drop) S32 count = items.size(); if(count == 0) { - rv = FALSE; + rv = false; } - else if(drop) + else { uuid_vec_t ids; ids.reserve(count); @@ -1613,17 +1724,48 @@ BOOL FSFloaterIM::dropCategory(LLInventoryCategory* category, BOOL drop) { ids.push_back(items.at(i)->getCreatorUUID()); } - inviteToSession(ids); + + if (canAddSelectedToChat(ids)) + { + if (drop) + { + mPendingParticipants.insert(mPendingParticipants.end(), ids.begin(), ids.end()); + } + } + else + { + rv = false; + } } } return rv; } +bool FSFloaterIM::dropPerson(LLUUID* person_id, bool drop) +{ + bool res = person_id && person_id->notNull(); + if(res) + { + uuid_vec_t ids; + ids.push_back(*person_id); + + res = canAddSelectedToChat(ids); + if(res && drop) + { + // these people will be added during the next draw() call + // (so they can be added all at once) + mPendingParticipants.push_back(*person_id); + } + } + + return res; +} + BOOL FSFloaterIM::isInviteAllowed() const { - - return ((IM_SESSION_CONFERENCE_START == mDialog) || - (IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID))); + return ( (IM_SESSION_CONFERENCE_START == mDialog) + || (IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID)) + || mIsP2PChat); } class FSSessionInviteResponder : public LLHTTPClient::Responder @@ -1761,10 +1903,22 @@ void FSFloaterIM::confirmLeaveCallCallback(const LLSD& notification, const LLSD& S32 option = LLNotificationsUtil::getSelectedOption(notification, response); const LLSD& payload = notification["payload"]; LLUUID session_id = payload["session_id"]; + bool snooze = payload["snooze"].asBoolean(); - LLFloater* im_floater = LLFloaterReg::findInstance("fs_impanel", session_id); + FSFloaterIM* im_floater = LLFloaterReg::findTypedInstance("fs_impanel", session_id); if (option == 0 && im_floater != NULL) { + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); + if (session) + { + if (snooze) + { + im_floater->confirmSnooze(); + return; + } + + session->mCloseAction = LLIMModel::LLIMSession::CLOSE_DEFAULT; + } im_floater->closeFloater(); } @@ -1841,6 +1995,7 @@ void FSFloaterIM::onClickCloseBtn(bool app_quitting) { LLSD payload; payload["session_id"] = mSessionID; + payload["snooze"] = false; LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); return; } @@ -1905,6 +2060,7 @@ void FSFloaterIM::initIMSession(const LLUUID& session_id) if (session) { + mIsP2PChat = session->isP2PSessionType(); mSessionInitialized = session->mSessionInitialized; mDialog = session->mType; } @@ -1932,3 +2088,158 @@ void FSFloaterIM::updateUnreadMessageNotification(S32 unread_messages) mUnreadMessagesNotificationPanel->setVisible(TRUE); } } + +void FSFloaterIM::onAddButtonClicked() +{ + LLView* button = findChild("add_participant_btn"); + LLFloater* root_floater = this; + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&FSFloaterIM::addSessionParticipants, this, _1), TRUE, TRUE, FALSE, root_floater->getName(), button); + if (!picker) + { + return; + } + + // Need to disable 'ok' button when selected users are already in conversation. + picker->setOkBtnEnableCb(boost::bind(&FSFloaterIM::canAddSelectedToChat, this, _1)); + + if (root_floater) + { + root_floater->addDependentFloater(picker); + } +} + +bool FSFloaterIM::canAddSelectedToChat(const uuid_vec_t& uuids) +{ + if (!LLIMModel::instance().findIMSession(mSessionID) + || mDialog == IM_SESSION_GROUP_START + || (mDialog == IM_SESSION_INVITE && gAgent.isInGroup(mSessionID))) + { + return false; + } + + if (mIsP2PChat) + { + // For a P2P session just check if we are not adding the other participant. + + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + if (*id == mOtherParticipantUUID) + { + return false; + } + } + } + else + { + // For a conference session we need to check against the list from LLSpeakerMgr, + // because this list may change when participants join or leave the session. + + LLSpeakerMgr::speaker_list_t speaker_list; + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + speaker_mgr->getSpeakerList(&speaker_list, true); + } + + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + for (LLSpeakerMgr::speaker_list_t::const_iterator it = speaker_list.begin(); + it != speaker_list.end(); ++it) + { + const LLPointer& speaker = *it; + if (*id == speaker->mID) + { + return false; + } + } + } + } + + return true; +} + +void FSFloaterIM::addSessionParticipants(const uuid_vec_t& uuids) +{ + if (mIsP2PChat) + { + LLSD payload; + LLSD args; + + LLNotificationsUtil::add("ConfirmAddingChatParticipants", args, payload, + boost::bind(&FSFloaterIM::addP2PSessionParticipants, this, _1, _2, uuids)); + } + else + { + if(findInstance(mSessionID)) + { + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + } + + inviteToSession(uuids); + } +} + +void FSFloaterIM::addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) + { + return; + } + + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + // first check whether this is a voice session + bool is_voice_call = voice_channel != NULL && voice_channel->isActive(); + + uuid_vec_t temp_ids; + uuid_vec_t invited_ids; + + // Add the initial participant of a P2P session + temp_ids.push_back(mOtherParticipantUUID); + temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); + + LLUUID session_id = mSessionID; + + // then we can close the current session + if(findInstance(mSessionID)) + { + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + + invited_ids.insert(invited_ids.end(), mInvitedParticipants.begin(), mInvitedParticipants.end()); + + // Ansariel: This will result in the floater actually being closed as opposed in CHUI! + onClose(false); + } + + // Start a new ad hoc voice call if we invite new participants to a P2P call, + // or start a text chat otherwise. + if (is_voice_call) + { + session_id = LLAvatarActions::startAdhocCall(temp_ids, session_id); + } + else + { + session_id = LLAvatarActions::startConference(temp_ids, session_id); + } + + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); + if (session) + { + session->mInitialInvitedIDs = invited_ids; + } +} + +void FSFloaterIM::loadInitialInvitedIDs() +{ + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); + if (session && !session->mInitialInvitedIDs.empty()) + { + mInvitedParticipants = session->mInitialInvitedIDs; + session->mInitialInvitedIDs.clear(); + } +} diff --git a/indra/newview/fsfloaterim.h b/indra/newview/fsfloaterim.h index 4c6ef4597a..eba991978d 100644 --- a/indra/newview/fsfloaterim.h +++ b/indra/newview/fsfloaterim.h @@ -150,6 +150,8 @@ public: void updateUnreadMessageNotification(S32 unread_messages); + void loadInitialInvitedIDs(); + protected: /* virtual */ void onClickCloseBtn(bool app_quitting = false); @@ -174,8 +176,9 @@ private: void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); void fetchAvatarName(LLUUID& agent_id); - BOOL dropCallingCard(LLInventoryItem* item, BOOL drop); - BOOL dropCategory(LLInventoryCategory* category, BOOL drop); + bool dropCallingCard(LLInventoryItem* item, bool drop); + bool dropCategory(LLInventoryCategory* category, bool drop); + bool dropPerson(LLUUID* person_id, bool drop); BOOL isInviteAllowed() const; BOOL inviteToSession(const uuid_vec_t& agent_ids); @@ -213,6 +216,15 @@ private: void sendParticipantsAddedNotification(const uuid_vec_t& uuids); + void confirmSnooze(); + void snoozeDurationCallback(const LLSD& notification, const LLSD& response); + void snooze(S32 duration = -1); + + void onAddButtonClicked(); + bool canAddSelectedToChat(const uuid_vec_t& uuids); + void addSessionParticipants(const uuid_vec_t& uuids); + void addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids); + FSPanelChatControlPanel* mControlPanel; LLUUID mSessionID; S32 mLastMessageIndex; @@ -240,12 +252,17 @@ private: bool mSessionInitialized; LLSD mQueuedMsgsForInit; + bool mIsP2PChat; + LLVoiceChannel* mVoiceChannel; S32 mInputEditorPad; S32 mChatLayoutPanelHeight; S32 mFloaterHeight; - + + uuid_vec_t mInvitedParticipants; + uuid_vec_t mPendingParticipants; + boost::signals2::connection mAvatarNameCacheConnection; }; diff --git a/indra/newview/fspanelprofile.cpp b/indra/newview/fspanelprofile.cpp index 0977655485..31beea3628 100644 --- a/indra/newview/fspanelprofile.cpp +++ b/indra/newview/fspanelprofile.cpp @@ -180,12 +180,12 @@ FSPanelProfileSecondLife::FSPanelProfileSecondLife() FSPanelProfileSecondLife::~FSPanelProfileSecondLife() { - if(getAvatarId().notNull()) + if (getAvatarId().notNull()) { LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this); } - if(LLVoiceClient::instanceExists()) + if (LLVoiceClient::instanceExists()) { LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this); } @@ -214,7 +214,7 @@ BOOL FSPanelProfileSecondLife::postBuild() mIMButton = getChild("im"); mOverflowButton = getChild("overflow_btn"); - mStatusText->setVisible(false); + mStatusText->setVisible(FALSE); LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; registrar.add("Profile.Call", boost::bind(&FSPanelProfileSecondLife::onCallButtonClick, this)); @@ -249,8 +249,8 @@ BOOL FSPanelProfileSecondLife::postBuild() mOverflowButton->setMenu(profile_menu, LLMenuButton::MP_TOP_RIGHT); // allow skins to have copy buttons for name and avatar URI -Zi - LLButton* copy_uri_button=findChild("copy_uri_button"); - LLButton* copy_name_button=findChild("copy_name_button"); + LLButton* copy_uri_button = findChild("copy_uri_button"); + LLButton* copy_name_button = findChild("copy_name_button"); if (copy_uri_button) { @@ -277,50 +277,50 @@ void FSPanelProfileSecondLife::onOpen(const LLSD& key) resetData(); LLUUID avatar_id = getAvatarId(); - LLAvatarPropertiesProcessor::getInstance()->addObserver(avatar_id,this); + LLAvatarPropertiesProcessor::getInstance()->addObserver(avatar_id, this); BOOL own_profile = getSelfProfile(); - mGroupInviteButton->setVisible( !own_profile ); - mShowOnMapButton->setVisible( !own_profile ); - mPayButton->setVisible( !own_profile ); - mTeleportButton->setVisible( !own_profile ); - mIMButton->setVisible( !own_profile ); - mAddFriendButton->setVisible( !own_profile ); - mBlockButton->setVisible( !own_profile ); - mUnblockButton->setVisible( !own_profile ); - mOverflowButton->setVisible( !own_profile ); - mGroupList->setShowNone( !own_profile ); + mGroupInviteButton->setVisible(!own_profile); + mShowOnMapButton->setVisible(!own_profile); + mPayButton->setVisible(!own_profile); + mTeleportButton->setVisible(!own_profile); + mIMButton->setVisible(!own_profile); + mAddFriendButton->setVisible(!own_profile); + mBlockButton->setVisible(!own_profile); + mUnblockButton->setVisible(!own_profile); + mOverflowButton->setVisible(!own_profile); + mGroupList->setShowNone(!own_profile); if (own_profile && !getEmbedded()) { // Group list control cannot toggle ForAgent loading // Less than ideal, but viewing own profile via search is edge case - mGroupList->enableForAgent( false ); + mGroupList->enableForAgent(false); } - if (own_profile && LLAvatarName::useDisplayNames() && !getEmbedded() ) + if (own_profile && LLAvatarName::useDisplayNames() && !getEmbedded()) { - mDisplayNameButton->setVisible( true ); - mDisplayNameButton->setEnabled( true ); + mDisplayNameButton->setVisible(TRUE); + mDisplayNameButton->setEnabled(TRUE); } mDescriptionEdit->setParseHTML( !own_profile && !getEmbedded() ); - FSDropTarget* drop_target = getChild ("drop_target"); - drop_target->setVisible( !own_profile ); - drop_target->setEnabled( !own_profile ); + FSDropTarget* drop_target = getChild("drop_target"); + drop_target->setVisible(!own_profile); + drop_target->setEnabled(!own_profile); if (!own_profile) { mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(avatar_id) ? LLAvatarTracker::instance().isBuddyOnline(avatar_id) : TRUE); - drop_target->setAgentID( avatar_id ); + drop_target->setAgentID(avatar_id); updateOnlineStatus(); } updateButtons(); - getChild("user_key")->setValue( avatar_id.asString() ); + getChild("user_key")->setValue(avatar_id.asString()); } void FSPanelProfileSecondLife::apply(LLAvatarData* data) @@ -769,7 +769,7 @@ void FSPanelProfileSecondLife::onAvatarNameCacheSetName(const LLUUID& agent_id, { // something is wrong, tell user to try again later LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); - return; + return; } LL_INFOS("LegacyProfile") << "name-change now " << LLDate::now() << " next_update " @@ -780,7 +780,7 @@ void FSPanelProfileSecondLife::onAvatarNameCacheSetName(const LLUUID& agent_id, { // if the update time is more than a year in the future, it means updates have been blocked // show a more general message - const int YEAR = 60*60*24*365; + static const S32 YEAR = 60*60*24*365; if (now_secs + YEAR < av_name.mNextUpdate) { LLNotificationsUtil::add("SetDisplayNameBlocked"); @@ -835,7 +835,7 @@ BOOL FSPanelProfileWeb::postBuild() mWebBrowser = getChild("profile_html"); mWebBrowser->addObserver(this); - mUrlEdit->setEnabled( FALSE ); + mUrlEdit->setEnabled(FALSE); return TRUE; } @@ -926,7 +926,7 @@ void FSPanelProfileWeb::onCommitLoad(LLUICtrl* ctrl) if (!mURLHome.empty()) { LLSD::String valstr = ctrl->getValue().asString(); - if (valstr == "") + if (valstr.empty()) { mWebBrowser->setVisible(TRUE); mPerformanceTimer.start(); @@ -950,7 +950,7 @@ void FSPanelProfileWeb::onCommitWebProfile(LLUICtrl* ctrl) if (!mURLWebProfile.empty()) { LLSD::String valstr = ctrl->getValue().asString(); - if (valstr == "") + if (valstr.empty()) { mWebBrowser->setVisible(TRUE); mPerformanceTimer.start(); @@ -1226,7 +1226,7 @@ void FSPanelPick::setAvatarId(const LLUUID& avatar_id) FSPanelProfileTab::setAvatarId(avatar_id); // creating new Pick - if (getPickId().isNull()) + if (getPickId().isNull() && getSelfProfile()) { mNewPick = true; @@ -1269,9 +1269,9 @@ void FSPanelPick::setAvatarId(const LLUUID& avatar_id) if (getSelfProfile() && !getEmbedded()) { - mPickName->setEnabled( TRUE ); - mPickDescription->setEnabled( TRUE ); - mSetCurrentLocationButton->setVisible( TRUE ); + mPickName->setEnabled(TRUE); + mPickDescription->setEnabled(TRUE); + mSetCurrentLocationButton->setVisible(TRUE); } /*else { @@ -1296,7 +1296,7 @@ BOOL FSPanelPick::postBuild() mSetCurrentLocationButton->setCommitCallback(boost::bind(&FSPanelPick::onClickSetLocation, this)); mPickName->setKeystrokeCallback(boost::bind(&FSPanelPick::onPickChanged, this, _1), NULL); - mPickName->setEnabled( FALSE ); + mPickName->setEnabled(FALSE); mPickDescription->setKeystrokeCallback(boost::bind(&FSPanelPick::onPickChanged, this, _1)); mPickDescription->setFocusReceivedCallback(boost::bind(&FSPanelPick::onDescriptionFocusReceived, this)); @@ -1322,6 +1322,7 @@ void FSPanelPick::processProperties(void* data, EAvatarProcessorType type) { return; } + LLPickData* pick_info = static_cast(data); if (!pick_info || pick_info->creator_id != getAvatarId() @@ -1363,6 +1364,7 @@ void FSPanelPick::setSnapshotId(const LLUUID& id) void FSPanelPick::setPickName(const std::string& name) { mPickName->setValue(name); + updateTabLabel(name); } const std::string FSPanelPick::getPickName() @@ -1408,6 +1410,11 @@ void FSPanelPick::onSnapshotChanged() void FSPanelPick::onPickChanged(LLUICtrl* ctrl) { + if (ctrl && ctrl == mPickName) + { + updateTabLabel(mPickName->getText()); + } + enableSaveButton(isDirty()); } @@ -1478,7 +1485,7 @@ void FSPanelPick::apply() std::string FSPanelPick::getLocationNotice() { - static std::string notice = getString("location_notice"); + static const std::string notice = getString("location_notice"); return notice; } @@ -1486,6 +1493,10 @@ void FSPanelPick::sendParcelInfoRequest() { if (mParcelId != mRequestedId) { + if (mRequestedId.notNull()) + { + LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mRequestedId, this); + } LLRemoteParcelInfoProcessor::getInstance()->addObserver(mParcelId, this); LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(mParcelId); @@ -1532,7 +1543,6 @@ void FSPanelPick::sendUpdate() pick_data.sort_order = 0; pick_data.enabled = TRUE; - // LLAvatarPropertiesProcessor::instance().sendPickInfoUpdate(&pick_data); LLAvatarPropertiesProcessor::getInstance()->sendPickInfoUpdate(&pick_data); if(mNewPick) @@ -1547,21 +1557,30 @@ void FSPanelPick::sendUpdate() // static std::string FSPanelPick::createLocationText(const std::string& owner_name, const std::string& original_name, const std::string& sim_name, const LLVector3d& pos_global) { - std::string location_text; - location_text.append(owner_name); + std::string location_text(owner_name); if (!original_name.empty()) { - if (!location_text.empty()) location_text.append(", "); + if (!location_text.empty()) + { + location_text.append(", "); + } location_text.append(original_name); } + if (!sim_name.empty()) { - if (!location_text.empty()) location_text.append(", "); + if (!location_text.empty()) + { + location_text.append(", "); + } location_text.append(sim_name); } - if (!location_text.empty()) location_text.append(" "); + if (!location_text.empty()) + { + location_text.append(" "); + } if (!pos_global.isNull()) { @@ -1573,6 +1592,15 @@ std::string FSPanelPick::createLocationText(const std::string& owner_name, const return location_text; } +void FSPanelPick::updateTabLabel(const std::string& title) +{ + setLabel(title); + LLTabContainer* parent = dynamic_cast(getParent()); + if (parent) + { + parent->setCurrentTabName(title); + } +} ////////////////////////////////////////////////////////////////////////// @@ -1598,11 +1626,11 @@ void FSPanelProfilePicks::onOpen(const LLSD& key) if (getSelfProfile() && !getEmbedded()) { - mNewButton->setVisible( TRUE ); - mNewButton->setEnabled( TRUE ); + mNewButton->setVisible(TRUE); + mNewButton->setEnabled(FALSE); - mDeleteButton->setVisible( TRUE ); - mDeleteButton->setEnabled( TRUE ); + mDeleteButton->setVisible(TRUE); + mDeleteButton->setEnabled(FALSE); } } @@ -1617,20 +1645,27 @@ BOOL FSPanelProfilePicks::postBuild() mDeleteButton->setCommitCallback(boost::bind(&FSPanelProfilePicks::onClickDelete, this)); mRlvBehaviorCallbackConnection = gRlvHandler.setBehaviourCallback(boost::bind(&FSPanelProfilePicks::updateRlvRestrictions, this, _1, _2)); - mNewButton->setEnabled(!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)); + + mNewButton->setEnabled(canAddNewPick()); + mDeleteButton->setEnabled(canDeletePick()); + + LLTextBox* intro_txt = getChild("Tell everyone about your favorite places in Second Life."); + intro_txt->setTextArg("[GRID]", LLTrans::getString("SECOND_LIFE")); return TRUE; } void FSPanelProfilePicks::onClickNewBtn() { - mNoItemsLabel->setVisible(false); + mNoItemsLabel->setVisible(FALSE); FSPanelPick* pick_panel = FSPanelPick::create(); pick_panel->setAvatarId(getAvatarId()); mTabContainer->addTabPanel( LLTabContainer::TabPanelParams(). panel(pick_panel). select_tab(true)); + mNewButton->setEnabled(canAddNewPick()); + mDeleteButton->setEnabled(canDeletePick()); } void FSPanelProfilePicks::onClickDelete() @@ -1639,43 +1674,38 @@ void FSPanelProfilePicks::onClickDelete() if (pick_panel) { LLUUID pick_id = pick_panel->getPickId(); - if (!pick_id.isNull()) - { - LLSD args; - args["PICK"] = pick_panel->getPickName(); - LLSD payload; - payload["pick_id"] = pick_id; - LLNotificationsUtil::add("DeleteAvatarPick", args, payload, boost::bind(&FSPanelProfilePicks::callbackDeletePick, this, _1, _2)); - return; - } + LLSD args; + args["PICK"] = pick_panel->getPickName(); + LLSD payload; + payload["pick_id"] = pick_id; + payload["tab_idx"] = mTabContainer->getCurrentPanelIndex(); + LLNotificationsUtil::add("DeleteAvatarPick", args, payload, boost::bind(&FSPanelProfilePicks::callbackDeletePick, this, _1, _2)); } } -bool FSPanelProfilePicks::callbackDeletePick(const LLSD& notification, const LLSD& response) +void FSPanelProfilePicks::callbackDeletePick(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (0 == option) { LLUUID pick_id = notification["payload"]["pick_id"].asUUID(); - LLAvatarPropertiesProcessor::getInstance()->sendPickDelete(pick_id); - // LLAvatarPropertiesProcessor::instance().sendPickDelete(pick_id); - mNewButton->setEnabled(!LLAgentPicksInfo::getInstance()->isPickLimitReached()); + S32 tab_idx = notification["payload"]["tab_idx"].asInteger(); - for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + FSPanelPick* pick_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (pick_panel && pick_panel->getPickId() == pick_id) { - FSPanelPick* pick_panel = (FSPanelPick*)mTabContainer->getPanelByIndex(tab_idx); - if (pick_panel) - { - if (pick_id == pick_panel->getPickId()) - { - mTabContainer->removeTabPanel(pick_panel); - } - } + mTabContainer->removeTabPanel(pick_panel); } - } - return false; + if (pick_id.notNull()) + { + LLAvatarPropertiesProcessor::getInstance()->sendPickDelete(pick_id); + } + + mNewButton->setEnabled(canAddNewPick()); + mDeleteButton->setEnabled(canDeletePick()); + } } void FSPanelProfilePicks::processProperties(void* data, EAvatarProcessorType type) @@ -1717,9 +1747,10 @@ void FSPanelProfilePicks::processProperties(void* data, EAvatarProcessorType typ label(pick_name)); } - mNewButton->setEnabled(!LLAgentPicksInfo::getInstance()->isPickLimitReached()); + mNewButton->setEnabled(canAddNewPick()); + mDeleteButton->setEnabled(canDeletePick()); - bool no_data = !mTabContainer->getTabCount(); + BOOL no_data = !mTabContainer->getTabCount(); mNoItemsLabel->setVisible(no_data); if (no_data) { @@ -1754,7 +1785,7 @@ void FSPanelProfilePicks::apply() { for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) { - FSPanelPick* pick_panel = (FSPanelPick*)mTabContainer->getPanelByIndex(tab_idx); + FSPanelPick* pick_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); if (pick_panel) { pick_panel->apply(); @@ -1780,10 +1811,22 @@ void FSPanelProfilePicks::updateRlvRestrictions(ERlvBehaviour behavior, ERlvPara { if (behavior == RLV_BHVR_SHOWLOC) { - mNewButton->setEnabled(type != RLV_TYPE_ADD); + mNewButton->setEnabled(canAddNewPick()); } } +bool FSPanelProfilePicks::canAddNewPick() +{ + return (!LLAgentPicksInfo::getInstance()->isPickLimitReached() && + mTabContainer->getTabCount() < LLAgentPicksInfo::getInstance()->getMaxNumberOfPicks() && + !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)); +} + +bool FSPanelProfilePicks::canDeletePick() +{ + return (mTabContainer->getTabCount() > 0); +} + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -2099,27 +2142,10 @@ void FSPanelProfile::processProperties(void* data, EAvatarProcessorType type) void FSPanelProfile::onTabChange() { - LLPanel* active_panel = mTabContainer->getCurrentPanel(); - - if (active_panel == mPanelSecondlife) + FSPanelProfileTab* active_panel = dynamic_cast(mTabContainer->getCurrentPanel()); + if (active_panel) { - mPanelSecondlife->updateData(); // load groups - } - else if (active_panel == mPanelWeb) - { - mPanelWeb->updateData(); // load web profile - } - else if (active_panel == mPanelPicks) - { - mPanelPicks->updateData(); - } - else if (active_panel == mPanelClassifieds) - { - mPanelClassifieds->updateData(); - } - else if (active_panel == mPanelNotes) - { - mPanelNotes->updateData(); + active_panel->updateData(); } } @@ -2164,8 +2190,8 @@ void FSPanelProfile::onOpen(const LLSD& key) // Only show commit buttons on own profile on floater version if (getSelfProfile() && !getEmbedded()) { - getChild("ok_btn")->setVisible( true ); - getChild("cancel_btn")->setVisible( true ); + getChild("ok_btn")->setVisible(TRUE); + getChild("cancel_btn")->setVisible(TRUE); } LLAvatarNameCache::get(getAvatarId(), boost::bind(&FSPanelProfile::onAvatarNameCache, this, _1, _2)); diff --git a/indra/newview/fspanelprofile.h b/indra/newview/fspanelprofile.h index 9d6cdb6ea7..9778061fcc 100644 --- a/indra/newview/fspanelprofile.h +++ b/indra/newview/fspanelprofile.h @@ -63,6 +63,11 @@ public: */ virtual void setAvatarId(const LLUUID& id); + /** + * Sends update data request to server. + */ + virtual void updateData() { } + /** * Processes data received from server. */ @@ -152,7 +157,7 @@ public: /** * Sends update data request to server. */ - void updateData(); + /*virtual*/ void updateData(); void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); @@ -297,7 +302,7 @@ public: /** * Loads web profile. */ - void updateData(); + /*virtual*/ void updateData(); /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); @@ -394,6 +399,8 @@ public: */ virtual void apply(); + void updateTabLabel(const std::string& title); + //This stuff we got from LLRemoteParcelObserver, in the last one we intentionally do nothing /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); /*virtual*/ void setParcelID(const LLUUID& parcel_id) { mParcelId = parcel_id; } @@ -529,16 +536,19 @@ public: /** * Sends update data request to server. */ - void updateData(); + /*virtual*/ void updateData(); private: void onClickNewBtn(); void onClickDelete(); - bool callbackDeletePick(const LLSD& notification, const LLSD& response); + void callbackDeletePick(const LLSD& notification, const LLSD& response); boost::signals2::connection mRlvBehaviorCallbackConnection; void updateRlvRestrictions(ERlvBehaviour behavior, ERlvParamType type); + bool canAddNewPick(); + bool canDeletePick(); + LLTabContainer* mTabContainer; LLUICtrl* mNoItemsLabel; LLButton* mNewButton; @@ -603,7 +613,7 @@ public: void resetData(); - void updateData(); + /*virtual*/ void updateData(); /** * Saves changes. @@ -643,6 +653,8 @@ public: /*virtual*/ BOOL postBuild(); + /*virtual*/ void updateData(); + /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); /*virtual*/ void onOpen(const LLSD& key); @@ -655,7 +667,6 @@ public: private: void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); void onTabChange(); - void updateData(); FSPanelProfileSecondLife* mPanelSecondlife; FSPanelProfileWeb* mPanelWeb; diff --git a/indra/newview/fsradarlistctrl.cpp b/indra/newview/fsradarlistctrl.cpp index 57e1d5a9c9..d25f7dbc9c 100644 --- a/indra/newview/fsradarlistctrl.cpp +++ b/indra/newview/fsradarlistctrl.cpp @@ -29,6 +29,7 @@ #include "fsradarlistctrl.h" #include "llscrolllistitem.h" +#include "lltooldraganddrop.h" #include "rlvhandler.h" static LLDefaultChildRegistry::Register r("radar_list"); @@ -68,3 +69,59 @@ BOOL FSRadarListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) } return handled; } + +BOOL FSRadarListCtrl::handleMouseDown(S32 x, S32 y, MASK mask) +{ + gFocusMgr.setMouseCapture(this); + + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + LLToolDragAndDrop::getInstance()->setDragStart(screen_x, screen_y); + + return FSScrollListCtrl::handleMouseDown(x, y, mask); +} + +BOOL FSRadarListCtrl::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (hasMouseCapture()) + { + gFocusMgr.setMouseCapture(NULL); + } + + return FSScrollListCtrl::handleMouseUp(x, y, mask); +} + +BOOL FSRadarListCtrl::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = hasMouseCapture(); + if (handled) + { + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + if(LLToolDragAndDrop::getInstance()->isOverThreshold(screen_x, screen_y)) + { + // First, create the global drag and drop object + std::vector types; + uuid_vec_t cargo_ids; + typedef std::vector item_vec_t; + item_vec_t selected_items = getAllSelected(); + for (item_vec_t::const_iterator it = selected_items.begin(); it != selected_items.end(); ++it) + { + cargo_ids.push_back((*it)->getUUID()); + } + types.resize(cargo_ids.size(), DAD_PERSON); + LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_PEOPLE; + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, src); + } + } + else + { + handled = FSScrollListCtrl::handleHover(x, y, mask); + } + + return handled; +} + diff --git a/indra/newview/fsradarlistctrl.h b/indra/newview/fsradarlistctrl.h index 7ba8109725..33bfdd2984 100644 --- a/indra/newview/fsradarlistctrl.h +++ b/indra/newview/fsradarlistctrl.h @@ -43,6 +43,9 @@ public: virtual ~FSRadarListCtrl() {} /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); protected: friend class LLUICtrlFactory; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ffaa3df795..36f8cf52db 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -120,6 +120,7 @@ // Linden library includes #include "llavatarnamecache.h" #include "lldiriterator.h" +#include "llexperiencecache.h" #include "llimagej2c.h" #include "llmemory.h" #include "llprimitive.h" @@ -4008,8 +4009,8 @@ LLSD LLAppViewer::getViewerInfo() const //} if (gPacketsIn > 0) { - info["PACKETS_LOST"] = LLStatViewer::PACKETS_LOST.getTotalSamples(); - info["PACKETS_IN"] = LLStatViewer::PACKETS_IN.getTotalSamples(); + info["PACKETS_LOST"] = S32(LLStatViewer::PACKETS_LOST.getTotalSamples()); + info["PACKETS_IN"] = S32(LLStatViewer::PACKETS_IN.getTotalSamples()); info["PACKETS_PCT"] = 100.f*info["PACKETS_LOST"].asReal() / info["PACKETS_IN"].asReal(); } @@ -5514,6 +5515,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.c_str()); + 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.c_str()); + 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. @@ -5725,7 +5752,7 @@ void LLAppViewer::idle() // floating throughout the various object lists. // idleNameCache(); - + idleExperienceCache(); idleNetwork(); @@ -6171,6 +6198,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 // @@ -6341,6 +6384,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 5fd2d726a7..dca4fc42b4 100755 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -123,6 +123,9 @@ public: void loadNameCache(); void saveNameCache(); + void loadExperienceCache(); + void saveExperienceCache(); + void removeMarkerFiles(); void removeDumpDir(); @@ -245,6 +248,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 eda36eba79..b32462890c 100755 --- a/indra/newview/llassetuploadqueue.cpp +++ b/indra/newview/llassetuploadqueue.cpp @@ -178,6 +178,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); @@ -201,7 +202,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; @@ -213,6 +215,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/llavataractions.cpp b/indra/newview/llavataractions.cpp index 9556cfba23..67df7c6dde 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -300,11 +300,17 @@ void LLAvatarActions::startCall(const LLUUID& id) } // static -void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id) +// [FS Communication UI] +//void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id) +const LLUUID LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id) +// { if (ids.size() == 0) { - return; + // [FS Communication UI] + //return; + return LLUUID::null; + // } // convert vector into std::vector for addSession @@ -318,7 +324,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floate { make_ui_sound("UISndInvalidOp"); RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF); - return; + return LLUUID::null; } id_array.push_back(idAgent); // [/RLVa:KB] @@ -331,12 +337,18 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floate ids[0], id_array, true, floater_id); if (session_id == LLUUID::null) { - return; + // [FS Communication UI] + //return; + return session_id; + // } gIMMgr->autoStartCallOnStartup(session_id); make_ui_sound("UISndStartIM"); + + // [FS Communication UI] + return session_id; } /* AD *TODO: Is this function needed any more? @@ -361,7 +373,10 @@ bool LLAvatarActions::canCall() } // static -void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& floater_id) +// [FS Communication UI] +//void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& floater_id) +const LLUUID LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& floater_id) +// { // *HACK: Copy into dynamic array std::vector id_array; @@ -375,7 +390,7 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& float { make_ui_sound("UISndInvalidOp"); RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF); - return; + return LLUUID::null; } id_array.push_back(idAgent); // [/RLVa:KB] @@ -386,7 +401,10 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& float if (session_id == LLUUID::null) { - return; + // [FS Communication UI] + //return; + return session_id; + // } // [FS communication UI] @@ -395,6 +413,9 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& float // [FS communication UI] make_ui_sound("UISndStartIM"); + + // [FS Communication UI] + return session_id; } static const char* get_profile_floater_name(const LLUUID& avatar_id) diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 34a434d8e4..0a795126a5 100755 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -84,12 +84,18 @@ public: /** * Start an ad-hoc conference voice call with multiple users in a specific IM floater. */ - static void startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); + // [FS Communication UI] + //static void startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); + static const LLUUID startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); + // /** * Start conference chat with the given avatars in a specific IM floater. */ - static void startConference(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); + // [FS Communication UI] + //static void startConference(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); + static const LLUUID startConference(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); + // /** * Show avatar profile. diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index 55edb2ecd9..985e39fc73 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 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 @@ -190,7 +195,7 @@ bool LLFloaterScriptQueue::onScriptModifyConfirmation(const LLSD& notification, getChild("queue output")->addSimpleElement(buffer, ADD_BOTTOM); - return nextObject(); + return startQueue(); } BOOL LLFloaterScriptQueue::isDone() const @@ -255,6 +260,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("compile_queue", mParent); + if(!queue) + return; + + queue->experienceIdsReceived(content["experience_ids"]); + } +}; + + + ///---------------------------------------------------------------------------- /// Class LLFloaterCompileQueue @@ -307,6 +346,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) { @@ -347,25 +401,52 @@ void LLFloaterCompileQueue::handleInventory(LLViewerObject *viewer_object, { LLInventoryItem *itemp = iter->second; LLScriptQueueData* datap = new LLScriptQueueData(getKey().asUUID(), - itemp->getName(), - viewer_object->getID(), - itemp->getUUID()); + viewer_object->getID(), itemp); - //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); + ExperienceAssociationResponder::fetchAssociatedExperience(itemp->getParentUUID(), itemp->getUUID(), + boost::bind(LLFloaterCompileQueue::requestAsset, datap, _1)); } } } + +void LLFloaterCompileQueue::requestAsset( LLScriptQueueData* datap, const LLSD& experience ) +{ + LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance("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("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, @@ -405,12 +486,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(); } } } @@ -422,7 +503,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) { @@ -430,15 +511,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)) { @@ -586,6 +667,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 0f758ab1cb..cd67c461e4 100755 --- a/indra/newview/llcompilequeue.h +++ b/indra/newview/llcompilequeue.h @@ -83,13 +83,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; @@ -133,6 +135,9 @@ public: LLAssetUploadQueue* getUploadQueue() { return mUploadQueue; } + void experienceIdsReceived( const LLSD& content ); + BOOL hasExperience(const LLUUID& id)const; + protected: LLFloaterCompileQueue(const LLSD& key); virtual ~LLFloaterCompileQueue(); @@ -141,16 +146,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/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index a50184460b..f2602c8c7d 100755 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -116,7 +116,7 @@ LLExpandableTextBox::LLTextBoxEx::LLTextBoxEx(const Params& p) mExpanderVisible(false) { setIsChrome(TRUE); - + setMaxTextLength(p.max_text_length); } void LLExpandableTextBox::LLTextBoxEx::reshape(S32 width, S32 height, BOOL called_from_parent) diff --git a/indra/newview/llexpandabletextbox.h b/indra/newview/llexpandabletextbox.h index 399e48bea2..5dea35bb82 100755 --- a/indra/newview/llexpandabletextbox.h +++ b/indra/newview/llexpandabletextbox.h @@ -103,7 +103,7 @@ public: Optional scroll; Optional max_height; - + Optional bg_visible, expanded_bg_visible; 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 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..ec6134a4b3 --- /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.c_str()); + LLSDSerialize::toPrettyXML(settings, stream); +} + + +void LLExperienceLog::loadEvents() +{ + LLSD settings = LLSD::emptyMap(); + + std::string filename = getFilename(); + llifstream stream(filename.c_str()); + 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(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 +{ +public: + typedef boost::signals2::signal + 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; +}; + + + + +#endif // LL_LLEXPERIENCELOG_H diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index 8196551219..a3c985bc88 100755 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -381,13 +381,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; @@ -396,15 +399,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 @@ -417,27 +417,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("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 select_callback_t; + // filter function for experiences, return true if the experience should be hidden. + typedef boost::function filter_function; + typedef std::vector 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 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..2b94102dc6 --- /dev/null +++ b/indra/newview/llfloaterexperienceprofile.cpp @@ -0,0 +1,1007 @@ +/** + * @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 HandleResponder : public LLHTTPClient::Responder +{ +public: + HandleResponder(const LLHandle& parent):mParent(parent){} + LLHandle mParent; + + virtual void httpFailure() + { + LL_WARNS() << "HandleResponder failed with code: " << getStatus() << ", reason: " << getReason() << LL_ENDL; + } +}; + +class ExperienceUpdateResponder : public HandleResponder +{ +public: + ExperienceUpdateResponder(const LLHandle& parent):HandleResponder(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 +{ +public: + IsAdminResponder(const LLHandle& parent):HandleResponder(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(PNL_TOP)->setVisible(TRUE); + parent->getChild(BTN_EDIT)->setVisible(TRUE); + } + } +}; + +BOOL LLFloaterExperienceProfile::postBuild() +{ + + if (mExperienceId.notNull()) + { + LLExperienceCache::fetch(mExperienceId, true); + LLExperienceCache::get(mExperienceId, boost::bind(&LLFloaterExperienceProfile::experienceCallback, + getDerivedHandle(), _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())); + } + } + } + + 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(EDIT TF_DESC)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + getChild(EDIT TF_MATURITY)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + getChild(EDIT TF_MRKT)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL); + getChild(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(EDIT TF_DESC)->setCommitOnFocusLost(TRUE); + + + LLEventPumps::instance().obtain("experience_permission").listen(mExperienceId.asString()+"-profile", + boost::bind(&LLFloaterExperienceProfile::experiencePermission, getDerivedHandle(this), _1)); + + return TRUE; +} + +void LLFloaterExperienceProfile::experienceCallback(LLHandle handle, const LLSD& experience ) +{ + LLFloaterExperienceProfile* pllpep = handle.get(); + if(pllpep) + { + pllpep->refreshExperience(experience); + } +} + + +bool LLFloaterExperienceProfile::experiencePermission( LLHandle handle, const LLSD& permission ) +{ + LLFloaterExperienceProfile* pllpep = handle.get(); + if(pllpep) + { + pllpep->updatePermission(permission); + } + return false; +} + + +void LLFloaterExperienceProfile::onClickEdit() +{ + LLTabContainer* tabs = getChild("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(PNL_IMAGE); + LLLayoutPanel* descriptionPanel = getChild(PNL_DESC); + LLLayoutPanel* locationPanel = getChild(PNL_LOC); + LLLayoutPanel* marketplacePanel = getChild(PNL_MRKT); + LLLayoutPanel* topPanel = getChild(PNL_TOP); + + + imagePanel->setVisible(FALSE); + descriptionPanel->setVisible(FALSE); + locationPanel->setVisible(FALSE); + marketplacePanel->setVisible(FALSE); + topPanel->setVisible(FALSE); + + + LLTextBox* child = getChild(TF_NAME); + //child->setText(experience[LLExperienceCache::NAME].asString()); + // FIRE-16402: Call the correct ctor for LLSLURL + //child->setText(LLSLURL("experience", experience[LLExperienceCache::EXPERIENCE_ID], "profile").getSLURLString()); + child->setText(LLSLURL("experience", experience[LLExperienceCache::EXPERIENCE_ID].asUUID(), "profile").getSLURLString()); + // + + LLLineEditor* linechild = getChild(EDIT TF_NAME); + linechild->setText(experience[LLExperienceCache::NAME].asString()); + + std::string value = experience[LLExperienceCache::DESCRIPTION].asString(); + LLExpandableTextBox* exchild = getChild(TF_DESC); + exchild->setText(value); + descriptionPanel->setVisible(value.length()>0); + + LLTextEditor* edit_child = getChild(EDIT TF_DESC); + edit_child->setText(value); + + mLocationSLURL = experience[LLExperienceCache::SLURL].asString(); + child = getChild(TF_SLURL); + bool has_slurl = mLocationSLURL.length()>0; + locationPanel->setVisible(has_slurl); + mLocationSLURL = LLSLURL(mLocationSLURL).getSLURLString(); + child->setText(mLocationSLURL); + + + child = getChild(EDIT TF_SLURL); + if(has_slurl) + { + child->setText(mLocationSLURL); + } + else + { + child->setText(getString("empty_slurl")); + } + + setMaturityString((U8)(experience[LLExperienceCache::MATURITY].asInteger()), getChild(TF_MATURITY), getChild(EDIT TF_MATURITY)); + + LLUUID id = experience[LLExperienceCache::AGENT_ID].asUUID(); + child = getChild(TF_OWNER); + value = LLSLURL("agent", id, "inspect").getSLURLString(); + child->setText(value); + + + id = experience[LLExperienceCache::GROUP_ID].asUUID(); + bool id_null = id.isNull(); + child = getChild(TF_GROUP); + value = LLSLURL("group", id, "inspect").getSLURLString(); + child->setText(value); + getChild(PNL_GROUP)->setVisible(!id_null); + + setEditGroup(id); + + getChild(BTN_SET_GROUP)->setEnabled(experience[LLExperienceCache::AGENT_ID].asUUID() == gAgent.getID()); + + LLCheckBoxCtrl* enable = getChild(EDIT BTN_ENABLE); + S32 properties = mExperienceDetails[LLExperienceCache::PROPERTIES].asInteger(); + enable->set(!(properties & LLExperienceCache::PROPERTY_DISABLED)); + + enable = getChild(EDIT BTN_PRIVATE); + enable->set(properties & LLExperienceCache::PROPERTY_PRIVATE); + + topPanel->setVisible(TRUE); + child=getChild(TF_GRID_WIDE); + child->setVisible(TRUE); + + if(properties & LLExperienceCache::PROPERTY_GRID) + { + child->setText(LLTrans::getString("Grid-Scope")); + } + else + { + child->setText(LLTrans::getString("Land-Scope")); + } + + if(getChild(BTN_EDIT)->getVisible()) + { + topPanel->setVisible(TRUE); + } + + if(properties & LLExperienceCache::PROPERTY_PRIVILEGED) + { + child = getChild(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 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(TF_MRKT); + child->setText(value); + if(value.size()) + { + marketplacePanel->setVisible(TRUE); + } + else + { + marketplacePanel->setVisible(FALSE); + } + } + else + { + marketplacePanel->setVisible(FALSE); + } + + linechild = getChild(EDIT TF_MRKT); + linechild->setText(value); + + if(data.has(IMG_LOGO)) + { + LLTextureCtrl* logo = getChild(IMG_LOGO); + + LLUUID id = data[IMG_LOGO].asUUID(); + logo->setImageAssetID(id); + imagePanel->setVisible(TRUE); + + logo = getChild(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(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(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(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())); +} + +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("tab_container"); + tabs->selectTabByName("panel_experience_info"); + } + else if(mSaveCompleteAction == CLOSE) + { + closeFloater(); + } +} + +void LLFloaterExperienceProfile::changeToView() +{ + if(mForceClose || !mDirty) + { + refreshExperience(mExperienceDetails); + LLTabContainer* tabs = getChild("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(EDIT TF_SLURL); + mLocationSLURL = LLSLURL(region->getName(), gAgent.getPositionGlobal()).getSLURLString(); + child->setText(mLocationSLURL); + onFieldChanged(); + } +} + +void LLFloaterExperienceProfile::onClickClear() +{ + LLTextBox* child = getChild(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(BTN_ALLOW); + button->setEnabled(FALSE); + + button=getChild(BTN_FORGET); + button->setEnabled(TRUE); + + button=getChild(BTN_BLOCK); + button->setEnabled(TRUE); +} + +void LLFloaterExperienceProfile::experienceForgotten() +{ + LLButton* button=getChild(BTN_ALLOW); + button->setEnabled(TRUE); + + button=getChild(BTN_FORGET); + button->setEnabled(FALSE); + + button=getChild(BTN_BLOCK); + button->setEnabled(TRUE); +} + +void LLFloaterExperienceProfile::experienceBlocked() +{ + LLButton* button=getChild(BTN_ALLOW); + button->setEnabled(TRUE); + + button=getChild(BTN_FORGET); + button->setEnabled(TRUE); + + button=getChild(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(EDIT TF_NAME)->getText(); + mPackage[LLExperienceCache::DESCRIPTION] = getChild(EDIT TF_DESC)->getText(); + if(mLocationSLURL.empty()) + { + mPackage[LLExperienceCache::SLURL] = LLStringUtil::null; + } + else + { + mPackage[LLExperienceCache::SLURL] = mLocationSLURL; + } + + mPackage[LLExperienceCache::MATURITY] = getChild(EDIT TF_MATURITY)->getSelectedValue().asInteger(); + + LLSD metadata; + + metadata[TF_MRKT] = getChild(EDIT TF_MRKT)->getText(); + metadata[IMG_LOGO] = getChild(EDIT IMG_LOGO)->getImageAssetID(); + + LLPointer 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(EDIT BTN_ENABLE); + if(enable->get()) + { + properties &= ~LLExperienceCache::PROPERTY_DISABLED; + } + else + { + properties |= LLExperienceCache::PROPERTY_DISABLED; + } + + enable = getChild(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("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(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 handle, const LLSD& experience); + static bool experiencePermission(LLHandle 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..777dc382cd --- /dev/null +++ b/indra/newview/llfloaterexperiences.cpp @@ -0,0 +1,340 @@ +/** + * @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 "llfloaterreg.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 NameMap; + typedef boost::function Callback; + LLExperienceListResponder(const LLHandle& parent, NameMap& nameMap, const std::string& errorMessage="ErrorMessage"):mParent(parent),mErrorMessage(errorMessage) + { + mNameMap.swap(nameMap); + } + + Callback mCallback; + LLHandle mParent; + NameMap mNameMap; + const std::string mErrorMessage; + /*virtual*/ void httpSuccess() + { + if(mParent.isDead()) + return; + + LLFloaterExperiences* parent=mParent.get(); + LLTabContainer* tabs = parent->getChild("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("xp_tabs")->addTabPanel(LLTabContainer::TabPanelParams(). + panel(newPanel). + label(LLTrans::getString(name)). + select_tab(select)); + + return newPanel; +} + +BOOL LLFloaterExperiences::postBuild() +{ + getChild("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("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("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("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("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(), nameMap)); + } + + updateInfo("GetAdminExperiences","Admin_Experiences_Tab"); + updateInfo("GetCreatorExperiences","Contrib_Experiences_Tab"); + + lookup_url = region->getCapability("AgentExperiences"); + if(!lookup_url.empty()) + { + nameMap["experience_ids"]="Owned_Experiences_Tab"; + LLExperienceListResponder* responder = new LLExperienceListResponder(getDerivedHandle(), nameMap, "ExperienceAcquireFailed"); + responder->mCallback = boost::bind(&LLFloaterExperiences::checkPurchaseInfo, this, _1, _2); + LLHTTPClient::get(lookup_url, responder); + } + } +} + +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("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")); + + LLFloaterExperiences::findInstance()->updateInfo("GetAdminExperiences","Admin_Experiences_Tab"); + LLFloaterExperiences::findInstance()->updateInfo("GetCreatorExperiences","Contrib_Experiences_Tab"); +} + +void LLFloaterExperiences::updateInfo(std::string experiences, std::string tab) +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + LLExperienceListResponder::NameMap nameMap; + std::string lookup_url = region->getCapability(experiences); + if(!lookup_url.empty()) + { + nameMap["experience_ids"]=tab; + LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle(), nameMap)); + } + } +} + +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(), nameMap, "ExperienceAcquireFailed"); + responder->mCallback = boost::bind(&LLFloaterExperiences::checkPurchaseInfo, this, _1, _2); + LLHTTPClient::post(url, content, responder); + } +} + +LLFloaterExperiences* LLFloaterExperiences::findInstance() +{ + return LLFloaterReg::findTypedInstance("experiences"); +} diff --git a/indra/newview/llfloaterexperiences.h b/indra/newview/llfloaterexperiences.h new file mode 100644 index 0000000000..769283ff07 --- /dev/null +++ b/indra/newview/llfloaterexperiences.h @@ -0,0 +1,60 @@ +/** + * @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); + static LLFloaterExperiences* findInstance(); +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; + void updateInfo(std::string experiences, std::string tab); + +private: + +}; + +#endif //LL_LLFLOATEREXPERIENCES_H diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index d22d9fc20d..96bff82db5 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" #include "llsdutil_math.h" @@ -121,6 +124,28 @@ public: } }; + +class LLPanelLandExperiences + : public LLPanel +{ +public: + LLPanelLandExperiences(LLSafeHandle& 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& mParcel; + + + LLPanelExperienceListEditor* mAllowed; + LLPanelExperienceListEditor* mBlocked; +}; + // 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] @@ -259,6 +284,7 @@ LLFloaterLand::LLFloaterLand(const LLSD& seed) 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); sObserver = new LLParcelSelectionObserver(); LLViewerParcelMgr::getInstance()->addObserver( sObserver ); @@ -299,6 +325,7 @@ void LLFloaterLand::refresh() mPanelMedia->refresh(); mPanelAccess->refresh(); mPanelCovenant->refresh(); + mPanelExperiences->refresh(); } @@ -359,6 +386,15 @@ void* LLFloaterLand::createPanelLandAccess(void* data) return self->mPanelAccess; } +// static +void* LLFloaterLand::createPanelLandExperiences(void* data) +{ + LLFloaterLand* self = (LLFloaterLand*)data; + self->mPanelExperiences = new LLPanelLandExperiences(self->mParcel); + return self->mPanelExperiences; +} + + //--------------------------------------------------------------------------- // LLPanelLandGeneral //--------------------------------------------------------------------------- @@ -2136,7 +2172,7 @@ void LLPanelLandOptions::refresh() else { // something selected, hooray! - LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion(); + //LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion(); // FIRE-16112 fix // Display options BOOL can_change_options = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_OPTIONS); @@ -2152,7 +2188,7 @@ void LLPanelLandOptions::refresh() mCheckGroupObjectEntry ->set( parcel->getAllowGroupObjectEntry() || parcel->getAllowAllObjectEntry()); mCheckGroupObjectEntry ->setEnabled( can_change_options && !parcel->getAllowAllObjectEntry() ); - BOOL region_damage = regionp ? regionp->getAllowDamage() : FALSE; + //BOOL region_damage = regionp ? regionp->getAllowDamage() : FALSE; // FIRE-16112 fix // FIRE-6604 : Reinstate the "Allow Other Residents to Edit Terrain" option in About Land BOOL can_change_terraform = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_EDIT); @@ -2161,7 +2197,10 @@ void LLPanelLandOptions::refresh() // mCheckSafe ->set( !parcel->getAllowDamage() ); - mCheckSafe ->setEnabled( can_change_options && region_damage ); + // FIRE-16112 fix + //mCheckSafe ->setEnabled( can_change_options && region_damage ); + mCheckSafe ->setEnabled( can_change_options ); + // mCheckFly ->set( parcel->getAllowFly() ); mCheckFly ->setEnabled( can_change_options ); @@ -2247,6 +2286,7 @@ void LLPanelLandOptions::refresh() // they can see the checkbox, but its disposition depends on the // state of the region + LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion(); // FIRE-16112 fix if (regionp) { if (regionp->getSimAccess() == SIM_ACCESS_PG) @@ -2627,7 +2667,7 @@ void LLPanelLandAccess::refresh() getChild("AllowedText")->setTextArg(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; @@ -2677,7 +2717,7 @@ void LLPanelLandAccess::refresh() getChild("BanCheck")->setTextArg(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; @@ -3303,3 +3343,103 @@ void insert_maturity_into_textbox(LLTextBox* target_textbox, LLFloater* names_fl target_textbox->appendText(LLViewerParcelMgr::getInstance()->getSelectionRegion()->getSimAccessString(), false); target_textbox->appendText(text_after_rating, false); } + +LLPanelLandExperiences::LLPanelLandExperiences( LLSafeHandle& 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)); + + // no privileged ones + mBlocked->addFilter(boost::bind(LLPanelExperiencePicker::FilterWithoutProperties, _1, LLExperienceCache::PROPERTY_PRIVILEGED|LLExperienceCache::PROPERTY_GRID)); + + getChild("trusted_layout_panel")->setVisible(FALSE); + getChild("experiences_help_text")->setVisible(FALSE); + getChild("allowed_text_help")->setText(getString("allowed_parcel_text")); + getChild("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(control_name); + if(child) + { + child->getChild("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(); + } +} + +void LLPanelLandExperiences::refresh() +{ + refreshPanel(mAllowed, EXPERIENCE_KEY_TYPE_ALLOWED); + refreshPanel(mBlocked, EXPERIENCE_KEY_TYPE_BLOCKED); +} diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h index cea2ce0865..5fb7b6d467 100755 --- a/indra/newview/llfloaterland.h +++ b/indra/newview/llfloaterland.h @@ -67,6 +67,7 @@ class LLPanelLandBan; class LLPanelLandRenters; class LLPanelLandCovenant; class LLParcel; +class LLPanelLandExperiences; class LLFloaterLand : public LLFloater @@ -102,6 +103,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); @@ -117,6 +119,7 @@ protected: LLPanelLandMedia* mPanelMedia; LLPanelLandAccess* mPanelAccess; LLPanelLandCovenant* mPanelCovenant; + LLPanelLandExperiences* mPanelExperiences; LLSafeHandle mParcel; diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp index 05a6555a8e..97fd2d1d03 100755 --- a/indra/newview/llfloaterproperties.cpp +++ b/indra/newview/llfloaterproperties.cpp @@ -63,6 +63,9 @@ #include "rlvhandler.h" // [/RLVa:KB] #include "llviewernetwork.h" // For OpenSim export perms +#include "llexperienceassociationresponder.h" +#include "llexperiencecache.h" +#include "llslurl.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLPropertiesObserver @@ -265,6 +268,17 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) is_obj_modify = object->permOwnerModify(); } + // Experience info + if(item->getInventoryType() == LLInventoryType::IT_LSL) + { + getChildView("LabelItemExperienceTitle")->setVisible(TRUE); + LLTextBox* tb = getChild("LabelItemExperience"); + tb->setText(getString("loading_experience")); + tb->setVisible(TRUE); + ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLFloaterProperties::setAssociatedExperience, getDerivedHandle(), _1)); + } + // + ////////////////////// // ITEM NAME & DESC // ////////////////////// @@ -574,6 +588,29 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) } } +// Experience info +void LLFloaterProperties::setAssociatedExperience( LLHandle hInfo, const LLSD& experience ) +{ + LLFloaterProperties* info = hInfo.get(); + if(info) + { + LLUUID id; + if(experience.has(LLExperienceCache::EXPERIENCE_ID)) + { + id=experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); + } + if(id.notNull()) + { + info->getChild("LabelItemExperience")->setText(LLSLURL("experience", id, "profile").getSLURLString()); + } + else + { + info->getChild("LabelItemExperience")->setText(LLTrans::getString("ExperienceNameNull")); + } + } +} +// + void LLFloaterProperties::onClickCreator() { LLInventoryItem* item = findItem(); diff --git a/indra/newview/llfloaterproperties.h b/indra/newview/llfloaterproperties.h index 7921917e3b..3612887a47 100755 --- a/indra/newview/llfloaterproperties.h +++ b/indra/newview/llfloaterproperties.h @@ -75,6 +75,9 @@ protected: void refreshFromItem(LLInventoryItem* item); virtual void draw(); + // Experience info + static void setAssociatedExperience( LLHandle hInfo, const LLSD& experience ); + protected: // The item id of the inventory item in question. LLUUID mItemID; diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 9b18c65102..7d59af2383 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 +#include "llpanelexperiencepicker.h" +#include "llexperiencecache.h" +#include "llpanelexperiences.h" // Aurora Sim - Region Settings Console #include "llviewernetwork.h" @@ -132,6 +137,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( @@ -233,6 +250,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); @@ -480,6 +505,16 @@ LLPanelRegionTerrainInfo* LLFloaterRegionInfo::getPanelRegionTerrain() return panel; } +LLPanelRegionExperiences* LLFloaterRegionInfo::getPanelExperiences() +{ + LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance("region_info"); + if (!floater) return NULL; + LLTabContainer* tab = floater->getChild("region_panels"); + return (LLPanelRegionExperiences*)tab->getChild("Experiences"); +} + + + // Aurora Sim - Region Settings Console // static LLPanelRegionOpenSettingsInfo* LLFloaterRegionInfo::getPanelOpenSettings() @@ -1572,6 +1607,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; } @@ -3081,6 +3121,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), @@ -3672,3 +3762,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("trusted_layout_panel")->setVisible(TRUE); + getChild("experiences_help_text")->setText(getString("estate_caption")); + getChild("trusted_text_help")->setText(getString("trusted_estate_text")); + getChild("allowed_text_help")->setText(getString("allowed_estate_text")); + getChild("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(control_name); + if(child) + { + child->getChild("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 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 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(), _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(), _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 6a170cbd28..8552e90e96 100755 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -63,6 +63,9 @@ class LLPanelRegionDebugInfo; class LLPanelRegionTerrainInfo; class LLPanelEstateInfo; class LLPanelEstateCovenant; +class LLPanelExperienceListEditor; +class LLPanelExperiences; +class LLPanelRegionExperiences; class LLEventTimer; class LLEnvironmentSettings; @@ -93,6 +96,7 @@ public: static LLPanelEstateInfo* getPanelEstate(); static LLPanelEstateCovenant* getPanelCovenant(); static LLPanelRegionTerrainInfo* getPanelRegionTerrain(); + static LLPanelRegionExperiences* getPanelExperiences(); // Aurora Sim - Region Settings Panel static LLPanelRegionOpenSettingsInfo* getPanelOpenSettings(); // Aurora Sim - Region Settings Panel @@ -479,4 +483,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 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 3059847ba6..946952b524 100755 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -81,6 +81,7 @@ #include "llagentui.h" #include "lltrans.h" +#include "llexperiencecache.h" //----------------------------------------------------------------------------- // Globals @@ -210,6 +211,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("details_edit"); + details->setValue(desc.str()); + } +} + void LLFloaterReporter::getObjectInfo(const LLUUID& object_id) { // TODO -- @@ -463,7 +488,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("reporter"); @@ -476,6 +501,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("reporter"); + f->getExperienceInfo(experience_id); // Need to deselect on close f->mDeselectOnClose = TRUE; @@ -485,9 +527,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 diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index cfeb987ff6..51c58584e7 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); @@ -103,7 +104,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); @@ -115,6 +116,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 names); void setFromAvatarID(const LLUUID& avatar_id); @@ -127,6 +129,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/llimview.cpp b/indra/newview/llimview.cpp index 9cbab1be51..e5322f3864 100755 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -538,6 +538,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& mHasOfflineMessage(has_offline_msg), // [SL:KB] - Patch: Chat-GroupSnooze | Checked: 2012-08-01 (Catznip-3.3) mCloseAction(CLOSE_DEFAULT), + mSnoozeTime(-1), // [/SL:KB] mParticipantUnreadMessageCount(0), mNumUnread(0), @@ -3415,6 +3416,11 @@ bool LLIMMgr::leaveSession(const LLUUID& session_id) static LLCachedControl s_nSnoozeTime(gSavedSettings, "GroupSnoozeTime", 900); snoozed_sessions_t::iterator itSession = mSnoozedSessions.find(session_id); F64 expirationTime = LLTimer::getTotalSeconds() + F64(s_nSnoozeTime); + if (im_session->mSnoozeTime > -1) + { + expirationTime = LLTimer::getTotalSeconds() + F64(im_session->mSnoozeTime); + im_session->mSnoozeTime = -1; + } if (mSnoozedSessions.end() != itSession) itSession->second = expirationTime; diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 12e8b8962f..fcae474cc8 100755 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -116,9 +116,12 @@ public: SType mSessionType; // [SL:KB] - Patch: Chat-GroupSnooze | Checked: 2014-03-01 (Catznip-3.6) SCloseAction mCloseAction; + S32 mSnoozeTime; // [/SL:KB] LLUUID mOtherParticipantID; uuid_vec_t mInitialTargetIDs; + // Needed to store IDs of initially invited agents; required for FS Communication UI, as original IM floater gets destroyed + uuid_vec_t mInitialInvitedIDs; std::string mHistoryFileName; // connection to voice channel state change signal diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 5962260703..040dfc5c1b 100755 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -174,7 +174,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 @@ -67,6 +67,30 @@ void LLScriptHandler::initChannel() mChannel.get()->init(channel_right_bound - channel_width, channel_right_bound); } +//-------------------------------------------------------------------------- +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(mChannel.get()); + if(channel) + { + channel->addToast(p); + } +} + //-------------------------------------------------------------------------- bool LLScriptHandler::processNotification(const LLNotificationPtr& notification) { @@ -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(mChannel.get()); - if(channel) - { - channel->addToast(p); - } + addToastWithNotification(notification); } return false; } +void LLScriptHandler::onChange( LLNotificationPtr notification ) +{ + LLScreenChannel* channel = dynamic_cast(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..fc4ee9862e --- /dev/null +++ b/indra/newview/llpanelexperiencelisteditor.cpp @@ -0,0 +1,262 @@ +/** + * @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 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("experience_list"); + mAdd = getChild("btn_add"); + mRemove = getChild("btn_remove"); + mProfile = getChild("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(); +} + + +void LLPanelExperienceListEditor::onRemove() +{ + // the commented out code in this function is handled by the callback and no longer necessary! + + std::vector items= mItems->getAllSelected(); + std::vector::iterator it = items.begin(); + for(/**/; it != items.end(); ++it) + { + if((*it) != NULL) + { + //mExperienceIds.erase((*it)->getValue()); + mRemovedCallback((*it)->getValue()); + } + } + mItems->selectFirstItem(); + checkButtonsEnabled(); + //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 items= mItems->getAllSelected(); + std::vector::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(), _1)); + } + + + if(mItems->getItemCount() == 0) + { + mItems->setCommentText(getString("no_results")); + } + + + checkButtonsEnabled(); +} + +void LLPanelExperienceListEditor::experienceDetailsCallback( LLHandle 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; + + std::string experience_name_string = experience[LLExperienceCache::NAME].asString(); + if (experience_name_string.empty()) + { + experience_name_string = LLTrans::getString("ExperienceNameUntitled"); + } + + item->getColumn(0)->setValue(experience_name_string); +} + +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("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 + +class LLScrollListCtrl; +class LLButton; +class LLFloaterExperiencePicker; + +class LLPanelExperienceListEditor : public LLPanel +{ +public: + + typedef boost::signals2::signal list_changed_signal_t; + // filter function for experiences, return true if the experience should be hidden. + typedef boost::function experience_function; + typedef std::vector filter_list; + typedef LLHandle 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 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 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("experience_log_list"); + mEventList->setCommitCallback(boost::bind(&LLPanelExperienceLog::onSelectionChanged, this)); + mEventList->setDoubleClickCallback( boost::bind(&LLPanelExperienceLog::onProfileExperience, this)); + + getChild("btn_clear")->setCommitCallback(boost::bind(&LLExperienceLog::clear, log)); + getChild("btn_clear")->setCommitCallback(boost::bind(&LLPanelExperienceLog::refresh, this)); + + getChild(BTN_PROFILE_XP)->setCommitCallback(boost::bind(&LLPanelExperienceLog::onProfileExperience, this)); + getChild(BTN_REPORT_XP )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onReportExperience, this)); + getChild("btn_notify" )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onNotify, this)); + getChild("btn_next" )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onNext, this)); + getChild("btn_prev" )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onPrev, this)); + + LLCheckBoxCtrl* check = getChild("notify_all"); + check->set(log->getNotifyNewEvent()); + check->setCommitCallback(boost::bind(&LLPanelExperienceLog::notifyChanged, this)); + + + LLSpinCtrl* spin = getChild("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("btn_next")->setEnabled(moreItems); + getChild("btn_prev")->setEnabled(mCurrentPage>0); + getChild("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("notify_all")->get()); +} + +void LLPanelExperienceLog::logSizeChanged() +{ + int value = (int)(getChild("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(BTN_REPORT_XP)->setEnabled(enabled); + getChild(BTN_PROFILE_XP)->setEnabled(enabled); + getChild("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..e81cdfa006 --- /dev/null +++ b/indra/newview/llpanelexperiencepicker.cpp @@ -0,0 +1,444 @@ +/** +* @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" +#include "lltrans.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 t_panel_status("llpanelexperiencepicker"); + +class LLExperienceSearchResponder : public LLHTTPClient::Responder +{ +public: + LLUUID mQueryID; + LLHandle mParent; + + LLExperienceSearchResponder(const LLUUID& id, const LLHandle& 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(TEXT_EDIT)->setKeystrokeCallback( boost::bind(&LLPanelExperiencePicker::editKeystroke, this, _1, _2),NULL); + + childSetAction(BTN_FIND, boost::bind(&LLPanelExperiencePicker::onBtnFind, this)); + getChildView(BTN_FIND)->setEnabled(TRUE); + + LLScrollListCtrl* searchresults = getChild(LIST_RESULTS); + searchresults->setDoubleClickCallback( boost::bind(&LLPanelExperiencePicker::onBtnSelect, this)); + searchresults->setCommitCallback(boost::bind(&LLPanelExperiencePicker::onList, this)); + getChildView(LIST_RESULTS)->setEnabled(FALSE); + getChild(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); + + // Remember maturity level + //getChild(TEXT_MATURITY)->setCurrentByIndex(2); + getChild(TEXT_MATURITY)->setCommitCallback(boost::bind(&LLPanelExperiencePicker::onMaturity, this)); + getChild(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(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(true); +} + +void LLPanelExperiencePicker::onBtnFind() +{ + mCurrentPage=1; + find(); +} + +void LLPanelExperiencePicker::onList() +{ + bool enabled = isSelectButtonEnabled(); + getChildView(BTN_OK)->setEnabled(enabled); + + enabled = enabled && getChild(LIST_RESULTS)->getNumSelected() == 1; + getChildView(BTN_PROFILE)->setEnabled(enabled); +} + +void LLPanelExperiencePicker::find() +{ + std::string text = getChild(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())); + + } + getChild(LIST_RESULTS)->deleteAllItems(); + getChild(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(LIST_RESULTS); + return list->getFirstSelectedIndex() >=0; +} + +void LLPanelExperiencePicker::getSelectedExperienceIds( const LLScrollListCtrl* results, uuid_vec_t &experience_ids ) +{ + std::vector items = results->getAllSelected(); + for(std::vector::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(LIST_RESULTS)->setAllowMultipleSelection(allow_multiple); +} + + +void name_callback(const LLHandle& 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(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(LIST_RESULTS); + uuid_vec_t experience_ids; + + getSelectedExperienceIds(results, experience_ids); + mSelectionCallback(experience_ids); + getChild(LIST_RESULTS)->deselectAllItems(TRUE); + if(mCloseOnSelect) + { + mCloseOnSelect = FALSE; + onBtnClose(); + } + } + else + { + onBtnProfile(); + } +} + +void LLPanelExperiencePicker::onBtnClose() +{ + LLFloater* floater = getParentByType(); + if (floater) + { + floater->closeFloater(); + } +} + +void LLPanelExperiencePicker::onBtnProfile() +{ + LLScrollListItem* item = getChild(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(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; + + std::string experience_name_string = experience[LLExperienceCache::NAME].asString(); + if (experience_name_string.empty()) + { + experience_name_string = LLTrans::getString("ExperienceNameUntitled"); + } + + 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_name_string; + 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(), experience[LLExperienceCache::EXPERIENCE_ID], _1, _2)); + } + + if (search_results->isEmpty()) + { + LLStringUtil::format_map_t map; + std::string search_text = getChild(TEXT_EDIT)->getValue().asString(); + map["[TEXT]"] = search_text; + if (search_text.empty()) + { + getChild(LIST_RESULTS)->setCommentText(getString("no_results")); + } + else + { + getChild(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(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(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::FilterWithoutProperties( const LLSD& experience, S32 prop) +{ + return ((experience[LLExperienceCache::PROPERTIES].asInteger() & prop) == prop); +} + +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..e39ffed70b --- /dev/null +++ b/indra/newview/llpanelexperiencepicker.h @@ -0,0 +1,95 @@ +/** +* @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 select_callback_t; + // filter function for experiences, return true if the experience should be hidden. + typedef boost::function filter_function; + typedef std::vector filter_list; + + LLPanelExperiencePicker(); + virtual ~LLPanelExperiencePicker(); + + BOOL postBuild(); + + void addFilter(filter_function func){mFilters.push_back(func);} + template + void addFilters(IT begin, IT end){mFilters.insert(mFilters.end(), begin, end);} + void setDefaultFilters(); + + static bool FilterWithProperty(const LLSD& experience, S32 prop); + static bool FilterWithoutProperties(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 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("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(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("button_panel")->setVisible(false); + } + else + { + getChild("button_panel")->setVisible(true); + LLButton* child = getChild("btn_action"); + child->setCommitCallback(cb); + child->setLabel(getString(label)); + } +} + +void LLPanelExperiences::enableButton( bool enable ) +{ + getChild("btn_action")->setEnabled(enable); +} + + +LLExperienceItem::LLExperienceItem() + : mName(NULL) +{ + buildFromFile("panel_experience_list_item.xml"); +} + +void LLExperienceItem::init( const LLUUID& id) +{ + mName = getChild("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("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(item1); + const LLExperienceItem* experience_item2 = dynamic_cast(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 8792b5360d..874af62933 100755 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -190,11 +190,13 @@ BOOL LLPanelGroup::postBuild() LLPanelGroupTab* panel_roles = findChild("group_roles_tab_panel"); LLPanelGroupTab* panel_notices = findChild("group_notices_tab_panel"); LLPanelGroupTab* panel_land = findChild("group_land_tab_panel"); + LLPanelGroupTab* panel_experiences = findChild("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) { @@ -447,6 +449,7 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) LLAccordionCtrlTab* tab_roles = getChild("group_roles_tab"); LLAccordionCtrlTab* tab_notices = getChild("group_notices_tab"); LLAccordionCtrlTab* tab_land = getChild("group_land_tab"); + LLAccordionCtrlTab* tab_experiences = getChild("group_experiences_tab"); if(mButtonJoin) mButtonJoin->setVisible(false); @@ -463,10 +466,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("group_name")->setVisible(false); getChild("group_name_editor")->setVisible(true); @@ -488,6 +494,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; @@ -496,6 +504,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("group_name")->setVisible(true); getChild("group_name_editor")->setVisible(false); @@ -563,6 +572,7 @@ bool LLPanelGroup::apply() && apply(findChild("group_roles_tab_panel")) && apply(findChild("group_notices_tab_panel")) && apply(findChild("group_land_tab_panel")) + && apply(findChild("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 t_panel_group_experiences("panel_group_experiences"); + + +class LLGroupExperienceResponder : public LLHTTPClient::Responder +{ +public: + LLHandle mHandle; + + LLGroupExperienceResponder(LLHandle 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("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())); + } +} + +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/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index 9caa748fc6..55067a55c0 100755 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -131,6 +131,14 @@ public: void createPick() { + // FIRE-7694 / BUG-932 / MAINT-1999 + if (LLAgentPicksInfo::getInstance()->isPickLimitReached()) + { + LLNotificationsUtil::add("PickLimitReached"); + return; + } + // + // open the new pick panel on the Picks floater LLFloater* picks_floater = LLFloaterReg::showInstance("picks"); diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 769fe76737..bda857d7b2 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -90,6 +90,9 @@ #include "llviewercontrol.h" #include "llappviewer.h" #include "llfloatergotoline.h" +#include "llexperiencecache.h" +#include "llfloaterexperienceprofile.h" +#include "llexperienceassociationresponder.h" #include "llloadingindicator.h" // Compile indicator #include "lliconctrl.h" // Compile indicator // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a) @@ -107,15 +110,15 @@ 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"; @@ -132,6 +135,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& parent):mParent(parent) + { + } + + LLHandle mParent; + + /*virtual*/ void httpSuccess() + { + LLLiveLSLEditor* parent = mParent.get(); + if(!parent) + return; + + parent->setExperienceIds(getContent()["experience_ids"]); + } +}; + // [SL:KB] - Patch: Build-ScriptRecover | Checked: 2011-11-23 (Catznip-3.2.0) | Added: Catznip-3.2.0 #include "lleventtimer.h" @@ -235,7 +258,7 @@ private: protected: LLLineEditor* mSearchBox; LLLineEditor* mReplaceBox; - void onSearchBoxCommit(); + void onSearchBoxCommit(); }; LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL; @@ -470,6 +493,54 @@ 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() { @@ -1120,7 +1191,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; @@ -1155,8 +1226,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())); @@ -1577,6 +1648,141 @@ 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("view_profile")->setVisible(FALSE); + } + else + { + mExperienceEnabled->setToolTip(getString("experience_enabled")); + mExperienceEnabled->setEnabled(getIsModifiable()); + mExperiences->setVisible(TRUE); + mExperienceEnabled->set(TRUE); + getChild("view_profile")->setToolTip(getString("show_experience_profile")); + 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 + { + std::string experience_name_string = experience[LLExperienceCache::NAME].asString(); + if (experience_name_string.empty()) + { + experience_name_string = LLTrans::getString("ExperienceNameUntitled"); + } + mExperiences->add(experience_name_string, id, position); + } + } + + if(!foundAssociated ) + { + const LLSD& experience = LLExperienceCache::get(associated); + if(experience.isDefined()) + { + std::string experience_name_string = experience[LLExperienceCache::NAME].asString(); + if (experience_name_string.empty()) + { + experience_name_string = LLTrans::getString("ExperienceNameUntitled"); + } + item=mExperiences->add(experience_name_string, 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("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())); + } + } +} + + + /// --------------------------------------------------------------------------- /// LLScriptEdContainer /// --------------------------------------------------------------------------- @@ -1659,7 +1865,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); @@ -2288,6 +2494,16 @@ BOOL LLLiveLSLEditor::postBuild() mScriptEd->mEditor->makePristine(); mScriptEd->mEditor->setFocus(TRUE); + + mExperiences = getChild("Experiences..."); + mExperiences->setCommitCallback(boost::bind(&LLLiveLSLEditor::experienceChanged, this)); + + mExperienceEnabled = getChild("enable_xp"); + + childSetCommitCallback("enable_xp", onToggleExperience, this); + childSetCommitCallback("view_profile", onViewProfile, this); + + return LLPreview::postBuild(); } @@ -2359,61 +2575,60 @@ void LLLiveLSLEditor::loadAsset() if(object) { LLViewerInventoryItem* item = dynamic_cast(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)))) + if(item) { - 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()) - { - // request the text from the object + ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLLiveLSLEditor::setAssociatedExperience, getDerivedHandle(), _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 LLSD* user_data = new LLSD(); user_data->with("taskid", mObjectUUID).with("itemid", mItemUUID); - 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; + 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. /* @@ -2450,6 +2665,8 @@ void LLLiveLSLEditor::loadAsset() time_corrected()); mAssetStatus = PREVIEW_ASSET_LOADED; } + + requestExperiences(); } // static @@ -2761,7 +2978,7 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) BOOL is_running = getChild( "running")->get(); if (!url.empty()) { - uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running); + uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running, mScriptEd->getAssociatedExperience()); } else if (gAssetStorage) { @@ -2790,7 +3007,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; @@ -2798,6 +3016,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)); } @@ -3076,3 +3295,18 @@ BOOL LLLiveLSLEditor::monoChecked() const } return FALSE; } + +void LLLiveLSLEditor::setAssociatedExperience( LLHandle 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 3c510ab398..2b3bb45786 100755 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -52,6 +52,7 @@ class LLVFS; class LLViewerInventoryItem; class LLScriptEdContainer; class LLFloaterGotoLine; +class LLFloaterExperienceProfile; // [SL:KB] - Patch: Build-ScriptRecover | Checked: 2011-11-23 (Catznip-3.2.0) class LLEventTimer; // [/SL:KB] @@ -127,13 +128,15 @@ public: static void onBtnInsertSample(void*); static void onBtnInsertFunction(LLUICtrl*, void*); static void onBtnLoadFromFile(void*); - static void onBtnSaveToFile(void*); + static void onBtnSaveToFile(void*); static void onBtnPrefs(void*); // Advanced Script Editor 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 ); void setScriptName(const std::string& name){mScriptName = name;}; @@ -182,8 +185,8 @@ private: void (*mSaveCallback)(void* userdata, BOOL close_after_save, bool sync); // void (*mSearchReplaceCallback) (void* userdata); - void* mUserdata; - LLComboBox *mFunctions; + void* mUserdata; + LLComboBox *mFunctions; BOOL mForceClose; LLPanel* mCodePanel; LLScrollListCtrl* mErrorList; @@ -195,6 +198,7 @@ private: BOOL mEnableSave; BOOL mHasScriptData; LLLiveLSLFile* mLiveFile; + LLUUID mAssociatedExperience; LLTextBox* mLineCol; // Advanced Script Editor //LLView* mSaveBtn; @@ -324,7 +328,7 @@ public: /*virtual*/ BOOL postBuild(); - void setIsNew() { mIsNew = TRUE; } + void setIsNew() { mIsNew = TRUE; } // Client LSL Bridge static void uploadAssetViaCapsStatic(const std::string& url, const std::string& filename, @@ -338,6 +342,17 @@ public: LLScriptEditor* getEditor() { return (mScriptEd) ? mScriptEd->mEditor : NULL; } // [/SL:KB] + static void setAssociatedExperience( LLHandle 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(); void closeIfNeeded(); @@ -347,10 +362,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, @@ -394,9 +410,16 @@ private: S32 mPendingUploads; BOOL getIsModifiable() const { return mIsModifiable; } // Evaluated on load assert - + LLCheckBoxCtrl* mMonoCheckbox; BOOL mIsModifiable; + + + LLComboBox* mExperiences; + LLCheckBoxCtrl* mExperienceEnabled; + LLSD mExperienceIds; + + LLHandle mExperienceProfile; }; #endif // LL_LLPREVIEWSCRIPT_H diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 1a2f28b7b7..9ce0043b80 100755 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -66,6 +66,22 @@ const F32 SECONDS_TO_SHOW_FILE_SAVED_MSG = 8.f; const F32 PREVIEW_TEXTURE_MAX_ASPECT = 200.f; const F32 PREVIEW_TEXTURE_MIN_ASPECT = 0.005f; +// FIRE-14111: File extension missing on Linux when saving a texture +std::string checkFileExtension(const std::string& filename, LLPreviewTexture::EFileformatType format) +{ + std::string tmp_name = filename; + std::string extension = (format == LLPreviewTexture::FORMAT_TGA ? ".tga" : ".png"); + + LLStringUtil::toLower(tmp_name); + size_t result = tmp_name.rfind(extension); + if (result == std::string::npos || result != tmp_name.length() - extension.size()) + { + return (filename + extension); + } + + return filename; +} +// LLPreviewTexture::LLPreviewTexture(const LLSD& key) : LLPreview((key.has("uuid") ? key.get("uuid") : key)), // Changed for texture preview mode @@ -390,7 +406,10 @@ void LLPreviewTexture::saveAs(EFileformatType format) break; } - if( !file_picker.getSaveFile( saveFilter, item ? LLDir::getScrubbedFileName(item->getName()) : LLStringUtil::null) ) + // FIRE-14111: File extension missing on Linux when saving a texture + //if( !file_picker.getSaveFile( saveFilter, item ? LLDir::getScrubbedFileName(item->getName()) : LLStringUtil::null) ) + if( !file_picker.getSaveFile( saveFilter, item ? checkFileExtension(LLDir::getScrubbedFileName(item->getName()), format) : LLStringUtil::null) ) + // { // User canceled or we failed to acquire save file. return; @@ -402,7 +421,10 @@ void LLPreviewTexture::saveAs(EFileformatType format) } // remember the user-approved/edited file name. - mSaveFileName = file_picker.getFirstFile(); + // FIRE-14111: File extension missing on Linux when saving a texture + //mSaveFileName = file_picker.getFirstFile(); + mSaveFileName = checkFileExtension(file_picker.getFirstFile(), format); + // mLoadingFullImage = TRUE; getWindow()->incBusyCount(); diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index c97137f0fa..8242368a64 100755 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -582,19 +582,23 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel) { std::vector::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 302a03669b..0fbefebd18 100755 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -44,6 +44,9 @@ #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewerobjectlist.h" +#include "llexperienceassociationresponder.h" +#include "llexperiencecache.h" +#include "lltrans.h" // [RLVa:KB] - Checked: 2010-08-25 (RLVa-1.2.2a) #include "rlvhandler.h" // [/RLVa:KB] @@ -321,6 +324,15 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) is_obj_modify = object->permOwnerModify(); } + if(item->getInventoryType() == LLInventoryType::IT_LSL) + { + getChildView("LabelItemExperienceTitle")->setVisible(TRUE); + LLTextBox* tb = getChild("LabelItemExperience"); + tb->setText(getString("loading_experience")); + tb->setVisible(TRUE); + ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLSidepanelItemInfo::setAssociatedExperience, getDerivedHandle(), _1)); + } + ////////////////////// // ITEM NAME & DESC // ////////////////////// @@ -691,6 +703,29 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) } } + +void LLSidepanelItemInfo::setAssociatedExperience( LLHandle 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("LabelItemExperience")->setText(LLSLURL("experience", id, "profile").getSLURLString()); + } + else + { + info->getChild("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 hInfo, const LLSD& experience ); + void startObjectInventoryObserver(); void stopObjectInventoryObserver(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 344e55c868..c18f88aec1 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" @@ -201,6 +202,7 @@ #include "llevents.h" #include "llstartuplistener.h" #include "lltoolbarview.h" +#include "llexperiencelog.h" #if LL_WINDOWS #include "lldxhardware.h" @@ -1779,6 +1781,9 @@ LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim, first_sim_size_x, // object is created. I think this must be done after setting the region. JC gAgent.setPositionAgent(agent_start_position_region); + display_startup(); + LLStartUp::initExperiences(); + display_startup(); LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT ); @@ -3539,6 +3544,14 @@ void LLStartUp::initNameCache() LLAvatarName::setTrimResidentSurname(gSavedSettings.getBOOL("FSTrimLegacyNames")); } + +void LLStartUp::initExperiences() +{ + LLAppViewer::instance()->loadExperienceCache(); + LLExperienceCache::initClass(); + LLExperienceLog::instance().initialize(); +} + void LLStartUp::cleanupNameCache() { LLAvatarNameCache::cleanupClass(); @@ -4429,3 +4442,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 deef578377..70579fad97 100755 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -110,6 +110,7 @@ public: static void fontInit(); static void initNameCache(); + static void initExperiences(); static void cleanupNameCache(); diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index 5ffc6a21fe..baac0965b7 100755 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -50,7 +50,7 @@ const S32 BOTTOM_PAD = VPAD * 3; const S32 IGNORE_BTN_TOP_DELTA = 3*VPAD;//additional ignore_btn padding S32 BUTTON_WIDTH = 90; -// *TODO: magic numbers(?) - copied from llnotify.cpp(250) +// *TODO: magic numbers - copied from llnotify.cpp(250) const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 2204b89f4b..53de30b332 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" @@ -262,8 +265,11 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("env_edit_water", "floater_edit_water_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("env_edit_day_cycle", "floater_edit_day_cycle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - + LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("experience_profile", "floater_experienceprofile.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("experience_search", "floater_experience_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("font_test", "floater_font_test.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("gestures", "floater_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index a34a74ef7d..8a230ea2a2 100755 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -196,7 +196,7 @@ private: // 500 means "Internal Server error" but we decided it's okay to // accept this and go past it in the MIME type probe // 302 means the resource can be found temporarily in a different place - added this for join.secondlife.com - // 499 is a code specifc to join.secondlife.com (?) apparently safe to ignore + // 499 is a code specifc to join.secondlife.com apparently safe to ignore // if( ((status >= 200) && (status < 300)) || // ((status >= 400) && (status < 499)) || // (status == 500) || diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 31d6cd4191..9e7e66f443 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -119,8 +119,13 @@ #include "llviewerregion.h" #include "llfloaterregionrestarting.h" +#include // +#include + +#include "llnotificationmanager.h" // +#include "llexperiencecache.h" + // Firestorm inclues -#include #include "animationexplorer.h" // Animation Explorer #include "fsareasearch.h" #include "fscommon.h" @@ -134,7 +139,6 @@ #include "llfloaterbump.h" #include "llfloaterreg.h" #include "llgiveinventory.h" -#include "llnotificationmanager.h" #include "lltexturefetch.h" #include "rlvactions.h" #include "rlvhandler.h" @@ -168,6 +172,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; @@ -7158,108 +7163,33 @@ 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(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()) - { - 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 (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()) - { - 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; - } - } - - 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; -} - -// some of the server notifications need special handling. This is where we do that. -bool handle_teleport_access_blocked(LLSD& llsdBlock, const std::string & notificationID, const std::string & defaultMessage) -{ - U8 regionAccess = static_cast(llsdBlock["_region_access"].asInteger()); - std::string regionMaturity = LLViewerRegion::accessToString(regionAccess); - LLStringUtil::toLower(regionMaturity); - llsdBlock["REGIONMATURITY"] = regionMaturity; - - bool returnValue = false; - LLNotificationPtr tp_failure_notification; - std::string notifySuffix; - - if (notificationID == std::string("TeleportEntryAccessBlocked")) - { - notifySuffix = "_Notify"; + U8 regionAccess = static_cast(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.isTeen()) { gAgent.clearTeleportRequest(); - tp_failure_notification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock); + maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock); returnValue = true; notifySuffix = "_NotifyAdultsOnly"; } else if (gAgent.prefersPG()) { - if (gAgent.hasRestartableFailedTeleportRequest()) - { - tp_failure_notification = LLNotificationsUtil::add(notificationID+"_ChangeAndReTeleport", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_and_reteleport_callback); - returnValue = true; - } - else - { - gAgent.clearTeleportRequest(); - tp_failure_notification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback); - returnValue = true; - } + maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback); + returnValue = true; } - else + else if (LLStringUtil::compareStrings(notificationID, "RegionEntryAccessBlocked") == 0) { - gAgent.clearTeleportRequest(); - tp_failure_notification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback); + maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock); returnValue = true; } } @@ -7268,54 +7198,157 @@ bool handle_teleport_access_blocked(LLSD& llsdBlock, const std::string & notific if (!gAgent.isAdult()) { gAgent.clearTeleportRequest(); - tp_failure_notification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock); + maturityLevelNotification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock); returnValue = true; notifySuffix = "_NotifyAdultsOnly"; } else if (gAgent.prefersPG() || gAgent.prefersMature()) { - if (gAgent.hasRestartableFailedTeleportRequest()) + 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; + } + } + + 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; +} + +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) + { + str<asUUID(), "profile").getSLURLString() << "\n"; + } + std::string str_list = str.str(); + if(!str_list.empty()) + { + LLNotificationsUtil::add("TrustedExperiencesAvailable", LLSD::emptyMap().with("EXPERIENCE_LIST", (LLSD)str_list)); + return true; + } + } + return false; +} + +// some of the server notifications need special handling. This is where we do that. +bool handle_teleport_access_blocked(LLSD& llsdBlock, const std::string & notificationID, const std::string & defaultMessage) +{ + bool returnValue = false; + if(llsdBlock.has("_region_access")) + { + U8 regionAccess = static_cast(llsdBlock["_region_access"].asInteger()); + std::string regionMaturity = LLViewerRegion::accessToString(regionAccess); + LLStringUtil::toLower(regionMaturity); + llsdBlock["REGIONMATURITY"] = regionMaturity; + + LLNotificationPtr tp_failure_notification; + std::string notifySuffix; + + if (notificationID == std::string("TeleportEntryAccessBlocked")) + { + notifySuffix = "_Notify"; + if (regionAccess == SIM_ACCESS_MATURE) + { + if (gAgent.isTeen()) { - tp_failure_notification = LLNotificationsUtil::add(notificationID+"_ChangeAndReTeleport", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_and_reteleport_callback); + gAgent.clearTeleportRequest(); + tp_failure_notification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock); returnValue = true; + + notifySuffix = "_NotifyAdultsOnly"; + } + else if (gAgent.prefersPG()) + { + if (gAgent.hasRestartableFailedTeleportRequest()) + { + tp_failure_notification = LLNotificationsUtil::add(notificationID+"_ChangeAndReTeleport", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_and_reteleport_callback); + returnValue = true; + } + else + { + gAgent.clearTeleportRequest(); + tp_failure_notification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback); + returnValue = true; + } } else { gAgent.clearTeleportRequest(); - tp_failure_notification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback); + tp_failure_notification = 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(); + tp_failure_notification = LLNotificationsUtil::add(notificationID+"_AdultsOnlyContent", llsdBlock); + returnValue = true; + + notifySuffix = "_NotifyAdultsOnly"; + } + else if (gAgent.prefersPG() || gAgent.prefersMature()) + { + if (gAgent.hasRestartableFailedTeleportRequest()) + { + tp_failure_notification = LLNotificationsUtil::add(notificationID+"_ChangeAndReTeleport", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_and_reteleport_callback); + returnValue = true; + } + else + { + gAgent.clearTeleportRequest(); + tp_failure_notification = LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback); + returnValue = true; + } + } + else + { + gAgent.clearTeleportRequest(); + tp_failure_notification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback); + returnValue = true; + } + } + } // End of special handling for "TeleportEntryAccessBlocked" + else + { // Normal case, no message munging + gAgent.clearTeleportRequest(); + if (LLNotifications::getInstance()->templateExists(notificationID)) + { + tp_failure_notification = LLNotificationsUtil::add(notificationID, llsdBlock, llsdBlock); + } else { - gAgent.clearTeleportRequest(); - tp_failure_notification = LLNotificationsUtil::add(notificationID+"_PreferencesOutOfSync", llsdBlock, llsdBlock, handle_prompt_for_maturity_level_change_callback); - returnValue = true; + llsdBlock["MESSAGE"] = defaultMessage; + tp_failure_notification = LLNotificationsUtil::add("GenericAlertOK", llsdBlock); } - } - } // End of special handling for "TeleportEntryAccessBlocked" - else - { // Normal case, no message munging - gAgent.clearTeleportRequest(); - if (LLNotifications::getInstance()->templateExists(notificationID)) - { - tp_failure_notification = LLNotificationsUtil::add(notificationID, llsdBlock, llsdBlock); + returnValue = true; } - else + + if ((tp_failure_notification == NULL) || tp_failure_notification->isIgnored()) { - llsdBlock["MESSAGE"] = defaultMessage; - tp_failure_notification = LLNotificationsUtil::add("GenericAlertOK", llsdBlock); + // Given a simple notification if no tp_failure_notification is set or it is ignore + LLNotificationsUtil::add(notificationID + notifySuffix, llsdBlock); } - returnValue = true; - } - - if ((tp_failure_notification == NULL) || tp_failure_notification->isIgnored()) - { - // Given a simple notification if no tp_failure_notification is set or it is ignore - LLNotificationsUtil::add(notificationID + notifySuffix, llsdBlock); } + handle_trusted_experiences_notification(llsdBlock); return returnValue; } @@ -7346,6 +7379,9 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem) } } + + handle_trusted_experiences_notification(llsdBlock); + if ( (notificationID == "RegionEntryAccessBlocked") || (notificationID == "LandClaimAccessBlocked") || @@ -7565,8 +7601,8 @@ void process_alert_core(const std::string& message, BOOL modal) std::string alert_name(message.substr(ALERT_PREFIX.length())); if (!handle_special_alerts(alert_name)) { - LLNotificationsUtil::add(alert_name); - } + LLNotificationsUtil::add(alert_name); + } } else if (message.find(NOTIFY_PREFIX) == 0) { @@ -7655,7 +7691,7 @@ void process_alert_core(const std::string& message, BOOL modal) LLFloaterRegionRestarting::close(); } - std::string new_msg =LLNotifications::instance().getGlobalString(text); + std::string new_msg =LLNotifications::instance().getGlobalString(text); // [RLVa:KB] - Checked: 2012-02-07 (RLVa-1.4.5) | Added: RLVa-1.4.5 if ( (new_msg == text) && (rlv_handler_t::isEnabled()) ) { @@ -7665,9 +7701,9 @@ void process_alert_core(const std::string& message, BOOL modal) RlvUtil::filterNames(new_msg); } // [/RLVa:KB] - args["MESSAGE"] = new_msg; - LLNotificationsUtil::add("SystemMessage", args); - } + args["MESSAGE"] = new_msg; + LLNotificationsUtil::add("SystemMessage", args); + } else if (modal) { LLSD args; @@ -8067,6 +8103,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 @@ -8076,6 +8118,16 @@ 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(); @@ -8112,7 +8164,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; } @@ -8145,8 +8217,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-Scope"); + } + else + { + args["GRID_WIDE"] = LLTrans::getString("Land-Scope"); + } + 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] @@ -8158,6 +8246,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 ); @@ -8175,6 +8264,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, @@ -8261,6 +8355,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data) payload["owner_name"] = owner_name; // [RLVa:KB] - Checked: 2012-07-28 (RLVa-1.4.7) + const char* notification = "ScriptQuestion"; if (rlv_handler_t::isEnabled()) { RlvUtil::filterScriptQuestions(questions, payload); @@ -8289,23 +8384,24 @@ void process_script_question(LLMessageSystem *msg, void **user_data) LLNotifications::instance().forceResponse( LLNotification::Params("ScriptQuestion").substitutions(args).payload(payload), 0/*YES*/); } - else if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) + else if (caution && gSavedSettings.getBOOL("PermissionsCautionEnabled")) // [/RLVa:KB] // 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 8cbb0d3df8..56b9473a77 100755 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -236,8 +236,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; @@ -754,7 +754,8 @@ bool LLViewerParcelMgr::allowAgentScripts(const LLViewerRegion* region, const LL bool LLViewerParcelMgr::allowAgentDamage(const LLViewerRegion* region, const LLParcel* parcel) const { return (region && region->getAllowDamage()) - && (parcel && parcel->getAllowDamage()); + //&& (parcel && parcel->getAllowDamage()); + || (parcel && parcel->getAllowDamage()); // FIRE-16112 fix } BOOL LLViewerParcelMgr::isOwnedAt(const LLVector3d& pos_global) const @@ -941,7 +942,7 @@ void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags) if (!region) return; LLMessageSystem *msg = gMessageSystem; - + if (flags & AL_BAN) { @@ -951,6 +952,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); @@ -1735,7 +1744,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; @@ -1925,6 +1934,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) ); @@ -1959,10 +1976,6 @@ void LLViewerParcelMgr::processParcelDwellReply(LLMessageSystem* msg, void**) void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) { - - LLUUID transactionUUID; - transactionUUID.generate(); - if (!mSelected) { return; @@ -1971,125 +1984,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 ); - } - - 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() ); - } + sendParcelAccessListUpdate(AL_ACCESS, parcel->mAccessList, region, parcel->getLocalID()); } 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; + sendParcelAccessListUpdate(AL_BAN, parcel->mBanList, region, parcel->getLocalID()); + } - 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++; - - } - - 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() ); - } + 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()); } } +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; + + LLUUID transactionUUID; + transactionUUID.generate(); + + + LLMessageSystem* msg = gMessageSystem; + + 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)) + { + // 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++; + + } + + 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 d3fa3d6575..05330b00c0 100755 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -229,7 +229,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); @@ -302,6 +302,8 @@ public: static BOOL isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power); private: + static void sendParcelAccessListUpdate(U32 flags, const std::map& 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 e0c61bf6a9..2c96486d57 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2902,8 +2902,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 41d5502599..19ae19cc20 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -44,6 +44,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 @@ -1420,7 +1421,10 @@ void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) { LLViewerJointAttachment* attachment = iter->second; - if (attachment->getValid()) + // Possible crash fix + //if (attachment->getValid()) + if (attachment && attachment->getValid()) + // { for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); @@ -1640,6 +1644,13 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& { LLViewerJointAttachment* attachment = iter->second; + // Possible crash fix + if (!attachment) + { + continue; + } + // + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) @@ -1705,6 +1716,13 @@ LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector { LLViewerJointAttachment* attachment = iter->second; + // Possible crash fix + if (!attachment) + { + continue; + } + // + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) @@ -1854,7 +1872,10 @@ void LLVOAvatar::releaseMeshData() ++iter) { LLViewerJointAttachment* attachment = iter->second; - if (!attachment->getIsHUDAttachment()) + // Possible crash fix + //if (!attachment->getIsHUDAttachment()) + if (attachment && !attachment->getIsHUDAttachment()) + // { attachment->setAttachmentVisibility(FALSE); } @@ -2241,7 +2262,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) idleUpdateBelowWater(); // wind effect uses this idleUpdateWindEffect(); } - + idleUpdateNameTag( root_pos_last ); idleUpdateRenderCost(); } @@ -2395,6 +2416,13 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) { LLViewerJointAttachment* attachment = iter->second; + // Possible crash fix + if (!attachment) + { + continue; + } + // + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) @@ -4332,6 +4360,13 @@ void LLVOAvatar::updateVisibility() { LLViewerJointAttachment* attachment = iter->second; + // Possible crash fix + if (!attachment) + { + continue; + } + // + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) @@ -6373,6 +6408,12 @@ U32 LLVOAvatar::getNumAttachments() const ++iter) { const LLViewerJointAttachment *attachment_pt = (*iter).second; + // Possible crash fix + if (!attachment_pt) + { + continue; + } + // num_attachments += attachment_pt->getNumObjects(); } return num_attachments; @@ -6458,6 +6499,14 @@ void LLVOAvatar::rebuildRiggedAttachments( void ) for ( attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter ) { LLViewerJointAttachment* pAttachment = iter->second; + + // Possible crash fix + if (!pAttachment) + { + continue; + } + // + LLViewerJointAttachment::attachedobjs_vec_t::iterator attachmentIterEnd = pAttachment->mAttachedObjects.end(); for ( LLViewerJointAttachment::attachedobjs_vec_t::iterator attachmentIter = pAttachment->mAttachedObjects.begin(); @@ -6501,7 +6550,10 @@ BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object) { LLViewerJointAttachment* attachment = iter->second; - if (attachment->isObjectAttached(viewer_object)) + // Possible crash fix + //if (attachment->isObjectAttached(viewer_object)) + if (attachment && attachment->isObjectAttached(viewer_object)) + // { mVisualComplexityStale = TRUE; cleanupAttachedMesh( viewer_object ); @@ -6770,6 +6822,14 @@ LLViewerObject * LLVOAvatar::findAttachmentByID( const LLUUID & target_id ) cons ++attachment_points_iter) { LLViewerJointAttachment* attachment = attachment_points_iter->second; + + // Possible crash fix + if (!attachment) + { + continue; + } + // + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) @@ -7596,6 +7656,14 @@ BOOL LLVOAvatar::hasHUDAttachment() const ++iter) { LLViewerJointAttachment* attachment = iter->second; + + // Possible crash fix + if (!attachment) + { + continue; + } + // + if (attachment->getIsHUDAttachment() && attachment->getNumObjects() > 0) { return TRUE; @@ -7612,7 +7680,10 @@ LLBBox LLVOAvatar::getHUDBBox() const ++iter) { LLViewerJointAttachment* attachment = iter->second; - if (attachment->getIsHUDAttachment()) + // Possible crash fix + //if (attachment->getIsHUDAttachment()) + if (attachment && attachment->getIsHUDAttachment()) + // { for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); @@ -8978,6 +9049,14 @@ void LLVOAvatar::calculateUpdateRenderCost() ++iter) { LLViewerJointAttachment* attachment = iter->second; + + // Possible crash fix + if (!attachment) + { + continue; + } + // + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 6c67c3b8d7..6edc60c698 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1266,6 +1266,12 @@ void LLVOAvatarSelf::updateAttachmentVisibility(U32 camera_mode) ++iter) { LLViewerJointAttachment* attachment = iter->second; + // Possible crash fix + if (!attachment) + { + continue; + } + // if (attachment->getIsHUDAttachment()) { attachment->setAttachmentVisibility(TRUE); diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index 631c2bfa65..8ba91990c7 100755 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -428,6 +428,7 @@ void LLWorldMap::reloadItems(bool force) LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_MATURE_EVENT); LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_ADULT_EVENT); LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_LAND_FOR_SALE); + LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_LAND_FOR_SALE_ADULT); } } 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/ansastorm/xui/en/panel_group_info_sidetray.xml b/indra/newview/skins/ansastorm/xui/en/panel_group_info_sidetray.xml index 12878828b6..a05a73eb24 100644 --- a/indra/newview/skins/ansastorm/xui/en/panel_group_info_sidetray.xml +++ b/indra/newview/skins/ansastorm/xui/en/panel_group_info_sidetray.xml @@ -176,6 +176,15 @@ background_visible="false" left="0" name="group_land_tab_panel" top="0" /> + diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 20740e856f..65d244fa7c 100755 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -230,6 +230,7 @@ with the same filename but different name + diff --git a/indra/newview/skins/default/xui/de/floater_about_land.xml b/indra/newview/skins/default/xui/de/floater_about_land.xml index 7899a06e65..f911f5af46 100755 --- a/indra/newview/skins/default/xui/de/floater_about_land.xml +++ b/indra/newview/skins/default/xui/de/floater_about_land.xml @@ -21,7 +21,7 @@ Restzeit - + Nur neue Benutzer @@ -327,9 +327,6 @@ Nur große Parzellen können in der Suche aufgeführt werden. Ort in Suche anzeigen ([DIRECTORY_FEE] L$/Woche) - - Avatare auf anderen Parzellen können - Anderen Einwohnern gestatten: @@ -499,5 +496,6 @@ Nur große Parzellen können in der Suche aufgeführt werden. + + + + + + (loading) + (unknown) @@ -178,6 +182,28 @@ width="252"> Wed May 24 12:50:46 2006 + + Experience: + + You: @@ -362,7 +388,7 @@ layout="topleft" left="225" name="BaseMaskDebug" - top="125" + top="145" width="330"> B: 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 87a5414bed..3ac56166f3 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,118 @@  - - You can not view or edit this script, since it has been set with restricted permissions. You need full permissions to view or edit a script inside an object. - - - Running - - - Script: [NAME] - - + + You can not view or edit this script, since it has been set with restricted permissions. You need full permissions to view or edit a script inside an object. + + + Running + + + Script: [NAME] + + + Uncheck to remove the current experience + + + You are not authorized for any experiences + + + Select to add an experience + + + Click to view the experience profile + + + Loading... + + - + + + + + Script: + + + Associated with: + + + You can contribute: + + + Associate with: + + + + + + EasySit Animator 1.2.4 + + + Kyle's Superhero RPG + + + Yes + + + + + + You are not a contributor to any experiences. + + diff --git a/indra/newview/skins/default/xui/en/role_actions.xml b/indra/newview/skins/default/xui/en/role_actions.xml index d160f64442..981e3e0387 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" /> + + + + 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 0f72ad904f..1aa80dfbfe 100755 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -1,444 +1,476 @@ - - (unknown) - - - (unknown / multiple) - - - (public) - - - You can: - - - Owner can: - - - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] - - - (Inventory) - - - (Inworld) - - - + + + + + + (loading) + (unknown) @@ -178,6 +182,28 @@ width="252"> Wed May 24 12:50:46 2006 + + Experience: + + You: diff --git a/indra/newview/skins/vintage/xui/en/floater_live_lsleditor.xml b/indra/newview/skins/vintage/xui/en/floater_live_lsleditor.xml index d85f88cddc..8fd25dae49 100644 --- a/indra/newview/skins/vintage/xui/en/floater_live_lsleditor.xml +++ b/indra/newview/skins/vintage/xui/en/floater_live_lsleditor.xml @@ -1,44 +1,63 @@  - - You can not view or edit this script, since it has been set with restricted permissions. You need full permissions to view or edit a script inside an object. - - - Running - - - Script: [NAME] - + 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="preview_lsl_text" + save_rect="true" + title="Script: New script" + width="508"> + + You can not view or edit this script, since it has been set with restricted permissions. You need full permissions to view or edit a script inside an object. + + + Running + + + Script: [NAME] + + + Uncheck to remove the current experience + + + You are not authorized for any experiences + + + Select to add an experience + + + Click to view the experience profile + + + Loading... + + + +