diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp
index 725f04b1f2..2f96c1ded4 100644
--- a/indra/llfilesystem/lldiskcache.cpp
+++ b/indra/llfilesystem/lldiskcache.cpp
@@ -333,11 +333,21 @@ const std::string LLDiskCache::metaDataToFilepath(const LLUUID& id, LLAssetType:
const std::string LLDiskCache::getCacheInfo()
{
+ LL_PROFILE_ZONE_SCOPED; // add some instrumentation
std::ostringstream cache_info;
F32 max_in_mb = (F32)mMaxSizeBytes / (1024.0f * 1024.0f);
- F32 percent_used = ((F32)dirFileSize(sCacheDir) / (F32)mMaxSizeBytes) * 100.0f;
-
+ // stall prevention. We still need to make sure this initialised when called at startup.
+ F32 percent_used;
+ if (mStoredCacheSize > 0)
+ {
+ percent_used = ((F32)mStoredCacheSize / (F32)mMaxSizeBytes) * 100.0f;
+ }
+ else
+ {
+ percent_used = ((F32)dirFileSize(sCacheDir) / (F32)mMaxSizeBytes) * 100.0f;
+ }
+ //
cache_info << std::fixed;
cache_info << std::setprecision(1);
cache_info << "Max size " << max_in_mb << " MB ";
diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp
index 5c37fee62c..4843ed83f1 100644
--- a/indra/llmessage/message.cpp
+++ b/indra/llmessage/message.cpp
@@ -113,10 +113,11 @@ class LLMessageHandlerBridge : public LLHTTPNode
void LLMessageHandlerBridge::post(LLHTTPNode::ResponsePtr response,
const LLSD& context, const LLSD& input) const
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
std::string name = context[CONTEXT_REQUEST][CONTEXT_WILDCARD]["message-name"];
char* namePtr = LLMessageStringTable::getInstance()->getString(name.c_str());
- LL_DEBUGS() << "Setting mLastSender " << input["sender"].asString() << LL_ENDL;
+ LL_DEBUGS("Messaging") << "Setting mLastSender " << input["sender"].asString() << LL_ENDL;
gMessageSystem->mLastSender = LLHost(input["sender"].asString());
gMessageSystem->mPacketsIn += 1;
gMessageSystem->mLLSDMessageReader->setMessage(namePtr, input["body"]);
@@ -2050,6 +2051,7 @@ void LLMessageSystem::dispatch(
const std::string& msg_name,
const LLSD& message)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
LLPointer responsep = LLSimpleResponse::create();
dispatch(msg_name, message, responsep);
}
@@ -2060,6 +2062,7 @@ void LLMessageSystem::dispatch(
const LLSD& message,
LLHTTPNode::ResponsePtr responsep)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if ((gMessageSystem->mMessageTemplates.find
(LLMessageStringTable::getInstance()->getString(msg_name.c_str())) ==
gMessageSystem->mMessageTemplates.end()) &&
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 7f8db4f836..0434e4ed5e 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -1651,7 +1651,19 @@ void LLTextBase::draw()
bg_rect.intersectWith( text_rect );
gl_rect_2d( text_rect, bg_color, true );
+
+ // Additionally set the font color of highlighted text instead of using LabelTextColor
+ const LLColor4& font_color = ll::ui::SearchableControl::getHighlightFontColor();
+ setColor(font_color);
+ //
}
+ // Set the font color back to LabelTextColor if not highlighted
+ else
+ {
+ const LLColor4& font_color = LLUIColorTable::instance().getColor("LabelTextColor");
+ setColor(font_color);
+ }
+ //
bool should_clip = mClip || mScroller != NULL;
// Fix text bleeding at top edge of scrolling text editors
diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp
index 7c5e441d60..040bc8e073 100644
--- a/indra/llwindow/lldxhardware.cpp
+++ b/indra/llwindow/lldxhardware.cpp
@@ -65,7 +65,15 @@ typedef BOOL ( WINAPI* PfnCoSetProxyBlanket )( IUnknown* pProxy, DWORD dwAuthnSv
//Getting the version of graphics controller driver via WMI
std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor)
{
- std::string mDriverVersion;
+ // Add caching for WMI query results
+ LL_PROFILE_ZONE_SCOPED;
+ static auto driver_version = std::string();
+
+ if (!driver_version.empty())
+ {
+ return driver_version; // Return cached version
+ }
+ //
HRESULT hres;
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
IWbemLocator *pLoc = NULL;
@@ -234,11 +242,11 @@ std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor)
std::string str = ll_convert_wide_to_string(ws);
LL_INFOS("AppInit") << " DriverVersion : " << str << LL_ENDL;
- if (mDriverVersion.empty())
+ if (driver_version.empty()) // caching version (also make the varname not stupid)
{
- mDriverVersion = str;
+ driver_version = str; // caching version (also make the varname not stupid)
}
- else if (mDriverVersion != str)
+ else if (driver_version != str) // caching version (also make the varname not stupid)
{
if (vendor == GPU_ANY)
{
@@ -274,7 +282,7 @@ std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor)
// supposed to always call CoUninitialize even if init returned false
CoUninitialize();
- return mDriverVersion;
+ return driver_version; // caching version of driver query
}
void get_wstring(IDxDiagContainer* containerp, const WCHAR* wszPropName, WCHAR* wszPropValue, int outputSize)
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 6a044fead8..aef53389de 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -96,6 +96,7 @@ set(viewer_SOURCE_FILES
dialogstack.cpp
exoflickr.cpp
exoflickrauth.cpp
+ fsprimfeedauth.cpp
exogroupmutelist.cpp
floatermedialists.cpp
fsareasearch.cpp
@@ -164,6 +165,7 @@ set(viewer_SOURCE_FILES
fspose.cpp
fsposeranimator.cpp
fsposingmotion.cpp
+ fsprimfeedauth.cpp
fsradar.cpp
fsradarentry.cpp
fsradarlistctrl.cpp
@@ -182,6 +184,8 @@ set(viewer_SOURCE_FILES
lfsimfeaturehandler.cpp
llflickrconnect.cpp
llfloaterflickr.cpp
+ fsprimfeedconnect.cpp
+ fsfloaterprimfeed.cpp
llpanelopenregionsettings.cpp
# [Legacy Bake]
llagentwearablesfetch.cpp
@@ -911,6 +915,7 @@ set(viewer_HEADER_FILES
dialogstack.h
exoflickr.h
exoflickrauth.h
+ fsprimfeedauth.h
exogroupmutelist.h
floatermedialists.h
fsareasearch.h
@@ -982,6 +987,7 @@ set(viewer_HEADER_FILES
fspose.h
fsposeranimator.h
fsposingmotion.h
+ fsprimfeedauth.h
fsradar.h
fsradarentry.h
fsradarlistctrl.h
@@ -1001,6 +1007,8 @@ set(viewer_HEADER_FILES
lfsimfeaturehandler.h
llflickrconnect.h
llfloaterflickr.h
+ fsprimfeedconnect.h
+ fsfloaterprimfeed.h
# [Legacy Bake]
llagentwearablesfetch.h
vjlocalmesh.h
diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml
index 7708f9a0c9..7f26b29024 100644
--- a/indra/newview/app_settings/commands.xml
+++ b/indra/newview/app_settings/commands.xml
@@ -254,6 +254,16 @@
is_running_function="Floater.IsOpen"
is_running_parameters="flickr"
/>
+
RenderShaderLightingMaxLevel
RenderShadowDetail
RenderShadowResolutionScale
+ RenderSkyAmbientScale
RenderSkyAutoAdjustAmbientScale
RenderSkyAutoAdjustBlueDensityScale
RenderSkyAutoAdjustBlueHorizonScale
RenderSkyAutoAdjustSunColorScale
RenderSkyAutoAdjustHDRScale
+ RenderSkyAutoAdjustLegacy
RenderSkyAutoAdjustProbeAmbiance
RenderSkySunlightScale
RenderSSAOIrradianceScale
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index af6841adf8..7788d78c2a 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -153,6 +153,17 @@
Value
http://phoenixviewer.com/app/fsdata/grids.xml
+ FSPrimfeedViewerApiKey
+
FSGridBuilderURL
+ FSLastSnapshotToPrimfeedHeight
+
+ FSLastSnapshotToPrimfeedWidth
+
+ FSLastSnapshotToPrimfeedResolution
+
FSLastSnapshotToTwitterHeight
+ FSNoVersionPopup
+
FSllOwnerSayToScriptDebugWindowRouting
+ FSRepeatedEnvTogglesShared
+
diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml
index 95e3c687dd..1b027b4d75 100644
--- a/indra/newview/app_settings/settings_per_account.xml
+++ b/indra/newview/app_settings/settings_per_account.xml
@@ -1358,6 +1358,83 @@
Value
0
+ FSPrimfeedOAuthToken
+
+ FSPrimfeedProfileLink
+
+ FSPrimfeedPlan
+
+ FSPrimfeedUsername
+
+ FSPrimfeedCommercialContent
+
+ FSPrimfeedAddToPublicGallery
+
+ FSPrimfeedOpenURLOnPost
+
FSProtectedFolders
+ FSPrimfeedPhotoRating
+
+ FSPrimfeedPhotoResolution
+
+ FlickrPhotoResolution
+
+ FlickrPhotoRating
+
diff --git a/indra/newview/bugsplatattributes.cpp b/indra/newview/bugsplatattributes.cpp
index fdc8871a26..d0a8b32701 100644
--- a/indra/newview/bugsplatattributes.cpp
+++ b/indra/newview/bugsplatattributes.cpp
@@ -64,6 +64,7 @@ std::string BugSplatAttributes::to_xml_token(const std::string& input)
bool BugSplatAttributes::writeToFile(const std::string& file_path)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING;
std::lock_guard lock(mMutex);
// Write to a temporary file first
diff --git a/indra/newview/fsareasearch.cpp b/indra/newview/fsareasearch.cpp
index 4e58c4e28e..ef4efc84fc 100644
--- a/indra/newview/fsareasearch.cpp
+++ b/indra/newview/fsareasearch.cpp
@@ -156,6 +156,7 @@ FSAreaSearch::FSAreaSearch(const LLSD& key) :
mFilterPhantom(false),
mFilterAttachment(false),
mFilterMoaP(false),
+ mFilterReflectionProbe(false),
mFilterDistance(false),
mFilterDistanceMin(0),
mFilterDistanceMax(999999),
@@ -166,6 +167,7 @@ FSAreaSearch::FSAreaSearch(const LLSD& key) :
mBeacons(false),
mExcludeAttachment(true),
mExcludeTemporary(true),
+ mExcludeReflectionProbe(false),
mExcludePhysics(true),
mExcludeChildPrims(true),
mExcludeNeighborRegions(true),
@@ -545,6 +547,11 @@ bool FSAreaSearch::isSearchableObject(LLViewerObject* objectp, LLViewerRegion* o
return false;
}
+ if (mExcludeReflectionProbe && objectp->mReflectionProbe.notNull())
+ {
+ return false;
+ }
+
return true;
}
@@ -908,6 +915,11 @@ void FSAreaSearch::matchObject(FSObjectProperties& details, LLViewerObject* obje
return;
}
+ if (mFilterReflectionProbe && !objectp->mReflectionProbe.notNull())
+ {
+ return;
+ }
+
//-----------------------------------------------------------------------
// Find text
//-----------------------------------------------------------------------
@@ -2217,6 +2229,10 @@ bool FSPanelAreaSearchFilter::postBuild()
mCheckboxExcludetemporary->set(true);
mCheckboxExcludetemporary->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
+ mCheckboxExcludeReflectionProbes = getChild("exclude_reflection_probes");
+ mCheckboxExcludeReflectionProbes->set(false);
+ mCheckboxExcludeReflectionProbes->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
+
mCheckboxExcludeChildPrim = getChild("exclude_childprim");
mCheckboxExcludeChildPrim->set(true);
mCheckboxExcludeChildPrim->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
@@ -2240,6 +2256,9 @@ bool FSPanelAreaSearchFilter::postBuild()
mCheckboxMoaP = getChild("filter_moap");
mCheckboxMoaP->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
+ mCheckboxReflectionProbe = getChild("filter_reflection_probe");
+ mCheckboxReflectionProbe->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
+
mCheckboxPermCopy = getChild("filter_perm_copy");
mCheckboxPermCopy->setCommitCallback(boost::bind(&FSPanelAreaSearchFilter::onCommitCheckbox, this));
@@ -2262,6 +2281,7 @@ void FSPanelAreaSearchFilter::onCommitCheckbox()
mFSAreaSearch->setFilterForSale(mCheckboxForSale->get());
mFSAreaSearch->setFilterDistance(mCheckboxDistance->get());
mFSAreaSearch->setFilterMoaP(mCheckboxMoaP->get());
+ mFSAreaSearch->setFilterReflectionProbe(mCheckboxReflectionProbe->get());
if (mCheckboxExcludePhysics->get())
{
@@ -2291,6 +2311,19 @@ void FSPanelAreaSearchFilter::onCommitCheckbox()
}
mFSAreaSearch->setFilterTemporary(mCheckboxTemporary->get());
+ if (mCheckboxExcludeReflectionProbes->get())
+ {
+ mFSAreaSearch->setFilterReflectionProbe(false);
+ mCheckboxReflectionProbe->set(false);
+ mCheckboxReflectionProbe->setEnabled(false);
+ mFSAreaSearch->setExcludeReflectionProbe(true);
+ }
+ else
+ {
+ mCheckboxReflectionProbe->setEnabled(true);
+ mFSAreaSearch->setExcludeReflectionProbe(false);
+ }
+
if (mCheckboxExcludeAttachment->get())
{
mFSAreaSearch->setFilterAttachment(false);
diff --git a/indra/newview/fsareasearch.h b/indra/newview/fsareasearch.h
index 78d40454dc..a4bb89c2b0 100644
--- a/indra/newview/fsareasearch.h
+++ b/indra/newview/fsareasearch.h
@@ -141,12 +141,14 @@ public:
void setFilterPhantom(bool b) { mFilterPhantom = b; }
void setFilterAttachment(bool b) { mFilterAttachment = b; }
void setFilterMoaP(bool b) { mFilterMoaP = b; }
+ void setFilterReflectionProbe(bool b) { mFilterReflectionProbe = b; }
void setRegexSearch(bool b) { mRegexSearch = b; }
void setBeacons(bool b) { mBeacons = b; }
void setExcludeAttachment(bool b) { mExcludeAttachment = b; }
void setExcludetemporary(bool b) { mExcludeTemporary = b; }
+ void setExcludeReflectionProbe(bool b) { mExcludeReflectionProbe = b; }
void setExcludePhysics(bool b) { mExcludePhysics = b; }
void setExcludeChildPrims(bool b) { mExcludeChildPrims = b; }
void setExcludeNeighborRegions(bool b) { mExcludeNeighborRegions = b; }
@@ -230,6 +232,7 @@ private:
bool mExcludeAttachment;
bool mExcludeTemporary;
+ bool mExcludeReflectionProbe;
bool mExcludePhysics;
bool mExcludeChildPrims;
bool mExcludeNeighborRegions;
@@ -240,6 +243,7 @@ private:
bool mFilterPhantom;
bool mFilterAttachment;
bool mFilterMoaP;
+ bool mFilterReflectionProbe;
bool mFilterForSale;
S32 mFilterForSaleMin;
@@ -382,6 +386,7 @@ private:
LLCheckBoxCtrl* mCheckboxLocked;
LLCheckBoxCtrl* mCheckboxPhantom;
LLCheckBoxCtrl* mCheckboxMoaP;
+ LLCheckBoxCtrl* mCheckboxReflectionProbe;
LLCheckBoxCtrl* mCheckboxDistance;
LLSpinCtrl* mSpinDistanceMinValue;
LLSpinCtrl* mSpinDistanceMaxValue;
@@ -393,6 +398,7 @@ private:
LLCheckBoxCtrl* mCheckboxExcludeAttachment;
LLCheckBoxCtrl* mCheckboxExcludePhysics;
LLCheckBoxCtrl* mCheckboxExcludetemporary;
+ LLCheckBoxCtrl* mCheckboxExcludeReflectionProbes;
LLCheckBoxCtrl* mCheckboxExcludeChildPrim;
LLCheckBoxCtrl* mCheckboxExcludeNeighborRegions;
LLCheckBoxCtrl* mCheckboxPermCopy;
diff --git a/indra/newview/fsfloaterim.cpp b/indra/newview/fsfloaterim.cpp
index 14d3529380..ef83dd0f4f 100644
--- a/indra/newview/fsfloaterim.cpp
+++ b/indra/newview/fsfloaterim.cpp
@@ -1269,6 +1269,22 @@ FSFloaterIM* FSFloaterIM::show(const LLUUID& session_id)
if (!gIMMgr->hasSession(session_id))
return nullptr;
+ // [FIRE-34494] fixes unable to open an IM with someone who started a group chat
+ // Prevent showing non-IM sessions in FSFloaterIM::show()
+ LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
+ if (!session || (
+ IM_NOTHING_SPECIAL != session->mType
+ && IM_SESSION_P2P_INVITE != session->mType
+ && IM_SESSION_INVITE != session->mType
+ && IM_SESSION_CONFERENCE_START != session->mType
+ && IM_SESSION_GROUP_START != session->mType))
+ {
+ LL_WARNS("IMVIEW") << "Attempted to show FSFloaterIM for non-IM session: "
+ << (session ? std::to_string(session->mType) : "null") << LL_ENDL;
+ return nullptr;
+ }
+ //
+
if (!isChatMultiTab())
{
//hide all
diff --git a/indra/newview/fsfloaterprimfeed.cpp b/indra/newview/fsfloaterprimfeed.cpp
new file mode 100644
index 0000000000..c665d09198
--- /dev/null
+++ b/indra/newview/fsfloaterprimfeed.cpp
@@ -0,0 +1,912 @@
+/**
+ * @file fsfloaterprimfeed.cpp
+ * @brief Implementation of primfeed floater
+ * @author beq@firestorm
+ *
+ * $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
+ * Phoenix Firestorm Viewer Source Code
+ * Copyright (C) 2025, Beq Janus
+ *
+ * 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
+ *
+ * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
+ * http://www.firestormviewer.org
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "fsfloaterprimfeed.h"
+#include "fsprimfeedconnect.h"
+#include "llagent.h"
+#include "llagentui.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfloaterreg.h"
+#include "lliconctrl.h"
+#include "llimagefiltersmanager.h"
+#include "llresmgr.h" // LLLocale
+#include "llsdserialize.h"
+#include "llloadingindicator.h"
+#include "llslurl.h"
+#include "lltrans.h"
+#include "llfloatersnapshot.h"
+#include "llsnapshotlivepreview.h"
+#include "llfloaterbigpreview.h"
+#include "llviewerregion.h"
+#include "llviewercontrol.h"
+#include "llviewermedia.h"
+#include "lltabcontainer.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include
+#include "llspinctrl.h"
+
+#include "llviewernetwork.h"
+#include "llnotificationsutil.h"
+#include "fsprimfeedauth.h"
+#include "llviewernetwork.h"
+
+static LLPanelInjector t_panel_photo("fsprimfeedphotopanel");
+static LLPanelInjector t_panel_account("fsprimfeedaccountpanel");
+
+///////////////////////////
+// FSPrimfeedPhotoPanel/////
+///////////////////////////
+
+FSPrimfeedPhotoPanel::FSPrimfeedPhotoPanel() :
+ mResolutionComboBox(nullptr),
+ mRefreshBtn(nullptr),
+ mWorkingLabel(nullptr),
+ mThumbnailPlaceholder(nullptr),
+ mDescriptionTextBox(nullptr),
+ mLocationCheckbox(nullptr),
+ mRatingComboBox(nullptr),
+ mPostButton(nullptr),
+ mBtnPreview(nullptr),
+ mBigPreviewFloater(nullptr)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", [this](LLUICtrl*, const LLSD&) { onSend(); });
+ mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", [this](LLUICtrl*, const LLSD&) { onClickNewSnapshot(); });
+ mCommitCallbackRegistrar.add("SocialSharing.BigPreview", [this](LLUICtrl*, const LLSD&) { onClickBigPreview(); });
+ mCommitCallbackRegistrar.add("Primfeed.Info",
+ [](LLUICtrl*, const LLSD& param)
+ {
+ const std::string url = param.asString();
+ LL_DEBUGS("primfeed") << "Info button clicked, opening " << url << LL_ENDL;
+ LLWeb::loadURLExternal(url);
+ });
+}
+
+FSPrimfeedPhotoPanel::~FSPrimfeedPhotoPanel()
+{
+ if (mPreviewHandle.get())
+ {
+ mPreviewHandle.get()->die();
+ }
+
+ FSPrimfeedAuth::sPrimfeedAuthPump->stopListening("FSPrimfeedAccountPanel");
+
+ gSavedSettings.setS32("FSLastSnapshotToPrimfeedResolution", getChild("resolution_combobox")->getCurrentIndex());
+ gSavedSettings.setS32("FSLastSnapshotToPrimfeedWidth", getChild("custom_snapshot_width")->getValue().asInteger());
+ gSavedSettings.setS32("FSLastSnapshotToPrimfeedHeight", getChild("custom_snapshot_height")->getValue().asInteger());
+}
+
+bool FSPrimfeedPhotoPanel::postBuild()
+{
+ setVisibleCallback([this](LLUICtrl*, bool visible) { onVisibilityChange(visible); });
+
+ mResolutionComboBox = getChild("resolution_combobox");
+ mResolutionComboBox->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
+ mFilterComboBox = getChild("filters_combobox");
+ mFilterComboBox->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
+ mRefreshBtn = getChild("new_snapshot_btn");
+ mBtnPreview = getChild("big_preview_btn");
+ mWorkingLabel = getChild("working_lbl");
+ mThumbnailPlaceholder = getChild("thumbnail_placeholder");
+ mDescriptionTextBox = getChild("photo_description");
+ mLocationCheckbox = getChild("add_location_cb");
+ mCommercialCheckbox = getChild("primfeed_commercial_content");
+ mPublicGalleryCheckbox = getChild("primfeed_add_to_public_gallery");
+ mRatingComboBox = getChild("rating_combobox");
+ mPostButton = getChild("post_photo_btn");
+ mCancelButton = getChild("cancel_photo_btn");
+ mBigPreviewFloater = dynamic_cast(LLFloaterReg::getInstance("big_preview"));
+
+ // Update custom resolution controls with lambdas
+ getChild("custom_snapshot_width")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
+ getChild("custom_snapshot_height")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
+ getChild("keep_aspect_ratio")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
+
+ getChild("resolution_combobox")->setCurrentByIndex(gSavedSettings.getS32("FSLastSnapshotToPrimfeedResolution"));
+ getChild("custom_snapshot_width")->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedWidth"));
+ getChild("custom_snapshot_height")->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedHeight"));
+
+ // Update filter list
+ std::vector filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
+ auto* filterbox = static_cast(mFilterComboBox);
+ for (const std::string& filter : filter_list)
+ {
+ filterbox->add(filter);
+ }
+
+ return LLPanel::postBuild();
+}
+
+// static
+void FSFloaterPrimfeed::update()
+{
+ if (LLFloaterReg::instanceVisible("primfeed"))
+ {
+ LLFloaterSnapshotBase::ImplBase::updatePreviewList(true, true);
+ }
+}
+
+// virtual
+S32 FSPrimfeedPhotoPanel::notify(const LLSD& info)
+{
+ if (info.has("snapshot-updating"))
+ {
+ // Disable the Post button and whatever else while the snapshot is not updated
+ // updateControls();
+ return 1;
+ }
+
+ if (info.has("snapshot-updated"))
+ {
+ // Enable the send/post/save buttons.
+ updateControls();
+
+ // The refresh button is initially hidden. We show it after the first update,
+ // i.e. after snapshot is taken
+
+ if (LLUICtrl* refresh_button = getRefreshBtn(); !refresh_button->getVisible())
+ {
+ refresh_button->setVisible(true);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+void FSPrimfeedPhotoPanel::draw()
+{
+ auto previewp = static_cast(mPreviewHandle.get());
+
+ // Enable interaction only if no transaction with the service is on-going (prevent duplicated posts)
+ auto can_post = !(FSPrimfeedConnect::instance().isTransactionOngoing()) && FSPrimfeedAuth::isAuthorized();
+
+ mCancelButton->setEnabled(can_post);
+ mDescriptionTextBox->setEnabled(can_post);
+ mRatingComboBox->setEnabled(can_post);
+ mResolutionComboBox->setEnabled(can_post);
+ mFilterComboBox->setEnabled(can_post);
+ mRefreshBtn->setEnabled(can_post);
+ mBtnPreview->setEnabled(can_post);
+ mLocationCheckbox->setEnabled(can_post);
+ mPublicGalleryCheckbox->setEnabled(can_post);
+ mCommercialCheckbox->setEnabled(can_post);
+
+ // Reassign the preview floater if we have the focus and the preview exists
+ if (hasFocus() && isPreviewVisible())
+ {
+ attachPreview();
+ }
+
+ // Toggle the button state as appropriate
+ bool preview_active = (isPreviewVisible() && mBigPreviewFloater->isFloaterOwner(getParentByType()));
+ mBtnPreview->setToggleState(preview_active);
+
+ // Display the preview if one is available
+ if (previewp && previewp->getThumbnailImage())
+ {
+ const LLRect& thumbnail_rect = mThumbnailPlaceholder->getRect();
+ const S32 thumbnail_w = previewp->getThumbnailWidth();
+ const S32 thumbnail_h = previewp->getThumbnailHeight();
+
+ // calc preview offset within the preview rect
+ const S32 local_offset_x = (thumbnail_rect.getWidth() - thumbnail_w) / 2;
+ const S32 local_offset_y = (thumbnail_rect.getHeight() - thumbnail_h) / 2;
+ S32 offset_x = thumbnail_rect.mLeft + local_offset_x;
+ S32 offset_y = thumbnail_rect.mBottom + local_offset_y;
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ // Apply floater transparency to the texture unless the floater is focused.
+ F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
+ LLColor4 color = LLColor4::white;
+ gl_draw_scaled_image(offset_x, offset_y, thumbnail_w, thumbnail_h, previewp->getThumbnailImage(), color % alpha);
+ }
+
+ // Update the visibility of the working (computing preview) label
+ mWorkingLabel->setVisible(!(previewp && previewp->getSnapshotUpToDate()));
+
+ // Enable Post if we have a preview to send and no on going connection being processed
+ mPostButton->setEnabled(can_post && (previewp && previewp->getSnapshotUpToDate()));
+
+ // Draw the rest of the panel on top of it
+ LLPanel::draw();
+}
+
+LLSnapshotLivePreview* FSPrimfeedPhotoPanel::getPreviewView()
+{
+ auto previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
+ return previewp;
+}
+
+void FSPrimfeedPhotoPanel::onVisibilityChange(bool visible)
+{
+ if (visible)
+ {
+ if (mPreviewHandle.get())
+ {
+ LLSnapshotLivePreview* preview = getPreviewView();
+ if (preview)
+ {
+ LL_DEBUGS() << "opened, updating snapshot" << LL_ENDL;
+ preview->updateSnapshot(true);
+ }
+ }
+ else
+ {
+ LLRect full_screen_rect = getRootView()->getRect();
+ LLSnapshotLivePreview::Params p;
+ p.rect(full_screen_rect);
+ auto previewp = new LLSnapshotLivePreview(p);
+ mPreviewHandle = previewp->getHandle();
+
+ previewp->setContainer(this);
+ previewp->setSnapshotType(LLSnapshotModel::SNAPSHOT_WEB);
+ previewp->setSnapshotFormat(LLSnapshotModel::SNAPSHOT_FORMAT_PNG);
+ previewp->setThumbnailSubsampled(true); // We want the preview to reflect the *saved* image
+ previewp->setAllowRenderUI(false); // We do not want the rendered UI in our snapshots
+ previewp->setAllowFullScreenPreview(false); // No full screen preview in SL Share mode
+ previewp->setThumbnailPlaceholderRect(mThumbnailPlaceholder->getRect());
+
+ updateControls();
+ }
+ }
+}
+
+void FSPrimfeedPhotoPanel::onClickNewSnapshot()
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ if (previewp)
+ {
+ previewp->updateSnapshot(true);
+ }
+}
+
+void FSPrimfeedPhotoPanel::onClickBigPreview()
+{
+ // Toggle the preview
+ if (isPreviewVisible())
+ {
+ LLFloaterReg::hideInstance("big_preview");
+ }
+ else
+ {
+ attachPreview();
+ LLFloaterReg::showInstance("big_preview");
+ }
+}
+
+bool FSPrimfeedPhotoPanel::isPreviewVisible() const
+{
+ return (mBigPreviewFloater && mBigPreviewFloater->getVisible());
+}
+
+void FSPrimfeedPhotoPanel::attachPreview()
+{
+ if (mBigPreviewFloater)
+ {
+ LLSnapshotLivePreview* previewp = getPreviewView();
+ mBigPreviewFloater->setPreview(previewp);
+ mBigPreviewFloater->setFloaterOwner(getParentByType());
+ }
+}
+
+void FSPrimfeedPhotoPanel::onSend()
+{
+ sendPhoto();
+}
+
+bool FSPrimfeedPhotoPanel::onPrimfeedConnectStateChange(const LLSD& /*data*/)
+{
+ if (FSPrimfeedAuth::isAuthorized())
+ {
+ sendPhoto();
+ }
+
+ return false;
+}
+
+void FSPrimfeedPhotoPanel::sendPhoto()
+{
+ auto ratingToString = [&](int rating)
+ {
+ static const std::array RATING_NAMES = {
+ "general", // 1
+ "moderate", // 2
+ "adult", // 3
+ "adult_plus" // 4
+ };
+
+ // clamp into [1,4]
+ int idx = llclamp(rating, 1, 4) - 1;
+ return RATING_NAMES[idx];
+ };
+ // Get the description (primfeed has no title/tags etc at this point)
+ std::string description = mDescriptionTextBox->getValue().asString();
+
+ // Get the content rating
+ int content_rating = mRatingComboBox->getValue().asInteger();
+ bool post_to_public_gallery = mPublicGalleryCheckbox->getValue().asBoolean();
+ bool commercial_content = mCommercialCheckbox->getValue().asBoolean();
+
+ // Get the image
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_POSTING);
+ LLSD params;
+ params["rating"] = ratingToString(content_rating);
+ params["content"] = description;
+ params["is_commercial"] = commercial_content;
+ params["post_to_public_gallery"] = post_to_public_gallery;
+ // Add the location if required
+
+ if (bool add_location = mLocationCheckbox->getValue().asBoolean(); add_location)
+ {
+ // Get the SLURL for the location
+ LLSLURL slurl;
+ LLAgentUI::buildSLURL(slurl);
+ std::string slurl_string = slurl.getSLURLString();
+
+ params["location"] = slurl_string;
+ }
+
+ FSPrimfeedConnect::instance().uploadPhoto(params, previewp->getFormattedImage().get(),
+ [this](bool success, const std::string& url)
+ {
+ if (success)
+ {
+ FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_POSTED);
+ static LLCachedControl open_url_on_post(gSavedPerAccountSettings,
+ "FSPrimfeedOpenURLOnPost", true);
+ if (open_url_on_post)
+ {
+ LLWeb::loadURLExternal(url);
+ }
+ LLSD args;
+ args["PF_POSTURL"] = url;
+ LLNotificationsUtil::add("FSPrimfeedUploadComplete", args);
+ }
+ else
+ {
+ mWorkingLabel->setValue("Error posting to Primfeed");
+ mPostButton->setEnabled(true);
+ }
+ });
+ updateControls();
+}
+
+void FSPrimfeedPhotoPanel::clearAndClose()
+{
+ mDescriptionTextBox->setValue("");
+
+ if (LLFloater* floater = getParentByType())
+ {
+ floater->closeFloater();
+ if (mBigPreviewFloater)
+ {
+ mBigPreviewFloater->closeOnFloaterOwnerClosing(floater);
+ }
+ }
+}
+
+void FSPrimfeedPhotoPanel::updateControls()
+{
+ // LLSnapshotLivePreview* previewp = getPreviewView();
+ updateResolution(false);
+}
+
+void FSPrimfeedPhotoPanel::updateResolution(bool do_update)
+{
+ auto combobox = static_cast(mResolutionComboBox);
+ auto filterbox = static_cast(mFilterComboBox);
+
+ std::string sdstring = combobox->getSelectedValue();
+ LLSD sdres;
+ std::stringstream sstream(sdstring);
+ LLSDSerialize::fromNotation(sdres, sstream, sdstring.size());
+
+ S32 width = sdres[0];
+ S32 height = sdres[1];
+
+ // Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
+ std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
+
+ if (auto previewp = static_cast(mPreviewHandle.get()); previewp && combobox->getCurrentIndex() >= 0)
+ {
+ checkAspectRatio(width);
+
+ S32 original_width = 0;
+ S32 original_height = 0;
+ previewp->getSize(original_width, original_height);
+
+ if (width == 0 || height == 0)
+ {
+ // take resolution from current window size
+ LL_DEBUGS() << "Setting preview res from window: " << gViewerWindow->getWindowWidthRaw() << "x"
+ << gViewerWindow->getWindowHeightRaw() << LL_ENDL;
+ previewp->setSize(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
+ }
+ else if (width == -1 || height == -1)
+ {
+ // take resolution from custom size
+ LLSpinCtrl* width_spinner = getChild("custom_snapshot_width");
+ LLSpinCtrl* height_spinner = getChild("custom_snapshot_height");
+ S32 custom_width = width_spinner->getValue().asInteger();
+ S32 custom_height = height_spinner->getValue().asInteger();
+ if (checkImageSize(previewp, custom_width, custom_height, true, previewp->getMaxImageSize()))
+ {
+ width_spinner->set((F32)custom_width);
+ height_spinner->set((F32)custom_height);
+ }
+ LL_DEBUGS() << "Setting preview res from custom: " << custom_width << "x" << custom_height << LL_ENDL;
+ previewp->setSize(custom_width, custom_height);
+ }
+ else
+ {
+ // use the resolution from the selected pre-canned drop-down choice
+ LL_DEBUGS() << "Setting preview res selected from combo: " << width << "x" << height << LL_ENDL;
+ previewp->setSize(width, height);
+ }
+
+ previewp->getSize(width, height);
+ if ((original_width != width) || (original_height != height))
+ {
+ previewp->setSize(width, height);
+ if (do_update)
+ {
+ previewp->updateSnapshot(true, true);
+ updateControls();
+ }
+ }
+ // Get the old filter, compare to the current one "filter_name" and set if changed
+ std::string original_filter = previewp->getFilter();
+ if (original_filter != filter_name)
+ {
+ previewp->setFilter(filter_name);
+ if (do_update)
+ {
+ previewp->updateSnapshot(false, true);
+ updateControls();
+ }
+ }
+ }
+
+ bool custom_resolution = static_cast(mResolutionComboBox)->getSelectedValue().asString() == "[i-1,i-1]";
+ getChild("custom_snapshot_width")->setEnabled(custom_resolution);
+ getChild("custom_snapshot_height")->setEnabled(custom_resolution);
+ getChild("keep_aspect_ratio")->setEnabled(custom_resolution);
+}
+
+void FSPrimfeedPhotoPanel::checkAspectRatio(S32 index)
+{
+ LLSnapshotLivePreview* previewp = getPreviewView();
+
+ bool keep_aspect = false;
+
+ if (0 == index) // current window size
+ {
+ keep_aspect = true;
+ }
+ else if (-1 == index)
+ {
+ keep_aspect = getChild("keep_aspect_ratio")->get();
+ }
+ else // predefined resolution
+ {
+ keep_aspect = false;
+ }
+
+ if (previewp)
+ {
+ previewp->mKeepAspectRatio = keep_aspect;
+ }
+}
+
+LLUICtrl* FSPrimfeedPhotoPanel::getRefreshBtn()
+{
+ return mRefreshBtn;
+}
+
+void FSPrimfeedPhotoPanel::onOpen(const LLSD& key)
+{
+ if (!FSPrimfeedAuth::isAuthorized())
+ {
+ // Reauthorise if necessary.
+ FSPrimfeedAuth::initiateAuthRequest();
+ LLSD dummy;
+ onPrimfeedConnectStateChange(dummy);
+ }
+}
+
+void FSPrimfeedPhotoPanel::uploadCallback(bool success, const LLSD& response)
+{
+ LLSD args;
+ if (success && response["stat"].asString() == "ok")
+ {
+ FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_POSTED);
+ args["PF_POSTURL"] = response["postUrl"];
+ LLNotificationsUtil::add("FSPrimfeedUploadComplete", args);
+ }
+ else
+ {
+ FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_POST_FAILED);
+ }
+}
+
+void FSPrimfeedPhotoPanel::primfeedAuthResponse(bool success, const LLSD& response)
+{
+ if (!success)
+ {
+ if (response.has("status") && response["status"].asString() == "reset")
+ {
+ LL_INFOS("Primfeed") << "Primfeed authorization has been reset." << LL_ENDL;
+ }
+ else
+ {
+ // Complain about failed auth here.
+ LL_WARNS("Primfeed") << "Primfeed authentication failed." << LL_ENDL;
+ }
+ }
+ onPrimfeedConnectStateChange(response);
+}
+
+bool FSPrimfeedPhotoPanel::checkImageSize(LLSnapshotLivePreview* previewp, S32& width, S32& height, bool isWidthChanged, S32 max_value)
+{
+ S32 w = width;
+ S32 h = height;
+
+ if (previewp && previewp->mKeepAspectRatio)
+ {
+ if (gViewerWindow->getWindowWidthRaw() < 1 || gViewerWindow->getWindowHeightRaw() < 1)
+ {
+ return false;
+ }
+
+ // aspect ratio of the current window
+ F32 aspect_ratio = static_cast(gViewerWindow->getWindowWidthRaw()) / static_cast(gViewerWindow->getWindowHeightRaw());
+
+ // change another value proportionally
+ if (isWidthChanged)
+ {
+ height = ll_round(static_cast(width) / aspect_ratio);
+ }
+ else
+ {
+ width = ll_round(static_cast(height) * aspect_ratio);
+ }
+
+ // bound w/h by the max_value
+ if (width > max_value || height > max_value)
+ {
+ if (width > height)
+ {
+ width = max_value;
+ height = ll_round(static_cast(width) / aspect_ratio);
+ }
+ else
+ {
+ height = max_value;
+ width = ll_round(static_cast(height) * aspect_ratio);
+ }
+ }
+ }
+
+ return (w != width || h != height);
+}
+
+///////////////////////////
+// FSPrimfeedAccountPanel///
+///////////////////////////
+
+FSPrimfeedAccountPanel::FSPrimfeedAccountPanel() :
+ mAccountConnectedAsLabel(nullptr),
+ mAccountNameLink(nullptr),
+ mAccountPlan(nullptr),
+ mPanelButtons(nullptr),
+ mConnectButton(nullptr),
+ mDisconnectButton(nullptr)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Connect", [this](LLUICtrl*, const LLSD&) { onConnect(); });
+ mCommitCallbackRegistrar.add("SocialSharing.Disconnect", [this](LLUICtrl*, const LLSD&) { onDisconnect(); });
+
+ FSPrimfeedAuth::sPrimfeedAuthPump->listen("FSPrimfeedAccountPanel",
+ [this](const LLSD& data)
+ {
+ bool success = data["success"].asBoolean();
+ primfeedAuthResponse(success, data);
+ return true;
+ });
+
+ setVisibleCallback([this](LLUICtrl*, bool visible) { onVisibilityChange(visible); });
+}
+
+bool FSPrimfeedAccountPanel::postBuild()
+{
+ mAccountConnectedAsLabel = getChild("connected_as_label");
+ mAccountNameLink = getChild("primfeed_account_name");
+ mAccountPlan = getChild("primfeed_account_plan");
+ mPanelButtons = getChild("panel_buttons");
+ mConnectButton = getChild("connect_btn");
+ mDisconnectButton = getChild("disconnect_btn");
+
+ LLSD dummy;
+ onPrimfeedConnectStateChange(dummy);
+ return LLPanel::postBuild();
+}
+
+void FSPrimfeedAccountPanel::draw()
+{
+ FSPrimfeedConnect::EConnectionState connection_state = FSPrimfeedConnect::instance().getConnectionState();
+ static FSPrimfeedConnect::EConnectionState last_state = FSPrimfeedConnect::PRIMFEED_DISCONNECTED;
+
+ // Update the connection state if it has changed
+ if (connection_state != last_state)
+ {
+ onPrimfeedConnectStateChange(LLSD());
+ last_state = connection_state;
+ }
+
+ LLPanel::draw();
+}
+
+void FSPrimfeedAccountPanel::primfeedAuthResponse(bool success, const LLSD& response)
+{
+ if (!success)
+ {
+ LL_WARNS("Primfeed") << "Primfeed authentication failed." << LL_ENDL;
+ LLWeb::loadURLExternal("https://www.primfeed.com/login");
+ }
+ onPrimfeedConnectStateChange(response);
+}
+
+void FSPrimfeedAccountPanel::onVisibilityChange(bool visible)
+{
+ if (visible)
+ {
+ // Connected
+ if (FSPrimfeedAuth::isAuthorized())
+ {
+ showConnectedLayout();
+ }
+ else
+ {
+ showDisconnectedLayout();
+ }
+ }
+}
+
+bool FSPrimfeedAccountPanel::onPrimfeedConnectStateChange(const LLSD&)
+{
+ if (FSPrimfeedAuth::isAuthorized() || FSPrimfeedConnect::instance().getConnectionState() == FSPrimfeedConnect::PRIMFEED_CONNECTING)
+ {
+ showConnectedLayout();
+ }
+ else
+ {
+ showDisconnectedLayout();
+ }
+ onPrimfeedConnectInfoChange();
+ return false;
+}
+
+bool FSPrimfeedAccountPanel::onPrimfeedConnectInfoChange()
+{
+ std::string clickable_name{ "" };
+
+ static LLCachedControl primfeed_username(gSavedPerAccountSettings, "FSPrimfeedUsername");
+ static LLCachedControl primfeed_profile_link(gSavedPerAccountSettings, "FSPrimfeedProfileLink");
+ static LLCachedControl primfeed_plan(gSavedPerAccountSettings, "FSPrimfeedPlan");
+
+ // Strings of format [http://www.somewebsite.com Click Me] become clickable text
+ if (!primfeed_username().empty())
+ {
+ clickable_name = std::string("[") + std::string(primfeed_profile_link) + " " + std::string(primfeed_username) + "]";
+ }
+
+ mAccountNameLink->setText(clickable_name);
+ mAccountPlan->setText(primfeed_plan());
+
+ return false;
+}
+
+void FSPrimfeedAccountPanel::showConnectButton()
+{
+ if (!mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(true);
+ mDisconnectButton->setVisible(false);
+ }
+}
+
+void FSPrimfeedAccountPanel::hideConnectButton()
+{
+ if (mConnectButton->getVisible())
+ {
+ mConnectButton->setVisible(false);
+ mDisconnectButton->setVisible(true);
+ }
+}
+
+void FSPrimfeedAccountPanel::showDisconnectedLayout()
+{
+ mAccountConnectedAsLabel->setText(getString("primfeed_disconnected"));
+ mAccountNameLink->setText(std::string(""));
+ mAccountPlan->setText(getString("primfeed_plan_unknown"));
+ showConnectButton();
+}
+
+void FSPrimfeedAccountPanel::showConnectedLayout()
+{
+ mAccountConnectedAsLabel->setText(getString("primfeed_connected"));
+ hideConnectButton();
+}
+
+void FSPrimfeedAccountPanel::onConnect()
+{
+ FSPrimfeedAuth::initiateAuthRequest();
+ LLSD dummy;
+ onPrimfeedConnectStateChange(dummy);
+}
+
+void FSPrimfeedAccountPanel::onDisconnect()
+{
+ FSPrimfeedAuth::resetAuthStatus();
+ LLSD dummy;
+ onPrimfeedConnectStateChange(dummy);
+}
+
+////////////////////////
+// FSFloaterPrimfeed/////
+////////////////////////
+
+FSFloaterPrimfeed::FSFloaterPrimfeed(const LLSD& key) :
+ LLFloater(key),
+ mPrimfeedPhotoPanel(nullptr),
+ mStatusErrorText(nullptr),
+ mStatusLoadingText(nullptr),
+ mStatusLoadingIndicator(nullptr),
+ mPrimfeedAccountPanel(nullptr)
+{
+ mCommitCallbackRegistrar.add("SocialSharing.Cancel", [this](LLUICtrl*, const LLSD&) { onCancel(); });
+}
+
+void FSFloaterPrimfeed::onClose(bool app_quitting)
+{
+ if (auto big_preview_floater = LLFloaterReg::getTypedInstance("big_preview"))
+ {
+ big_preview_floater->closeOnFloaterOwnerClosing(this);
+ }
+ LLFloater::onClose(app_quitting);
+}
+
+void FSFloaterPrimfeed::onCancel()
+{
+ if (auto big_preview_floater = LLFloaterReg::getTypedInstance("big_preview"))
+ {
+ big_preview_floater->closeOnFloaterOwnerClosing(this);
+ }
+ closeFloater();
+}
+
+bool FSFloaterPrimfeed::postBuild()
+{
+ // Keep tab of the Photo Panel
+ mPrimfeedPhotoPanel = static_cast(getChild("panel_primfeed_photo"));
+ mPrimfeedAccountPanel = static_cast(getChild("panel_primfeed_account"));
+ // Connection status widgets
+ mStatusErrorText = getChild("connection_error_text");
+ mStatusLoadingText = getChild("connection_loading_text");
+ mStatusLoadingIndicator = getChild("connection_loading_indicator");
+
+ return LLFloater::postBuild();
+}
+
+void FSFloaterPrimfeed::showPhotoPanel()
+{
+ auto parent = dynamic_cast(mPrimfeedPhotoPanel->getParent());
+ if (!parent)
+ {
+ LL_WARNS() << "Cannot find panel container" << LL_ENDL;
+ return;
+ }
+
+ parent->selectTabPanel(mPrimfeedPhotoPanel);
+}
+
+void FSFloaterPrimfeed::draw()
+{
+ if (mStatusErrorText && mStatusLoadingText && mStatusLoadingIndicator)
+ {
+ mStatusErrorText->setVisible(false);
+ mStatusLoadingText->setVisible(false);
+ mStatusLoadingIndicator->setVisible(false);
+
+ FSPrimfeedConnect::EConnectionState connection_state = FSPrimfeedConnect::instance().getConnectionState();
+ std::string status_text;
+
+ if (FSPrimfeedAuth::isAuthorized())
+ {
+ switch (connection_state)
+ {
+ case FSPrimfeedConnect::PRIMFEED_POSTING:
+ {
+ // Posting indicator
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialPrimfeedPosting");
+ mStatusLoadingText->setValue(status_text);
+ mStatusLoadingIndicator->setVisible(true);
+ break;
+ }
+ case FSPrimfeedConnect::PRIMFEED_POST_FAILED:
+ {
+ // Error posting to the service
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialPrimfeedErrorPosting");
+ mStatusErrorText->setValue(status_text);
+ break;
+ }
+ default:
+ {
+ // LL_WARNS("Prmfeed") << "unexpected state" << connection_state << LL_ENDL;
+ break;
+ }
+ }
+ }
+ else if (FSPrimfeedAuth::isPendingAuth())
+ {
+ // Show the status text when authorisation is pending
+ mStatusLoadingText->setVisible(true);
+ status_text = LLTrans::getString("SocialPrimfeedConnecting");
+ mStatusLoadingText->setValue(status_text);
+ }
+ else
+ {
+ // Show the status text when not authorised
+ mStatusErrorText->setVisible(true);
+ status_text = LLTrans::getString("SocialPrimfeedNotAuthorized");
+ mStatusErrorText->setValue(status_text);
+ }
+ }
+ LLFloater::draw();
+}
+
+void FSFloaterPrimfeed::onOpen(const LLSD& key)
+{
+ mPrimfeedPhotoPanel->onOpen(key);
+}
+
+LLSnapshotLivePreview* FSFloaterPrimfeed::getPreviewView()
+{
+ if (mPrimfeedPhotoPanel)
+ {
+ return mPrimfeedPhotoPanel->getPreviewView();
+ }
+ return nullptr;
+}
diff --git a/indra/newview/fsfloaterprimfeed.h b/indra/newview/fsfloaterprimfeed.h
new file mode 100644
index 0000000000..312f06c130
--- /dev/null
+++ b/indra/newview/fsfloaterprimfeed.h
@@ -0,0 +1,154 @@
+/**
+* @file fsfloaterprimfeed.cpp
+* @brief Declaration of primfeed floater
+* @author beq@firestorm
+*
+ * $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
+ * Phoenix Firestorm Viewer Source Code
+ * Copyright (C) 2025, Beq Janus
+ *
+ * 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
+ *
+ * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
+ * http://www.firestormviewer.org
+ * $/LicenseInfo$
+*/
+#ifndef FS_FLOATERPRIMFEED_H
+#define FS_FLOATERPRIMFEED_H
+
+#include "llfloater.h"
+#include "lltextbox.h"
+#include "llviewertexture.h"
+
+class LLIconCtrl;
+class LLCheckBoxCtrl;
+class LLSnapshotLivePreview;
+class LLFloaterBigPreview;
+
+/*
+ * (TODO) Beq: Refactor this with Flickr
+ * Primfeed floater is copied heavily from the LLFlaoterFlickr class and deliberately implemetns much of the underlying plumbinng into the connector class.
+ * Once this is bedded in and any initial issues are addressed, it would be sensible to refactor both the flickr and primfeed classes to share a common base.
+ * In particular a ref counted test for the livepreview would eliminate the need for the static update method in the app mainloop.
+*/
+class FSPrimfeedPhotoPanel : public LLPanel
+{
+public:
+ FSPrimfeedPhotoPanel();
+ ~FSPrimfeedPhotoPanel();
+
+ bool postBuild() override;
+ S32 notify(const LLSD& info);
+ void draw() override;
+
+ LLSnapshotLivePreview* getPreviewView();
+ void onVisibilityChange(bool new_visibility);
+ void onClickNewSnapshot();
+ void onClickBigPreview();
+ void onSend();
+ bool onPrimfeedConnectStateChange(const LLSD& data);
+
+ void sendPhoto();
+ void clearAndClose();
+
+ void updateControls();
+ void updateResolution(bool do_update);
+ void checkAspectRatio(S32 index);
+ LLUICtrl* getRefreshBtn();
+
+ void onOpen(const LLSD& key) override;
+ void primfeedAuthResponse(bool success, const LLSD& response);
+ void uploadCallback(bool success, const LLSD& response);
+
+private:
+ bool isPreviewVisible() const;
+ void attachPreview();
+
+ bool checkImageSize(LLSnapshotLivePreview* previewp, S32& width, S32& height, bool isWidthChanged, S32 max_value);
+
+ LLHandle mPreviewHandle;
+
+ LLUICtrl * mResolutionComboBox;
+ LLUICtrl * mFilterComboBox;
+ LLUICtrl * mRefreshBtn;
+ LLUICtrl * mWorkingLabel;
+ LLUICtrl * mThumbnailPlaceholder;
+ LLUICtrl * mDescriptionTextBox;
+ LLUICtrl * mLocationCheckbox;
+
+ LLUICtrl * mCommercialCheckbox;
+ LLUICtrl * mPublicGalleryCheckbox;
+ LLUICtrl * mRatingComboBox;
+ LLUICtrl * mPostButton;
+ LLUICtrl * mCancelButton;
+ LLButton * mBtnPreview;
+
+ LLFloaterBigPreview * mBigPreviewFloater;
+};
+
+class FSPrimfeedAccountPanel : public LLPanel
+{
+public:
+ FSPrimfeedAccountPanel();
+ bool postBuild() override;
+ void draw() override;
+
+private:
+ void onVisibilityChange(bool new_visibility);
+ void primfeedAuthResponse(bool success, const LLSD& response);
+ bool onPrimfeedConnectStateChange(const LLSD& data);
+ bool onPrimfeedConnectInfoChange();
+ void onConnect();
+ void onDisconnect();
+
+ void showConnectButton();
+ void hideConnectButton();
+ void showDisconnectedLayout();
+ void showConnectedLayout();
+
+ LLTextBox* mAccountConnectedAsLabel;
+ LLTextBox* mAccountNameLink;
+ LLTextBox* mAccountPlan;
+ LLUICtrl* mPanelButtons;
+ LLUICtrl* mConnectButton;
+ LLUICtrl* mDisconnectButton;
+};
+
+
+class FSFloaterPrimfeed : public LLFloater
+{
+public:
+ explicit FSFloaterPrimfeed(const LLSD& key);
+ static void update();
+ bool postBuild() override;
+ void draw() override;
+ void onClose(bool app_quitting) override;
+ void onCancel();
+
+ void showPhotoPanel();
+
+ void onOpen(const LLSD& key) override;
+ LLSnapshotLivePreview* getPreviewView();
+
+private:
+ FSPrimfeedPhotoPanel* mPrimfeedPhotoPanel;
+ FSPrimfeedAccountPanel* mPrimfeedAccountPanel;
+ LLTextBox* mStatusErrorText;
+ LLTextBox* mStatusLoadingText;
+ LLUICtrl* mStatusLoadingIndicator;
+};
+
+#endif // LL_FSFLOATERPRIMFEED_H
+
diff --git a/indra/newview/fspanellogin.cpp b/indra/newview/fspanellogin.cpp
index 1c6d7ddf98..595b47dc47 100644
--- a/indra/newview/fspanellogin.cpp
+++ b/indra/newview/fspanellogin.cpp
@@ -923,6 +923,12 @@ void FSPanelLogin::loadLoginPage()
// login page (web) content version
params["login_content_version"] = gSavedSettings.getString("LoginContentVersion");
+ // No version popup
+ if (gSavedSettings.getBOOL("FSNoVersionPopup"))
+ {
+ params["noversionpopup"] = "true";
+ }
+
// Make an LLURI with this augmented info
std::string url = login_page.scheme().empty()? login_page.authority() : login_page.scheme() + "://" + login_page.authority();
LLURI login_uri(LLURI::buildHTTP(url,
diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h
index 8056ba7785..fd67de8770 100644
--- a/indra/newview/fsposeranimator.h
+++ b/indra/newview/fsposeranimator.h
@@ -190,18 +190,18 @@ public:
///
/// An ordered list of poser joints, clustered by body-area.
/// Order is based on ease-of-use.
- /// Not necessarily exhaustive, just the joints we care to edit without adding UI clutter.
///
///
/// For an implementation of something other than LLJoints, different name(s) may be required.
+ /// A bvhEndSiteValue is only required if the bone has no descendants.
///
const std::vector PoserJoints{
// head, torso, legs
- { "mHead", "", BODY, { "mEyeLeft", "mEyeRight", "mFaceRoot" }, "0.000 0.076 0.000" },
+ { "mHead", "", BODY, { "mEyeLeft", "mEyeRight", "mFaceRoot", "mSkull" }, "0.000 0.076 0.000" },
{ "mNeck", "", BODY, { "mHead" }, "0.000 0.251 -0.010" },
- { "mPelvis", "", WHOLEAVATAR, { "mTorso", "mHipLeft", "mHipRight", "mTail1", "mGroin", "mHindLimbsRoot" }, "0.000000 0.000000 0.000000" },
+ { "mPelvis", "", WHOLEAVATAR, { "mSpine1", "mHipLeft", "mHipRight", "mTail1", "mGroin", "mHindLimbsRoot" }, "0.000000 0.000000 0.000000" },
{ "mChest", "", BODY, { "mNeck", "mCollarLeft", "mCollarRight", "mWingsRoot" }, "0.000 0.205 -0.015" },
- { "mTorso", "", BODY, { "mChest" }, "0.000 0.084 0.000" },
+ { "mTorso", "", BODY, { "mSpine3" }, "0.000 0.084 0.000" },
{ "mCollarLeft", "mCollarRight", BODY, { "mShoulderLeft" }, "0.085 0.165 -0.021" },
{ "mShoulderLeft", "mShoulderRight", BODY, { "mElbowLeft" }, "0.079 0.000 0.000" },
{ "mElbowLeft", "mElbowRight", BODY, { "mWristLeft" }, "0.248 0.000 0.000" },
@@ -212,10 +212,12 @@ public:
{ "mWristRight", "mWristLeft", BODY, { "mHandThumb1Right", "mHandIndex1Right", "mHandMiddle1Right", "mHandRing1Right", "mHandPinky1Right" }, "-0.205 0.000 0.000", "", true },
{ "mHipLeft", "mHipRight", BODY, { "mKneeLeft" }, "0.127 -0.041 0.034" },
{ "mKneeLeft", "mKneeRight", BODY, { "mAnkleLeft" }, "-0.046 -0.491 -0.001" },
- { "mAnkleLeft", "mAnkleRight", BODY, {}, "0.001 -0.468 -0.029", "0.000 -0.061 0.112" },
- { "mHipRight", "mHipLeft", BODY, { "mKneeRight" }, "-0.129 -0.041 0.034", "0.000 -0.061 0.112", true },
+ { "mAnkleLeft", "mAnkleRight", BODY, { "mToeLeft" }, "0.001 -0.468 -0.029" },
+ { "mToeLeft", "mToeRight", BODY, {}, "0.000 0.109 0.000", "0.000 0.020 0.000" },
+ { "mHipRight", "mHipLeft", BODY, { "mKneeRight" }, "-0.129 -0.041 0.034", "", true },
{ "mKneeRight", "mKneeLeft", BODY, { "mAnkleRight" }, "0.049 -0.491 -0.001", "", true },
- { "mAnkleRight", "mAnkleLeft", BODY, {}, "0.000 -0.468 -0.029", "0.000 -0.061 0.112", true },
+ { "mAnkleRight", "mAnkleLeft", BODY, { "mToeRight" }, "0.000 -0.468 -0.029", "", true },
+ { "mToeRight", "mToeLeft", BODY, {}, "0.000 0.109 0.000", "0.000 0.020 0.000", true },
// face
{ "mFaceRoot",
@@ -225,10 +227,10 @@ public:
"mFaceForeheadLeft", "mFaceForeheadCenter", "mFaceForeheadRight",
"mFaceEyebrowOuterLeft", "mFaceEyebrowCenterLeft", "mFaceEyebrowInnerLeft",
"mFaceEyebrowOuterRight", "mFaceEyebrowCenterRight", "mFaceEyebrowInnerRight",
- "mFaceEyeLidUpperLeft", "mFaceEyeLidLowerLeft",
- "mFaceEyeLidUpperRight", "mFaceEyeLidLowerRight",
+ "mFaceEyeLidUpperLeft", "mFaceEyeLidLowerLeft", "mFaceEyecornerInnerLeft",
+ "mFaceEyeLidUpperRight", "mFaceEyeLidLowerRight", "mFaceEyecornerInnerRight",
"mFaceEar1Left", "mFaceEar1Right",
- "mFaceNoseLeft", "mFaceNoseCenter", "mFaceNoseRight",
+ "mFaceNoseBase", "mFaceNoseBridge", "mFaceNoseLeft", "mFaceNoseCenter", "mFaceNoseRight",
"mFaceCheekUpperLeft", "mFaceCheekLowerLeft",
"mFaceCheekUpperRight", "mFaceCheekLowerRight",
"mFaceJaw", "mFaceTeethUpper"
@@ -247,14 +249,18 @@ public:
{ "mEyeLeft", "mEyeRight", FACE, {}, "-0.036 0.079 0.098", "0.000 0.000 0.025" },
{ "mEyeRight", "mEyeLeft", FACE, {}, "0.036 0.079 0.098", "0.000 0.000 0.025", true },
{ "mFaceEyeLidUpperLeft", "mFaceEyeLidUpperRight", FACE, {}, "0.036 0.034 0.073", "0.000 0.005 0.027" },
+ { "mFaceEyecornerInnerLeft", "mFaceEyecornerInnerRight", FACE, {}, "0.032 0.075 0.017", "0.000 0.016 0.000" },
{ "mFaceEyeLidLowerLeft", "mFaceEyeLidLowerRight", FACE, {}, "0.036 0.034 0.073", "0.000 -0.007 0.024" },
{ "mFaceEyeLidUpperRight", "mFaceEyeLidUpperLeft", FACE, {}, "-0.036 0.034 0.073", "0.000 0.005 0.027", true },
+ { "mFaceEyecornerInnerRight", "mFaceEyecornerInnerLeft", FACE, {}, "0.032 0.075 -0.017", "0.000 0.016 0.000", true },
{ "mFaceEyeLidLowerRight", "mFaceEyeLidLowerLeft", FACE, {}, "-0.036 0.034 0.073", "0.000 -0.007 0.024", true },
{ "mFaceEar1Left", "mFaceEar1Right", FACE, { "mFaceEar2Left" }, "0.080 0.002 0.000", "" },
{ "mFaceEar2Left", "mFaceEar2Right", FACE, {}, "0.018 0.025 -0.019", "0.000 0.033 0.000" },
{ "mFaceEar1Right", "mFaceEar1Left", FACE, { "mFaceEar2Right" }, "-0.080 0.002 0.000", "", true },
{ "mFaceEar2Right", "mFaceEar2Left", FACE, {}, "-0.018 0.025 -0.019", "0.000 0.033 0.000", true },
+ { "mFaceNoseBase", "", FACE, {}, "-0.016 0.094 0.000", "0.000 0.014 0.000" },
+ { "mFaceNoseBridge", "", FACE, {}, "0.020 0.091 0.000", "0.008 0.015 0.000" },
{ "mFaceNoseLeft", "mFaceNoseRight", FACE, {}, "0.015 -0.004 0.086", "0.004 0.000 0.015" },
{ "mFaceNoseCenter", "", FACE, {}, "0.000 0.000 0.102", "0.000 0.000 0.025" },
{ "mFaceNoseRight", "mFaceNoseLeft", FACE, {}, "-0.015 -0.004 0.086", "-0.004 0.000 0.015", true },
@@ -343,11 +349,39 @@ public:
{ "mWing4Right", "mWing4Left", MISC, {}, "-0.173 0.000 -0.171", "-0.132 0.000 -0.146", true },
{ "mWing4FanRight", "mWing4FanLeft", MISC, {}, "-0.173 0.000 -0.171", "-0.062 -0.159 -0.068", true },
+ // Misc body parts
+ { "mSkull", "", MISC, {}, "0.079 0.000 0.000", "0.033 0.000 0.000" },
+ { "mSpine1", "", MISC, { "mSpine2" }, "0.084 0.000 0.000" },
+ { "mSpine2", "", MISC, { "mTorso", }, "-0.084 0.000 0.000" },
+ { "mSpine3", "", MISC, { "mSpine4" }, "0.205 -0.015 0.000" },
+ { "mSpine4", "", MISC, { "mChest", }, "-0.205 0.015 0.000" },
+
// Collision Volumes
+ { "HEAD", "", COL_VOLUMES },
+ { "NECK", "", COL_VOLUMES },
+ { "L_CLAVICLE", "R_CLAVICLE", COL_VOLUMES },
+ { "R_CLAVICLE", "L_CLAVICLE", COL_VOLUMES, {}, "", "", true },
+ { "CHEST", "", COL_VOLUMES },
{ "LEFT_PEC", "RIGHT_PEC", COL_VOLUMES },
{ "RIGHT_PEC", "LEFT_PEC", COL_VOLUMES, {}, "", "", true },
+ { "UPPER_BACK", "", COL_VOLUMES },
+ { "LEFT_HANDLE", "RIGHT_HANDLE", COL_VOLUMES },
+ { "RIGHT_HANDLE", "LEFT_HANDLE", COL_VOLUMES, {}, "", "", true },
{ "BELLY", "", COL_VOLUMES },
+ { "PELVIS", "", COL_VOLUMES },
{ "BUTT", "", COL_VOLUMES },
+ { "L_UPPER_ARM", "R_UPPER_ARM", COL_VOLUMES },
+ { "R_UPPER_ARM", "L_UPPER_ARM", COL_VOLUMES, {}, "", "", true },
+ { "L_LOWER_ARM", "R_LOWER_ARM", COL_VOLUMES },
+ { "R_LOWER_ARM", "L_LOWER_ARM", COL_VOLUMES, {}, "", "", true },
+ { "L_HAND", "R_HAND", COL_VOLUMES },
+ { "R_HAND", "L_HAND", COL_VOLUMES, {}, "", "", true },
+ { "L_UPPER_LEG", "R_UPPER_LEG", COL_VOLUMES },
+ { "R_UPPER_LEG", "L_UPPER_LEG", COL_VOLUMES, {}, "", "", true },
+ { "L_LOWER_LEG", "R_LOWER_LEG", COL_VOLUMES },
+ { "R_LOWER_LEG", "L_LOWER_LEG", COL_VOLUMES, {}, "", "", true },
+ { "L_FOOT", "R_FOOT", COL_VOLUMES },
+ { "R_FOOT", "L_FOOT", COL_VOLUMES, {}, "", "", true },
};
public:
diff --git a/indra/newview/fsprimfeedauth.cpp b/indra/newview/fsprimfeedauth.cpp
new file mode 100644
index 0000000000..c264c3b8dc
--- /dev/null
+++ b/indra/newview/fsprimfeedauth.cpp
@@ -0,0 +1,450 @@
+/**
+ * @file fsprimfeedauth.cpp
+ * @file fsprimfeedauth.h
+ * @brief Primfeed Authorisation workflow class
+ * @author beq@firestorm
+ * $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
+ * Phoenix Firestorm Viewer Source Code
+ * Copyright (C) 2025, Beq Janus
+ *
+ * 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
+ *
+ * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
+ * http://www.firestormviewer.org
+ * $/LicenseInfo$
+*/
+
+/*
+ * Handles Primfeed authentication and authorisation through a multi-factor OAuth flow.
+ *
+ * This module integrates with Primfeed’s Third Party Viewers API.
+ * The authentication flow is as follows:
+ * 1. Initiate a login request:
+ * POST https://api.primfeed.com/pf/viewer/create-login-request
+ * Headers:
+ * pf-viewer-api-key:
+ * pf-user-uuid:
+ * Response:
+ * { "requestId": "<64-char string>" }
+ *
+ * 2. Redirect the user to:
+ * https://www.primfeed.com/oauth/viewer?r=&v=
+ *
+ * 3. The user is shown an approval screen. When they click Authorize,
+ * an in-world message is sent:
+ * #PRIMFEED_OAUTH:
+ * We intercept this code through an onChat handle then call onOauthTokenReceived().
+ *
+ * 4. Validate the login request:
+ * POST https://api.primfeed.com/pf/viewer/validate-request
+ * Headers:
+ * Authorization: Bearer
+ * pf-viewer-api-key:
+ * pf-viewer-request-id:
+ * Response: HTTP 204
+ *
+ * 5. Optionally, check user status:
+ * GET https://api.primfeed.com/pf/viewer/user
+ * Headers:
+ * Authorization: Bearer
+ * pf-viewer-api-key:
+ * Response: { "plan": "free" } (or "pro")
+ */
+#include "llviewerprecompiledheaders.h"
+#include "fsprimfeedauth.h"
+#include "fsprimfeedconnect.h"
+#include "llimview.h"
+#include "llnotificationsutil.h"
+#include "llfloaterimnearbychathandler.h"
+#include "llnotificationmanager.h"
+#include "llagent.h"
+#include "llevents.h"
+#include "fscorehttputil.h"
+#include "llwindow.h"
+#include "llviewerwindow.h"
+#include "lluri.h"
+#include "llsdjson.h"
+#include
+
+using Callback = FSPrimfeedAuth::authorized_callback_t;
+
+// private instance variable
+std::shared_ptr FSPrimfeedAuth::sPrimfeedAuth;
+std::unique_ptr FSPrimfeedAuth::sPrimfeedAuthPump = std::make_unique("PrimfeedAuthResponse");
+
+// Helper callback that unpacks HTTP POST response data.
+void FSPrimfeedAuthResponse(LLSD const &aData, Callback callback)
+{
+ LLSD header = aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS][LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(
+ aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]);
+
+ const LLSD::Binary &rawData = aData[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary();
+ std::string result;
+ result.assign(rawData.begin(), rawData.end());
+
+ // Assume JSON response.
+
+ LLSD resultLLSD;
+ if(!result.empty())
+ {
+ resultLLSD = LlsdFromJson(boost::json::parse(result));
+ }
+ callback((status.getType() == HTTP_OK ||
+ status.getType() == HTTP_NO_CONTENT), resultLLSD);
+}
+
+void FSPrimfeedAuth::initiateAuthRequest()
+{
+ // This function is called to initiate the authentication request.
+ // It should be called when the user clicks the "Authenticate" button.
+ // Also triggered on opening the floater.
+ // The actual implementation is in the create() method.
+
+ if (!isAuthorized())
+ {
+ if (sPrimfeedAuth)
+ {
+ LLNotificationsUtil::add("PrimfeedAuthorizationAlreadyInProgress");
+ return;
+ }
+ // If no token stored, begin the login request; otherwise check user status.
+ sPrimfeedAuth = FSPrimfeedAuth::create(
+ [](bool success, const LLSD &response)
+ {
+ LLSD event_data = response;
+ event_data["success"] = success;
+ sPrimfeedAuthPump->post(event_data);
+ // Now that auth is complete, clear the static pointer.
+ sPrimfeedAuth.reset();
+ }
+ );
+ FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTING);
+ }
+ else
+ {
+ LLNotificationsUtil::add("PrimfeedAlreadyAuthorized");
+ }
+}
+
+void FSPrimfeedAuth::resetAuthStatus()
+{
+ sPrimfeedAuth.reset();
+ gSavedPerAccountSettings.setString("FSPrimfeedOAuthToken", "");
+ gSavedPerAccountSettings.setString("FSPrimfeedProfileLink", "");
+ gSavedPerAccountSettings.setString("FSPrimfeedPlan", "");
+ gSavedPerAccountSettings.setString("FSPrimfeedUsername", "");
+ LLSD event_data;
+ event_data["status"] = "reset";
+ event_data["success"] = "false";
+ sPrimfeedAuthPump->post(event_data);
+ FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_DISCONNECTED);
+}
+
+
+FSPrimfeedAuth::FSPrimfeedAuth(authorized_callback_t callback)
+ : mCallback(callback)
+{
+ mChatMessageConnection = LLNotificationsUI::LLNotificationManager::instance().getChatHandler()->addNewChatCallback(
+ [this](const LLSD &message) {
+ LL_DEBUGS("FSPrimfeedAuth") << "Received chat message: " << message["message"].asString() << LL_ENDL;
+ this->onChatMessage(message);
+ });
+}
+
+FSPrimfeedAuth::~FSPrimfeedAuth()
+{
+ if (mChatMessageConnection.connected())
+ {
+ try
+ {
+ mChatMessageConnection.disconnect();
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS("FSPrimfeedAuth") << "Exception during chat connection disconnect: " << e.what() << LL_ENDL;
+ }
+ catch (...)
+ {
+ LL_WARNS("FSPrimfeedAuth") << "Unknown exception during chat connection disconnect." << LL_ENDL;
+ }
+ }
+}
+
+// Factory method to create a shared pointer to FSPrimfeedAuth.
+std::shared_ptr FSPrimfeedAuth::create(authorized_callback_t callback)
+{
+ // Ensure only one authentication attempt is in progress.
+ if (sPrimfeedAuth)
+ {
+ // Already in progress; return the existing instance.
+ return sPrimfeedAuth;
+ }
+ auto auth = std::shared_ptr(new FSPrimfeedAuth(callback));
+ if(!auth)
+ {
+ return nullptr;
+ }
+
+ FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTING);
+
+ // If no token stored, begin the login request; otherwise check user status.
+ if (gSavedPerAccountSettings.getString("FSPrimfeedOAuthToken").empty())
+ {
+ auth->beginLoginRequest();
+ }
+ else
+ {
+ auth->checkUserStatus();
+ }
+ return auth;
+}
+
+void FSPrimfeedAuth::beginLoginRequest()
+{
+ // Get our API key and user UUID.
+ std::string viewer_api_key = gSavedSettings.getString("FSPrimfeedViewerApiKey");
+ std::string user_uuid = gAgent.getID().asString();
+
+ std::string url = "https://api.primfeed.com/pf/viewer/create-login-request";
+ std::string post_data = ""; // No body parameters required.
+
+ // Create the headers object.
+ LLCore::HttpHeaders::ptr_t pHeader(new LLCore::HttpHeaders());
+ LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions());
+
+ pHeader->append("pf-viewer-api-key", viewer_api_key);
+ pHeader->append("pf-user-uuid", user_uuid);
+
+ // Set up HTTP options
+ options->setWantHeaders(true);
+ options->setRetries(0);
+ options->setTimeout(PRIMFEED_CONNECT_TIMEOUT);
+
+ // Capture shared_ptr to self
+ auto self = shared_from_this();
+
+ const auto end(pHeader->end());
+ for (auto it(pHeader->begin()); end != it; ++it)
+ {
+ LL_DEBUGS("Primfeed") << "Header: " << it->first << " = " << it->second << LL_ENDL;
+ }
+
+ // Pass both success and failure callbacks
+ FSCoreHttpUtil::callbackHttpPostRaw(
+ url,
+ post_data,
+ [self](LLSD const &aData) {
+ LL_DEBUGS("FSPrimfeedAuth") << "Login request response(OK): " << aData << LL_ENDL;
+ FSPrimfeedAuthResponse(aData,
+ [self](bool success, const LLSD &response) {
+ self->gotRequestId(success, response);
+ }
+ );
+ },
+ [self](LLSD const &aData) {
+ LL_DEBUGS("FSPrimfeedAuth") << "Login request response(FAIL): " << aData << LL_ENDL;
+ FSPrimfeedAuthResponse(aData,
+ [self](bool success, const LLSD &response) {
+ self->gotRequestId(success, response);
+ }
+ );
+ },
+ pHeader,
+ options
+ );
+}
+
+void FSPrimfeedAuth::gotRequestId(bool success, const LLSD &response)
+{
+ if (!success)
+ {
+ LLNotificationsUtil::add("PrimfeedLoginRequestFailed");
+ mCallback(false, LLSD());
+ return;
+ }
+ mRequestId = response["requestId"].asString();
+ if (mRequestId.empty())
+ {
+ LLNotificationsUtil::add("PrimfeedLoginRequestFailed");
+ mCallback(false, LLSD());
+ return;
+ }
+ // Open the browser for user approval.
+ std::string viewer_api_key = gSavedSettings.getString("FSPrimfeedViewerApiKey");
+ std::string auth_url = "https://www.primfeed.com/oauth/viewer?r=" + mRequestId + "&v=" + viewer_api_key;
+ gViewerWindow->getWindow()->spawnWebBrowser(auth_url, true);
+
+}
+
+/// This function is called by the chat interceptor when the message
+/// "#PRIMFEED_OAUTH: " is intercepted.
+void FSPrimfeedAuth::onOauthTokenReceived(const std::string_view& oauth_token)
+{
+ if (oauth_token.empty())
+ {
+ mCallback(false, LLSD());
+ return;
+ }
+ mOauthToken = oauth_token;
+ validateRequest();
+}
+
+void FSPrimfeedAuth::onChatMessage(const LLSD& message)
+{
+ constexpr std::string_view oauth_msg_prefix = "#PRIMFEED_OAUTH: ";
+ const std::string msg = message["message"].asString();
+ if (msg.find(std::string(oauth_msg_prefix)) == 0)
+ {
+ std::string_view oauth_token(msg.data() + oauth_msg_prefix.size(), msg.size() - oauth_msg_prefix.size());
+ LL_DEBUGS("Primfeed") << "Received OAuth token: " << msg << "extracted:<" << oauth_token << ">" << LL_ENDL;
+ onOauthTokenReceived(oauth_token);
+ }
+}
+
+
+void FSPrimfeedAuth::validateRequest()
+{
+ // No POST body needed.
+ std::string post_data = "";
+ std::string url = "https://api.primfeed.com/pf/viewer/validate-request";
+
+ // Retrieve the viewer API key.
+ std::string viewer_api_key = gSavedSettings.getString("FSPrimfeedViewerApiKey");
+
+ // Create and populate the headers.
+ LLCore::HttpHeaders::ptr_t pHeader(new LLCore::HttpHeaders());
+ pHeader->append("Authorization", "Bearer " + mOauthToken);
+ pHeader->append("pf-viewer-api-key", viewer_api_key);
+ pHeader->append("pf-viewer-request-id", mRequestId);
+
+ // Set HTTP options
+ LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions());
+ options->setWantHeaders(true);
+ options->setRetries(0);
+ options->setTimeout(PRIMFEED_CONNECT_TIMEOUT);
+
+ // print out pHeader for debuging using iterating over pHeader and using LL_DEBUGS
+ const auto end(pHeader->end());
+ for (auto it(pHeader->begin()); end != it; ++it)
+ {
+ LL_DEBUGS("Primfeed") << "Header: " << it->first << " = " << it->second << LL_ENDL;
+ }
+
+ auto self = shared_from_this();
+ try
+ {
+ FSCoreHttpUtil::callbackHttpPostRaw(
+ url,
+ post_data,
+ [self](LLSD const &aData) {
+ LL_DEBUGS("FSPrimfeedAuth") << "Validation-request response(OK): " << aData << LL_ENDL;
+ FSPrimfeedAuthResponse(aData,
+ [self](bool success, const LLSD &response) {
+ self->gotValidateResponse(success, response);
+ }
+ );
+ },
+ [self](LLSD const &aData) {
+ LL_INFOS("FSPrimfeedAuth") << "Validation-request response(FAIL): " << aData << LL_ENDL;
+ FSPrimfeedAuthResponse(aData,
+ [self](bool success, const LLSD &response) {
+ self->gotValidateResponse(success, response);
+ }
+ );
+ },
+ pHeader,
+ options
+ );
+ }
+ catch(const std::exception& e)
+ {
+ LL_WARNS("Primfeed") << "Primfeed validation failed " << e.what() << LL_ENDL;
+ }
+
+}
+
+
+void FSPrimfeedAuth::gotValidateResponse(bool success, const LLSD &response)
+{
+ if (!success)
+ {
+ LLNotificationsUtil::add("PrimfeedValidateFailed");
+ mCallback(false, response);
+ return;
+ }
+ checkUserStatus();
+}
+
+void FSPrimfeedAuth::checkUserStatus()
+{
+ std::string viewer_api_key = gSavedSettings.getString("FSPrimfeedViewerApiKey");
+
+ // Build the base URL without query parameters.
+ std::string url = "https://api.primfeed.com/pf/viewer/user";
+ LL_DEBUGS("Primfeed") << "URL: " << url << LL_ENDL;
+
+ // Create and populate the headers.
+ LLCore::HttpHeaders::ptr_t pHeader(new LLCore::HttpHeaders());
+ pHeader->append("Authorization", "Bearer " + mOauthToken);
+ pHeader->append("pf-viewer-api-key", viewer_api_key);
+
+ // Set HTTP options.
+ LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions());
+ options->setWantHeaders(true);
+ options->setRetries(0);
+ options->setTimeout(PRIMFEED_CONNECT_TIMEOUT);
+
+ // Make the HTTP GET request, passing in the headers and options.
+ FSCoreHttpUtil::callbackHttpGetRaw(
+ url,
+ [this](LLSD const &aData) {
+ LL_DEBUGS("FSPrimfeedAuth") << "Check-user-status response: " << aData << LL_ENDL;
+ FSPrimfeedAuthResponse(aData, [this](bool success, const LLSD &response) {
+ this->gotUserStatus(success, response);
+ });
+ },
+ [this](LLSD const &aData) {
+ LL_INFOS("FSPrimfeedAuth") << "Check-user-status response (failure): " << aData << LL_ENDL;
+ // Optionally, call the same processing for failure or handle separately.
+ FSPrimfeedAuthResponse(aData, [this](bool success, const LLSD &response){
+ this->gotUserStatus(success, response);
+ });
+ },
+ pHeader,
+ options
+ );
+}
+
+
+void FSPrimfeedAuth::gotUserStatus(bool success, const LLSD &response)
+{
+ LL_INFOS("Primfeed") << "User status: " << response << "(" << success << ")" << LL_ENDL;
+ if (success && response.has("plan"))
+ {
+ gSavedPerAccountSettings.setString("FSPrimfeedOAuthToken", mOauthToken);
+ gSavedPerAccountSettings.setString("FSPrimfeedPlan", response["plan"].asString());
+ gSavedPerAccountSettings.setString("FSPrimfeedProfileLink", response["link"].asString());
+ gSavedPerAccountSettings.setString("FSPrimfeedUsername", response["username"].asString());
+ FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_CONNECTED);
+ mCallback(true, response);
+ }
+ else
+ {
+ LLNotificationsUtil::add("PrimfeedUserStatusFailed");
+ FSPrimfeedConnect::instance().setConnectionState(FSPrimfeedConnect::PRIMFEED_DISCONNECTED);
+ mCallback(false, response);
+ }
+}
diff --git a/indra/newview/fsprimfeedauth.h b/indra/newview/fsprimfeedauth.h
new file mode 100644
index 0000000000..43fc36a937
--- /dev/null
+++ b/indra/newview/fsprimfeedauth.h
@@ -0,0 +1,92 @@
+/**
+* @file fsprimfeedauth.h
+* @brief Primfeed Authorisation workflow class
+* @author beq@firestorm
+*
+ * $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
+ * Phoenix Firestorm Viewer Source Code
+ * Copyright (C) 2025, Beq Janus
+ *
+ * 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
+ *
+ * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
+ * http://www.firestormviewer.org
+ * $/LicenseInfo$
+*/
+#ifndef FSPRIMFEEDAUTH_H
+#define FSPRIMFEEDAUTH_H
+
+#include "llsd.h"
+#include "llviewercontrol.h"
+#include
+#include
+
+/*
+* Primfeed authentication workflow class.
+*
+* This class handles the Primfeed OAuth login flow and provides methods to
+* check the user status and receive a callback when the authentication
+* process is complete.
+* based on the workflow documented at https://docs.primfeed.com/api/third-party-viewers
+*/
+class FSPrimfeedAuth : public std::enable_shared_from_this
+{
+public:
+ // Callback type: first parameter indicates success and the second holds any LLSD response.
+ using authorized_callback_t = std::function;
+ static std::shared_ptr create(authorized_callback_t callback);
+ static std::unique_ptr sPrimfeedAuthPump;
+ ~FSPrimfeedAuth();
+
+ // Should be called by the chat interceptor when an oauth token is received.
+ void onOauthTokenReceived(const std::string_view& oauth_token);
+ void onInstantMessage(const LLSD& message);
+ void onChatMessage(const LLSD& message);
+
+ // Begin the login request flow.
+ void beginLoginRequest();
+ // Check the user status.
+ void checkUserStatus();
+ static bool isPendingAuth(){ return (sPrimfeedAuth != nullptr); }
+ static bool isAuthorized(){ return (!gSavedPerAccountSettings.getString("FSPrimfeedOAuthToken").empty()); }
+ static void initiateAuthRequest();
+ static void resetAuthStatus();
+
+private:
+ static std::shared_ptr sPrimfeedAuth;
+
+ explicit FSPrimfeedAuth(authorized_callback_t callback);
+ authorized_callback_t mCallback;
+ std::string mOauthToken;
+ std::string mRequestId;
+
+ // Callback when a login request response is received.
+ void gotRequestId(bool success, const LLSD &response);
+ // Validate the login request.
+ void validateRequest();
+ // Callback when the validate response is received.
+ void gotValidateResponse(bool success, const LLSD &response);
+ // Callback when the user status response is received.
+ void gotUserStatus(bool success, const LLSD &response);
+
+ boost::signals2::connection mInstantMessageConnection;
+ boost::signals2::connection mChatMessageConnection;
+ // Static flag to prevent duplicate authentication attempts.
+ static std::atomic sAuthorisationInProgress;
+
+ static constexpr U32 PRIMFEED_CONNECT_TIMEOUT = 300; // 5 minute timeout should work
+};
+
+#endif // FSPRIMFEEDAUTH_H
\ No newline at end of file
diff --git a/indra/newview/fsprimfeedconnect.cpp b/indra/newview/fsprimfeedconnect.cpp
new file mode 100644
index 0000000000..f6d91480a2
--- /dev/null
+++ b/indra/newview/fsprimfeedconnect.cpp
@@ -0,0 +1,189 @@
+/**
+* @file fsprimfeedconnect.cpp
+* @brief Primfeed connector class
+* @author beq@firestorm
+*
+ * $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
+ * Phoenix Firestorm Viewer Source Code
+ * Copyright (C) 2025, Beq Janus
+ *
+ * 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
+ *
+ * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
+ * http://www.firestormviewer.org
+ * $/LicenseInfo$
+*/
+#include "fsprimfeedconnect.h"
+#include "fsprimfeedauth.h"
+#include "llviewercontrol.h"
+#include "llcoros.h"
+#include "llsdjson.h"
+
+// The connector workflow for Primfeed is realtively simple and mostly just builds on top of the established Auth workflow
+// and the posting endpoint documented at https://docs.primfeed.com/api/third-party-viewers#creating-a-post
+
+FSPrimfeedConnect::FSPrimfeedConnect() = default;
+
+void FSPrimfeedConnect::uploadPhoto(const LLSD& params, LLImageFormatted* image, post_callback_t callback)
+{
+ LL_DEBUGS("primfeed") << "uploadPhoto() called" << LL_ENDL;
+ if (!FSPrimfeedAuth::isAuthorized())
+ {
+ LL_WARNS("primfeed") << "Authorization failed, aborting.\n" << LL_ENDL;
+ callback(false, "");
+ return;
+ }
+ LL_DEBUGS("primfeed") << "Authorization successful" << LL_ENDL;
+
+ mPostCallback = callback;
+ LL_DEBUGS("primfeed") << "Launching upload coroutine" << LL_ENDL;
+ LLCoros::instance().launch(
+ "FSPrimfeedConnect::uploadPhotoCoro",
+ [this, params, image]() { uploadPhotoCoro(params, image); }
+ );
+}
+
+void FSPrimfeedConnect::uploadPhotoCoro(const LLSD& params, LLImageFormatted* image)
+{
+ LL_DEBUGS("primfeed") << "Entered uploadPhotoCoro" << LL_ENDL;
+ setConnectionState(PRIMFEED_POSTING);
+ LL_DEBUGS("primfeed") << "Connection state set to PRIMFEED_POSTING" << LL_ENDL;
+
+ const std::string fmt = (image->getCodec() == EImageCodec::IMG_CODEC_JPEG) ? "jpg" : "png";
+ LL_DEBUGS("primfeed") << "Image format: " << fmt << LL_ENDL;
+
+ const std::string boundary = "----------------------------0123456789abcdef";
+ const std::string sep = "\n";
+ const std::string dash = "--" + boundary;
+
+ LL_DEBUGS("primfeed") << "Building multipart body" << LL_ENDL;
+ LLCore::BufferArray::ptr_t raw(new LLCore::BufferArray());
+ LLCore::BufferArrayStream body(raw.get());
+ auto addPart = [&](const std::string& name, const std::string& val)
+ {
+ LL_DEBUGS("primfeed") << "Adding part: " << name << "=" << val << LL_ENDL;
+ body << dash << sep
+ << "Content-Disposition: form-data; name=\"" << name << "\"" << sep << sep
+ << val << sep;
+ };
+
+ addPart("commercial", params["commercial"].asBoolean() ? "true" : "false");
+ addPart("rating", params["rating"].asString());
+ addPart("content", params["content"].asString());
+ addPart("publicGallery", params["post_to_public_gallery"].asBoolean()? "true" : "false");
+
+ if (params.has("location") && !params["location"].asString().empty())
+ {
+ addPart("location", params["location"].asString());
+ }
+
+ LL_DEBUGS("primfeed") << "Adding image file header" << LL_ENDL;
+ body << dash << sep
+ << "Content-Disposition: form-data; name=\"image\"; filename=\"snapshot." << fmt << "\"" << sep
+ << "Content-Type: image/" << fmt << sep << sep;
+
+ U8* data = image->getData();
+ S32 size = image->getDataSize();
+ LL_DEBUGS("primfeed") << "Appending image data, size=" << size << LL_ENDL;
+ // yep this seems inefficient, but all other occurrences in the codebase do it this way.
+ for (S32 i = 0; i < size; ++i)
+ {
+ body << data[i];
+ }
+ body << sep;
+
+ body << dash << "--" << sep;
+ LL_DEBUGS("primfeed") << "Multipart body ready" << LL_ENDL;
+
+ // Setup HTTP
+ LL_DEBUGS("primfeed") << "Preparing HTTP request" << LL_ENDL;
+ LLCore::HttpRequest::policy_t policy = LLCore::HttpRequest::DEFAULT_POLICY_ID;
+ LLCoreHttpUtil::HttpCoroutineAdapter adapter("PrimfeedUpload", policy);
+ LLCore::HttpRequest::ptr_t request(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t options(new LLCore::HttpOptions);
+ options->setWantHeaders(true);
+
+ LL_DEBUGS("primfeed") << "Setting HTTP headers" << LL_ENDL;
+ LLCore::HttpHeaders::ptr_t headers(new LLCore::HttpHeaders);
+ std::string token = gSavedPerAccountSettings.getString("FSPrimfeedOAuthToken");
+ std::string apiKey = gSavedSettings.getString("FSPrimfeedViewerApiKey");
+ headers->append("Authorization", "Bearer " + token);
+ headers->append("pf-viewer-api-key", apiKey);
+ headers->append("Content-Type", "multipart/form-data; boundary=" + boundary);
+ LL_DEBUGS("primfeed") << "Dumping HTTP headers for POST:" << LL_ENDL;
+ for (auto it = headers->begin(); it != headers->end(); ++it)
+ {
+ LL_DEBUGS("primfeed") << it->first << ": " << it->second << LL_ENDL;
+ }
+ LL_DEBUGS("primfeed") << "Headers set" << LL_ENDL;
+
+ LL_DEBUGS("primfeed") << "Starting HTTP POST" << LL_ENDL;
+ LLSD result = adapter.postRawAndSuspend(request,
+ "https://api.primfeed.com/pf/viewer/post",
+ raw,
+ options,
+ headers);
+ LL_DEBUGS("primfeed") << "HTTP POST complete" << LL_ENDL;
+
+ const LLSD::Binary &rawData = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary();
+ std::string response_raw;
+ response_raw.assign(rawData.begin(), rawData.end());
+ LLSD result_LLSD;
+ if(!response_raw.empty())
+ {
+ result_LLSD = LlsdFromJson(boost::json::parse(response_raw));
+ }
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]);
+ bool success = (status.getType() == HTTP_OK);
+ LL_DEBUGS("primfeed") << "HTTP status =" << (success?"OK":"FAIL") << " "<< status.getMessage() << LL_ENDL;
+
+ std::string url;
+ if (success)
+ {
+ url = result_LLSD["url"].asString();
+ LL_DEBUGS("primfeed") << "Received URL=" << url << LL_ENDL;
+ }
+
+ LL_DEBUGS("primfeed") << "Invoking callback" << LL_ENDL;
+ mPostCallback(success, url);
+ setConnectionState(success ? PRIMFEED_POSTED : PRIMFEED_POST_FAILED);
+ LL_DEBUGS("primfeed") << "Final state set" << LL_ENDL;
+}
+
+// Handle connection state transitions
+void FSPrimfeedConnect::setConnectionState(EConnectionState state)
+{
+ LL_DEBUGS("primfeed") << "setConnectionState(" << state << ")" << LL_ENDL;
+ mConnectionState = state;
+}
+
+FSPrimfeedConnect::EConnectionState FSPrimfeedConnect::getConnectionState() const
+{
+ return mConnectionState;
+}
+
+bool FSPrimfeedConnect::isTransactionOngoing() const
+{
+ return (mConnectionState == PRIMFEED_CONNECTING ||
+ mConnectionState == PRIMFEED_POSTING ||
+ mConnectionState == PRIMFEED_DISCONNECTING);
+}
+
+void FSPrimfeedConnect::loadPrimfeedInfo()
+{
+ LL_DEBUGS("primfeed") << "loadPrimfeedInfo() called" << LL_ENDL;
+ // Nothing to do here for Primfeed
+ setConnectionState(PRIMFEED_CONNECTED);
+}
\ No newline at end of file
diff --git a/indra/newview/fsprimfeedconnect.h b/indra/newview/fsprimfeedconnect.h
new file mode 100644
index 0000000000..409a93faed
--- /dev/null
+++ b/indra/newview/fsprimfeedconnect.h
@@ -0,0 +1,82 @@
+/**
+* @file fsprimfeedconect.h
+* @brief Primfeed connector class
+* @author beq@firestorm
+*
+ * $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
+ * Phoenix Firestorm Viewer Source Code
+ * Copyright (C) 2025, Beq Janus
+ *
+ * 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
+ *
+ * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
+ * http://www.firestormviewer.org
+ * $/LicenseInfo$
+*/
+#ifndef FS_PRIMFEEDCONNECT_H
+#define FS_PRIMFEEDCONNECT_H
+
+#include "llsingleton.h"
+#include "llsd.h"
+#include "llimage.h"
+#include "fsprimfeedauth.h"
+#include "llcorehttputil.h"
+#include "bufferarray.h"
+#include "llcoros.h"
+#include "llviewercontrol.h" // for gSavedSettings/gSavedPerAccountSettings
+#include
+
+// Coro based connector designed to interface with floater designed along the same principles as LLFloaterFlickr.cpp
+
+class FSPrimfeedConnect : public LLSingleton
+{
+ LLSINGLETON(FSPrimfeedConnect);
+public:
+ // Connection states for Primfeed operations
+ enum EConnectionState
+ {
+ PRIMFEED_DISCONNECTED = 0,
+ PRIMFEED_CONNECTING,
+ PRIMFEED_CONNECTED,
+ PRIMFEED_POSTING,
+ PRIMFEED_POSTED,
+ PRIMFEED_POST_FAILED,
+ PRIMFEED_DISCONNECTING
+ };
+
+ // Callback invoked on post completion: success flag and URL (empty on failure)
+ using post_callback_t = std::function;
+
+ // Posts a snapshot to Primfeed; requires FSPrimfeedAuth::isAuthorized()
+ void uploadPhoto(const LLSD& params, LLImageFormatted* image, post_callback_t callback);
+
+ // Retrieve and update account info from Primfeed (not used kept for compatibility)
+ void loadPrimfeedInfo();
+
+ void setConnectionState(EConnectionState state);
+ EConnectionState getConnectionState() const;
+ bool isTransactionOngoing() const;
+
+private:
+ // Internal coroutine entry-point for uploads
+ void uploadPhotoCoro(const LLSD& params, LLImageFormatted* image);
+
+ // Cached callback until coroutine completes
+ post_callback_t mPostCallback;
+
+ // Current connection/post state
+ EConnectionState mConnectionState = PRIMFEED_DISCONNECTED;
+};
+#endif // FS_PRIMFEEDCONNECT_H
\ No newline at end of file
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 1379b2923e..9f2ac58d2d 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -235,6 +235,7 @@
#include "llfloatersimplesnapshot.h"
#include "llfloatersnapshot.h"
#include "llfloaterflickr.h"
+#include "fsfloaterprimfeed.h" // Primfeed Floater
#include "llsidepanelinventory.h"
#include "llatmosphere.h"
@@ -1751,6 +1752,7 @@ bool LLAppViewer::doFrame()
LLFloaterSnapshot::update(); // take snapshots
LLFloaterSimpleSnapshot::update();
LLFloaterFlickr::update(); // FIRE-35002 - Flickr preview not updating whne opened directly from tool tray icon
+ FSFloaterPrimfeed::update(); // Primfeed support
gGLActive = false;
}
@@ -3795,6 +3797,7 @@ bool LLAppViewer::waitForUpdater()
void LLAppViewer::writeDebugInfo(bool isStatic)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING; // improve instrumentation
#if LL_WINDOWS && LL_BUGSPLAT
// Improve Bugsplat tracking by using attributes for certain static data items.
const LLSD& info = getViewerInfo();
@@ -3823,6 +3826,7 @@ void LLAppViewer::writeDebugInfo(bool isStatic)
LLSD LLAppViewer::getViewerInfo() const
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING; // improve instrumentation
// The point of having one method build an LLSD info block and the other
// construct the user-visible About string is to ensure that the same info
// is available to a getInfo() caller as to the user opening
@@ -4135,6 +4139,7 @@ LLSD LLAppViewer::getViewerInfo() const
// info["DISK_CACHE_INFO"] = LLDiskCache::getInstance()->getCacheInfo();
if (auto cache = LLDiskCache::getInstance(); cache)
{
+ LL_PROFILE_ZONE_NAMED("gvi-getCacheInfo"); // improve instrumentation
info["DISK_CACHE_INFO"] = cache->getCacheInfo();
}
//
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 5fb5bece82..8d5e26caa1 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -644,11 +644,13 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
// Use the Attributes API on Windows to enhance crash metadata
void LLAppViewerWin32::bugsplatAddStaticAttributes(const LLSD& info)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING;
#ifdef LL_BUGSPLAT
auto& bugSplatMap = BugSplatAttributes::instance();
static bool write_once_after_startup = false;
if (!write_once_after_startup )
{
+ LL_PROFILE_ZONE_NAMED("bs-st-att-once")
// Only write the attributes that are fixed once after we've started.
// note we might update them more than once and some/many may be empty during startup as we want to catch early crashes
// once we're started we can assume they don't change for this run.
diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp
index 5e3f1c09ae..a668f0957a 100644
--- a/indra/newview/llfloaterimnearbychathandler.cpp
+++ b/indra/newview/llfloaterimnearbychathandler.cpp
@@ -670,7 +670,14 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
}
}
-
+ // Hide Primfeed OAuth message from chat to prevent accidental leak of secret.
+ const std::string primfeed_oauth = "#PRIMFEED_OAUTH: ";
+ if( chat_msg.mText.compare(0, primfeed_oauth.length(), primfeed_oauth) == 0 && chat_msg.mChatType == CHAT_TYPE_IM && chat_msg.mSourceType == CHAT_SOURCE_OBJECT )
+ {
+ // Don't show the message in chat.
+ return;
+ }
+ //
nearby_chat->addMessage(chat_msg, true, args);
if (chat_msg.mSourceType == CHAT_SOURCE_AGENT
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index 31e0c9841a..5e30ae8adb 100644
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -30,6 +30,7 @@
#include "llfloaterreg.h"
#include "llfloaterflickr.h" // Share to Flickr
+#include "fsfloaterprimfeed.h" // Share to Primfeed
#include "llimagefiltersmanager.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
@@ -1463,12 +1464,12 @@ bool LLFloaterSnapshot::isWaitingState()
// FIRE-35002 - Post to flickr broken, improved solution
// bool LLFloaterSnapshotBase::ImplBase::updatePreviewList(bool initialized)
-bool LLFloaterSnapshotBase::ImplBase::updatePreviewList(bool initialized, bool have_flickr)
+bool LLFloaterSnapshotBase::ImplBase::updatePreviewList(bool initialized, bool have_socials)
//
{
// Share to Flickr
//if (!initialized)
- if (!initialized && !have_flickr)
+ if (!initialized && !have_socials)
//
return false;
@@ -1487,16 +1488,18 @@ void LLFloaterSnapshotBase::ImplBase::updateLivePreview()
{
// don't update preview for hidden floater
// FIRE-35002 - Post to flickr broken
- LLFloaterFlickr* floater_flickr = LLFloaterReg::findTypedInstance("flickr");
- auto have_flickr = floater_flickr != nullptr;
+ bool have_socials = (
+ LLFloaterReg::findTypedInstance("flickr") != nullptr ||
+ LLFloaterReg::findTypedInstance("primfeed") != nullptr
+ );
if ( ((mFloater && mFloater->isInVisibleChain()) ||
- have_flickr) &&
- ImplBase::updatePreviewList(true, have_flickr))
+ have_socials) &&
+ ImplBase::updatePreviewList(true, have_socials))
//
{
LL_DEBUGS() << "changed" << LL_ENDL;
updateControls(mFloater);
- }
+ }
}
//static
diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h
index 9b47fd1a9d..d73f766185 100644
--- a/indra/newview/llfloatersnapshot.h
+++ b/indra/newview/llfloatersnapshot.h
@@ -122,7 +122,7 @@ public:
virtual EStatus getStatus() const { return mStatus; }
virtual void setNeedRefresh(bool need);
- static bool updatePreviewList(bool initialized, bool have_flickr = false); // FIRE-35002 - Post to flickr broken, improved solution
+ static bool updatePreviewList(bool initialized, bool have_socials = false); // FIRE-35002 - Post to flickr broken, improved solution
void setAdvanced(bool advanced) { mAdvanced = advanced; }
void setSkipReshaping(bool skip) { mSkipReshaping = skip; }
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 48e305b4c4..3db291ed40 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -3943,16 +3943,55 @@ LLUUID LLIMMgr::addSession(
//works only for outgoing ad-hoc sessions
if (new_session &&
((IM_NOTHING_SPECIAL == dialog) || (IM_SESSION_P2P_INVITE == dialog) || (IM_SESSION_CONFERENCE_START == dialog)) &&
- ids.size())
+ // [FIRE-34494] fix unable to open an IM with someone who started a group chat
+ //ids.size())
+ !ids.empty())
+ //
{
session = LLIMModel::getInstance()->findAdHocIMSession(ids);
if (session)
{
- new_session = false;
- session_id = session->mSessionID;
+// [FIRE-34494] fix unable to open an IM with someone who started a group chat
+ // new_session = false;
+ // session_id = session->mSessionID;
+
+ // Protect against wrong session type reuse (e.g., conference reused for IM)
+ if (session->mType != dialog)
+ {
+ LL_WARNS("IMVIEW") << "Discarding mismatched session type reuse: expected "
+ << dialog << " but found " << session->mType
+ << " for session " << session->mSessionID
+ << ". This may indicate improper reuse of a session object." << LL_ENDL;
+ session = nullptr;
+ new_session = true;
+ session_id = computeSessionID(dialog, other_participant_id);
+ }
+ else
+ {
+ new_session = false;
+ session_id = session->mSessionID;
+ }
}
}
+ if (session && session->mType != dialog)
+ {
+ // Prevent reusing a session of the wrong type
+ session = nullptr;
+ new_session = true;
+
+ // Recompute session ID depending on dialog type
+ if (dialog == IM_SESSION_CONFERENCE_START)
+ {
+ session_id.generate();
+ }
+ else
+ {
+ session_id = computeSessionID(dialog, other_participant_id);
+ }
+//
+ }
+
//Notify observers that a session was added
if (new_session)
{
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 228aa5bd1e..f29bc9782b 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -346,6 +346,15 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)
LL_DEBUGS("LLLogin") << "reason " << reason_response
<< " message " << message_response
<< LL_ENDL;
+
+ if (response.has("mfa_hash"))
+ {
+ mRequestData["params"]["mfa_hash"] = response["mfa_hash"];
+ mRequestData["params"]["token"] = "";
+
+ saveMFAHash(response);
+ }
+
// For the cases of critical message or TOS agreement,
// start the TOS dialog. The dialog response will be handled
// by the LLLoginInstance::handleTOSResponse() callback.
@@ -609,6 +618,24 @@ bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & respon
return true;
}
+void LLLoginInstance::saveMFAHash(LLSD const& response)
+{
+ std::string grid(LLGridManager::getInstance()->getGridId());
+ std::string user_id(LLStartUp::getUserId());
+
+ // Only save mfa_hash for future logins if the user wants their info remembered.
+ if (response.has("mfa_hash") && gSavedSettings.getBOOL("RememberUser") && LLLoginInstance::getInstance()->saveMFA())
+ {
+ gSecAPIHandler->addToProtectedMap("mfa_hash", grid, user_id, response["mfa_hash"]);
+ }
+ else if (!LLLoginInstance::getInstance()->saveMFA())
+ {
+ gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id);
+ }
+ // TODO(brad) - related to SL-17223 consider building a better interface that sync's automatically
+ gSecAPIHandler->syncProtectedMap();
+}
+
std::string construct_start_string()
{
std::string start;
diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h
index e5cb14b262..3634dc39bc 100644
--- a/indra/newview/lllogininstance.h
+++ b/indra/newview/lllogininstance.h
@@ -73,6 +73,8 @@ public:
void setNotificationsInterface(LLNotificationsInterface* ni) { mNotifications = ni; }
LLNotificationsInterface& getNotificationsInterface() const { return *mNotifications; }
+ void saveMFAHash(LLSD const& response);
+
private:
typedef std::shared_ptr ResponsePtr;
void constructAuthParams(LLPointer user_credentials);
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 973e3cc9e1..fc23e98b72 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -6215,6 +6215,19 @@ bool LLMeshRepository::meshUploadEnabled()
bool LLMeshRepository::meshRezEnabled()
{
static LLCachedControl mesh_enabled(gSavedSettings, "MeshEnabled");
+// FIRE-35602 etc - Mesh not appearing after TP/login (opensim only)
+// For OpenSim there is still an outside chance that mesh rezzing is disabled on the sim/region
+// restore the old behaviour but keep the bias to mesh_enabled == true in the underlying checks.
+#ifdef OPENSIM
+ if (LLGridManager::instance().isInOpenSim())
+ {
+ if (LLViewerRegion* region = gAgent.getRegion(); mesh_enabled && region)
+ {
+ return region->meshRezEnabled();
+ }
+ }
+#endif // OPENSIM
+//
return mesh_enabled;
}
diff --git a/indra/newview/llpanelsnapshotoptions.cpp b/indra/newview/llpanelsnapshotoptions.cpp
index 58340a98d9..13aae9c37a 100644
--- a/indra/newview/llpanelsnapshotoptions.cpp
+++ b/indra/newview/llpanelsnapshotoptions.cpp
@@ -32,6 +32,7 @@
#include "llfloatersnapshot.h" // FIXME: create a snapshot model
#include "llfloaterreg.h"
#include "llfloaterflickr.h" // Share to Flickr
+#include "fsfloaterprimfeed.h" // Share to Primfeed
/**
* Provides several ways to save a snapshot.
@@ -52,6 +53,7 @@ private:
void onSaveToInventory();
void onSaveToComputer();
void onSendToFlickr(); // Share to Flickr
+ void onSendToPrimfeed(); // Share to Primfeed
LLFloaterSnapshotBase* mSnapshotFloater;
};
@@ -65,6 +67,7 @@ LLPanelSnapshotOptions::LLPanelSnapshotOptions()
mCommitCallbackRegistrar.add("Snapshot.SaveToInventory", boost::bind(&LLPanelSnapshotOptions::onSaveToInventory, this));
mCommitCallbackRegistrar.add("Snapshot.SaveToComputer", boost::bind(&LLPanelSnapshotOptions::onSaveToComputer, this));
mCommitCallbackRegistrar.add("Snapshot.SendToFlickr", boost::bind(&LLPanelSnapshotOptions::onSendToFlickr, this)); // Share to Flickr
+ mCommitCallbackRegistrar.add("Snapshot.SendToPrimfeed", boost::bind(&LLPanelSnapshotOptions::onSendToPrimfeed, this)); // Share to Primfeed
}
// virtual
@@ -113,11 +116,23 @@ void LLPanelSnapshotOptions::onSendToFlickr()
{
LLFloaterReg::hideInstance("snapshot");
- LLFloaterFlickr* flickr_floater = dynamic_cast(LLFloaterReg::getInstance("flickr"));
- if (flickr_floater)
+ if (auto flickr_floater = LLFloaterReg::getTypedInstance("flickr"))
{
flickr_floater->showPhotoPanel();
}
LLFloaterReg::showInstance("flickr");
}
//
+
+// Share to Primfeed
+void LLPanelSnapshotOptions::onSendToPrimfeed()
+{
+ LLFloaterReg::hideInstance("snapshot");
+
+ if (auto primfeed_floater = LLFloaterReg::getTypedInstance("primfeed"))
+ {
+ primfeed_floater->showPhotoPanel();
+ }
+ LLFloaterReg::showInstance("primfeed");
+}
+//
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 218cf85c4a..260dd31cd3 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -8002,19 +8002,20 @@ bool LLSelectMgr::canSelectObject(LLViewerObject* object, bool ignore_select_own
// only select my own objects
return false;
}
+
+ // FIRE-14593: Option to select only copyable objects
+ if (!object->permCopy() && gSavedSettings.getBOOL("FSSelectCopyableOnly"))
+ {
+ return false;
+ }
+ //
+ // FIRE-17696: Option to select only locked objects
+ if (gSavedSettings.getBOOL("FSSelectLockedOnly") && object->permMove() && !object->isPermanentEnforced())
+ {
+ return false;
+ }
+ // // Can't select objects that are not owned by you or group
}
- // FIRE-14593: Option to select only copyable objects
- if (!object->permCopy() && gSavedSettings.getBOOL("FSSelectCopyableOnly"))
- {
- return false;
- }
- //
- // FIRE-17696: Option to select only locked objects
- if (gSavedSettings.getBOOL("FSSelectLockedOnly") && object->permMove() && !object->isPermanentEnforced())
- {
- return false;
- }
- //
// Can't select orphans
if (object->isOrphaned()) return false;
diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp
index da1834608b..c4dad3d4eb 100644
--- a/indra/newview/llsnapshotlivepreview.cpp
+++ b/indra/newview/llsnapshotlivepreview.cpp
@@ -36,6 +36,7 @@
#include "llfloaterperms.h"
#include "llfloaterreg.h"
#include "llfloaterflickr.h" // Share to Flickr
+#include "fsfloaterprimfeed.h" // Share to Primfeed
#include "llimagefilter.h"
#include "llimagefiltersmanager.h"
#include "llimagebmp.h"
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 0af84eb9ac..fa28def9ab 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -1950,9 +1950,9 @@ bool idle_startup()
// Wait for notification confirmation
if (STATE_LOGIN_CONFIRM_NOTIFICATON == LLStartUp::getStartupState())
{
- display_startup();
+ do_startup_frame();
gViewerWindow->getProgressView()->setVisible(false);
- display_startup();
+ do_startup_frame();
ms_sleep(1);
return false;
}
@@ -2298,10 +2298,10 @@ bool idle_startup()
//so I just moved nearby history loading a few states further
if (gSavedPerAccountSettings.getBOOL("LogShowHistory"))
{
- FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance();
- if (nearby_chat) nearby_chat->loadHistory();
+ if (FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance())
+ nearby_chat->loadHistory();
}
- display_startup();
+ do_startup_frame();
// [FS communication UI]
// FIRE-18250: Option to disable default eye movement
@@ -2549,13 +2549,14 @@ bool idle_startup()
LL_INFOS() << "Requesting Money Balance" << LL_ENDL;
LLStatusBar::sendMoneyBalanceRequest();
- do_startup_frame();
// Moved before inventory creation.
// request all group information
LL_INFOS("Agent_GroupData") << "GROUPDEBUG: Requesting Agent Data during startup" << LL_ENDL;
gAgent.sendAgentDataUpdateRequest();
- display_startup();
//
+
+ do_startup_frame();
+
// Inform simulator of our language preference
LLAgentLanguage::update();
@@ -2808,7 +2809,7 @@ bool idle_startup()
// Create the inventory views
LL_INFOS() << "Creating Inventory Views" << LL_ENDL;
LLFloaterReg::getInstance("inventory");
- //do_startup_frame();
+ do_startup_frame();
// [RLVa:KB] - Checked: RLVa-1.1.0
if (RlvHandler::isEnabled())
@@ -2898,7 +2899,7 @@ bool idle_startup()
//ok, we're done, set it back to false.
gSavedSettings.setBOOL("FSFirstRunAfterSettingsRestore", false);
}
- display_startup();
+ do_startup_frame();
//
if (gSavedSettings.getBOOL("HelpFloaterOpen"))
@@ -3074,7 +3075,7 @@ bool idle_startup()
}
}
#endif // OPENSIM
- display_startup();
+ do_startup_frame();
//
LLStartUp::setStartupState( STATE_PRECACHE );
@@ -4972,24 +4973,7 @@ bool process_login_success_response(U32 &first_sim_size_x, U32 &first_sim_size_y
LLViewerMedia::getInstance()->openIDSetup(openid_url, openid_token);
}
- // Only save mfa_hash for future logins if the user wants their info remembered.
- if(response.has("mfa_hash")
- && gSavedSettings.getBOOL("RememberUser")
- && LLLoginInstance::getInstance()->saveMFA())
- {
- std::string grid(LLGridManager::getInstance()->getGridId());
- std::string user_id(gUserCredential->userID());
- gSecAPIHandler->addToProtectedMap("mfa_hash", grid, user_id, response["mfa_hash"]);
- // TODO(brad) - related to SL-17223 consider building a better interface that sync's automatically
- gSecAPIHandler->syncProtectedMap();
- }
- else if (!LLLoginInstance::getInstance()->saveMFA())
- {
- std::string grid(LLGridManager::getInstance()->getGridId());
- std::string user_id(gUserCredential->userID());
- gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id);
- gSecAPIHandler->syncProtectedMap();
- }
+ LLLoginInstance::getInstance()->saveMFAHash(response);
// OpenSim legacy economy support
#ifdef OPENSIM
@@ -5092,6 +5076,7 @@ bool process_login_success_response(U32 &first_sim_size_x, U32 &first_sim_size_y
}
//
+
bool success = false;
// JC: gesture loading done below, when we have an asset system
// in place. Don't delete/clear gUserCredentials until then.
diff --git a/indra/newview/llteleporthistory.cpp b/indra/newview/llteleporthistory.cpp
index 3d90734d96..3c20615a24 100644
--- a/indra/newview/llteleporthistory.cpp
+++ b/indra/newview/llteleporthistory.cpp
@@ -44,6 +44,10 @@
#include "llavatarname.h"
#include "llavatarnamecache.h"
+#include "llviewernetwork.h" // Access to GridManager
+#include "lfsimfeaturehandler.h" // Access to hyperGridURL
+#include "llworldmapmessage.h" // Access to sendNamedRegionRequest
+
// [RLVa:KB] - Checked: 2010-09-03 (RLVa-1.2.1b)
#include "rlvhandler.h"
// [/RLVa:KB]
@@ -98,6 +102,46 @@ void LLTeleportHistory::goToItem(int idx)
return;
}
+ // [FIRE-35355] OpenSim global position is dependent on the Grid you are on
+ #ifdef OPENSIM
+ if (LLGridManager::getInstance()->isInOpenSim())
+ {
+ if (mItems[mCurrentItem].mRegionID != mItems[idx].mRegionID)
+ {
+ LLSLURL slurl = mItems[idx].mSLURL;
+ std::string grid = slurl.getGrid();
+ std::string current_grid = LFSimFeatureHandler::instance().hyperGridURL();
+ std::string gatekeeper = LLGridManager::getInstance()->getGatekeeper(grid);
+
+ // Requesting region information from the server is only required when changing grid
+ if (slurl.isValid() && grid != current_grid)
+ {
+ if (!gatekeeper.empty())
+ {
+ slurl = LLSLURL(gatekeeper + ":" + slurl.getRegion(), slurl.getPosition(), true);
+ }
+
+ if (mRequestedItem != -1)
+ {
+ return; // We already have a request in progress and don't want to spam the server
+ }
+
+ mRequestedItem = idx;
+
+ LLWorldMapMessage::getInstance()->sendNamedRegionRequest(
+ slurl.getRegion(),
+ boost::bind(&LLTeleportHistory::regionNameCallback, this, idx, _1, _2, _3, _4),
+ slurl.getSLURLString(),
+ true
+ );
+
+ return; // The teleport will occur in the callback with the correct global position
+ }
+ }
+ }
+ #endif
+ //
+
// Attempt to teleport to the requested item.
gAgent.teleportViaLocation(mItems[idx].mGlobalPos);
mRequestedItem = idx;
@@ -210,6 +254,22 @@ void LLTeleportHistory::updateCurrentLocation(const LLVector3d& new_pos)
mItems[mCurrentItem] = LLTeleportHistoryItem(RlvStrings::getString(RlvStringKeys::Hidden::Parcel), LLVector3d::zero);
}
// [/RLVa:KB]
+
+ // [FIRE-35355] OpenSim global position is dependent on the Grid you are on,
+ // so we need to store the slurl so we can request the global position later
+ #ifdef OPENSIM
+ if (LLGridManager::getInstance()->isInOpenSim())
+ {
+ auto regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ LLVector3 new_pos_local = gAgent.getPosAgentFromGlobal(new_pos);
+ LLSLURL slurl = LLSLURL(LFSimFeatureHandler::instance().hyperGridURL(), regionp->getName(), new_pos_local);
+ mItems[mCurrentItem].mSLURL = slurl;
+ }
+ }
+ #endif
+ //
}
//dump(); // LO - removing the dump from happening every time we TP.
@@ -287,3 +347,35 @@ void LLTeleportHistory::dump() const
LL_INFOS() << line.str() << LL_ENDL;
}
}
+
+// [FIRE-35355] Callback for OpenSim so we can teleport to the correct global position on another grid
+void LLTeleportHistory::regionNameCallback(int idx, U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport)
+{
+ if (region_handle)
+ {
+ // Sanity checks again just in case since time has passed since the request was made
+ if (idx < 0 || idx >= (int)mItems.size())
+ {
+ LL_WARNS() << "Invalid teleport history index (" << idx << ") specified" << LL_ENDL;
+ return;
+ }
+
+ if (idx == mCurrentItem)
+ {
+ LL_WARNS() << "Will not teleport to the same location." << LL_ENDL;
+ return;
+ }
+
+ LLVector3d origin_pos = from_region_handle(region_handle);
+ LLVector3d global_pos(origin_pos + LLVector3d(slurl.getPosition()));
+
+ // Attempt to teleport to the target grids region
+ gAgent.teleportViaLocation(global_pos);
+ }
+ else
+ {
+ LL_WARNS() << "Invalid teleport history region handle" << LL_ENDL;
+ onTeleportFailed();
+ }
+}
+//
diff --git a/indra/newview/llteleporthistory.h b/indra/newview/llteleporthistory.h
index 6962f4afe3..5d705fe90d 100644
--- a/indra/newview/llteleporthistory.h
+++ b/indra/newview/llteleporthistory.h
@@ -35,6 +35,8 @@
#include
#include "llteleporthistorystorage.h"
+#include "llslurl.h" // Access to LLSLURL
+
/**
* An item of the teleport history.
@@ -47,8 +49,11 @@ public:
LLTeleportHistoryItem()
{}
- LLTeleportHistoryItem(std::string title, LLVector3d global_pos)
- : mTitle(title), mGlobalPos(global_pos)
+ // [FIRE-35355] OpenSim requires knowing the grid to teleport correctly if changing grids
+ //LLTeleportHistoryItem(std::string title, LLVector3d global_pos)
+ // : mTitle(title), mGlobalPos(global_pos)
+ LLTeleportHistoryItem(std::string title, LLVector3d global_pos, const LLSLURL& slurl = LLSLURL())
+ : mTitle(title), mGlobalPos(global_pos), mSLURL(slurl)
{}
/**
@@ -61,6 +66,7 @@ public:
std::string mFullTitle; // human-readable location title including coordinates
LLVector3d mGlobalPos; // global position
LLUUID mRegionID; // region ID for getting the region info
+ LLSLURL mSLURL; // [FIRE-35355] slurl for the location required for OpenSim
};
/**
@@ -180,6 +186,10 @@ private:
*/
static std::string getCurrentLocationTitle(bool full, const LLVector3& local_pos_override);
+ // [FIRE-35355] Callback for OpenSim so we can teleport to the correct global position on another grid
+ void regionNameCallback(int idx, U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport);
+ //
+
/**
* Actually, the teleport history.
*/
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 8648338468..df7fcc9495 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -226,6 +226,7 @@
#include "lggbeamcolormapfloater.h"
#include "lggbeammapfloater.h"
#include "llfloaterdisplayname.h"
+#include "fsfloaterprimfeed.h"
#include "llfloaterflickr.h"
#include "llfloaterscriptrecover.h"
#include "llfloatersearchreplace.h"
@@ -632,6 +633,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("export_collada", "floater_export_collada.xml", (LLFloaterBuildFunc)&LLFloaterReg::build);
LLFloaterReg::add("delete_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build);
LLFloaterReg::add("flickr", "floater_flickr.xml", (LLFloaterBuildFunc)&LLFloaterReg::build);
+ LLFloaterReg::add("primfeed", "floater_primfeed.xml", (LLFloaterBuildFunc)&LLFloaterReg::build);
LLFloaterReg::add("fs_asset_blacklist", "floater_fs_asset_blacklist.xml", (LLFloaterBuildFunc)&LLFloaterReg::build);
LLFloaterReg::add("fs_avatar_render_settings", "floater_fs_avatar_render_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build);
LLFloaterReg::add("fs_blocklist", "floater_fs_blocklist.xml", (LLFloaterBuildFunc)&LLFloaterReg::build);
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index a85a7e8de2..dbba65666f 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -2757,6 +2757,28 @@ class LLAdvancedCompressFileTest : public view_listener_t
}
};
+// Primfeed integration test functions (can be removed when the feature is stable)
+///////////////////
+// PRIMFEED AUTH //
+///////////////////
+#include "fsprimfeedauth.h"
+class LLAdvancedPrimfeedAuth : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ FSPrimfeedAuth::initiateAuthRequest();
+ return true;
+ }
+};
+class LLAdvancedPrimfeedAuthReset : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ FSPrimfeedAuth::resetAuthStatus();
+ return true;
+ }
+};
+//
/////////////////////////
// SHOW DEBUG SETTINGS //
@@ -11787,6 +11809,19 @@ class LLWorldEnvSettings : public view_listener_t
#endif
//
+ // Redundant environment toggles revert to shared environment
+ LLSettingsSky::ptr_t sky = LLEnvironment::instance().getEnvironmentFixedSky(LLEnvironment::ENV_LOCAL);
+ LLUUID skyid = (sky) ? sky->getAssetId() : LLUUID::null;
+ bool repeatedEnvTogglesShared = gSavedSettings.getBOOL("FSRepeatedEnvTogglesShared");
+
+ if(repeatedEnvTogglesShared && ((skyid == LLEnvironment::KNOWN_SKY_SUNRISE && event_name == "sunrise") ||
+ (skyid == LLEnvironment::KNOWN_SKY_MIDDAY && event_name == "noon") ||
+ (skyid == LLEnvironment::KNOWN_SKY_LEGACY_MIDDAY && event_name == "legacy noon") ||
+ (skyid == LLEnvironment::KNOWN_SKY_SUNSET && event_name == "sunset") ||
+ (skyid == LLEnvironment::KNOWN_SKY_MIDNIGHT && event_name == "midnight")))
+ event_name = "region";
+ //
+
if (event_name == "sunrise")
{
// FIRE-29926 - allow manually selected environments to have a user defined transition time.
@@ -12809,6 +12844,8 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedCheckShowObjectUpdates(), "Advanced.CheckShowObjectUpdates");
view_listener_t::addMenu(new LLAdvancedCompressImage(), "Advanced.CompressImage");
view_listener_t::addMenu(new LLAdvancedCompressFileTest(), "Advanced.CompressFileTest");
+ view_listener_t::addMenu(new LLAdvancedPrimfeedAuth(), "Advanced.PrimfeedAuth");
+ view_listener_t::addMenu(new LLAdvancedPrimfeedAuthReset(), "Advanced.PrimfeedAuthReset");
view_listener_t::addMenu(new LLAdvancedShowDebugSettings(), "Advanced.ShowDebugSettings");
view_listener_t::addMenu(new LLAdvancedEnableViewAdminOptions(), "Advanced.EnableViewAdminOptions");
view_listener_t::addMenu(new LLAdvancedToggleViewAdminOptions(), "Advanced.ToggleViewAdminOptions");
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 041502bebd..b49d6223e5 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -6674,6 +6674,7 @@ void process_alert_core(const std::string& message, bool modal)
if (text.substr(0, restart_cancelled.length()) == restart_cancelled)
{
LLFloaterRegionRestarting::close();
+ fs_report_region_restart_to_channel(-1); // Announce region restart to a defined chat channel
}
std::string new_msg =LLNotifications::instance().getGlobalString(text);
@@ -8742,7 +8743,14 @@ void fs_report_region_restart_to_channel(S32 seconds)
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_ChatData);
- msg->addStringFast(_PREHASH_Message, "region_restart_in:" + llformat("%d", seconds));
+ if(seconds >= 0)
+ {
+ msg->addStringFast(_PREHASH_Message, "region_restart_in:" + llformat("%d", seconds));
+ }
+ else // Input is a negative number
+ {
+ msg->addStringFast(_PREHASH_Message, "region_restart_cancelled");
+ }
msg->addU8Fast(_PREHASH_Type, CHAT_TYPE_WHISPER);
msg->addS32("Channel", channel);
gAgent.sendReliableMessage();
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 787066c7d7..c214507a46 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -121,6 +121,7 @@ namespace
void newRegionEntry(LLViewerRegion& region)
{
+ LL_PROFILE_ZONE_SCOPED; // improve instrumentation
LL_INFOS("LLViewerRegion") << "Entering region [" << region.getName() << "]" << LL_ENDL;
gDebugInfo["CurrentRegion"] = region.getName();
LLAppViewer::instance()->writeDebugInfo();
@@ -3983,6 +3984,15 @@ bool LLViewerRegion::bakesOnMeshEnabled() const
mSimulatorFeatures["BakesOnMeshEnabled"].asBoolean());
}
+// FIRE-35602 etc - Mesh not appearing after TP/login (opensim only)
+#ifdef OPENSIM
+bool LLViewerRegion::meshRezEnabled() const
+{
+ return (mSimulatorFeatures.has("MeshRezEnabled") && mSimulatorFeatures["MeshRezEnabled"].asBoolean());
+}
+#endif
+//
+
bool LLViewerRegion::dynamicPathfindingEnabled() const
{
return ( mSimulatorFeatures.has("DynamicPathfindingEnabled") &&
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index e56db5009c..d7475d440d 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -355,7 +355,10 @@ public:
U8 getCentralBakeVersion() { return mCentralBakeVersion; }
void getInfo(LLSD& info);
-
+// FIRE-35602 etc - Mesh not appearing after TP/login (opensim only)
+#ifdef OPENSIM
+#endif // OPENSIM
+//
bool meshUploadEnabled() const;
bool bakesOnMeshEnabled() const;
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index ff7d0e2b10..fd18971733 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -111,6 +111,7 @@
#include "llfloatertools.h"
#include "llfloatersnapshot.h" // for snapshotFrame
#include "llfloaterflickr.h" // for snapshotFrame
+#include "fsfloaterprimfeed.h" // for snapshotFrame
#include "llsnapshotlivepreview.h" // for snapshotFrame
// #include "llpanelface.h" // switchable edit texture/materials panel - include not needed
#include "llpathfindingpathtool.h"
@@ -8049,12 +8050,12 @@ bool LLPipeline::renderSnapshotFrame(LLRenderTarget* src, LLRenderTarget* dst)
}
const bool simple_snapshot_visible = LLFloaterReg::instanceVisible("simple_snapshot");
const bool flickr_snapshot_visible = LLFloaterReg::instanceVisible("flickr");
+ const bool primfeed_snapshot_visible = LLFloaterReg::instanceVisible("primfeed"); // Primfeed integration
const bool snapshot_visible = LLFloaterReg::instanceVisible("snapshot");
- const bool any_snapshot_visible = simple_snapshot_visible || flickr_snapshot_visible || snapshot_visible;
+ const bool any_snapshot_visible = simple_snapshot_visible || flickr_snapshot_visible || primfeed_snapshot_visible || snapshot_visible; // Primfeed integration
if (!show_frame || !any_snapshot_visible || !gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
{
return false;
-
}
LLSnapshotLivePreview * previewView = nullptr;
if (snapshot_visible)
@@ -8068,6 +8069,13 @@ bool LLPipeline::renderSnapshotFrame(LLRenderTarget* src, LLRenderTarget* dst)
auto * floater = dynamic_cast(LLFloaterReg::findInstance("flickr"));
previewView = floater->getPreviewView();
}
+ // Primfeed integration
+ if (primfeed_snapshot_visible && !previewView)
+ {
+ auto * floater = dynamic_cast(LLFloaterReg::findInstance("primfeed"));
+ previewView = floater->getPreviewView();
+ }
+ //
if(!previewView)
{
return false;
diff --git a/indra/newview/skins/ansastorm/xui/pl/panel_edit_skin.xml b/indra/newview/skins/ansastorm/xui/pl/panel_edit_skin.xml
index 5c14e5e3d4..be7a5f8f57 100644
--- a/indra/newview/skins/ansastorm/xui/pl/panel_edit_skin.xml
+++ b/indra/newview/skins/ansastorm/xui/pl/panel_edit_skin.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/floater_camera.xml b/indra/newview/skins/ansastorm/xui/pt/floater_camera.xml
new file mode 100644
index 0000000000..22c7d7c3f5
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/floater_camera.xml
@@ -0,0 +1,41 @@
+
+
+
+ Girar a câmera em torno do foco
+
+
+ Zoom da câmera em direção ao foco
+
+
+ Mover a câmera para cima, para baixo, esquerda e direita
+
+
+ Visualizar Objeto
+
+
+ Use a predefinição
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/floater_preview_texture.xml b/indra/newview/skins/ansastorm/xui/pt/floater_preview_texture.xml
new file mode 100644
index 0000000000..2eaa181826
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/floater_preview_texture.xml
@@ -0,0 +1,46 @@
+
+
+
+ Textura: [NAME]
+
+
+ Copiar para o inventário
+
+
+ [weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt], [hour24, datetime, slt]:[min, datetime, slt]:[second, datetime, slt] [timezone, datetime, slt]
+
+
+ Descrição:
+
+
+ Carregado por:
+
+
+
+ Data:
+
+
+
+
+ Visualizar a aparência
+
+
+
+ Sem restrição
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_eyes.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_eyes.xml
new file mode 100644
index 0000000000..b27070c65e
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_eyes.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_gloves.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_gloves.xml
new file mode 100644
index 0000000000..f6d3e05f66
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_gloves.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_hair.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_hair.xml
new file mode 100644
index 0000000000..8f3e46f6b9
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_hair.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ As opções de pelos faciais
+estão disponíveis apenas para avatares masculinos.
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_jacket.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_jacket.xml
new file mode 100644
index 0000000000..e7e4d62cf0
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_jacket.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_pants.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_pants.xml
new file mode 100644
index 0000000000..fb57b3902e
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_pants.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_physics.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_physics.xml
new file mode 100644
index 0000000000..a248975c0d
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_physics.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+ O movimento dos seios está disponível
+apenas para avatares femininos.
+
+
+
+
+
+
+
+ O decote está disponível
+apenas para avatares femininos.
+
+
+
+
+
+
+
+ O movimento dos seios está disponível
+apenas para avatares femininos.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_shape.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_shape.xml
new file mode 100644
index 0000000000..95b1f200d6
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_shape.xml
@@ -0,0 +1,59 @@
+
+
+
+ Metros
+
+
+ Pés
+
+
+ Altura:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_shirt.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_shirt.xml
new file mode 100644
index 0000000000..c0bbbfc658
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_shirt.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_shoes.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_shoes.xml
new file mode 100644
index 0000000000..d19ef26705
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_shoes.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_skin.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_skin.xml
new file mode 100644
index 0000000000..43dde9cc92
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_skin.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_skirt.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_skirt.xml
new file mode 100644
index 0000000000..e1867f356f
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_skirt.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_socks.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_socks.xml
new file mode 100644
index 0000000000..36c541e35c
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_socks.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_underpants.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_underpants.xml
new file mode 100644
index 0000000000..b280aa0915
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_underpants.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_edit_undershirt.xml b/indra/newview/skins/ansastorm/xui/pt/panel_edit_undershirt.xml
new file mode 100644
index 0000000000..c442aee47c
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_edit_undershirt.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/ansastorm/xui/pt/panel_main_inventory.xml b/indra/newview/skins/ansastorm/xui/pt/panel_main_inventory.xml
new file mode 100644
index 0000000000..64b65303bc
--- /dev/null
+++ b/indra/newview/skins/ansastorm/xui/pt/panel_main_inventory.xml
@@ -0,0 +1,115 @@
+
+
+
+ Obtendo [ITEM_COUNT] itens... [FILTER]
+
+
+ [ITEM_COUNT] itens [FILTER]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Itens
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/textures/icons/primfeed.png b/indra/newview/skins/default/textures/icons/primfeed.png
new file mode 100644
index 0000000000..6b8dd91da9
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/primfeed.png differ
diff --git a/indra/newview/skins/default/textures/icons/primfeed_white.png b/indra/newview/skins/default/textures/icons/primfeed_white.png
new file mode 100644
index 0000000000..4eeda44554
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/primfeed_white.png differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index da82bd327a..250474db34 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -166,6 +166,7 @@ with the same filename but different name
+
diff --git a/indra/newview/skins/default/xui/de/floater_fs_area_search.xml b/indra/newview/skins/default/xui/de/floater_fs_area_search.xml
index b01ac3b095..2bf0e04a63 100644
--- a/indra/newview/skins/default/xui/de/floater_fs_area_search.xml
+++ b/indra/newview/skins/default/xui/de/floater_fs_area_search.xml
@@ -62,6 +62,7 @@
+
und
@@ -92,6 +93,7 @@
+
diff --git a/indra/newview/skins/default/xui/de/floater_fs_poser.xml b/indra/newview/skins/default/xui/de/floater_fs_poser.xml
index 18c0a59cb1..6d407011b8 100644
--- a/indra/newview/skins/default/xui/de/floater_fs_poser.xml
+++ b/indra/newview/skins/default/xui/de/floater_fs_poser.xml
@@ -14,13 +14,22 @@
Hintere Glieder
Flügel
Ohren/Nase
+ Körper
+ Körper
+ Arme
+ Beine
Ganzer Avatar
Oberkörper
+ Wirbelsäule 1
+ Wirbelsäule 2
+ Wirbelsäule 3
+ Wirbelsäule 4
Brust
Hals
Kopf
+ Schädel
Rechtes Auge
Linkes Auge
Linker Stirnwinkel
@@ -32,13 +41,17 @@
Mit. rechte Augenbraue
In. rechte Augenbraue
Linkes oberes Augenlid
+ In. linker Augenwinkel
Linkes unteres Augenlid
Rechtes oberes Augenlid
+ In. rechter Augenwinkel
Rechtes unteres Augenlid
Linkes oberes Ohr
Linkes unteres Ohr
Rechtes oberes Ohr
Rechtes unteres Ohr
+ Basis Nase
+ Nosenbrücke
Nase links
Nase Mitte
Nase rechts
@@ -55,16 +68,12 @@
Zungenspite
Kieferform
Stirn Mitte
- Nase Basis
Obere Zähne
Linke obere Lippe
Rechte obere Lippe
Linker Mundwinkel
Rechter Mundwinkel
Mittlere obere Lippe
- In. linker Augenwinkel
- In. rechter Augenwinkel
- Nasenbrücke
Kragen
Ganzer Arm
Unterarm
@@ -140,10 +149,31 @@
Rechts 2
Rechts 3
Rechts 4
+ Kopf
+ Nacken
+ Brust
Hintern
Bauch
Linke Brustmuskeln
Rechte Brustmuskeln
+ Li. Schlüsselbein
+ Re .Schlüsselbein
+ Linker Obererm
+ Rechter Oberarm
+ Linker Unterarm
+ Rechter Unterarm
+ Linke Hand
+ Rechte Hand
+ Oberer Rücken
+ Linke Taille
+ Rechte Taille
+ Becken
+ Linker Oberschenkel
+ Rechter Oberschenkel
+ Linker Unterschenkel
+ Rechter Unterschenkel
+ Linker Fuß
+ Rechter Fuß
Pose laden
Pose speichern
Diff. laden
diff --git a/indra/newview/skins/default/xui/de/floater_primfeed.xml b/indra/newview/skins/default/xui/de/floater_primfeed.xml
new file mode 100644
index 0000000000..c3ceda10ab
--- /dev/null
+++ b/indra/newview/skins/default/xui/de/floater_primfeed.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+ Fehler
+
+
+ Laden...
+
+
+
+
diff --git a/indra/newview/skins/default/xui/de/menu_viewer.xml b/indra/newview/skins/default/xui/de/menu_viewer.xml
index 29ca760d66..cacd875562 100644
--- a/indra/newview/skins/default/xui/de/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/de/menu_viewer.xml
@@ -104,6 +104,7 @@
+
@@ -673,6 +674,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml
index 0d67f93863..6d896559d4 100644
--- a/indra/newview/skins/default/xui/de/notifications.xml
+++ b/indra/newview/skins/default/xui/de/notifications.xml
@@ -3017,13 +3017,10 @@ Wählen Sie eine kleinere Landfläche.
[MESSAGE]
-
- [MESSAGE]
-
[MESSAGE]
-
+
[MESSAGE]
@@ -5780,6 +5777,9 @@ Flickr-Verifikation fehlgeschlagen. Bitte erneut versuchen sowie den eingegebene
Das Foto kann jetzt [https://www.flickr.com/photos/me/[ID] hier] betrachtet werden.
+
+Der Primfeed-Post kann jetzt [[PF_POSTURL] hier] betrachtet werden.
+
Welche Bezeichnung soll für die Region
@@ -5874,4 +5874,25 @@ https://wiki.firestormviewer.org/antivirus_whitelisting
Bestehende Pose „[POSE_NAME]“ überschreiben?
+
+ Login-Anfrage wurde von Primfeed abgelehnt.
+
+
+ Primfeed-Autorisierung fehlgeschlagen. Die Autorisierungssequenz war nicht vollständig.
+
+
+ Primfeed-Autorisierung läuft bereits. Bitte schließen Sie die Primfeed-Autorisierung in Ihrem Webbrowser ab, bevor Sie es erneut versuchen.
+
+
+ Primfeed-Autorisierung abgschlossen. Sie können jetzt Fotos auf Primfeed posten.
+
+
+ Primfeed-Benutzervalidierung fehlgeschlagen. Primfeed ist dieser Account unbekannt oder das Anmelden ist fehlgeschlagen.
+
+
+ Sie haben dieses Konto bereits mit Primfeed verknüpft. Bitte benutzen Sie den Rücksetzen-Button, um neu zu beginnen.
+
+
+ Primfeed-Benutzeranmeldung erfolgreich, allerdings ist die Statusprüfung fehlgeschlagen. Bitte prüfen Sie, ob Primfeed korrekt funktioniert.
+
diff --git a/indra/newview/skins/default/xui/de/panel_flickr_account.xml b/indra/newview/skins/default/xui/de/panel_flickr_account.xml
index 416761f0da..42337f7fb1 100644
--- a/indra/newview/skins/default/xui/de/panel_flickr_account.xml
+++ b/indra/newview/skins/default/xui/de/panel_flickr_account.xml
@@ -1,7 +1,7 @@
-
+
Nicht mit Flickr verbunden.
diff --git a/indra/newview/skins/default/xui/de/panel_preferences_firestorm.xml b/indra/newview/skins/default/xui/de/panel_preferences_firestorm.xml
index e0ac638d58..e8aa7b83ba 100644
--- a/indra/newview/skins/default/xui/de/panel_preferences_firestorm.xml
+++ b/indra/newview/skins/default/xui/de/panel_preferences_firestorm.xml
@@ -40,6 +40,7 @@
+
diff --git a/indra/newview/skins/default/xui/de/panel_primfeed_account.xml b/indra/newview/skins/default/xui/de/panel_primfeed_account.xml
new file mode 100644
index 0000000000..73217f616f
--- /dev/null
+++ b/indra/newview/skins/default/xui/de/panel_primfeed_account.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Nicht mit Primfeed verbunden.
+
+
+ Konto-Typ:
+
+
+
+
+
+ [https://docs.primfeed.com Mehr über Primfeed erfahren]
+
+
+
diff --git a/indra/newview/skins/default/xui/de/panel_primfeed_photo.xml b/indra/newview/skins/default/xui/de/panel_primfeed_photo.xml
new file mode 100644
index 0000000000..1f58d06d0d
--- /dev/null
+++ b/indra/newview/skins/default/xui/de/panel_primfeed_photo.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+ Aktualisiere...
+
+
+
+
+
+
+ Beschreibung:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/indra/newview/skins/default/xui/de/panel_snapshot_options.xml b/indra/newview/skins/default/xui/de/panel_snapshot_options.xml
index 7ab1c65181..346bbce756 100644
--- a/indra/newview/skins/default/xui/de/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/de/panel_snapshot_options.xml
@@ -19,6 +19,9 @@
+
+
+
diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml
index 673008f518..457872b48a 100644
--- a/indra/newview/skins/default/xui/de/strings.xml
+++ b/indra/newview/skins/default/xui/de/strings.xml
@@ -387,24 +387,6 @@ Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten ha
Verbindungsabbruch wird getestet
-
- Mit Facebook verbinden...
-
-
- Posten...
-
-
- Facebook-Verbindung trennen...
-
-
- Problem beim Verbinden mit Facebook
-
-
- Problem beim Posten auf Facebook
-
-
- Problem beim Trennen der Facebook-Verbindung
-
Verbinden mit Flickr...
@@ -423,23 +405,17 @@ Wenn Sie der Ansicht sind, dass Sie diese Meldung fälschlicherweise erhalten ha
Problem beim Trennen der Flickr-Verbindung
-
- Verbinden mit Twitter...
+
+ Verbinden mit Primfeed...
-
- Posten...
+
+ Nicht autorisiert...
-
- Twitter-Verbindung wird getrennt...
+
+ Teilen...
-
- Problem beim Verbinden mit Twitter
-
-
- Problem beim Posten auf Twitter
-
-
- Problem beim Trennen der Twitter-Verbindung
+
+ Problem beim Posten auf Primfeed
Schwarzweiß
@@ -6126,6 +6102,9 @@ Setzen Sie den Editorpfad in Anführungszeichen
Poser
+
+ Primfeed
+
Avatar ändern
@@ -6327,6 +6306,9 @@ Setzen Sie den Editorpfad in Anführungszeichen
Eigenen Avatar und Animeshe posieren
+
+ Auf Primfeed posten
+
gegenwärtig in der unteren Symbolleiste
diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml
index 36be04f12c..5ba46354d8 100644
--- a/indra/newview/skins/default/xui/en/floater_about.xml
+++ b/indra/newview/skins/default/xui/en/floater_about.xml
@@ -91,7 +91,7 @@ Dummy Name replaced at run time
-Angeldark Raymaker, Ansariel Hiller, ArminWeatherHax, Arrehn Oberlander, Beq Janus, Cinder Roxley, Holy Gavenkrantz, Jessica Lyon, Kadah Coba, Kitty Barnett, Liny Odell, LordGregGreg Back, minerjr, Mobius Ryba, Nicky Dasmijn, PanteraPolnocy, ScentualLust, Selo Jacobus, SimonLsAlt, Tankmaster Finesmith, Techwolf Lupindo, Tonya Souther, Tozh Taurog, Vortex Saito, WoLf Loonie, Wolfspirit Magic, Yay N' Stuff (mygoditsfullofstars), and Zi Ree.
+Angeldark Raymaker, Ansariel Hiller, ArminWeatherHax, Arrehn Oberlander, Beq Janus, Chanayane, Cinder Roxley, Hecklezz, Holy Gavenkrantz, Jessica Lyon, Kadah Coba, Kitty Barnett, Liny Odell, LordGregGreg Back, minerjr, Mobius Ryba, Nicky Dasmijn, PanteraPolnocy, ScentualLust, Selo Jacobus, SimonLsAlt, Tankmaster Finesmith, Techwolf Lupindo, Tonya Souther, Tozh Taurog, Vortex Saito, WoLf Loonie, Wolfspirit Magic, Yay N' Stuff (mygoditsfullofstars), and Zi Ree.
-Aira Yumi, Albatroz Hird, Alexie Birman, Andromeda Rage, Angus Boyd, Animats, Armin Weatherwax, Ayane Lyla, Casper Warden, Chalice Yao, Chaser Zaks, Chorazin Allen, Cron Stardust, Damian Zhaoying, Dan Threebeards, Darlcat, Dawa Gurbux, Dax Dupont, Denver Maksim, Dragonborn Forzane, Drake Arconis, Felyza Wishbringer, f0rbidden, Fractured Crystal, Geenz Spad, Gibson Firehawk, Hecklezz, Hitomi Tiponi, humbletim, Inusaito Sayori, Jean Severine, Katharine Berry, Kittin Ninetails, Kool Koolhoven, Lance Corrimal, Lassie, Latif Khalifa, Laurent Bechir, Logue Takacs, Magne Metaverse LLC, Magus Freston, Makidoll, Manami Hokkigai, MartinRJ Fayray, McCabe Maxstead, Melancholy Lemon, Melysmile, Mimika Oh, minerjr, Mister Acacia, MorganMegan, Morgan Pennent, Mysty Saunders, Nagi Michinaga, Name Short, nhede Core, NiranV Dean, Nogardrevlis Lectar, Oren Hurvitz, paperwork, Penny Patton, Peyton Menges, programmtest, Qwerty Venom, rafak360, Rebecca Ashbourne, Revolution Smythe, Romka Swallowtail, Sahkolihaa Contepomi, sal Kaligawa, Samm Florian, Satomi Ahn, Sei Lisa, Sekkmer, Sempervirens Oddfellow, Shin Wasp, Shyotl Kuhr, Sione Lomu, Skills Hak, StarlightShining, Sunset Faulkes, Tapple Gao, Testicular Slingshot, Thickbrick Sleaford, Ubit Umarov, Vaalith Jinn, Vincent Sylvester, Whirly Fizzle, Xenhat Liamano, 小滢 Zi Ying, Zwagoth Klaar and others.
+Aira Yumi, Albatroz Hird, Alexie Birman, Andromeda Rage, Angus Boyd, Animats, Armin Weatherwax, Ayane Lyla, Casper Warden, Chalice Yao, Chaser Zaks, Chorazin Allen, Cron Stardust, Damian Zhaoying, Dan Threebeards, Darlcat, Dawa Gurbux, Dax Dupont, Denver Maksim, Dragonborn Forzane, Drake Arconis, Felyza Wishbringer, f0rbidden, Fractured Crystal, Geenz Spad, Gibson Firehawk, Hecklezz, Hitomi Tiponi, humbletim, Inusaito Sayori, Jean Severine, Katharine Berry, Kittin Ninetails, Kool Koolhoven, Lance Corrimal, Lassie, Latif Khalifa, Laurent Bechir, Logue Takacs, Magne Metaverse LLC, Magus Freston, Makidoll, Manami Hokkigai, MartinRJ Fayray, McCabe Maxstead, Melancholy Lemon, Melysmile, Mimika Oh, minerjr, Mister Acacia, MorganMegan, Morgan Pennent, Mysty Saunders, Nagi Michinaga, Name Short, nhede Core, NiranV Dean, Nogardrevlis Lectar, olizinha, Oren Hurvitz, paperwork, Penny Patton, Peyton Menges, programmtest, Qwerty Venom, rafak360, Rebecca Ashbourne, Revolution Smythe, Romka Swallowtail, Sahkolihaa Contepomi, sal Kaligawa, Samm Florian, Satomi Ahn, Sei Lisa, Sekkmer, Sempervirens Oddfellow, Shin Wasp, Shyotl Kuhr, Sione Lomu, Skills Hak, StarlightShining, Sunset Faulkes, Tapple Gao, Testicular Slingshot, Thickbrick Sleaford, Ubit Umarov, Vaalith Jinn, Vincent Sylvester, Whirly Fizzle, Xenhat Liamano, 小滢 Zi Ying, Zwagoth Klaar and others.
+
@@ -525,7 +534,7 @@
layout="topleft"
left_pad="5"
max_val="999999999"
- min_val="0"
+ min_val="0"
name="max_distance"
top_delta="-4"
width="80"/>
@@ -570,6 +579,14 @@
name="exclude_temporary"
label="Temporary"
width="80"/>
+
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH NEGATE_ROLL
+ SWAP_YAW_AND_ROLL NEGATE_PITCH NEGATE_ROLL
+ SWAP_YAW_AND_ROLL NEGATE_PITCH NEGATE_ROLL
SWAP_YAW_AND_ROLL NEGATE_PITCH NEGATE_ROLL
+ SWAP_YAW_AND_ROLL NEGATE_PITCH NEGATE_ROLL
+ SWAP_YAW_AND_ROLL NEGATE_PITCH NEGATE_ROLL
SWAP_YAW_AND_ROLL NEGATE_PITCH NEGATE_ROLL
SWAP_YAW_AND_ROLL NEGATE_PITCH NEGATE_ROLL
+ SWAP_YAW_AND_ROLL NEGATE_PITCH NEGATE_ROLL
NEGATE_PITCH
NEGATE_PITCH
SWAP_ROLL_AND_PITCH NEGATE_ROLL NEGATE_PITCH
@@ -39,9 +44,11 @@ width="430">
NEGATE_PITCH
NEGATE_PITCH NEGATE_ROLL
NEGATE_PITCH
+ NEGATE_PITCH
NEGATE_PITCH
NEGATE_PITCH NEGATE_ROLL
NEGATE_PITCH
+ NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
@@ -54,6 +61,8 @@ width="430">
SWAP_YAW_AND_ROLL NEGATE_PITCH
NEGATE_PITCH
NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
NEGATE_PITCH
NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
@@ -100,7 +109,28 @@ width="430">
SWAP_X2Z_Y2X_Z2Y NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_X2Y_Y2Z_Z2X NEGATE_PITCH NEGATE_YAW
+ SWAP_X2Y_Y2Z_Z2X
+ SWAP_ROLL_AND_PITCH
+ SWAP_ROLL_AND_PITCH
+ SWAP_ROLL_AND_PITCH
+ SWAP_X2Z_Y2X_Z2Y
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
@@ -113,6 +143,8 @@ width="430">
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
+ SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
SWAP_YAW_AND_ROLL NEGATE_PITCH
@@ -144,14 +176,23 @@ width="430">
Hind Limbs
Wings
Ears/Nose
+ Body
+ Body
+ Arms
+ Legs
Whole Avatar
Torso
+ Spine 1
+ Spine 2
+ Spine 3
+ Spine 4
Chest
Neck
Head
+ Skull
Right Eye
Left Eye
Forehead Left Side
@@ -163,13 +204,17 @@ width="430">
Eyebrow Middle Right
Eyebrow Inner Right
EyeLid Upper Left
+ Eye Inner Corner Left
EyeLid Lower Left
EyeLid Upper Right
+ Eye Inner Corner Right
EyeLid Lower Right
Ear Upper Left
Ear Lower Left
Ear Upper Right
Ear Lower Right
+ Nose Base
+ Nose Bridge
Nose Left
Nose Middle
Nose Right
@@ -186,16 +231,12 @@ width="430">
Tongue Tip
Jaw Shaper
Forehead Middle
- Nose Base
Teeth Upper
Lip Upper Left
Lip Upper Right
Lip Corner Left
Lip Corner Right
Lip Upper Middle
- Eye corner Inner Left
- Eye corner Inner Right
- Nose Bridge
Collar
Whole Arm
Forearm
@@ -271,10 +312,31 @@ width="430">
Right 2
Right 3
Right 4
+ Head
+ Neck
+ Chest
Bottom
Belly
Left Pec
Right Pec
+ Left Clavicle
+ Right Clavicle
+ Left Upper Arm
+ Right Upper Arm
+ Left Lower Arm
+ Right Lower Arm
+ Left Hand
+ Right Hand
+ Upper Back
+ Left Waist
+ Right Waist
+ Pelvis
+ Left Upper Leg
+ Right Upper Leg
+ Left Lower Leg
+ Right Lower Leg
+ Left Foot
+ Right Foot
diff --git a/indra/newview/skins/default/xui/en/floater_primfeed.xml b/indra/newview/skins/default/xui/en/floater_primfeed.xml
new file mode 100644
index 0000000000..6b03fee976
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_primfeed.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+ Error
+
+
+
+ Loading...
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 46995fe18a..e9c20d98a6 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -853,6 +853,13 @@
function="Floater.Toggle"
parameter="flickr"/>
+
+
+
@@ -5669,6 +5676,18 @@
+
+
+
+
+
+
[MESSAGE]
+
+
+ [MESSAGE]
+
Your snapshot can now be viewed [https://www.flickr.com/photos/me/[ID] here].
+
+
+Your Primfeed post can now be viewed [[PF_POSTURL] here].
@@ -14693,4 +14706,60 @@ https://wiki.firestormviewer.org/antivirus_whitelisting
yestext="Okay"/>
+
+Login request denied by Primfeed.
+
+
+Primfeed authorization failed. The authorization sequence was not completed.
+
+
+Primfeed authorization is already in progress. Please complete the Primfeed authorization in your web browser before trying again.
+
+
+Primfeed authorization completed. You may now post images to Primfeed.
+
+
+Primfeed user validation failed. Primfeed did not recognise this account, or the login failed.
+
+
+You have already linked this account to Primfeed. Use the reset button if you wish to start over.
+
+
+Primfeed user login successful, but status checks have failed. Please check the Primfeed is working.
+
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_UI.xml b/indra/newview/skins/default/xui/en/panel_preferences_UI.xml
index 73f4bd18ea..f1593ae59c 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_UI.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_UI.xml
@@ -273,7 +273,6 @@
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_firestorm.xml b/indra/newview/skins/default/xui/en/panel_preferences_firestorm.xml
index 73b834275e..20ee2fdc43 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_firestorm.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_firestorm.xml
@@ -218,6 +218,17 @@
left="10"
width="500"
tool_tip="Restores the current environment settings after next login."/>
+
diff --git a/indra/newview/skins/default/xui/en/panel_primfeed_account.xml b/indra/newview/skins/default/xui/en/panel_primfeed_account.xml
new file mode 100644
index 0000000000..ee10966b51
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_primfeed_account.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+ Not connected to Primfeed.
+
+
+
+ Account type:
+
+
+
+
+
+
+
+ [https://docs.primfeed.com Learn more about Primfeed]
+
+
+
diff --git a/indra/newview/skins/default/xui/en/panel_primfeed_photo.xml b/indra/newview/skins/default/xui/en/panel_primfeed_photo.xml
new file mode 100644
index 0000000000..061b206e0d
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_primfeed_photo.xml
@@ -0,0 +1,352 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ x
+
+
+
+
+
+
+ Refreshing...
+
+
+
+
+
+
+
+ Description:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
index d6f10ff08b..153a63fa6a 100644
--- a/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_options.xml
@@ -9,7 +9,7 @@
animate="false"
name="option_buttons"
orientation="vertical"
- height="152"
+ height="157"
width="208"
left="0"
top="9"
@@ -52,6 +52,7 @@
height="22"
image_overlay="Snapshot_Inventory"
image_overlay_alignment="left"
+ image_overlay_width="16"
image_top_pad="-1"
imgoverlay_label_space="10"
label="Save to Inventory"
@@ -111,6 +112,30 @@
function="Snapshot.SendToFlickr"/>
+
+
+
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 3c986885d1..7bbb20a0d9 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -237,6 +237,10 @@ If you feel this is an error, please contact support@secondlife.com
Problem connecting to Flickr
Problem posting to Flickr
Problem disconnecting from Flickr
+ Connecting to Primfeed...
+ Not Authorized...
+ Posting...
+ Problem posting to Primfeed
Black & White
@@ -2771,6 +2775,8 @@ name="Command_360_Capture_Label">360° Snapshot
Beacons
Poser
Pose your avatar and animated objects
+ Primfeed
+ Post directly to your Primfeed account.
Capture a 360° equirectangular image
diff --git a/indra/newview/skins/default/xui/es/floater_about.xml b/indra/newview/skins/default/xui/es/floater_about.xml
index 15cf48db0e..03b6daec06 100644
--- a/indra/newview/skins/default/xui/es/floater_about.xml
+++ b/indra/newview/skins/default/xui/es/floater_about.xml
@@ -1,5 +1,5 @@
-
+
[APP_NAME] [VIEWER_VERSION_0].[VIEWER_VERSION_1].[VIEWER_VERSION_2] ([VIEWER_VERSION_3]) [BUILD_DATE] [BUILD_TIME] ([CHANNEL]) [BUILD_TYPE]
[[VIEWER_RELEASE_NOTES_URL] Notas de la versión]
@@ -42,7 +42,7 @@ Calidad de dibujo (Render quality): [RENDERQUALITY]
Error obteniendo las notas de la versión de servidor.
-
+
Para obtener la información más reciente acerca de Firestorm, visita
@@ -58,10 +58,10 @@ Los Linden son,
con contribuciones de código de:
-
+
-
+
Firestorm es un proyecto de desarrollo de la comunidad para mejorar la experiencia de uso del Visor de SecondLife(tm). Compilamos contribuciones de varios desarrolladores de la comunidad junto con el código de Linden lab y el nuestro propio para brindarte un visor de calidad, enriquecido con nuevas características y respaldado por un amplio equipo de voluntarios para darte soporte. Firestorm llega a ti a través de The Phoenix Firestorm Project, Inc., una organización sin ánimo de lucro.
Forman el Equipo de Desarrollo de Firestorm:
diff --git a/indra/newview/skins/default/xui/fr/floater_fs_poser.xml b/indra/newview/skins/default/xui/fr/floater_fs_poser.xml
index ea7eff527e..2715702cc7 100644
--- a/indra/newview/skins/default/xui/fr/floater_fs_poser.xml
+++ b/indra/newview/skins/default/xui/fr/floater_fs_poser.xml
@@ -13,11 +13,20 @@
Membres postérieurs
Ailes
Oreilles/nez
+ Corps
+ Corps
+ Bras
+ Jambes
Tout l'avatar
Torse
+ Colonne vertébrale 1
+ Colonne vertébrale 2
+ Colonne vertébrale 3
+ Colonne vertébrale 4
Poitrine
Cou
Tête
+ Crâne
Oeil droit
Oeil gauche
Front, côté gauche
@@ -29,13 +38,17 @@
Sourcil, milieu droit
Sourcil, intérieur droit
Paupière, en haut à gauche
+ Coin interne gauche de l'œil
Paupière, en bas à gauche
Paupière, en haut à droite
+ Coin interne droit de l'œil
Paupière en bas à droite
Oreille en haut à gauche
Oreille en bas à gauche
Oreille en haut à droite
Oreille en bas à droite
+ Base du nez
+ Arête du nez
Nez à gauche
Nez au milieu
Nez à droite
@@ -52,16 +65,12 @@
Extrémité de la langue
Forme de la mâchoire
Milieu du front
- Base du nez
Dents du haut
Lèvre supérieure à gauche
Lèvre supérieure à droite
Coin gauche de la bouche
Coin droit de la bouche
Milieu de la lèvre supérieure
- Coin interne gauche de l'œil
- Coin interne droit de l'œil
- Arête du nez
Col
Bras entier
Avant-bras
@@ -137,10 +146,31 @@
Droite 2
Droite 3
Droite 4
+ Tête
+ Nuque
+ Poitrine
Fesses
Ventre
Sein gauche
Sein droit
+ Clavicule gauche
+ Clavicule droite
+ Haut du bras gauche
+ Haut du bras droit
+ Bas du bras gauche
+ Bas du bras droit
+ Main gauche
+ Main droite
+ Haut du dos
+ Taille à gauche
+ Taille à droite
+ Bassin
+ Haut de la jambe gauche
+ Haut de la jambe droite
+ Bas de la jambe gauche
+ Bas de la jambe droite
+ Pied gauche
+ Pied droit
Charger pose
Enr. pose
Charger diff
diff --git a/indra/newview/skins/default/xui/fr/floater_primfeed.xml b/indra/newview/skins/default/xui/fr/floater_primfeed.xml
new file mode 100644
index 0000000000..c8bbeab03e
--- /dev/null
+++ b/indra/newview/skins/default/xui/fr/floater_primfeed.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+ Erreur
+
+
+ Chargement...
+
+
+
+
diff --git a/indra/newview/skins/default/xui/fr/menu_viewer.xml b/indra/newview/skins/default/xui/fr/menu_viewer.xml
index 4fd69128f6..00df7d41ce 100644
--- a/indra/newview/skins/default/xui/fr/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/fr/menu_viewer.xml
@@ -93,6 +93,7 @@
+
@@ -616,6 +617,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml
index 71a7609482..93701cf7bc 100644
--- a/indra/newview/skins/default/xui/fr/notifications.xml
+++ b/indra/newview/skins/default/xui/fr/notifications.xml
@@ -3065,6 +3065,9 @@ Voulez-vous autoriser [APP_NAME] à poster sur votre compte Flickr?
Votre photo est visible maintenant [https://www.flickr.com/photos/me/[ID] ici].
+
+ Votre message Primfeed peut maintenant être consulté [[PF_POSTURL] ici].
+
Avis d'événement :
@@ -5632,4 +5635,29 @@ Pour obtenir des instructions détaillées sur la manière de mettre [APP_NAME]
https://wiki.firestormviewer.org/antivirus_whitelisting
+
+ Remplacer la pose “[POSE_NAME]”?
+
+
+
+ Demande de connexion refusée par Primfeed.
+
+
+ L'autorisation Primfeed a échoué. La séquence d'autorisation n'a pas été achevée.
+
+
+ L'autorisation Primfeed est déjà en cours. Veuillez compléter l'autorisation Primfeed dans votre navigateur web avant de réessayer.
+
+
+ Autorisation Primfeed terminée. Vous pouvez maintenant poster des images sur Primfeed.
+
+
+ La validation de l'utilisateur Primfeed a échoué. Primfeed n'a pas reconnu ce compte ou la connexion a échoué.
+
+
+ Vous avez déjà lié ce compte à Primfeed. Utilisez le bouton de réinitialisation si vous souhaitez recommencer.
+
+
+ La connexion de l'utilisateur Primfeed a réussi, mais les vérifications d'état ont échoué. Veuillez vérifier que Primfeed fonctionne.
+
diff --git a/indra/newview/skins/default/xui/fr/panel_primfeed_account.xml b/indra/newview/skins/default/xui/fr/panel_primfeed_account.xml
new file mode 100644
index 0000000000..31987c136b
--- /dev/null
+++ b/indra/newview/skins/default/xui/fr/panel_primfeed_account.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Non connecté(e) à Primfeed.
+
+
+ Type de compte :
+
+
+
+
+
+ [https://docs.primfeed.com En savoir plus sur Primfeed]
+
+
+
diff --git a/indra/newview/skins/default/xui/fr/panel_primfeed_photo.xml b/indra/newview/skins/default/xui/fr/panel_primfeed_photo.xml
new file mode 100644
index 0000000000..04d216c1b8
--- /dev/null
+++ b/indra/newview/skins/default/xui/fr/panel_primfeed_photo.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+ Rafraichissement...
+
+
+
+
+
+
+ Description :
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/indra/newview/skins/default/xui/fr/panel_snapshot_options.xml b/indra/newview/skins/default/xui/fr/panel_snapshot_options.xml
index a24f566d4c..3654e9b8ba 100644
--- a/indra/newview/skins/default/xui/fr/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/fr/panel_snapshot_options.xml
@@ -1,13 +1,30 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Les frais sont basés sur votre niveau d'abonnement. Plus haut est ce niveau, plus bas sont les frais.
diff --git a/indra/newview/skins/default/xui/ja/floater_fs_poser.xml b/indra/newview/skins/default/xui/ja/floater_fs_poser.xml
index ce2cd14fb1..d963ad9f56 100644
--- a/indra/newview/skins/default/xui/ja/floater_fs_poser.xml
+++ b/indra/newview/skins/default/xui/ja/floater_fs_poser.xml
@@ -14,13 +14,22 @@
後ろ足
羽
耳/鼻
+ 体
+ 体
+ 腕
+ 脚
アバター全体
胴体
+ 背骨1
+ 背骨2
+ 背骨3
+ 背骨4
胸部
首
頭
+ 頭蓋骨
右目
左目
額の左側
@@ -32,8 +41,10 @@
右眉毛の中側
右眉毛の内側
左の上瞼
+ 左の目頭
右の下瞼
左の上瞼
+ 右の目頭
右の下瞼
左耳の上
左耳の下
@@ -62,8 +73,6 @@
唇の角左
唇の角右
唇の角中央
- 左目尻の内側
- 右目尻の内側
鼻橋
襟
腕全体
@@ -140,10 +149,31 @@
右2
右3
右4
- お尻
+ 頭
+ 首
+ 胸
+ 尻
腹
左胸筋
右胸筋
+ 左鎖骨
+ 右鎖骨
+ 左上腕
+ 右上腕
+ 左腕
+ 右腕
+ 左手
+ 右手
+ 上背
+ 左腰
+ 右腰
+ 骨盤
+ 左腿
+ 右腿
+ 左脹脛
+ 右脹脛
+ 左足
+ 右足
ポーズを読み込む
ポーズを保存
diff --git a/indra/newview/skins/default/xui/ja/floater_primfeed.xml b/indra/newview/skins/default/xui/ja/floater_primfeed.xml
new file mode 100644
index 0000000000..60b3240b0d
--- /dev/null
+++ b/indra/newview/skins/default/xui/ja/floater_primfeed.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+ エラー
+
+
+ 読み込んでいます…
+
+
+
+
diff --git a/indra/newview/skins/default/xui/ja/menu_viewer.xml b/indra/newview/skins/default/xui/ja/menu_viewer.xml
index 1989dceb32..a0c7a496eb 100644
--- a/indra/newview/skins/default/xui/ja/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/ja/menu_viewer.xml
@@ -671,6 +671,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/ja/notifications.xml b/indra/newview/skins/default/xui/ja/notifications.xml
index 9c4cc79abc..4ef76ff045 100644
--- a/indra/newview/skins/default/xui/ja/notifications.xml
+++ b/indra/newview/skins/default/xui/ja/notifications.xml
@@ -5357,6 +5357,9 @@ http://opensimulator.org/wiki/inventory を使用して問題を修正できま
スナップショットは[http://www.flickr.com/photos/upload/edit/?ids=[ID] ここ]から閲覧できます。
+
+ あなたのPrimfeedへの投稿は[[PF_POSTURL] ここ]から閲覧できます。
+
リージョン「[REGION]」にどのラベルを使用しますか?
@@ -5645,4 +5648,25 @@ https://wiki.firestormviewer.org/antivirus_whitelisting
「[POSE_NAME]」を上書きしてもよろしいですか?
+
+ Primfeedへのログインリクエストが拒否されました。
+
+
+ Primfeed認証に失敗しました。認証シーケンスが完了していません。
+
+
+ すでにPrimfeed認証が進行中です。ブラウザでPrimfeed認証を完了してから、もう一度お試しください。
+
+
+ Primfeed認証が成功しました。Primfeedに画像を投稿できるようになりました。
+
+
+ Primfeedユーザーの検証に失敗しました。Primfeedはこのアカウントを認識しなかったか、ログインに失敗しました。
+
+
+ あなたはすでにPrimfeedにリンクされています。リセットボタンを押すことで、最初からやり直すことができます。
+
+
+ Primfeedへのユーザログインは成功しましたが、ステータスチェックに失敗しました。Primfeedが機能しているかどうかを確認してください。
+
diff --git a/indra/newview/skins/default/xui/ja/panel_primfeed_account.xml b/indra/newview/skins/default/xui/ja/panel_primfeed_account.xml
new file mode 100644
index 0000000000..05d89758af
--- /dev/null
+++ b/indra/newview/skins/default/xui/ja/panel_primfeed_account.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Primfeedに接続されていません。
+
+
+ アカウント種別:
+
+
+
+
+
+ [https://docs.primfeed.com Primfeedについて学ぶ]
+
+
+
diff --git a/indra/newview/skins/default/xui/ja/panel_primfeed_photo.xml b/indra/newview/skins/default/xui/ja/panel_primfeed_photo.xml
new file mode 100644
index 0000000000..cee3b27e78
--- /dev/null
+++ b/indra/newview/skins/default/xui/ja/panel_primfeed_photo.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ✕
+
+
+
+ リフレッシュしています…
+
+
+
+
+
+
+ 説明:
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/ja/panel_snapshot_options.xml b/indra/newview/skins/default/xui/ja/panel_snapshot_options.xml
index 5bd1d45bbd..13c2f3439f 100644
--- a/indra/newview/skins/default/xui/ja/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/ja/panel_snapshot_options.xml
@@ -13,6 +13,9 @@
+
+
+
diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml
index 0aca57f555..da49303840 100644
--- a/indra/newview/skins/default/xui/ja/strings.xml
+++ b/indra/newview/skins/default/xui/ja/strings.xml
@@ -379,6 +379,10 @@ https://secondlife.com/viewer-access-faq
Flickrからの切断時にエラーが発生
+ Primfeedに接続しています…
+ 認証されていません…
+ 投稿しています…
+ Primfeedへの投稿時に問題が発生しました。
白黒
@@ -5730,6 +5734,7 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ
ビーコン
ポーザー
アバターとアニメーションオブジェクトにポーズをつけます。
+ Primfeedに直接投稿します。
360度の正距円筒図法のスナップショット画像を撮影します。
diff --git a/indra/newview/skins/default/xui/pl/floater_fs_area_search.xml b/indra/newview/skins/default/xui/pl/floater_fs_area_search.xml
index 0543b37394..8ef3eb1af2 100644
--- a/indra/newview/skins/default/xui/pl/floater_fs_area_search.xml
+++ b/indra/newview/skins/default/xui/pl/floater_fs_area_search.xml
@@ -62,6 +62,7 @@
+
oraz
@@ -91,6 +92,7 @@
+
diff --git a/indra/newview/skins/default/xui/pl/floater_fs_poser.xml b/indra/newview/skins/default/xui/pl/floater_fs_poser.xml
index 12f86013f1..b0a399dd06 100644
--- a/indra/newview/skins/default/xui/pl/floater_fs_poser.xml
+++ b/indra/newview/skins/default/xui/pl/floater_fs_poser.xml
@@ -13,11 +13,20 @@
Kończyny tylne
Skrzydła
Uszy/Nos
+ Ciało
+ Ciało
+ Ramiona
+ Nogi
Cały awatar
Tułów
+ Kręgosłup 1
+ Kręgosłup 2
+ Kręgosłup 3
+ Kręgosłup 4
Klatka piersiowa
Szyja
Głowa
+ Czaszka
Prawe oko
Lewe oko
Czoło, lewa strona
@@ -29,13 +38,17 @@
Brwi środkowe prawe
Brwi wewnętrzne prawe
Powieka górna lewa
+ Wewn. lewy kącik oka
Powieka dolna lewa
Powieka górna prawa
+ Wewn. prawy kącik oka
Powieka dolna prawa
Ucho górne lewe
Ucho dolne lewe
Ucho górne prawe
Ucho dolne prawe
+ Podstawa nosa
+ Grzbiet nosa
Nos lewy
Nos środkowy
Nos prawy
@@ -52,16 +65,12 @@
Czubek języka
Kształt szczęki
Środek czoła
- Podstawa nosa
Zęby górne
Warga górna lewa
Warga górna prawa
Lewy kącik ust
Prawy kącik ust
Warga górna środkowa
- Wewn. lewy kącik oka
- Wewn. prawy kącik oka
- Grzbiet nosa
Obojczyk
Całe ramię
Przedramię
@@ -137,10 +146,31 @@
Prawo 2
Prawo 3
Prawo 4
+ Głowa
+ Szyja
+ Kl. piersiowa
Pośladki
Brzuch
Lewa pierś
Prawa pierś
+ Lewy obojczyk
+ Prawy obojczyk
+ Lewe górne ramię
+ Prawe górne ramię
+ Lewe przedramię
+ Prawe przedramię
+ Lewa ręka
+ Prawa ręka
+ Górna część pleców
+ Lewa talia
+ Prawa talia
+ Miednica
+ Lewa górna noga
+ Prawa górna noga
+ Lewa dolna noga
+ Prawa dolna noga
+ Lewa stopa
+ Prawa stopa
Ładuj pozę
Zapis pozy
Ładuj różn.
diff --git a/indra/newview/skins/default/xui/pl/floater_primfeed.xml b/indra/newview/skins/default/xui/pl/floater_primfeed.xml
new file mode 100644
index 0000000000..31cdd56d89
--- /dev/null
+++ b/indra/newview/skins/default/xui/pl/floater_primfeed.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+ Błąd
+
+
+ Wczytywanie...
+
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/menu_viewer.xml b/indra/newview/skins/default/xui/pl/menu_viewer.xml
index 61861792cf..0597ca0bff 100644
--- a/indra/newview/skins/default/xui/pl/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/pl/menu_viewer.xml
@@ -658,6 +658,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/pl/notifications.xml b/indra/newview/skins/default/xui/pl/notifications.xml
index 67ff7344e6..5fe5f77086 100644
--- a/indra/newview/skins/default/xui/pl/notifications.xml
+++ b/indra/newview/skins/default/xui/pl/notifications.xml
@@ -5226,6 +5226,9 @@ Czy chcesz autoryzować [APP_NAME] do przesyłania zdjęć na Flickr?
Możesz znaleźć swoje zdjęcie [https://www.flickr.com/photos/me/[ID] tutaj].
+
+ Możesz znaleźć swój post [[PF_POSTURL] tutaj].
+
Jaką etykietę chcesz nadać regionowi
"[REGION]"?
@@ -5489,4 +5492,25 @@ https://wiki.firestormviewer.org/antivirus_whitelisting
Nadpisać istniejącą pozę “[POSE_NAME]”?
+
+ Żądanie logowania odrzucone przez Primfeed.
+
+
+ Autoryzacja w Primfeed nie powiodła się. Sekwencja autoryzacji nie została ukończona.
+
+
+ Autoryzacja z Primfeed jest już w toku. Przed ponowną próbą należy ukończyć autoryzację Primfeed w przeglądarce internetowej.
+
+
+ Autoryzacja w Primfeed została ukończona. Teraz możesz publikować obrazy w Primfeed.
+
+
+ Weryfikacja użytkownika w Primfeed nie powiodła się. Primfeed nie rozpoznał tego konta lub logowanie nie powiodło się.
+
+
+ Połączyłeś/aś już to konto z Primfeed. Użyj przycisku resetowania, jeśli chcesz zacząć od nowa.
+
+
+ Zalogowanie użytkownika w Primfeed powiodło się, ale sprawdzenie statusu już nie. Sprawdź, czy Primfeed działa.
+
diff --git a/indra/newview/skins/default/xui/pl/panel_primfeed_account.xml b/indra/newview/skins/default/xui/pl/panel_primfeed_account.xml
new file mode 100644
index 0000000000..b5616667a4
--- /dev/null
+++ b/indra/newview/skins/default/xui/pl/panel_primfeed_account.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Brak połączenia z Primfeed.
+
+
+ Typ konta:
+
+
+
+
+
+ [https://docs.primfeed.com Więcej o Primfeed]
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/panel_primfeed_photo.xml b/indra/newview/skins/default/xui/pl/panel_primfeed_photo.xml
new file mode 100644
index 0000000000..603ead08a2
--- /dev/null
+++ b/indra/newview/skins/default/xui/pl/panel_primfeed_photo.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+ Odświeżanie...
+
+
+
+
+
+
+ Opis:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/panel_snapshot_options.xml b/indra/newview/skins/default/xui/pl/panel_snapshot_options.xml
index c5fa01fb81..3d1d9cbb1b 100644
--- a/indra/newview/skins/default/xui/pl/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/pl/panel_snapshot_options.xml
@@ -13,6 +13,9 @@
+
+
+
diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml
index 208a5877ee..97460b2e2c 100644
--- a/indra/newview/skins/default/xui/pl/strings.xml
+++ b/indra/newview/skins/default/xui/pl/strings.xml
@@ -392,6 +392,18 @@ Jeśli myślisz, że to błąd skontaktuj się z support@secondlife.com
Problem z rozłączaniem z Flickr
+
+ Łączenie z Primfeed...
+
+
+ Brak uprawnień...
+
+
+ Wysyłanie...
+
+
+ Problem z wysyłaniem na Primfeed
+
Czerń i biel
@@ -5503,6 +5515,9 @@ Spróbuj załączyć ścieżkę do edytora w cytowaniu.
Ustaw swojego awatara oraz animowane obiekty
+
+ Publikuj bezpośrednio na swoim koncie Primfeed.
+
Uchwyć równoprostokątny obraz 360°
diff --git a/indra/newview/skins/default/xui/ru/notifications.xml b/indra/newview/skins/default/xui/ru/notifications.xml
index d5b1bf8128..bbeed352ce 100644
--- a/indra/newview/skins/default/xui/ru/notifications.xml
+++ b/indra/newview/skins/default/xui/ru/notifications.xml
@@ -3637,8 +3637,8 @@ URL: [AUDIOURL]
Если вы - владелец премиум-аккаунта, [[PREMIUM_URL] щелкните здесь], чтобы получить право на анимационное изменение голоса.
- Срок действия одного или нескольких ваших типов анимационного изменения голоса истекает через [INTERVAL] дней или раньше.
-[[URL] Щелкните здесь], чтобы обновить подписку.
+ Срок действия одного или нескольких ваших типов анимационного изменения голоса истекает через [INTERVAL] дней или раньше.
+[[URL] Щелкните здесь], чтобы обновить подписку.
Если вы - владелец премиум-аккаунта, [[PREMIUM_URL] щелкните здесь], чтобы получить право на анимационное изменение голоса.
@@ -5630,4 +5630,9 @@ https://wiki.firestormviewer.org/fs_voice
https://wiki.firestormviewer.org/antivirus_whitelisting
+
+ Перезаписать существующую позу “[POSE_NAME]”?
+
+
+
diff --git a/indra/newview/skins/default/xui/ru/panel_preferences_chat.xml b/indra/newview/skins/default/xui/ru/panel_preferences_chat.xml
index 109e1e6393..5f72cc99e8 100644
--- a/indra/newview/skins/default/xui/ru/panel_preferences_chat.xml
+++ b/indra/newview/skins/default/xui/ru/panel_preferences_chat.xml
@@ -29,6 +29,7 @@
+
diff --git a/indra/newview/skins/default/xui/ru/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/ru/panel_preferences_graphics1.xml
index b8e405f89e..dfd6881374 100644
--- a/indra/newview/skins/default/xui/ru/panel_preferences_graphics1.xml
+++ b/indra/newview/skins/default/xui/ru/panel_preferences_graphics1.xml
@@ -218,6 +218,16 @@
+
+ Макс. датчиков отражения
+
+
+
+
+
+
+
+
Карты тонов:
diff --git a/indra/newview/skins/default/xui/zh/floater_fs_poser.xml b/indra/newview/skins/default/xui/zh/floater_fs_poser.xml
index 5787ea87ea..5df4b17d34 100644
--- a/indra/newview/skins/default/xui/zh/floater_fs_poser.xml
+++ b/indra/newview/skins/default/xui/zh/floater_fs_poser.xml
@@ -13,11 +13,20 @@
後肢
翼
耳朵/鼻子
+ 身體
+ 身體
+ 雙臂
+ 雙腿
化身整體
軀幹
+ 脊柱1
+ 脊柱2
+ 脊柱3
+ 脊柱4
胸部
頸部
頭顱
+ 頭骨
右眼
左眼
左額頭角
@@ -29,13 +38,17 @@
右眉毛中
右眉毛內
左上眼瞼
+ 左内眼角
左下眼瞼
右上眼瞼
+ 右内眼角
右下眼瞼
左耳上
左耳下
右耳上
右耳下
+ 鼻翼
+ 鼻樑
鼻子左
鼻子中
鼻子右
@@ -52,17 +65,13 @@
舌尖
下頜形狀
額頭中
- 鼻根
上牙床
上唇左
上唇右
左唇角
右唇角
上唇中
- 左內眼角
- 右內眼角
- 鼻樑
- 頸根部
+ 頸部左
左肩
左肘
左腕
@@ -81,7 +90,7 @@
左拇指根
左拇指中
左拇指尖
- 頸根部
+ 頸部右
右肩
右肘
右腕
@@ -128,19 +137,40 @@
尾巴5
尾巴末梢
腹股溝
- 根
- 左基礎
+ 後肢根部
+ 左後肢
左2
左3
左4
- 右基礎
+ 右後肢
右2
右3
右4
+ 頭部
+ 頸部
+ 胸部
臀部
腹部
左胸肌
右胸肌
+ 左鎖骨
+ 右鎖骨
+ 左上臂
+ 右上臂
+ 左下臂
+ 右下臂
+ 左手
+ 右手
+ 上背部
+ 左腰
+ 右腰
+ 骨盆
+ 左大腿
+ 右大腿
+ 左小腿
+ 右小腿
+ 左腳
+ 右腳
載入姿勢
儲存姿勢
載入差異
diff --git a/indra/newview/skins/default/xui/zh/floater_primfeed.xml b/indra/newview/skins/default/xui/zh/floater_primfeed.xml
new file mode 100644
index 0000000000..c3fc7b4175
--- /dev/null
+++ b/indra/newview/skins/default/xui/zh/floater_primfeed.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+ 錯誤
+
+
+ 加載中...
+
+
+
+
diff --git a/indra/newview/skins/default/xui/zh/menu_viewer.xml b/indra/newview/skins/default/xui/zh/menu_viewer.xml
index fe3c048f1d..68f17fadc7 100644
--- a/indra/newview/skins/default/xui/zh/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/zh/menu_viewer.xml
@@ -95,6 +95,7 @@
+
@@ -638,6 +639,8 @@
+
+
diff --git a/indra/newview/skins/default/xui/zh/notifications.xml b/indra/newview/skins/default/xui/zh/notifications.xml
index 42afa43745..316ab075e7 100644
--- a/indra/newview/skins/default/xui/zh/notifications.xml
+++ b/indra/newview/skins/default/xui/zh/notifications.xml
@@ -5457,7 +5457,7 @@ switch語句在沒有預設標籤情況下的行為不正確已被修正。
爲了使用 Flickr 上傳功能,[APP_NAME] 需要被授權訪問您的賬戶。繼續操作將會打開 Flickr 網站的一個網頁,在那裡您需要登錄並授權 [APP_NAME]。然後,您需要將顯示的程式碼輸入到 [APP_NAME] 中。
-是否授權 [APP_NAME] 在您的 Flickr 賬戶下發布照片?
+是否授權 [APP_NAME] 在您的 Flickr 賬戶下發布相片?
@@ -5474,6 +5474,9 @@ Flickr 驗證失敗。請重試並檢查輸入的程式碼。
現在可以在這裡 https://www.flickr.com/photos/me/[ID] 檢視相片。
+
+ Primfeed發佈已完成,可以[在這裡[PF_POSTURL]]查看。
+
應該為區域「[REGION]」
使用什麼標籤?
@@ -5753,4 +5756,25 @@ https://wiki.firestormviewer.org/antivirus_whitelisting
是否覆蓋現有的姿勢「[POSE_NAME]」?
+
+ Primfeed 登錄請求被拒絕。
+
+
+ Primfeed 授權失敗。授權程序尚未完成。
+
+
+ Primfeed 授權正在處理中。請在您的網頁瀏覽器中完成 Primfeed 授權後再重試。
+
+
+ Primfeed 授權已完成。您現在可以在 Primfeed 平台上發佈相片。
+
+
+ Primfeed 帳戶驗證失敗。Primfeed 無法識別此帳戶或登錄失敗。
+
+
+ 此帳戶已連接至 Primfeed。若需重新授權,請使用重設按鈕。
+
+
+ Primfeed 用戶已成功登錄,但狀態檢查失敗。請確認 Primfeed 服務是否正常運作。
+
diff --git a/indra/newview/skins/default/xui/zh/panel_primfeed_account.xml b/indra/newview/skins/default/xui/zh/panel_primfeed_account.xml
new file mode 100644
index 0000000000..2aaad426ae
--- /dev/null
+++ b/indra/newview/skins/default/xui/zh/panel_primfeed_account.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ 尚未連接到Primfeed。
+
+
+ 帳戶類型:
+
+
+
+
+
+ [https://docs.primfeed.com 點擊了解更多關於Primfeed的資訊]
+
+
+
diff --git a/indra/newview/skins/default/xui/zh/panel_primfeed_photo.xml b/indra/newview/skins/default/xui/zh/panel_primfeed_photo.xml
new file mode 100644
index 0000000000..40de4cfee8
--- /dev/null
+++ b/indra/newview/skins/default/xui/zh/panel_primfeed_photo.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+ 正在更新...
+
+
+
+
+
+
+ 描述:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/indra/newview/skins/default/xui/zh/panel_snapshot_options.xml b/indra/newview/skins/default/xui/zh/panel_snapshot_options.xml
index 65aec14d19..8f31949fac 100644
--- a/indra/newview/skins/default/xui/zh/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/zh/panel_snapshot_options.xml
@@ -13,6 +13,9 @@
+
+
+
diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml
index 1741143e1c..0c2bff8390 100644
--- a/indra/newview/skins/default/xui/zh/strings.xml
+++ b/indra/newview/skins/default/xui/zh/strings.xml
@@ -436,7 +436,18 @@ support@secondlife.com.
斷開與Flickr的連線時發生錯誤
-
+
+ 正在連接到Primfeed...
+
+
+ 未經授權...
+
+
+ 正在發布...
+
+
+ 發布到Primfeed時出現問題
+
黑 & 白
@@ -6292,6 +6303,12 @@ support@secondlife.com.
編輯你的化身與動畫物件的姿勢
+
+ Primfeed
+
+
+ 直接發佈到您的Primfeed賬戶。
+
拍攝360°旋轉圖像
diff --git a/indra/newview/skins/starlight/xui/en/floater_tools.xml b/indra/newview/skins/starlight/xui/en/floater_tools.xml
index 0e10d331d7..7966ddb6ca 100644
--- a/indra/newview/skins/starlight/xui/en/floater_tools.xml
+++ b/indra/newview/skins/starlight/xui/en/floater_tools.xml
@@ -3333,16 +3333,27 @@ Low ↔ Lwst
top_pad="1"
name="btn_reset_scripts"
width="90" />
+
diff --git a/indra/newview/skins/starlightcui/xui/en/floater_tools.xml b/indra/newview/skins/starlightcui/xui/en/floater_tools.xml
index 97b4d49fea..3267067be7 100644
--- a/indra/newview/skins/starlightcui/xui/en/floater_tools.xml
+++ b/indra/newview/skins/starlightcui/xui/en/floater_tools.xml
@@ -3334,16 +3334,27 @@ Low ↔ Lwst
top_pad="1"
name="btn_reset_scripts"
width="90" />
+
diff --git a/indra/newview/skins/starlightcui/xui/en/sidepanel_task_info.xml b/indra/newview/skins/starlightcui/xui/en/sidepanel_task_info.xml
index 52b7dc039a..94e3ec5e1a 100644
--- a/indra/newview/skins/starlightcui/xui/en/sidepanel_task_info.xml
+++ b/indra/newview/skins/starlightcui/xui/en/sidepanel_task_info.xml
@@ -67,7 +67,7 @@
font="SansSerifHuge"
height="26"
layout="topleft"
- left_pad="10"
+ left="48"
name="title"
text_color="LtGray"
top="0"
diff --git a/indra/newview/skins/vintage/xui/en/floater_tools.xml b/indra/newview/skins/vintage/xui/en/floater_tools.xml
index 2428970032..39bc23dfdb 100644
--- a/indra/newview/skins/vintage/xui/en/floater_tools.xml
+++ b/indra/newview/skins/vintage/xui/en/floater_tools.xml
@@ -3335,16 +3335,27 @@ Low ↔ Lwst
top_pad="1"
name="btn_reset_scripts"
width="90" />
+