summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRider Linden <rider@lindenlab.com>2026-10-02 09:29:55 -0700
committerGitHub <noreply@github.com>2026-10-02 09:29:55 -0700
commit48e1f6f25b952eeb35e891cd73265e652198cae6 (patch)
tree334d05075af85eebca7477078e6ec95e78a510c3
parent8c98605cae0ed5e4d0d70e806703ef13f277e245 (diff)
parent01786bf78c825cce2639c482bfc97ae5f3161a1b (diff)
Merge branch 'release/26.4' into rider/issue-710
-rw-r--r--indra/llfilesystem/lldiriterator.cpp2
-rw-r--r--indra/llfilesystem/lldiskcache.cpp16
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llbuycurrencyhtml.cpp101
-rw-r--r--indra/newview/llbuycurrencyhtml.h19
-rw-r--r--indra/newview/llfloaterbuycurrency.cpp40
-rw-r--r--indra/newview/llfloaterbuycurrency.h16
7 files changed, 175 insertions, 30 deletions
diff --git a/indra/llfilesystem/lldiriterator.cpp b/indra/llfilesystem/lldiriterator.cpp
index ca00d25fb7..ec6d1726f0 100644
--- a/indra/llfilesystem/lldiriterator.cpp
+++ b/indra/llfilesystem/lldiriterator.cpp
@@ -69,7 +69,7 @@ LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask)
if (!is_dir)
{
- LL_WARNS() << "Invalid path: \"" << dir_path.string() << "\"" << LL_ENDL;
+ LL_WARNS() << "Invalid path: \"" << dirname << "\"" << LL_ENDL;
return;
}
diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp
index e7b43e15e2..5af801a1df 100644
--- a/indra/llfilesystem/lldiskcache.cpp
+++ b/indra/llfilesystem/lldiskcache.cpp
@@ -34,6 +34,7 @@
#include "llapp.h"
#include "llassettype.h"
#include "lldir.h"
+#include "llstring.h"
#include <chrono>
#include <filesystem>
@@ -116,14 +117,14 @@ void LLDiskCache::purge()
}
if (std::filesystem::is_regular_file(*iter, ec) && !ec)
{
- if ((*iter).path().string().find(CACHE_FILENAME_PREFIX) != std::string::npos)
+ if (fsyspath((*iter).path()).string().find(CACHE_FILENAME_PREFIX) != std::string::npos)
{
uintmax_t file_size = std::filesystem::file_size(*iter, ec);
if (ec)
{
continue;
}
- const std::string file_path = (*iter).path().string();
+ const std::string file_path = fsyspath((*iter).path()).string();
const std::filesystem::file_time_type file_time = std::filesystem::last_write_time(*iter, ec);
if (ec)
{
@@ -165,7 +166,7 @@ void LLDiskCache::purge()
}
if (should_remove)
{
- std::filesystem::remove(entry.second.second, ec);
+ std::filesystem::remove(fsyspath(entry.second.second), ec);
if (ec)
{
LL_WARNS() << "Failed to delete cache file " << entry.second.second << ": " << ec.message() << LL_ENDL;
@@ -245,7 +246,7 @@ void LLDiskCache::clearCache()
{
if (std::filesystem::is_regular_file(*iter, ec) && !ec)
{
- if ((*iter).path().string().find(CACHE_FILENAME_PREFIX) != std::string::npos)
+ if (fsyspath((*iter).path()).string().find(CACHE_FILENAME_PREFIX) != std::string::npos)
{
std::filesystem::remove(*iter, ec);
if (ec)
@@ -274,8 +275,9 @@ void LLDiskCache::removeOldVFSFiles()
{
if (std::filesystem::is_regular_file(*iter, ec) && !ec)
{
- if (((*iter).path().string().find(CACHE_FORMAT) != std::string::npos) ||
- ((*iter).path().string().find(DB_FORMAT) != std::string::npos))
+ const std::string file_path = fsyspath((*iter).path()).string();
+ if ((file_path.find(CACHE_FORMAT) != std::string::npos) ||
+ (file_path.find(DB_FORMAT) != std::string::npos))
{
std::filesystem::remove(*iter, ec);
if (ec)
@@ -311,7 +313,7 @@ uintmax_t LLDiskCache::dirFileSize(const std::string& dir)
{
if (std::filesystem::is_regular_file(*iter, ec) && !ec)
{
- if ((*iter).path().string().find(CACHE_FILENAME_PREFIX) != std::string::npos)
+ if (fsyspath((*iter).path()).string().find(CACHE_FILENAME_PREFIX) != std::string::npos)
{
uintmax_t file_size = std::filesystem::file_size(*iter, ec);
if (!ec)
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index ba44b68307..f6f11f9578 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1194,6 +1194,17 @@
<key>Value</key>
<string>https://accounts.secondlife.com/packs/floater?lang=[LANGUAGE]&amp;shortfall=[SHORTFALL]</string>
</map>
+ <key>BuyCurrencyAddPaymentURL</key>
+ <map>
+ <key>Comment</key>
+ <string>URL the viewer probes at login; a 200 means the L$ packs floater handles residents without a payment method</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>https://accounts.secondlife.com/packs/floater/add-payment-enabled</string>
+ </map>
<key>PaymentMethodURL</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llbuycurrencyhtml.cpp b/indra/newview/llbuycurrencyhtml.cpp
index 9ad068cf7f..f5bc3b5357 100644
--- a/indra/newview/llbuycurrencyhtml.cpp
+++ b/indra/newview/llbuycurrencyhtml.cpp
@@ -89,6 +89,8 @@ public:
LLBuyCurrencyHTMLHandler gBuyCurrencyHTMLHandler;
bool LLBuyCurrencyHTML::sWebFloaterEnabled = false;
+bool LLBuyCurrencyHTML::sAddPaymentEnabled = false;
+LLFetchAvatarPaymentInfo* LLBuyCurrencyHTML::sPaymentInfoRequest = NULL;
////////////////////////////////////////////////////////////////////////////////
// static
@@ -110,11 +112,37 @@ static void checkFeatureFlag_coro(std::string check_url)
}
// static
+static void checkAddPaymentFlag_coro(std::string check_url)
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter = std::make_shared<LLCoreHttpUtil::HttpCoroutineAdapter>("CheckBuyCurrencyAddPaymentURL", httpPolicy);
+ LLCore::HttpRequest::ptr_t httpRequest = std::make_shared<LLCore::HttpRequest>();
+
+ // a redirect to some other page that answers 200 must not read as enabled
+ LLCore::HttpOptions::ptr_t httpOptions = std::make_shared<LLCore::HttpOptions>();
+ httpOptions->setRetries(0);
+ httpOptions->setFollowRedirects(false);
+
+ LLSD result = httpAdapter->getRawAndSuspend(httpRequest, check_url, httpOptions);
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ LLBuyCurrencyHTML::sAddPaymentEnabled = status.isHttpStatus() && status.getType() == 200;
+ LL_INFOS("LLBuyCurrency") << "Add payment probe returned " << status.toString()
+ << ", in-floater add payment " << (LLBuyCurrencyHTML::sAddPaymentEnabled ? "enabled" : "disabled") << LL_ENDL;
+}
+
+// static
void LLBuyCurrencyHTML::checkFeatureFlag()
{
std::string check_url = LLFloaterBuyCurrencyHTML::buildURL();
LLCoros::instance().launch("checkFeatureFlag_coro",
[check_url]() { checkFeatureFlag_coro(check_url); });
+
+ std::string add_payment_url = gSavedSettings.getString("BuyCurrencyAddPaymentURL");
+ LLCoros::instance().launch("checkAddPaymentFlag_coro",
+ [add_payment_url]() { checkAddPaymentFlag_coro(add_payment_url); });
}
////////////////////////////////////////////////////////////////////////////////
@@ -124,15 +152,7 @@ void LLBuyCurrencyHTML::checkFeatureFlag()
// the case where the amount is not requested.
void LLBuyCurrencyHTML::openCurrencyFloater()
{
- if (gSavedSettings.getBOOL("BuyCurrencyHTML") && sWebFloaterEnabled)
- {
- LLBuyCurrencyHTML::showDialog();
- }
- else
- {
- // legacy version
- LLFloaterBuyCurrency::buyCurrency();
- }
+ routeCurrencyRequest(false, LLStringUtil::null, 0);
}
////////////////////////////////////////////////////////////////////////////////
@@ -142,19 +162,68 @@ void LLBuyCurrencyHTML::openCurrencyFloater()
// the case where the amount and a string to display are requested.
void LLBuyCurrencyHTML::openCurrencyFloater( const std::string& message, S32 sum )
{
- if (gSavedSettings.getBOOL("BuyCurrencyHTML") && sWebFloaterEnabled)
+ routeCurrencyRequest(true, message, sum);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+// Without the in-floater add payment flow, the web floater is opened only for
+// residents with payment info on file; the rest go to the LindeX payment page.
+void LLBuyCurrencyHTML::routeCurrencyRequest( bool has_target, const std::string& message, S32 sum )
+{
+ if (!gSavedSettings.getBOOL("BuyCurrencyHTML") || !sWebFloaterEnabled)
{
- LLBuyCurrencyHTML::showDialog(sum - gStatusBar->getBalance());
- LLFloaterBuyCurrencyHTML* floater = dynamic_cast<LLFloaterBuyCurrencyHTML*>(LLFloaterReg::getInstance("buy_currency_html"));
- if (floater)
+ // legacy version, which runs its own payment info check
+ if (has_target)
{
- floater->setFallbackContext(message, sum);
+ LLFloaterBuyCurrency::buyCurrency(message, sum);
}
+ else
+ {
+ LLFloaterBuyCurrency::buyCurrency();
+ }
+ }
+ else if (sAddPaymentEnabled)
+ {
+ openWebFloater(has_target, message, sum);
}
else
{
- // legacy version
- LLFloaterBuyCurrency::buyCurrency( message, sum );
+ delete sPaymentInfoRequest;
+ sPaymentInfoRequest = new LLFetchAvatarPaymentInfo(
+ [has_target, message, sum](bool has_piof)
+ {
+ delete sPaymentInfoRequest;
+ sPaymentInfoRequest = NULL;
+
+ if (has_piof)
+ {
+ openWebFloater(has_target, message, sum);
+ }
+ else
+ {
+ LL_INFOS("LLBuyCurrency") << "No payment info on file, opening payment method page" << LL_ENDL;
+ LLFloaterBuyCurrency::openPaymentMethodPage();
+ }
+ });
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLBuyCurrencyHTML::openWebFloater( bool has_target, const std::string& message, S32 sum )
+{
+ if (!has_target)
+ {
+ LLBuyCurrencyHTML::showDialog();
+ return;
+ }
+
+ LLBuyCurrencyHTML::showDialog(sum - gStatusBar->getBalance());
+ LLFloaterBuyCurrencyHTML* floater = dynamic_cast<LLFloaterBuyCurrencyHTML*>(LLFloaterReg::getInstance("buy_currency_html"));
+ if (floater)
+ {
+ floater->setFallbackContext(message, sum);
}
}
diff --git a/indra/newview/llbuycurrencyhtml.h b/indra/newview/llbuycurrencyhtml.h
index 2f78199dcb..c8a98c42a9 100644
--- a/indra/newview/llbuycurrencyhtml.h
+++ b/indra/newview/llbuycurrencyhtml.h
@@ -29,6 +29,8 @@
#include "llsingleton.h"
+class LLFetchAvatarPaymentInfo;
+
class LLBuyCurrencyHTML
{
public:
@@ -45,10 +47,25 @@ class LLBuyCurrencyHTML
// close (and destroy) the currency floater
static void closeDialog();
- // probe BuyCurrencyPacksURL once at login; clears sWebFloaterEnabled if 501
+ // probe BuyCurrencyPacksURL once at login; clears sWebFloaterEnabled if 501.
+ // also launches the BuyCurrencyAddPaymentURL probe that sets sAddPaymentEnabled
static void checkFeatureFlag();
static bool sWebFloaterEnabled;
+
+ // true only when BuyCurrencyAddPaymentURL answered 200, meaning the web floater
+ // can add a payment method; otherwise residents without one are sent to the LindeX
+ static bool sAddPaymentEnabled;
+
+ private:
+ // route to the legacy floater, the web floater, or the LindeX payment page
+ static void routeCurrencyRequest( bool has_target, const std::string& message, S32 sum );
+
+ // open the web floater, with the shortfall and fallback context when there is a target
+ static void openWebFloater( bool has_target, const std::string& message, S32 sum );
+
+ // pending payment info check made before opening the web floater
+ static LLFetchAvatarPaymentInfo* sPaymentInfoRequest;
};
#endif // LL_LLBUYCURRENCYHTML_H
diff --git a/indra/newview/llfloaterbuycurrency.cpp b/indra/newview/llfloaterbuycurrency.cpp
index 56b3229fda..7533145625 100644
--- a/indra/newview/llfloaterbuycurrency.cpp
+++ b/indra/newview/llfloaterbuycurrency.cpp
@@ -338,19 +338,39 @@ void LLFloaterBuyCurrency::handleBuyCurrency(bool has_piof, bool has_target, con
}
else
{
- const std::string& grid_id = LLGridManager::getInstance()->getGridId();
- const std::string& grid_id_lower = utf8str_tolower(grid_id);
- const std::string url = gSavedSettings.getString(grid_id_lower == "damballah" ? "PaymentMethodStagingURL" : "PaymentMethodURL");
- LLWeb::loadURL(url);
+ openPaymentMethodPage();
}
}
+// static
+void LLFloaterBuyCurrency::openPaymentMethodPage()
+{
+ const std::string& grid_id = LLGridManager::getInstance()->getGridId();
+ const std::string& grid_id_lower = utf8str_tolower(grid_id);
+ const std::string url = gSavedSettings.getString(grid_id_lower == "damballah" ? "PaymentMethodStagingURL" : "PaymentMethodURL");
+ LLWeb::loadURL(url);
+}
+
LLFetchAvatarPaymentInfo::LLFetchAvatarPaymentInfo(bool has_target, const std::string& name, S32 price)
: mAvatarID(gAgent.getID()),
mHasTarget(has_target),
mPrice(price),
mName(name)
{
+ sendRequest();
+}
+
+LLFetchAvatarPaymentInfo::LLFetchAvatarPaymentInfo(const callback_t& cb)
+: mAvatarID(gAgent.getID()),
+ mHasTarget(false),
+ mPrice(0),
+ mCallback(cb)
+{
+ sendRequest();
+}
+
+void LLFetchAvatarPaymentInfo::sendRequest()
+{
LLAvatarPropertiesProcessor* processor = LLAvatarPropertiesProcessor::getInstance();
// register ourselves as an observer
processor->addObserver(mAvatarID, this);
@@ -369,6 +389,16 @@ void LLFetchAvatarPaymentInfo::processProperties(void* data, EAvatarProcessorTyp
if (data && type == APT_PROPERTIES)
{
LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
- LLFloaterBuyCurrency::handleBuyCurrency(LLAvatarPropertiesProcessor::hasPaymentInfoOnFile(avatar_data), mHasTarget, mName, mPrice);
+ bool has_piof = LLAvatarPropertiesProcessor::hasPaymentInfoOnFile(avatar_data);
+ if (mCallback)
+ {
+ // call a local copy, since the callback may delete this observer
+ callback_t cb = mCallback;
+ cb(has_piof);
+ }
+ else
+ {
+ LLFloaterBuyCurrency::handleBuyCurrency(has_piof, mHasTarget, mName, mPrice);
+ }
}
}
diff --git a/indra/newview/llfloaterbuycurrency.h b/indra/newview/llfloaterbuycurrency.h
index 1b04c9c6d2..c6a80de926 100644
--- a/indra/newview/llfloaterbuycurrency.h
+++ b/indra/newview/llfloaterbuycurrency.h
@@ -30,21 +30,34 @@
#include "llavatarpropertiesprocessor.h"
#include "stdtypes.h"
#include "llagent.h"
+
+#include <functional>
+
class LLFloater;
class LLFetchAvatarPaymentInfo : public LLAvatarPropertiesObserver
{
public:
+ typedef std::function<void(bool has_piof)> callback_t;
+
+ // hands the result to LLFloaterBuyCurrency::handleBuyCurrency
LLFetchAvatarPaymentInfo(bool has_target, const std::string& name = std::string(), S32 price = 0);
+
+ // hands the result to cb, which may delete this observer
+ LLFetchAvatarPaymentInfo(const callback_t& cb);
+
~LLFetchAvatarPaymentInfo();
void processProperties(void* data, EAvatarProcessorType type);
private:
+ void sendRequest();
+
LLUUID mAvatarID;
bool mHasTarget;
std::string mName;
S32 mPrice;
+ callback_t mCallback;
};
@@ -62,6 +75,9 @@ public:
a space and the price will be appended
*/
+ // open the LindeX page where a resident adds a payment method
+ static void openPaymentMethodPage();
+
static LLFloater* buildFloater(const LLSD& key);
static LLFetchAvatarPaymentInfo* sPropertiesRequest;