summaryrefslogtreecommitdiff
path: root/indra/llui/llnotifications.cpp
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
committerAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
commit1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch)
treeab243607f74f78200787bba5b9b88f07ef1b966f /indra/llui/llnotifications.cpp
parent6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff)
parente1623bb276f83a43ce7a197e388720c05bdefe61 (diff)
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts: # autobuild.xml # indra/cmake/CMakeLists.txt # indra/cmake/GoogleMock.cmake # indra/llaudio/llaudioengine_fmodstudio.cpp # indra/llaudio/llaudioengine_fmodstudio.h # indra/llaudio/lllistener_fmodstudio.cpp # indra/llaudio/lllistener_fmodstudio.h # indra/llaudio/llstreamingaudio_fmodstudio.cpp # indra/llaudio/llstreamingaudio_fmodstudio.h # indra/llcharacter/llmultigesture.cpp # indra/llcharacter/llmultigesture.h # indra/llimage/llimage.cpp # indra/llimage/llimagepng.cpp # indra/llimage/llimageworker.cpp # indra/llimage/tests/llimageworker_test.cpp # indra/llmessage/tests/llmockhttpclient.h # indra/llprimitive/llgltfmaterial.h # indra/llrender/llfontfreetype.cpp # indra/llui/llcombobox.cpp # indra/llui/llfolderview.cpp # indra/llui/llfolderviewmodel.h # indra/llui/lllineeditor.cpp # indra/llui/lllineeditor.h # indra/llui/lltextbase.cpp # indra/llui/lltextbase.h # indra/llui/lltexteditor.cpp # indra/llui/lltextvalidate.cpp # indra/llui/lltextvalidate.h # indra/llui/lluictrl.h # indra/llui/llview.cpp # indra/llwindow/llwindowmacosx.cpp # indra/newview/app_settings/settings.xml # indra/newview/llappearancemgr.cpp # indra/newview/llappearancemgr.h # indra/newview/llavatarpropertiesprocessor.cpp # indra/newview/llavatarpropertiesprocessor.h # indra/newview/llbreadcrumbview.cpp # indra/newview/llbreadcrumbview.h # indra/newview/llbreastmotion.cpp # indra/newview/llbreastmotion.h # indra/newview/llconversationmodel.h # indra/newview/lldensityctrl.cpp # indra/newview/lldensityctrl.h # indra/newview/llface.inl # indra/newview/llfloatereditsky.cpp # indra/newview/llfloatereditwater.cpp # indra/newview/llfloateremojipicker.h # indra/newview/llfloaterimsessiontab.cpp # indra/newview/llfloaterprofiletexture.cpp # indra/newview/llfloaterprofiletexture.h # indra/newview/llgesturemgr.cpp # indra/newview/llgesturemgr.h # indra/newview/llimpanel.cpp # indra/newview/llimpanel.h # indra/newview/llinventorybridge.cpp # indra/newview/llinventorybridge.h # indra/newview/llinventoryclipboard.cpp # indra/newview/llinventoryclipboard.h # indra/newview/llinventoryfunctions.cpp # indra/newview/llinventoryfunctions.h # indra/newview/llinventorygallery.cpp # indra/newview/lllistbrowser.cpp # indra/newview/lllistbrowser.h # indra/newview/llpanelobjectinventory.cpp # indra/newview/llpanelprofile.cpp # indra/newview/llpanelprofile.h # indra/newview/llpreviewgesture.cpp # indra/newview/llsavedsettingsglue.cpp # indra/newview/llsavedsettingsglue.h # indra/newview/lltooldraganddrop.cpp # indra/newview/llurllineeditorctrl.cpp # indra/newview/llvectorperfoptions.cpp # indra/newview/llvectorperfoptions.h # indra/newview/llviewerparceloverlay.cpp # indra/newview/llviewertexlayer.cpp # indra/newview/llviewertexturelist.cpp # indra/newview/macmain.h # indra/test/test.cpp
Diffstat (limited to 'indra/llui/llnotifications.cpp')
-rw-r--r--indra/llui/llnotifications.cpp4012
1 files changed, 2009 insertions, 2003 deletions
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 9ba8e9e645..9549860972 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -1,2003 +1,2009 @@
-/**
-* @file llnotifications.cpp
-* @brief Non-UI queue manager for keeping a prioritized list of notifications
-*
-* $LicenseInfo:firstyear=2008&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2010, Linden Research, Inc.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation;
-* version 2.1 of the License only.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*
-* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-* $/LicenseInfo$
-*/
-
-#include "linden_common.h"
-
-#include "llnotifications.h"
-#include "llnotificationtemplate.h"
-#include "llnotificationvisibilityrule.h"
-
-#include "llavatarnamecache.h"
-#include "llinstantmessage.h"
-#include "llcachename.h"
-#include "llxmlnode.h"
-#include "lluictrl.h"
-#include "lluictrlfactory.h"
-#include "lldir.h"
-#include "llsdserialize.h"
-#include "lltrans.h"
-#include "llstring.h"
-#include "llsdparam.h"
-#include "llsdutil.h"
-
-#include <algorithm>
-#include <boost/regex.hpp>
-
-
-const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
-
-void NotificationPriorityValues::declareValues()
-{
- declare("low", NOTIFICATION_PRIORITY_LOW);
- declare("normal", NOTIFICATION_PRIORITY_NORMAL);
- declare("high", NOTIFICATION_PRIORITY_HIGH);
- declare("critical", NOTIFICATION_PRIORITY_CRITICAL);
-}
-
-LLNotificationForm::FormElementBase::FormElementBase()
-: name("name"),
- enabled("enabled", true)
-{}
-
-LLNotificationForm::FormIgnore::FormIgnore()
-: text("text"),
- control("control"),
- invert_control("invert_control", false),
- save_option("save_option", false),
- session_only("session_only", false),
- checkbox_only("checkbox_only", false)
-{}
-
-LLNotificationForm::FormButton::FormButton()
-: index("index"),
- text("text"),
- ignore("ignore"),
- is_default("default"),
- width("width", 0),
- type("type")
-{
- // set type here so it gets serialized
- type = "button";
-}
-
-LLNotificationForm::FormInput::FormInput()
-: type("type"),
- text("text"),
- max_length_chars("max_length_chars"),
- width("width", 0),
- value("value")
-{}
-
-LLNotificationForm::FormElement::FormElement()
-: button("button"),
- input("input")
-{}
-
-LLNotificationForm::FormElements::FormElements()
-: elements("")
-{}
-
-LLNotificationForm::Params::Params()
-: name("name"),
- ignore("ignore"),
- form_elements("")
-{}
-
-
-
-bool filterIgnoredNotifications(LLNotificationPtr notification)
-{
- LLNotificationFormPtr form = notification->getForm();
- // Check to see if the user wants to ignore this alert
- return !notification->getForm()->getIgnored();
-}
-
-bool handleIgnoredNotification(const LLSD& payload)
-{
- if (payload["sigtype"].asString() == "add")
- {
- LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
- if (!pNotif) return false;
-
- LLNotificationFormPtr form = pNotif->getForm();
- LLSD response;
- switch(form->getIgnoreType())
- {
- case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE:
- case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY:
- response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON);
- break;
- case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE:
- response = LLUI::getInstance()->mSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName());
- break;
- case LLNotificationForm::IGNORE_SHOW_AGAIN:
- break;
- default:
- return false;
- }
- pNotif->setIgnored(true);
- pNotif->respond(response);
- return true; // don't process this item any further
- }
- return false;
-}
-
-bool defaultResponse(const LLSD& payload)
-{
- if (payload["sigtype"].asString() == "add")
- {
- LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
- if (pNotif)
- {
- // supply default response
- pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON));
- }
- }
- return false;
-}
-
-bool visibilityRuleMached(const LLSD& payload)
-{
- // This is needed because LLNotifications::isVisibleByRules may have cancelled the notification.
- // Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order.
- return true;
-}
-
-
-namespace LLNotificationFilters
-{
- // a sample filter
- bool includeEverything(LLNotificationPtr p)
- {
- return true;
- }
-};
-
-LLNotificationForm::LLNotificationForm()
-: mIgnore(IGNORE_NO)
-{
-}
-
-LLNotificationForm::LLNotificationForm( const LLNotificationForm& other )
-{
- mFormData = other.mFormData;
- mIgnore = other.mIgnore;
- mIgnoreMsg = other.mIgnoreMsg;
- mIgnoreSetting = other.mIgnoreSetting;
- mInvertSetting = other.mInvertSetting;
-}
-
-LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p)
-: mIgnore(IGNORE_NO),
- mInvertSetting(false) // ignore settings by default mean true=show, false=ignore
-{
- if (p.ignore.isProvided())
- {
- // For all cases but IGNORE_CHECKBOX_ONLY this is name for use in preferences
- mIgnoreMsg = p.ignore.text;
-
- LLUI *ui_inst = LLUI::getInstance();
- if (p.ignore.checkbox_only)
- {
- mIgnore = IGNORE_CHECKBOX_ONLY;
- }
- else if (!p.ignore.save_option)
- {
- mIgnore = p.ignore.session_only ? IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY : IGNORE_WITH_DEFAULT_RESPONSE;
- }
- else
- {
- // remember last option chosen by user and automatically respond with that in the future
- mIgnore = IGNORE_WITH_LAST_RESPONSE;
- ui_inst->mSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
- }
-
- bool show_notification = true;
- if (p.ignore.control.isProvided())
- {
- mIgnoreSetting = ui_inst->mSettingGroups["config"]->getControl(p.ignore.control());
- mInvertSetting = p.ignore.invert_control;
- }
- else if (mIgnore > IGNORE_NO)
- {
- ui_inst->mSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", LLControlVariable::PERSIST_NONDFT);
- mIgnoreSetting = ui_inst->mSettingGroups["ignores"]->getControl(name);
- }
- }
-
- LLParamSDParser parser;
- parser.writeSD(mFormData, p.form_elements);
-
- for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray();
- it != end_it;
- ++it)
- {
- // lift contents of form element up a level, since element type is already encoded in "type" param
- if (it->isMap() && it->beginMap() != it->endMap())
- {
- *it = it->beginMap()->second;
- }
- }
-
- LL_DEBUGS("Notifications") << name << LL_ENDL;
- LL_DEBUGS("Notifications") << ll_pretty_print_sd(mFormData) << LL_ENDL;
-}
-
-LLNotificationForm::LLNotificationForm(const LLSD& sd)
- : mIgnore(IGNORE_NO)
-{
- if (sd.isArray())
- {
- mFormData = sd;
- }
- else
- {
- LL_WARNS("Notifications") << "Invalid form data " << sd << LL_ENDL;
- mFormData = LLSD::emptyArray();
- }
-}
-
-LLSD LLNotificationForm::asLLSD() const
-{
- return mFormData;
-}
-
-LLSD LLNotificationForm::getElement(const std::string& element_name)
-{
- for (LLSD::array_const_iterator it = mFormData.beginArray();
- it != mFormData.endArray();
- ++it)
- {
- if ((*it)["name"].asString() == element_name) return (*it);
- }
- return LLSD();
-}
-
-
-bool LLNotificationForm::hasElement(const std::string& element_name) const
-{
- for (LLSD::array_const_iterator it = mFormData.beginArray();
- it != mFormData.endArray();
- ++it)
- {
- if ((*it)["name"].asString() == element_name) return true;
- }
- return false;
-}
-
-void LLNotificationForm::getElements(LLSD& elements, S32 offset)
-{
- //Finds elements that the template did not add
- LLSD::array_const_iterator it = mFormData.beginArray() + offset;
-
- //Keeps track of only the dynamic elements
- for(; it != mFormData.endArray(); ++it)
- {
- elements.append(*it);
- }
-}
-
-bool LLNotificationForm::getElementEnabled(const std::string& element_name) const
-{
- for (LLSD::array_const_iterator it = mFormData.beginArray();
- it != mFormData.endArray();
- ++it)
- {
- if ((*it)["name"].asString() == element_name)
- {
- return (*it)["enabled"].asBoolean();
- }
- }
-
- return false;
-}
-
-void LLNotificationForm::setElementEnabled(const std::string& element_name, bool enabled)
-{
- for (LLSD::array_iterator it = mFormData.beginArray();
- it != mFormData.endArray();
- ++it)
- {
- if ((*it)["name"].asString() == element_name)
- {
- (*it)["enabled"] = enabled;
- }
- }
-}
-
-
-void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value, bool enabled)
-{
- LLSD element;
- element["type"] = type;
- element["name"] = name;
- element["text"] = name;
- element["value"] = value;
- element["index"] = LLSD::Integer(mFormData.size());
- element["enabled"] = enabled;
- mFormData.append(element);
-}
-
-void LLNotificationForm::append(const LLSD& sub_form)
-{
- if (sub_form.isArray())
- {
- for (LLSD::array_const_iterator it = sub_form.beginArray();
- it != sub_form.endArray();
- ++it)
- {
- mFormData.append(*it);
- }
- }
-}
-
-void LLNotificationForm::formatElements(const LLSD& substitutions)
-{
- for (LLSD::array_iterator it = mFormData.beginArray();
- it != mFormData.endArray();
- ++it)
- {
- // format "text" component of each form element
- if ((*it).has("text"))
- {
- std::string text = (*it)["text"].asString();
- LLStringUtil::format(text, substitutions);
- (*it)["text"] = text;
- }
- if ((*it)["type"].asString() == "text" && (*it).has("value"))
- {
- std::string value = (*it)["value"].asString();
- LLStringUtil::format(value, substitutions);
- (*it)["value"] = value;
- }
- }
-}
-
-std::string LLNotificationForm::getDefaultOption()
-{
- for (LLSD::array_const_iterator it = mFormData.beginArray();
- it != mFormData.endArray();
- ++it)
- {
- if ((*it)["default"]) return (*it)["name"].asString();
- }
- return "";
-}
-
-LLControlVariablePtr LLNotificationForm::getIgnoreSetting()
-{
- return mIgnoreSetting;
-}
-
-bool LLNotificationForm::getIgnored()
-{
- bool show = true;
- if (mIgnore > LLNotificationForm::IGNORE_NO
- && mIgnoreSetting)
- {
- show = mIgnoreSetting->getValue().asBoolean();
- if (mInvertSetting) show = !show;
- }
- return !show;
-}
-
-void LLNotificationForm::setIgnored(bool ignored)
-{
- if (mIgnoreSetting)
- {
- if (mInvertSetting) ignored = !ignored;
- mIgnoreSetting->setValue(!ignored);
- }
-}
-
-LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Params& p)
-: mName(p.name),
- mType(p.type),
- mMessage(p.value),
- mFooter(p.footer.value),
- mLabel(p.label),
- mIcon(p.icon),
- mURL(p.url.value),
- mExpireSeconds(p.duration),
- mExpireOption(p.expire_option),
- mURLOption(p.url.option),
- mURLTarget(p.url.target),
- mForceUrlsExternal(p.force_urls_external),
- mUnique(p.unique.isProvided()),
- mCombineBehavior(p.unique.combine),
- mPriority(p.priority),
- mPersist(p.persist),
- mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()),
- mLogToChat(p.log_to_chat),
- mLogToIM(p.log_to_im),
- mShowToast(p.show_toast),
- mFadeToast(p.fade_toast),
- mSoundName("")
-{
- if (p.sound.isProvided()
- && LLUI::getInstance()->mSettingGroups["config"]->controlExists(p.sound))
- {
- mSoundName = p.sound;
- }
-
- for (const LLNotificationTemplate::UniquenessContext& context : p.unique.contexts)
- {
- mUniqueContext.push_back(context.value);
- }
-
- LL_DEBUGS("Notifications") << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL;
-
- for (const LLNotificationTemplate::Tag& tag : p.tags)
- {
- LL_DEBUGS("Notifications") << " tag \"" << std::string(tag.value) << "\"" << LL_ENDL;
- mTags.push_back(tag.value);
- }
-
- mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form));
-}
-
-LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p)
-{
- if (p.show.isChosen())
- {
- mType = p.show.type;
- mTag = p.show.tag;
- mName = p.show.name;
- mVisible = true;
- }
- else if (p.hide.isChosen())
- {
- mType = p.hide.type;
- mTag = p.hide.tag;
- mName = p.hide.name;
- mVisible = false;
- }
- else if (p.respond.isChosen())
- {
- mType = p.respond.type;
- mTag = p.respond.tag;
- mName = p.respond.name;
- mVisible = false;
- mResponse = p.respond.response;
- }
-}
-
-LLNotification::LLNotification(const LLSDParamAdapter<Params>& p) :
- mTimestamp(p.time_stamp),
- mSubstitutions(p.substitutions),
- mPayload(p.payload),
- mExpiresAt(p.expiry),
- mTemporaryResponder(false),
- mRespondedTo(false),
- mPriority(p.priority),
- mCancelled(false),
- mIgnored(false),
- mResponderObj(NULL),
- mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()),
- mOfferFromAgent(p.offer_from_agent),
- mIsDND(p.is_dnd)
-{
- if (p.functor.name.isChosen())
- {
- mResponseFunctorName = p.functor.name;
- }
- else if (p.functor.function.isChosen())
- {
- mResponseFunctorName = LLUUID::generateNewID().asString();
- LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function());
-
- mTemporaryResponder = true;
- }
- else if(p.functor.responder.isChosen())
- {
- mResponder = p.functor.responder;
- }
-
- if(p.responder.isProvided())
- {
- mResponderObj = p.responder;
- }
-
- init(p.name, p.form_elements);
-}
-
-
-LLSD LLNotification::asLLSD(bool excludeTemplateElements)
-{
- LLParamSDParser parser;
-
- Params p;
- p.id = mId;
- p.name = mTemplatep->mName;
- p.substitutions = mSubstitutions;
- p.payload = mPayload;
- p.time_stamp = mTimestamp;
- p.expiry = mExpiresAt;
- p.priority = mPriority;
-
- LLNotificationFormPtr templateForm = mTemplatep->mForm;
- LLSD formElements = mForm->asLLSD();
-
- //All form elements (dynamic or not)
- if(!excludeTemplateElements)
- {
- p.form_elements = formElements;
- }
- //Only dynamic form elements (exclude template elements)
- else if(templateForm->getNumElements() < formElements.size())
- {
- LLSD dynamicElements;
- //Offset to dynamic elements and store them
- mForm->getElements(dynamicElements, templateForm->getNumElements());
- p.form_elements = dynamicElements;
- }
-
- if(mResponder)
- {
- p.functor.responder_sd = mResponder->asLLSD();
- }
-
- if(!mResponseFunctorName.empty())
- {
- p.functor.name = mResponseFunctorName;
- }
-
- LLSD output;
- parser.writeSD(output, p);
- return output;
-}
-
-void LLNotification::update()
-{
- LLNotifications::instance().update(shared_from_this());
-}
-
-void LLNotification::updateFrom(LLNotificationPtr other)
-{
- // can only update from the same notification type
- if (mTemplatep != other->mTemplatep) return;
-
- // NOTE: do NOT change the ID, since it is the key to
- // this given instance, just update all the metadata
- //mId = other->mId;
-
- mPayload = other->mPayload;
- mSubstitutions = other->mSubstitutions;
- mTimestamp = other->mTimestamp;
- mExpiresAt = other->mExpiresAt;
- mCancelled = other->mCancelled;
- mIgnored = other->mIgnored;
- mPriority = other->mPriority;
- mForm = other->mForm;
- mResponseFunctorName = other->mResponseFunctorName;
- mRespondedTo = other->mRespondedTo;
- mResponse = other->mResponse;
- mTemporaryResponder = other->mTemporaryResponder;
-
- update();
-}
-
-const LLNotificationFormPtr LLNotification::getForm()
-{
- return mForm;
-}
-
-void LLNotification::cancel()
-{
- mCancelled = true;
-}
-
-LLSD LLNotification::getResponseTemplate(EResponseTemplateType type)
-{
- LLSD response = LLSD::emptyMap();
- for (S32 element_idx = 0;
- element_idx < mForm->getNumElements();
- ++element_idx)
- {
- LLSD element = mForm->getElement(element_idx);
- if (element.has("name"))
- {
- response[element["name"].asString()] = element["value"];
- }
-
- if ((type == WITH_DEFAULT_BUTTON)
- && element["default"].asBoolean())
- {
- response[element["name"].asString()] = true;
- }
- }
- return response;
-}
-
-//static
-S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response)
-{
- LLNotificationForm form(notification["form"]);
-
- for (S32 element_idx = 0;
- element_idx < form.getNumElements();
- ++element_idx)
- {
- LLSD element = form.getElement(element_idx);
-
- // only look at buttons
- if (element["type"].asString() == "button"
- && response[element["name"].asString()].asBoolean())
- {
- return element["index"].asInteger();
- }
- }
-
- return -1;
-}
-
-//static
-std::string LLNotification::getSelectedOptionName(const LLSD& response)
-{
- for (LLSD::map_const_iterator response_it = response.beginMap();
- response_it != response.endMap();
- ++response_it)
- {
- if (response_it->second.isBoolean() && response_it->second.asBoolean())
- {
- return response_it->first;
- }
- }
- return "";
-}
-
-
-void LLNotification::respond(const LLSD& response)
-{
- // *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()
- mRespondedTo = true;
- mResponse = response;
-
- if(mResponder)
- {
- mResponder->handleRespond(asLLSD(), response);
- }
- else if (!mResponseFunctorName.empty())
- {
- // look up the functor
- LLNotificationFunctorRegistry::ResponseFunctor functor =
- LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
- // and then call it
- functor(asLLSD(), response);
- }
- else if (mCombinedNotifications.empty())
- {
- // no registered responder
- return;
- }
-
- if (mTemporaryResponder)
- {
- LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
- mResponseFunctorName = "";
- mTemporaryResponder = false;
- }
-
- if (mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO)
- {
- mForm->setIgnored(mIgnored);
- if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
- {
- LLUI::getInstance()->mSettingGroups["ignores"]->setLLSD("Default" + getName(), response);
- }
- }
-
- for (std::vector<LLNotificationPtr>::const_iterator it = mCombinedNotifications.begin(); it != mCombinedNotifications.end(); ++it)
- {
- if ((*it))
- {
- (*it)->respond(response);
- }
- }
-
- update();
-}
-
-void LLNotification::respondWithDefault()
-{
- respond(getResponseTemplate(WITH_DEFAULT_BUTTON));
-}
-
-
-const std::string& LLNotification::getName() const
-{
- return mTemplatep->mName;
-}
-
-const std::string& LLNotification::getIcon() const
-{
- return mTemplatep->mIcon;
-}
-
-
-bool LLNotification::isPersistent() const
-{
- return mTemplatep->mPersist;
-}
-
-std::string LLNotification::getType() const
-{
- return (mTemplatep ? mTemplatep->mType : "");
-}
-
-S32 LLNotification::getURLOption() const
-{
- return (mTemplatep ? mTemplatep->mURLOption : -1);
-}
-
-S32 LLNotification::getURLOpenExternally() const
-{
- return(mTemplatep? mTemplatep->mURLTarget == "_external": -1);
-}
-
-bool LLNotification::getForceUrlsExternal() const
-{
- return (mTemplatep ? mTemplatep->mForceUrlsExternal : false);
-}
-
-bool LLNotification::hasUniquenessConstraints() const
-{
- return (mTemplatep ? mTemplatep->mUnique : false);
-}
-
-bool LLNotification::matchesTag(const std::string& tag)
-{
- bool result = false;
-
- if(mTemplatep)
- {
- std::list<std::string>::iterator it;
- for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++)
- {
- if((*it) == tag)
- {
- result = true;
- break;
- }
- }
- }
-
- return result;
-}
-
-void LLNotification::setIgnored(bool ignore)
-{
- mIgnored = ignore;
-}
-
-void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
-{
- if (mTemporaryResponder)
- // get rid of the old one
- LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
- mResponseFunctorName = responseFunctorName;
- mTemporaryResponder = false;
-}
-
-void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb)
-{
- if(mTemporaryResponder)
- {
- LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
- }
-
- LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
-}
-
-void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
-{
- mResponder = responder;
-}
-
-bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
-{
- if (this->mTemplatep->mName != that->mTemplatep->mName)
- {
- return false; // must have the same template name or forget it
- }
- if (this->mTemplatep->mUnique)
- {
- const LLSD& these_substitutions = this->getSubstitutions();
- const LLSD& those_substitutions = that->getSubstitutions();
- const LLSD& this_payload = this->getPayload();
- const LLSD& that_payload = that->getPayload();
-
- // highlander bit sez there can only be one of these
- for (std::vector<std::string>::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end();
- it != end_it;
- ++it)
- {
- // if templates differ in either substitution strings or payload with the given field name
- // then they are considered inequivalent
- // use of get() avoids converting the LLSD value to a map as the [] operator would
- if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString()
- || this_payload.get(*it).asString() != that_payload.get(*it).asString())
- {
- return false;
- }
- }
- return true;
- }
-
- return false;
-}
-
-void LLNotification::init(const std::string& template_name, const LLSD& form_elements)
-{
- mTemplatep = LLNotifications::instance().getTemplate(template_name);
- if (!mTemplatep) return;
-
- // add default substitutions
- const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs();
- for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin();
- iter != default_args.end(); ++iter)
- {
- mSubstitutions[iter->first] = iter->second;
- }
- mSubstitutions["_URL"] = getURL();
- mSubstitutions["_NAME"] = template_name;
- // TODO: something like this so that a missing alert is sensible:
- //mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
-
- mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
- mForm->append(form_elements);
-
- // apply substitution to form labels
- mForm->formatElements(mSubstitutions);
-
- mIgnored = mForm->getIgnored();
-
- LLDate rightnow = LLDate::now();
- if (mTemplatep->mExpireSeconds)
- {
- mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds);
- }
-
- if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED)
- {
- mPriority = mTemplatep->mPriority;
- }
-}
-
-std::string LLNotification::summarize() const
-{
- std::string s = "Notification(";
- s += getName();
- s += ") : ";
- s += mTemplatep ? mTemplatep->mMessage : "";
- // should also include timestamp and expiration time (but probably not payload)
- return s;
-}
-
-std::string LLNotification::getMessage() const
-{
- // all our callers cache this result, so it gives us more flexibility
- // to do the substitution at call time rather than attempting to
- // cache it in the notification
- if (!mTemplatep)
- return std::string();
-
- std::string message = mTemplatep->mMessage;
- LLStringUtil::format(message, mSubstitutions);
- return message;
-}
-
-std::string LLNotification::getFooter() const
-{
- if (!mTemplatep)
- return std::string();
-
- std::string footer = mTemplatep->mFooter;
- LLStringUtil::format(footer, mSubstitutions);
- return footer;
-}
-
-std::string LLNotification::getLabel() const
-{
- std::string label = mTemplatep->mLabel;
- LLStringUtil::format(label, mSubstitutions);
- return (mTemplatep ? label : "");
-}
-
-std::string LLNotification::getURL() const
-{
- if (!mTemplatep)
- return std::string();
- std::string url = mTemplatep->mURL;
- LLStringUtil::format(url, mSubstitutions);
- return (mTemplatep ? url : "");
-}
-
-bool LLNotification::canLogToChat() const
-{
- return mTemplatep->mLogToChat;
-}
-
-bool LLNotification::canLogToIM() const
-{
- return mTemplatep->mLogToIM;
-}
-
-bool LLNotification::canShowToast() const
-{
- return mTemplatep->mShowToast;
-}
-
-bool LLNotification::canFadeToast() const
-{
- return mTemplatep->mFadeToast;
-}
-
-bool LLNotification::hasFormElements() const
-{
- return mTemplatep->mForm->getNumElements() != 0;
-}
-
-void LLNotification::playSound()
-{
- make_ui_sound(mTemplatep->mSoundName.c_str());
-}
-
-LLNotification::ECombineBehavior LLNotification::getCombineBehavior() const
-{
- return mTemplatep->mCombineBehavior;
-}
-
-void LLNotification::updateForm( const LLNotificationFormPtr& form )
-{
- mForm = form;
-}
-
-void LLNotification::repost()
-{
- mRespondedTo = false;
- LLNotifications::instance().update(shared_from_this());
-}
-
-
-
-// =========================================================
-// LLNotificationChannel implementation
-// ---
-LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot)
-{
- // when someone wants to connect to a channel, we first throw them
- // all of the notifications that are already in the channel
- // we use a special signal called "load" in case the channel wants to care
- // only about new notifications
- LLMutexLock lock(&mItemsMutex);
- for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
- {
- slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
- }
- // and then connect the signal so that all future notifications will also be
- // forwarded.
- return mChanged.connect(slot);
-}
-
-LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot)
-{
- for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
- {
- slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
- }
- return mChanged.connect(slot, boost::signals2::at_front);
-}
-
-LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot)
-{
- // these two filters only fire for notifications added after the current one, because
- // they don't participate in the hierarchy.
- return mPassedFilter.connect(slot);
-}
-
-LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot)
-{
- return mFailedFilter.connect(slot);
-}
-
-// external call, conforms to our standard signature
-bool LLNotificationChannelBase::updateItem(const LLSD& payload)
-{
- // first check to see if it's in the master list
- LLNotificationPtr pNotification = LLNotifications::instance().find(payload["id"]);
- if (!pNotification)
- return false; // not found
-
- return updateItem(payload, pNotification);
-}
-
-
-//FIX QUIT NOT WORKING
-
-
-// internal call, for use in avoiding lookup
-bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification)
-{
- std::string cmd = payload["sigtype"];
- LLNotificationSet::iterator foundItem = mItems.find(pNotification);
- bool wasFound = (foundItem != mItems.end());
- bool passesFilter = mFilter ? mFilter(pNotification) : true;
-
- // first, we offer the result of the filter test to the simple
- // signals for pass/fail. One of these is guaranteed to be called.
- // If either signal returns true, the change processing is NOT performed
- // (so don't return true unless you know what you're doing!)
- bool abortProcessing = false;
- if (passesFilter)
- {
- onFilterPass(pNotification);
- abortProcessing = mPassedFilter(payload);
- }
- else
- {
- onFilterFail(pNotification);
- abortProcessing = mFailedFilter(payload);
- }
-
- if (abortProcessing)
- {
- return true;
- }
-
- if (cmd == "load")
- {
- // should be no reason we'd ever get a load if we already have it
- // if passes filter send a load message, else do nothing
- assert(!wasFound);
- if (passesFilter)
- {
- // not in our list, add it and say so
- mItems.insert(pNotification);
- onLoad(pNotification);
- abortProcessing = mChanged(payload);
- }
- }
- else if (cmd == "change")
- {
- // if it passes filter now and was found, we just send a change message
- // if it passes filter now and wasn't found, we have to add it
- // if it doesn't pass filter and wasn't found, we do nothing
- // if it doesn't pass filter and was found, we need to delete it
- if (passesFilter)
- {
- if (wasFound)
- {
- // it already existed, so this is a change
- // since it changed in place, all we have to do is resend the signal
- onChange(pNotification);
- abortProcessing = mChanged(payload);
- }
- else
- {
- // not in our list, add it and say so
- mItems.insert(pNotification);
- onChange(pNotification);
- // our payload is const, so make a copy before changing it
- LLSD newpayload = payload;
- newpayload["sigtype"] = "add";
- abortProcessing = mChanged(newpayload);
- }
- }
- else
- {
- if (wasFound)
- {
- // it already existed, so this is a delete
- mItems.erase(pNotification);
- onChange(pNotification);
- // our payload is const, so make a copy before changing it
- LLSD newpayload = payload;
- newpayload["sigtype"] = "delete";
- abortProcessing = mChanged(newpayload);
- }
- // didn't pass, not on our list, do nothing
- }
- }
- else if (cmd == "add")
- {
- // should be no reason we'd ever get an add if we already have it
- // if passes filter send an add message, else do nothing
- assert(!wasFound);
- if (passesFilter)
- {
- // not in our list, add it and say so
- mItems.insert(pNotification);
- onAdd(pNotification);
- abortProcessing = mChanged(payload);
- }
- }
- else if (cmd == "delete")
- {
- // if we have it in our list, pass on the delete, then delete it, else do nothing
- if (wasFound)
- {
- onDelete(pNotification);
- abortProcessing = mChanged(payload);
- mItems.erase(pNotification);
- }
- }
- return abortProcessing;
-}
-
-LLNotificationChannel::LLNotificationChannel(const Params& p)
-: LLNotificationChannelBase(p.filter()),
- LLInstanceTracker<LLNotificationChannel, std::string>(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()),
- mName(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString())
-{
- for (const std::string& source : p.sources)
- {
- connectToChannel(source);
- }
-}
-
-
-LLNotificationChannel::LLNotificationChannel(const std::string& name,
- const std::string& parent,
- LLNotificationFilter filter)
-: LLNotificationChannelBase(filter),
- LLInstanceTracker<LLNotificationChannel, std::string>(name),
- mName(name)
-{
- // bind to notification broadcast
- connectToChannel(parent);
-}
-
-LLNotificationChannel::~LLNotificationChannel()
-{
- for (LLBoundListener &listener : mListeners)
- {
- listener.disconnect();
- }
-}
-
-bool LLNotificationChannel::isEmpty() const
-{
- return mItems.empty();
-}
-
-S32 LLNotificationChannel::size() const
-{
- return mItems.size();
-}
-
-size_t LLNotificationChannel::size()
-{
- return mItems.size();
-}
-
-void LLNotificationChannel::forEachNotification(NotificationProcess process)
-{
- LLMutexLock lock(&mItemsMutex);
- std::for_each(mItems.begin(), mItems.end(), process);
-}
-
-std::string LLNotificationChannel::summarize()
-{
- std::string s("Channel '");
- s += mName;
- s += "'\n ";
- LLMutexLock lock(&mItemsMutex);
- for (LLNotificationChannel::Iterator it = mItems.begin(); it != mItems.end(); ++it)
- {
- s += (*it)->summarize();
- s += "\n ";
- }
- return s;
-}
-
-void LLNotificationChannel::connectToChannel( const std::string& channel_name )
-{
- if (channel_name.empty())
- {
- mListeners.push_back(LLNotifications::instance().connectChanged(
- boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
- }
- else
- {
- mParents.push_back(channel_name);
- LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name);
- mListeners.push_back(p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
- }
-}
-
-// ---
-// END OF LLNotificationChannel implementation
-// =========================================================
-
-
-// ============================================== ===========
-// LLNotifications implementation
-// ---
-LLNotifications::LLNotifications()
-: LLNotificationChannelBase(LLNotificationFilters::includeEverything),
- mIgnoreAllNotifications(false)
-{
- mListener.reset(new LLNotificationsListener(*this));
- LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
-
- // touch the instance tracker for notification channels, so that it will still be around in our destructor
- LLInstanceTracker<LLNotificationChannel, std::string>::instanceCount();
-}
-
-void LLNotifications::clear()
-{
- mDefaultChannels.clear();
-}
-
-// The expiration channel gets all notifications that are cancelled
-bool LLNotifications::expirationFilter(LLNotificationPtr pNotification)
-{
- return pNotification->isCancelled() || pNotification->isRespondedTo();
-}
-
-bool LLNotifications::expirationHandler(const LLSD& payload)
-{
- if (payload["sigtype"].asString() != "delete")
- {
- // anything added to this channel actually should be deleted from the master
- cancel(find(payload["id"]));
- return true; // don't process this item any further
- }
- return false;
-}
-
-bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
-{
- if (!pNotif->hasUniquenessConstraints())
- {
- return true;
- }
-
- // checks against existing unique notifications
- for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
- existing_it != mUniqueNotifications.end();
- ++existing_it)
- {
- LLNotificationPtr existing_notification = existing_it->second;
- if (pNotif != existing_notification
- && pNotif->isEquivalentTo(existing_notification))
- {
- if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD)
- {
- cancel(existing_notification);
- return true;
- }
- else
- {
- return false;
- }
- }
- }
-
- return true;
-}
-
-bool LLNotifications::uniqueHandler(const LLSD& payload)
-{
- std::string cmd = payload["sigtype"];
-
- LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
- if (pNotif && pNotif->hasUniquenessConstraints())
- {
- if (cmd == "add")
- {
- // not a duplicate according to uniqueness criteria, so we keep it
- // and store it for future uniqueness checks
- mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif));
- }
- else if (cmd == "delete")
- {
- mUniqueNotifications.erase(pNotif->getName());
- }
- }
-
- return false;
-}
-
-bool LLNotifications::failedUniquenessTest(const LLSD& payload)
-{
- LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
-
- std::string cmd = payload["sigtype"];
-
- if (!pNotif || cmd != "add")
- {
- return false;
- }
-
- switch(pNotif->getCombineBehavior())
- {
- case LLNotification::REPLACE_WITH_NEW:
- // Update the existing unique notification with the data from this particular instance...
- // This guarantees that duplicate notifications will be collapsed to the one
- // most recently triggered
- for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
- existing_it != mUniqueNotifications.end();
- ++existing_it)
- {
- LLNotificationPtr existing_notification = existing_it->second;
- if (pNotif != existing_notification
- && pNotif->isEquivalentTo(existing_notification))
- {
- // copy notification instance data over to oldest instance
- // of this unique notification and update it
- existing_notification->updateFrom(pNotif);
- // then delete the new one
- cancel(pNotif);
- }
- }
- break;
- case LLNotification::COMBINE_WITH_NEW:
- // Add to the existing unique notification with the data from this particular instance...
- // This guarantees that duplicate notifications will be collapsed to the one
- // most recently triggered
- for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
- existing_it != mUniqueNotifications.end();
- ++existing_it)
- {
- LLNotificationPtr existing_notification = existing_it->second;
- if (pNotif != existing_notification
- && pNotif->isEquivalentTo(existing_notification))
- {
- // copy the notifications from the newest instance into the oldest
- existing_notification->mCombinedNotifications.push_back(pNotif);
- existing_notification->mCombinedNotifications.insert(existing_notification->mCombinedNotifications.end(),
- pNotif->mCombinedNotifications.begin(), pNotif->mCombinedNotifications.end());
-
- // pop up again
- existing_notification->update();
- }
- }
- break;
- case LLNotification::KEEP_OLD:
- break;
- case LLNotification::CANCEL_OLD:
- // already handled by filter logic
- break;
- default:
- break;
- }
-
- return false;
-}
-
-LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
-{
- return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName).get());
-}
-
-
-// this function is called once at construction time, after the object is constructed.
-void LLNotifications::initSingleton()
-{
- loadTemplates();
- loadVisibilityRules();
- createDefaultChannels();
-}
-
-void LLNotifications::cleanupSingleton()
-{
- clear();
-}
-
-void LLNotifications::createDefaultChannels()
-{
- LL_INFOS("Notifications") << "Generating default notification channels" << LL_ENDL;
- // now construct the various channels AFTER loading the notifications,
- // because the history channel is going to rewrite the stored notifications file
- mDefaultChannels.push_back(new LLNotificationChannel("Enabled", "",
- !boost::bind(&LLNotifications::getIgnoreAllNotifications, this)));
- mDefaultChannels.push_back(new LLNotificationChannel("Expiration", "Enabled",
- boost::bind(&LLNotifications::expirationFilter, this, _1)));
- mDefaultChannels.push_back(new LLNotificationChannel("Unexpired", "Enabled",
- !boost::bind(&LLNotifications::expirationFilter, this, _1))); // use negated bind
- mDefaultChannels.push_back(new LLNotificationChannel("Unique", "Unexpired",
- boost::bind(&LLNotifications::uniqueFilter, this, _1)));
- mDefaultChannels.push_back(new LLNotificationChannel("Ignore", "Unique",
- filterIgnoredNotifications));
- mDefaultChannels.push_back(new LLNotificationChannel("VisibilityRules", "Ignore",
- boost::bind(&LLNotifications::isVisibleByRules, this, _1)));
- mDefaultChannels.push_back(new LLNotificationChannel("Visible", "VisibilityRules",
- &LLNotificationFilters::includeEverything));
- mDefaultChannels.push_back(new LLPersistentNotificationChannel());
-
- // connect action methods to these channels
- getChannel("Enabled")->connectFailedFilter(&defaultResponse);
- getChannel("Expiration")->connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));
- // uniqueHandler slot should be added as first slot of the signal due to
- // usage LLStopWhenHandled combiner in LLStandardSignal
- getChannel("Unique")->connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
- getChannel("Unique")->connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
- getChannel("Ignore")->connectFailedFilter(&handleIgnoredNotification);
- getChannel("VisibilityRules")->connectFailedFilter(&visibilityRuleMached);
-}
-
-
-LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
-{
- if (mTemplates.count(name))
- {
- return mTemplates[name];
- }
- else
- {
- return mTemplates["MissingAlert"];
- }
-}
-
-bool LLNotifications::templateExists(const std::string& name)
-{
- return (mTemplates.count(name) != 0);
-}
-
-void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option)
-{
- LLNotificationPtr temp_notify(new LLNotification(params));
- LLSD response = temp_notify->getResponseTemplate();
- LLSD selected_item = temp_notify->getForm()->getElement(option);
-
- if (selected_item.isUndefined())
- {
- LL_WARNS("Notifications") << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL;
- return;
- }
- response[selected_item["name"].asString()] = true;
-
- temp_notify->respond(response);
-}
-
-LLNotifications::TemplateNames LLNotifications::getTemplateNames() const
-{
- TemplateNames names;
- for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it)
- {
- names.push_back(it->first);
- }
- return names;
-}
-
-typedef std::map<std::string, std::string> StringMap;
-void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
-{
- // walk the list of attributes looking for replacements
- for (LLXMLAttribList::iterator it=node->mAttributes.begin();
- it != node->mAttributes.end(); ++it)
- {
- std::string value = it->second->getValue();
- if (value[0] == '$')
- {
- value.erase(0, 1); // trim off the $
- std::string replacement;
- StringMap::const_iterator found = replacements.find(value);
- if (found != replacements.end())
- {
- replacement = found->second;
- LL_DEBUGS("Notifications") << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL;
- it->second->setValue(replacement);
- }
- else
- {
- LL_WARNS("Notifications") << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL;
- }
- }
- }
-
- // now walk the list of children and call this recursively.
- for (LLXMLNodePtr child = node->getFirstChild();
- child.notNull(); child = child->getNextSibling())
- {
- replaceSubstitutionStrings(child, replacements);
- }
-}
-
-void replaceFormText(LLNotificationForm::Params& form, const std::string& pattern, const std::string& replace)
-{
- if (form.ignore.isProvided() && form.ignore.text() == pattern)
- {
- form.ignore.text = replace;
- }
-
- for (LLNotificationForm::FormElement& element : form.form_elements.elements)
- {
- if (element.button.isChosen() && element.button.text() == pattern)
- {
- element.button.text = replace;
- }
- }
-}
-
-void addPathIfExists(const std::string& new_path, std::vector<std::string>& paths)
-{
- if (gDirUtilp->fileExists(new_path))
- {
- paths.push_back(new_path);
- }
-}
-
-bool LLNotifications::loadTemplates()
-{
- LL_INFOS("Notifications") << "Reading notifications template" << LL_ENDL;
- // Passing findSkinnedFilenames(constraint=LLDir::ALL_SKINS) makes it
- // output all relevant pathnames instead of just the ones from the most
- // specific skin.
- std::vector<std::string> search_paths =
- gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", LLDir::ALL_SKINS);
-
- std::string base_filename = search_paths.front();
- LLXMLNodePtr root;
- bool success = LLXMLNode::getLayeredXMLNode(root, search_paths);
-
- if (!success || root.isNull() || !root->hasName( "notifications" ))
- {
- LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
- LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL;
- return false;
- }
-
- LLNotificationTemplate::Notifications params;
- LLXUIParser parser;
- parser.readXUI(root, params, base_filename);
-
- if(!params.validateBlock())
- {
- LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
- LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL;
- return false;
- }
-
- mTemplates.clear();
-
- for (const LLNotificationTemplate::GlobalString& string : params.strings)
- {
- mGlobalStrings[string.name] = string.value;
- }
-
- std::map<std::string, LLNotificationForm::Params> form_templates;
-
- for (const LLNotificationTemplate::Template& notification_template : params.templates)
- {
- form_templates[notification_template.name] = notification_template.form;
- }
-
- for (LLNotificationTemplate::Params& notification : params.notifications)
- {
- if (notification.form_ref.form_template.isChosen())
- {
- // replace form contents from template
- notification.form_ref.form = form_templates[notification.form_ref.form_template.name];
- if(notification.form_ref.form_template.yes_text.isProvided())
- {
- replaceFormText(notification.form_ref.form, "$yestext", notification.form_ref.form_template.yes_text);
- }
- if(notification.form_ref.form_template.no_text.isProvided())
- {
- replaceFormText(notification.form_ref.form, "$notext", notification.form_ref.form_template.no_text);
- }
- if(notification.form_ref.form_template.cancel_text.isProvided())
- {
- replaceFormText(notification.form_ref.form, "$canceltext", notification.form_ref.form_template.cancel_text);
- }
- if(notification.form_ref.form_template.help_text.isProvided())
- {
- replaceFormText(notification.form_ref.form, "$helptext", notification.form_ref.form_template.help_text);
- }
- if(notification.form_ref.form_template.ignore_text.isProvided())
- {
- replaceFormText(notification.form_ref.form, "$ignoretext", notification.form_ref.form_template.ignore_text);
- }
- }
- mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification));
- }
-
- LL_INFOS("Notifications") << "...done" << LL_ENDL;
-
- return true;
-}
-
-bool LLNotifications::loadVisibilityRules()
-{
- const std::string xml_filename = "notification_visibility.xml";
- // Note that here we're looking for the "en" version, the default
- // language, rather than the most localized version of this file.
- std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, xml_filename);
-
- LLNotificationVisibilityRule::Rules params;
- LLSimpleXUIParser parser;
- parser.readXUI(full_filename, params);
-
- if(!params.validateBlock())
- {
- LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
- LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL;
- return false;
- }
-
- mVisibilityRules.clear();
-
- for (const LLNotificationVisibilityRule::Rule& rule : params.rules)
- {
- mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(rule)));
- }
-
- return true;
-}
-
-// Add a simple notification (from XUI)
-void LLNotifications::addFromCallback(const LLSD& name)
-{
- add(name.asString(), LLSD(), LLSD());
-}
-
-LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload)
-{
- LLNotification::Params::Functor functor_p;
- functor_p.name = name;
- return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
-}
-
-LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, const std::string& functor_name)
-{
- LLNotification::Params::Functor functor_p;
- functor_p.name = functor_name;
- return add(LLNotification::Params().name(name)
- .substitutions(substitutions)
- .payload(payload)
- .functor(functor_p));
-}
-
-//virtual
-LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor)
-{
- LLNotification::Params::Functor functor_p;
- functor_p.function = functor;
- return add(LLNotification::Params().name(name)
- .substitutions(substitutions)
- .payload(payload)
- .functor(functor_p));
-}
-
-// generalized add function that takes a parameter block object for more complex instantiations
-LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
-{
- LLNotificationPtr pNotif(new LLNotification(p));
- add(pNotif);
- return pNotif;
-}
-
-
-void LLNotifications::add(const LLNotificationPtr pNotif)
-{
- if (pNotif == NULL) return;
-
- // first see if we already have it -- if so, that's a problem
- LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it != mItems.end())
- {
- LL_ERRS() << "Notification added a second time to the master notification channel." << LL_ENDL;
- }
-
- updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif);
-}
-
-void LLNotifications::load(const LLNotificationPtr pNotif)
-{
- if (pNotif == NULL) return;
-
- // first see if we already have it -- if so, that's a problem
- LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it != mItems.end())
- {
- LL_ERRS() << "Notification loaded a second time to the master notification channel." << LL_ENDL;
- }
-
- updateItem(LLSD().with("sigtype", "load").with("id", pNotif->id()), pNotif);
-}
-
-void LLNotifications::cancel(LLNotificationPtr pNotif)
-{
- if (pNotif == NULL || pNotif->isCancelled()) return;
-
- LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it != mItems.end())
- {
- pNotif->cancel();
- updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
- }
-}
-
-void LLNotifications::cancelByName(const std::string& name)
-{
- LLMutexLock lock(&mItemsMutex);
- std::vector<LLNotificationPtr> notifs_to_cancel;
- for (LLNotificationSet::iterator it=mItems.begin(), end_it = mItems.end();
- it != end_it;
- ++it)
- {
- LLNotificationPtr pNotif = *it;
- if (pNotif->getName() == name)
- {
- notifs_to_cancel.push_back(pNotif);
- }
- }
-
- for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
- it != end_it;
- ++it)
- {
- LLNotificationPtr pNotif = *it;
- pNotif->cancel();
- updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
- }
-}
-
-void LLNotifications::cancelByOwner(const LLUUID ownerId)
-{
- LLMutexLock lock(&mItemsMutex);
- std::vector<LLNotificationPtr> notifs_to_cancel;
- for (LLNotificationSet::iterator it = mItems.begin(), end_it = mItems.end();
- it != end_it;
- ++it)
- {
- LLNotificationPtr pNotif = *it;
- if (pNotif && pNotif->getPayload().get("owner_id").asUUID() == ownerId)
- {
- notifs_to_cancel.push_back(pNotif);
- }
- }
-
- for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
- it != end_it;
- ++it)
- {
- LLNotificationPtr pNotif = *it;
- pNotif->cancel();
- updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
- }
-}
-
-void LLNotifications::update(const LLNotificationPtr pNotif)
-{
- LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it != mItems.end())
- {
- updateItem(LLSD().with("sigtype", "change").with("id", pNotif->id()), pNotif);
- }
-}
-
-
-LLNotificationPtr LLNotifications::find(LLUUID uuid)
-{
- LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid)));
- LLNotificationSet::iterator it=mItems.find(target);
- if (it == mItems.end())
- {
- LL_DEBUGS("Notifications") << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << LL_ENDL;
- return LLNotificationPtr((LLNotification*)NULL);
- }
- else
- {
- return *it;
- }
-}
-
-std::string LLNotifications::getGlobalString(const std::string& key) const
-{
- GlobalStringMap::const_iterator it = mGlobalStrings.find(key);
- if (it != mGlobalStrings.end())
- {
- return it->second;
- }
- else
- {
- // if we don't have the key as a global, return the key itself so that the error
- // is self-diagnosing.
- return key;
- }
-}
-
-void LLNotifications::setIgnoreAllNotifications(bool setting)
-{
- mIgnoreAllNotifications = setting;
-}
-bool LLNotifications::getIgnoreAllNotifications()
-{
- return mIgnoreAllNotifications;
-}
-
-void LLNotifications::setIgnored(const std::string& name, bool ignored)
-{
- LLNotificationTemplatePtr templatep = getTemplate(name);
- templatep->mForm->setIgnored(ignored);
-}
-
-bool LLNotifications::getIgnored(const std::string& name)
-{
- LLNotificationTemplatePtr templatep = getTemplate(name);
- return (mIgnoreAllNotifications) || ( (templatep->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) && (templatep->mForm->getIgnored()) );
-}
-
-bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
-{
- if(n->isRespondedTo())
- {
- // This avoids infinite recursion in the case where the filter calls respond()
- return true;
- }
-
- VisibilityRuleList::iterator it;
-
- for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++)
- {
- // An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule.
- LL_DEBUGS("Notifications")
- << "notification \"" << n->getName() << "\" "
- << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, "
- << "name = \"" << (*it)->mName << "\" "
- << "tag = \"" << (*it)->mTag << "\" "
- << "type = \"" << (*it)->mType << "\" "
- << LL_ENDL;
-
- if(!(*it)->mType.empty())
- {
- if((*it)->mType != n->getType())
- {
- // Type doesn't match, so skip this rule.
- continue;
- }
- }
-
- if(!(*it)->mTag.empty())
- {
- // check this notification's tag(s) against it->mTag and continue if no match is found.
- if(!n->matchesTag((*it)->mTag))
- {
- // This rule's non-empty tag didn't match one of the notification's tags. Skip this rule.
- continue;
- }
- }
-
- if(!(*it)->mName.empty())
- {
- // check this notification's name against the notification's name and continue if no match is found.
- if((*it)->mName != n->getName())
- {
- // This rule's non-empty name didn't match the notification. Skip this rule.
- continue;
- }
- }
-
- // If we got here, the rule matches. Don't evaluate subsequent rules.
- if(!(*it)->mVisible)
- {
- // This notification is being hidden.
-
- if((*it)->mResponse.empty())
- {
- // Response property is empty. Cancel this notification.
- LL_DEBUGS("Notifications") << "cancelling notification " << n->getName() << LL_ENDL;
-
- cancel(n);
- }
- else
- {
- // Response property is not empty. Return the specified response.
- LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON);
- // TODO: verify that the response template has an item with the correct name
- response[(*it)->mResponse] = true;
-
- LL_DEBUGS("Notifications") << "responding to notification " << n->getName() << " with response = " << response << LL_ENDL;
-
- n->respond(response);
- }
-
- return false;
- }
-
- // If we got here, exit the loop and return true.
- break;
- }
-
- LL_DEBUGS("Notifications") << "allowing notification " << n->getName() << LL_ENDL;
-
- return true;
-}
-
-
-// ---
-// END OF LLNotifications implementation
-// =========================================================
-
-std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
-{
- s << notification.summarize();
- return s;
-}
-
-void LLPostponedNotification::lookupName(const LLUUID& id,
- bool is_group)
-{
- if (is_group)
- {
- gCacheName->getGroup(id,
- boost::bind(&LLPostponedNotification::onGroupNameCache,
- this, _1, _2, _3));
- }
- else
- {
- fetchAvatarName(id);
- }
-}
-
-void LLPostponedNotification::onGroupNameCache(const LLUUID& id,
- const std::string& full_name,
- bool is_group)
-{
- finalizeName(full_name);
-}
-
-void LLPostponedNotification::fetchAvatarName(const LLUUID& id)
-{
- if (id.notNull())
- {
- if (mAvatarNameCacheConnection.connected())
- {
- mAvatarNameCacheConnection.disconnect();
- }
-
- mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLPostponedNotification::onAvatarNameCache, this, _1, _2));
- }
-}
-
-void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id,
- const LLAvatarName& av_name)
-{
- mAvatarNameCacheConnection.disconnect();
-
- std::string name = av_name.getCompleteName();
-
- // from PE merge - we should figure out if this is the right thing to do
- if (name.empty())
- {
- LL_WARNS("Notifications") << "Empty name received for Id: " << agent_id << LL_ENDL;
- name = SYSTEM_FROM;
- }
-
- finalizeName(name);
-}
-
-void LLPostponedNotification::finalizeName(const std::string& name)
-{
- mName = name;
- modifyNotificationParams();
- LLNotifications::instance().add(mParams);
- cleanup();
-}
+/**
+* @file llnotifications.cpp
+* @brief Non-UI queue manager for keeping a prioritized list of notifications
+*
+* $LicenseInfo:firstyear=2008&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "linden_common.h"
+
+#include "llnotifications.h"
+#include "llnotificationtemplate.h"
+#include "llnotificationvisibilityrule.h"
+
+#include "llavatarnamecache.h"
+#include "llinstantmessage.h"
+#include "llcachename.h"
+#include "llxmlnode.h"
+#include "lluictrl.h"
+#include "lluictrlfactory.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+#include "lltrans.h"
+#include "llstring.h"
+#include "llsdparam.h"
+#include "llsdutil.h"
+
+#include <algorithm>
+#include <boost/regex.hpp>
+
+
+const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
+
+void NotificationPriorityValues::declareValues()
+{
+ declare("low", NOTIFICATION_PRIORITY_LOW);
+ declare("normal", NOTIFICATION_PRIORITY_NORMAL);
+ declare("high", NOTIFICATION_PRIORITY_HIGH);
+ declare("critical", NOTIFICATION_PRIORITY_CRITICAL);
+}
+
+LLNotificationForm::FormElementBase::FormElementBase()
+: name("name"),
+ enabled("enabled", true)
+{}
+
+LLNotificationForm::FormIgnore::FormIgnore()
+: text("text"),
+ control("control"),
+ invert_control("invert_control", false),
+ save_option("save_option", false),
+ session_only("session_only", false),
+ checkbox_only("checkbox_only", false)
+{}
+
+LLNotificationForm::FormButton::FormButton()
+: index("index"),
+ text("text"),
+ ignore("ignore"),
+ is_default("default"),
+ width("width", 0),
+ type("type")
+{
+ // set type here so it gets serialized
+ type = "button";
+}
+
+LLNotificationForm::FormInput::FormInput()
+: type("type"),
+ text("text"),
+ max_length_chars("max_length_chars"),
+ allow_emoji("allow_emoji"),
+ width("width", 0),
+ value("value")
+{}
+
+LLNotificationForm::FormElement::FormElement()
+: button("button"),
+ input("input")
+{}
+
+LLNotificationForm::FormElements::FormElements()
+: elements("")
+{}
+
+LLNotificationForm::Params::Params()
+: name("name"),
+ ignore("ignore"),
+ form_elements("")
+{}
+
+
+
+bool filterIgnoredNotifications(LLNotificationPtr notification)
+{
+ LLNotificationFormPtr form = notification->getForm();
+ // Check to see if the user wants to ignore this alert
+ return !notification->getForm()->getIgnored();
+}
+
+bool handleIgnoredNotification(const LLSD& payload)
+{
+ if (payload["sigtype"].asString() == "add")
+ {
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+ if (!pNotif) return false;
+
+ LLNotificationFormPtr form = pNotif->getForm();
+ LLSD response;
+ switch(form->getIgnoreType())
+ {
+ case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE:
+ case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY:
+ response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON);
+ break;
+ case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE:
+ response = LLUI::getInstance()->mSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName());
+ break;
+ case LLNotificationForm::IGNORE_SHOW_AGAIN:
+ break;
+ default:
+ return false;
+ }
+ pNotif->setIgnored(true);
+ pNotif->respond(response);
+ return true; // don't process this item any further
+ }
+ return false;
+}
+
+bool defaultResponse(const LLSD& payload)
+{
+ if (payload["sigtype"].asString() == "add")
+ {
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+ if (pNotif)
+ {
+ // supply default response
+ pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON));
+ }
+ }
+ return false;
+}
+
+bool visibilityRuleMached(const LLSD& payload)
+{
+ // This is needed because LLNotifications::isVisibleByRules may have cancelled the notification.
+ // Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order.
+ return true;
+}
+
+
+namespace LLNotificationFilters
+{
+ // a sample filter
+ bool includeEverything(LLNotificationPtr p)
+ {
+ return true;
+ }
+};
+
+LLNotificationForm::LLNotificationForm()
+: mIgnore(IGNORE_NO)
+{
+}
+
+LLNotificationForm::LLNotificationForm( const LLNotificationForm& other )
+{
+ mFormData = other.mFormData;
+ mIgnore = other.mIgnore;
+ mIgnoreMsg = other.mIgnoreMsg;
+ mIgnoreSetting = other.mIgnoreSetting;
+ mInvertSetting = other.mInvertSetting;
+}
+
+LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p)
+: mIgnore(IGNORE_NO),
+ mInvertSetting(false) // ignore settings by default mean true=show, false=ignore
+{
+ if (p.ignore.isProvided())
+ {
+ // For all cases but IGNORE_CHECKBOX_ONLY this is name for use in preferences
+ mIgnoreMsg = p.ignore.text;
+
+ LLUI *ui_inst = LLUI::getInstance();
+ if (p.ignore.checkbox_only)
+ {
+ mIgnore = IGNORE_CHECKBOX_ONLY;
+ }
+ else if (!p.ignore.save_option)
+ {
+ mIgnore = p.ignore.session_only ? IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY : IGNORE_WITH_DEFAULT_RESPONSE;
+ }
+ else
+ {
+ // remember last option chosen by user and automatically respond with that in the future
+ mIgnore = IGNORE_WITH_LAST_RESPONSE;
+ ui_inst->mSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
+ }
+
+ bool show_notification = true;
+ if (p.ignore.control.isProvided())
+ {
+ mIgnoreSetting = ui_inst->mSettingGroups["config"]->getControl(p.ignore.control());
+ mInvertSetting = p.ignore.invert_control;
+ }
+ else if (mIgnore > IGNORE_NO)
+ {
+ ui_inst->mSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", LLControlVariable::PERSIST_NONDFT);
+ mIgnoreSetting = ui_inst->mSettingGroups["ignores"]->getControl(name);
+ }
+ }
+
+ LLParamSDParser parser;
+ parser.writeSD(mFormData, p.form_elements);
+
+ for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray();
+ it != end_it;
+ ++it)
+ {
+ // lift contents of form element up a level, since element type is already encoded in "type" param
+ if (it->isMap() && it->beginMap() != it->endMap())
+ {
+ *it = it->beginMap()->second;
+ }
+ }
+
+ LL_DEBUGS("Notifications") << name << LL_ENDL;
+ LL_DEBUGS("Notifications") << ll_pretty_print_sd(mFormData) << LL_ENDL;
+}
+
+LLNotificationForm::LLNotificationForm(const LLSD& sd)
+ : mIgnore(IGNORE_NO)
+{
+ if (sd.isArray())
+ {
+ mFormData = sd;
+ }
+ else
+ {
+ LL_WARNS("Notifications") << "Invalid form data " << sd << LL_ENDL;
+ mFormData = LLSD::emptyArray();
+ }
+}
+
+LLSD LLNotificationForm::asLLSD() const
+{
+ return mFormData;
+}
+
+LLSD LLNotificationForm::getElement(const std::string& element_name)
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["name"].asString() == element_name) return (*it);
+ }
+ return LLSD();
+}
+
+
+bool LLNotificationForm::hasElement(const std::string& element_name) const
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["name"].asString() == element_name) return true;
+ }
+ return false;
+}
+
+void LLNotificationForm::getElements(LLSD& elements, S32 offset)
+{
+ //Finds elements that the template did not add
+ LLSD::array_const_iterator it = mFormData.beginArray() + offset;
+
+ //Keeps track of only the dynamic elements
+ for(; it != mFormData.endArray(); ++it)
+ {
+ elements.append(*it);
+ }
+}
+
+bool LLNotificationForm::getElementEnabled(const std::string& element_name) const
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["name"].asString() == element_name)
+ {
+ return (*it)["enabled"].asBoolean();
+ }
+ }
+
+ return false;
+}
+
+void LLNotificationForm::setElementEnabled(const std::string& element_name, bool enabled)
+{
+ for (LLSD::array_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["name"].asString() == element_name)
+ {
+ (*it)["enabled"] = enabled;
+ }
+ }
+}
+
+
+void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value, bool enabled)
+{
+ LLSD element;
+ element["type"] = type;
+ element["name"] = name;
+ element["text"] = name;
+ element["value"] = value;
+ element["index"] = LLSD::Integer(mFormData.size());
+ element["enabled"] = enabled;
+ mFormData.append(element);
+}
+
+void LLNotificationForm::append(const LLSD& sub_form)
+{
+ if (sub_form.isArray())
+ {
+ for (LLSD::array_const_iterator it = sub_form.beginArray();
+ it != sub_form.endArray();
+ ++it)
+ {
+ mFormData.append(*it);
+ }
+ }
+}
+
+void LLNotificationForm::formatElements(const LLSD& substitutions)
+{
+ for (LLSD::array_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ // format "text" component of each form element
+ if ((*it).has("text"))
+ {
+ std::string text = (*it)["text"].asString();
+ LLStringUtil::format(text, substitutions);
+ (*it)["text"] = text;
+ }
+ if ((*it)["type"].asString() == "text" && (*it).has("value"))
+ {
+ std::string value = (*it)["value"].asString();
+ LLStringUtil::format(value, substitutions);
+ (*it)["value"] = value;
+ }
+ }
+}
+
+std::string LLNotificationForm::getDefaultOption()
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["default"]) return (*it)["name"].asString();
+ }
+ return "";
+}
+
+LLControlVariablePtr LLNotificationForm::getIgnoreSetting()
+{
+ return mIgnoreSetting;
+}
+
+bool LLNotificationForm::getIgnored()
+{
+ bool show = true;
+ if (mIgnore > LLNotificationForm::IGNORE_NO
+ && mIgnoreSetting)
+ {
+ show = mIgnoreSetting->getValue().asBoolean();
+ if (mInvertSetting) show = !show;
+ }
+ return !show;
+}
+
+void LLNotificationForm::setIgnored(bool ignored)
+{
+ if (mIgnoreSetting)
+ {
+ if (mInvertSetting) ignored = !ignored;
+ mIgnoreSetting->setValue(!ignored);
+ }
+}
+
+LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Params& p)
+: mName(p.name),
+ mType(p.type),
+ mMessage(p.value),
+ mFooter(p.footer.value),
+ mLabel(p.label),
+ mIcon(p.icon),
+ mURL(p.url.value),
+ mExpireSeconds(p.duration),
+ mExpireOption(p.expire_option),
+ mURLOption(p.url.option),
+ mURLTarget(p.url.target),
+ mForceUrlsExternal(p.force_urls_external),
+ mUnique(p.unique.isProvided()),
+ mCombineBehavior(p.unique.combine),
+ mPriority(p.priority),
+ mPersist(p.persist),
+ mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()),
+ mLogToChat(p.log_to_chat),
+ mLogToIM(p.log_to_im),
+ mShowToast(p.show_toast),
+ mFadeToast(p.fade_toast),
+ mSoundName("")
+{
+ if (p.sound.isProvided()
+ && LLUI::getInstance()->mSettingGroups["config"]->controlExists(p.sound))
+ {
+ mSoundName = p.sound;
+ }
+
+ for (const LLNotificationTemplate::UniquenessContext& context : p.unique.contexts)
+ {
+ mUniqueContext.push_back(context.value);
+ }
+
+ LL_DEBUGS("Notifications") << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL;
+
+ for (const LLNotificationTemplate::Tag& tag : p.tags)
+ {
+ LL_DEBUGS("Notifications") << " tag \"" << std::string(tag.value) << "\"" << LL_ENDL;
+ mTags.push_back(tag.value);
+ }
+
+ mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form));
+}
+
+LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p)
+{
+ if (p.show.isChosen())
+ {
+ mType = p.show.type;
+ mTag = p.show.tag;
+ mName = p.show.name;
+ mVisible = true;
+ }
+ else if (p.hide.isChosen())
+ {
+ mType = p.hide.type;
+ mTag = p.hide.tag;
+ mName = p.hide.name;
+ mVisible = false;
+ }
+ else if (p.respond.isChosen())
+ {
+ mType = p.respond.type;
+ mTag = p.respond.tag;
+ mName = p.respond.name;
+ mVisible = false;
+ mResponse = p.respond.response;
+ }
+}
+
+LLNotification::LLNotification(const LLSDParamAdapter<Params>& p) :
+ mTimestamp(p.time_stamp),
+ mSubstitutions(p.substitutions),
+ mPayload(p.payload),
+ mExpiresAt(p.expiry),
+ mTemporaryResponder(false),
+ mRespondedTo(false),
+ mPriority(p.priority),
+ mCancelled(false),
+ mIgnored(false),
+ mResponderObj(NULL),
+ mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()),
+ mOfferFromAgent(p.offer_from_agent),
+ mIsDND(p.is_dnd)
+{
+ if (p.functor.name.isChosen())
+ {
+ mResponseFunctorName = p.functor.name;
+ }
+ else if (p.functor.function.isChosen())
+ {
+ mResponseFunctorName = LLUUID::generateNewID().asString();
+ LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function());
+
+ mTemporaryResponder = true;
+ }
+ else if(p.functor.responder.isChosen())
+ {
+ mResponder = p.functor.responder;
+ }
+
+ if(p.responder.isProvided())
+ {
+ mResponderObj = p.responder;
+ }
+
+ init(p.name, p.form_elements);
+}
+
+
+LLSD LLNotification::asLLSD(bool excludeTemplateElements)
+{
+ LLParamSDParser parser;
+
+ Params p;
+ p.id = mId;
+ p.name = mTemplatep->mName;
+ p.substitutions = mSubstitutions;
+ p.payload = mPayload;
+ p.time_stamp = mTimestamp;
+ p.expiry = mExpiresAt;
+ p.priority = mPriority;
+
+ LLNotificationFormPtr templateForm = mTemplatep->mForm;
+ LLSD formElements = mForm->asLLSD();
+
+ //All form elements (dynamic or not)
+ if(!excludeTemplateElements)
+ {
+ p.form_elements = formElements;
+ }
+ //Only dynamic form elements (exclude template elements)
+ else if(templateForm->getNumElements() < formElements.size())
+ {
+ LLSD dynamicElements;
+ //Offset to dynamic elements and store them
+ mForm->getElements(dynamicElements, templateForm->getNumElements());
+ p.form_elements = dynamicElements;
+ }
+
+ if(mResponder)
+ {
+ p.functor.responder_sd = mResponder->asLLSD();
+ }
+
+ if(!mResponseFunctorName.empty())
+ {
+ p.functor.name = mResponseFunctorName;
+ }
+
+ LLSD output;
+ parser.writeSD(output, p);
+ return output;
+}
+
+void LLNotification::update()
+{
+ LLNotifications::instance().update(shared_from_this());
+}
+
+void LLNotification::updateFrom(LLNotificationPtr other)
+{
+ // can only update from the same notification type
+ if (mTemplatep != other->mTemplatep) return;
+
+ // NOTE: do NOT change the ID, since it is the key to
+ // this given instance, just update all the metadata
+ //mId = other->mId;
+
+ mPayload = other->mPayload;
+ mSubstitutions = other->mSubstitutions;
+ mTimestamp = other->mTimestamp;
+ mExpiresAt = other->mExpiresAt;
+ mCancelled = other->mCancelled;
+ mIgnored = other->mIgnored;
+ mPriority = other->mPriority;
+ mForm = other->mForm;
+ mResponseFunctorName = other->mResponseFunctorName;
+ mRespondedTo = other->mRespondedTo;
+ mResponse = other->mResponse;
+ mTemporaryResponder = other->mTemporaryResponder;
+
+ update();
+}
+
+const LLNotificationFormPtr LLNotification::getForm()
+{
+ return mForm;
+}
+
+void LLNotification::cancel()
+{
+ mCancelled = true;
+}
+
+LLSD LLNotification::getResponseTemplate(EResponseTemplateType type)
+{
+ LLSD response = LLSD::emptyMap();
+ for (S32 element_idx = 0;
+ element_idx < mForm->getNumElements();
+ ++element_idx)
+ {
+ LLSD element = mForm->getElement(element_idx);
+ if (element.has("name"))
+ {
+ response[element["name"].asString()] = element["value"];
+ }
+
+ if ((type == WITH_DEFAULT_BUTTON)
+ && element["default"].asBoolean())
+ {
+ response[element["name"].asString()] = true;
+ }
+ }
+ return response;
+}
+
+//static
+S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response)
+{
+ LLNotificationForm form(notification["form"]);
+
+ for (S32 element_idx = 0;
+ element_idx < form.getNumElements();
+ ++element_idx)
+ {
+ LLSD element = form.getElement(element_idx);
+
+ // only look at buttons
+ if (element["type"].asString() == "button"
+ && response[element["name"].asString()].asBoolean())
+ {
+ return element["index"].asInteger();
+ }
+ }
+
+ return -1;
+}
+
+//static
+std::string LLNotification::getSelectedOptionName(const LLSD& response)
+{
+ for (LLSD::map_const_iterator response_it = response.beginMap();
+ response_it != response.endMap();
+ ++response_it)
+ {
+ if (response_it->second.isBoolean() && response_it->second.asBoolean())
+ {
+ return response_it->first;
+ }
+ }
+ return "";
+}
+
+
+void LLNotification::respond(const LLSD& response)
+{
+ // *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()
+ mRespondedTo = true;
+ mResponse = response;
+
+ if(mResponder)
+ {
+ mResponder->handleRespond(asLLSD(), response);
+ }
+ else if (!mResponseFunctorName.empty())
+ {
+ // look up the functor
+ LLNotificationFunctorRegistry::ResponseFunctor functor =
+ LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
+ // and then call it
+ functor(asLLSD(), response);
+ }
+ else if (mCombinedNotifications.empty())
+ {
+ // no registered responder
+ return;
+ }
+
+ if (mTemporaryResponder)
+ {
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
+ mResponseFunctorName = "";
+ mTemporaryResponder = false;
+ }
+
+ if (mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO)
+ {
+ mForm->setIgnored(mIgnored);
+ if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
+ {
+ LLUI::getInstance()->mSettingGroups["ignores"]->setLLSD("Default" + getName(), response);
+ }
+ }
+
+ for (std::vector<LLNotificationPtr>::const_iterator it = mCombinedNotifications.begin(); it != mCombinedNotifications.end(); ++it)
+ {
+ if ((*it))
+ {
+ (*it)->respond(response);
+ }
+ }
+
+ update();
+}
+
+void LLNotification::respondWithDefault()
+{
+ respond(getResponseTemplate(WITH_DEFAULT_BUTTON));
+}
+
+
+const std::string& LLNotification::getName() const
+{
+ return mTemplatep->mName;
+}
+
+const std::string& LLNotification::getIcon() const
+{
+ return mTemplatep->mIcon;
+}
+
+
+bool LLNotification::isPersistent() const
+{
+ return mTemplatep->mPersist;
+}
+
+std::string LLNotification::getType() const
+{
+ return (mTemplatep ? mTemplatep->mType : "");
+}
+
+S32 LLNotification::getURLOption() const
+{
+ return (mTemplatep ? mTemplatep->mURLOption : -1);
+}
+
+S32 LLNotification::getURLOpenExternally() const
+{
+ return(mTemplatep? mTemplatep->mURLTarget == "_external": -1);
+}
+
+bool LLNotification::getForceUrlsExternal() const
+{
+ return (mTemplatep ? mTemplatep->mForceUrlsExternal : false);
+}
+
+bool LLNotification::hasUniquenessConstraints() const
+{
+ return (mTemplatep ? mTemplatep->mUnique : false);
+}
+
+bool LLNotification::matchesTag(const std::string& tag)
+{
+ bool result = false;
+
+ if(mTemplatep)
+ {
+ std::list<std::string>::iterator it;
+ for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++)
+ {
+ if((*it) == tag)
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+void LLNotification::setIgnored(bool ignore)
+{
+ mIgnored = ignore;
+}
+
+void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
+{
+ if (mTemporaryResponder)
+ // get rid of the old one
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
+ mResponseFunctorName = responseFunctorName;
+ mTemporaryResponder = false;
+}
+
+void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb)
+{
+ if(mTemporaryResponder)
+ {
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
+ }
+
+ LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
+}
+
+void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
+{
+ mResponder = responder;
+}
+
+bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
+{
+ if (this->mTemplatep->mName != that->mTemplatep->mName)
+ {
+ return false; // must have the same template name or forget it
+ }
+ if (this->mTemplatep->mUnique)
+ {
+ const LLSD& these_substitutions = this->getSubstitutions();
+ const LLSD& those_substitutions = that->getSubstitutions();
+ const LLSD& this_payload = this->getPayload();
+ const LLSD& that_payload = that->getPayload();
+
+ // highlander bit sez there can only be one of these
+ for (std::vector<std::string>::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end();
+ it != end_it;
+ ++it)
+ {
+ // if templates differ in either substitution strings or payload with the given field name
+ // then they are considered inequivalent
+ // use of get() avoids converting the LLSD value to a map as the [] operator would
+ if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString()
+ || this_payload.get(*it).asString() != that_payload.get(*it).asString())
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void LLNotification::init(const std::string& template_name, const LLSD& form_elements)
+{
+ mTemplatep = LLNotifications::instance().getTemplate(template_name);
+ if (!mTemplatep) return;
+
+ // add default substitutions
+ const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs();
+ for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin();
+ iter != default_args.end(); ++iter)
+ {
+ mSubstitutions[iter->first] = iter->second;
+ }
+ mSubstitutions["_URL"] = getURL();
+ mSubstitutions["_NAME"] = template_name;
+ // TODO: something like this so that a missing alert is sensible:
+ //mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
+
+ mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
+ mForm->append(form_elements);
+
+ // apply substitution to form labels
+ mForm->formatElements(mSubstitutions);
+
+ mIgnored = mForm->getIgnored();
+
+ LLDate rightnow = LLDate::now();
+ if (mTemplatep->mExpireSeconds)
+ {
+ mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds);
+ }
+
+ if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED)
+ {
+ mPriority = mTemplatep->mPriority;
+ }
+}
+
+std::string LLNotification::summarize() const
+{
+ std::string s = "Notification(";
+ s += getName();
+ s += ") : ";
+ s += mTemplatep ? mTemplatep->mMessage : "";
+ // should also include timestamp and expiration time (but probably not payload)
+ return s;
+}
+
+std::string LLNotification::getMessage() const
+{
+ // all our callers cache this result, so it gives us more flexibility
+ // to do the substitution at call time rather than attempting to
+ // cache it in the notification
+ if (!mTemplatep)
+ return std::string();
+
+ std::string message = mTemplatep->mMessage;
+ LLStringUtil::format(message, mSubstitutions);
+ return message;
+}
+
+std::string LLNotification::getFooter() const
+{
+ if (!mTemplatep)
+ return std::string();
+
+ std::string footer = mTemplatep->mFooter;
+ LLStringUtil::format(footer, mSubstitutions);
+ return footer;
+}
+
+std::string LLNotification::getLabel() const
+{
+ std::string label = mTemplatep->mLabel;
+ LLStringUtil::format(label, mSubstitutions);
+ return (mTemplatep ? label : "");
+}
+
+std::string LLNotification::getURL() const
+{
+ if (!mTemplatep)
+ return std::string();
+ std::string url = mTemplatep->mURL;
+ LLStringUtil::format(url, mSubstitutions);
+ return (mTemplatep ? url : "");
+}
+
+bool LLNotification::canLogToChat() const
+{
+ return mTemplatep->mLogToChat;
+}
+
+bool LLNotification::canLogToIM() const
+{
+ return mTemplatep->mLogToIM;
+}
+
+bool LLNotification::canShowToast() const
+{
+ return mTemplatep->mShowToast;
+}
+
+bool LLNotification::canFadeToast() const
+{
+ return mTemplatep->mFadeToast;
+}
+
+bool LLNotification::hasFormElements() const
+{
+ return mTemplatep->mForm->getNumElements() != 0;
+}
+
+void LLNotification::playSound()
+{
+ make_ui_sound(mTemplatep->mSoundName.c_str());
+}
+
+LLNotification::ECombineBehavior LLNotification::getCombineBehavior() const
+{
+ return mTemplatep->mCombineBehavior;
+}
+
+void LLNotification::updateForm( const LLNotificationFormPtr& form )
+{
+ mForm = form;
+}
+
+void LLNotification::repost()
+{
+ mRespondedTo = false;
+ LLNotifications::instance().update(shared_from_this());
+}
+
+
+
+// =========================================================
+// LLNotificationChannel implementation
+// ---
+LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot)
+{
+ // when someone wants to connect to a channel, we first throw them
+ // all of the notifications that are already in the channel
+ // we use a special signal called "load" in case the channel wants to care
+ // only about new notifications
+ LLMutexLock lock(&mItemsMutex);
+ for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
+ }
+ // and then connect the signal so that all future notifications will also be
+ // forwarded.
+ return mChanged.connect(slot);
+}
+
+LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot)
+{
+ for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
+ }
+ return mChanged.connect(slot, boost::signals2::at_front);
+}
+
+LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot)
+{
+ // these two filters only fire for notifications added after the current one, because
+ // they don't participate in the hierarchy.
+ return mPassedFilter.connect(slot);
+}
+
+LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot)
+{
+ return mFailedFilter.connect(slot);
+}
+
+// external call, conforms to our standard signature
+bool LLNotificationChannelBase::updateItem(const LLSD& payload)
+{
+ // first check to see if it's in the master list
+ LLNotificationPtr pNotification = LLNotifications::instance().find(payload["id"]);
+ if (!pNotification)
+ return false; // not found
+
+ return updateItem(payload, pNotification);
+}
+
+
+//FIX QUIT NOT WORKING
+
+
+// internal call, for use in avoiding lookup
+bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification)
+{
+ std::string cmd = payload["sigtype"];
+ LLNotificationSet::iterator foundItem = mItems.find(pNotification);
+ bool wasFound = (foundItem != mItems.end());
+ bool passesFilter = mFilter ? mFilter(pNotification) : true;
+
+ // first, we offer the result of the filter test to the simple
+ // signals for pass/fail. One of these is guaranteed to be called.
+ // If either signal returns true, the change processing is NOT performed
+ // (so don't return true unless you know what you're doing!)
+ bool abortProcessing = false;
+ if (passesFilter)
+ {
+ onFilterPass(pNotification);
+ abortProcessing = mPassedFilter(payload);
+ }
+ else
+ {
+ onFilterFail(pNotification);
+ abortProcessing = mFailedFilter(payload);
+ }
+
+ if (abortProcessing)
+ {
+ return true;
+ }
+
+ if (cmd == "load")
+ {
+ // should be no reason we'd ever get a load if we already have it
+ // if passes filter send a load message, else do nothing
+ assert(!wasFound);
+ if (passesFilter)
+ {
+ // not in our list, add it and say so
+ mItems.insert(pNotification);
+ onLoad(pNotification);
+ abortProcessing = mChanged(payload);
+ }
+ }
+ else if (cmd == "change")
+ {
+ // if it passes filter now and was found, we just send a change message
+ // if it passes filter now and wasn't found, we have to add it
+ // if it doesn't pass filter and wasn't found, we do nothing
+ // if it doesn't pass filter and was found, we need to delete it
+ if (passesFilter)
+ {
+ if (wasFound)
+ {
+ // it already existed, so this is a change
+ // since it changed in place, all we have to do is resend the signal
+ onChange(pNotification);
+ abortProcessing = mChanged(payload);
+ }
+ else
+ {
+ // not in our list, add it and say so
+ mItems.insert(pNotification);
+ onChange(pNotification);
+ // our payload is const, so make a copy before changing it
+ LLSD newpayload = payload;
+ newpayload["sigtype"] = "add";
+ abortProcessing = mChanged(newpayload);
+ }
+ }
+ else
+ {
+ if (wasFound)
+ {
+ // it already existed, so this is a delete
+ mItems.erase(pNotification);
+ onChange(pNotification);
+ // our payload is const, so make a copy before changing it
+ LLSD newpayload = payload;
+ newpayload["sigtype"] = "delete";
+ abortProcessing = mChanged(newpayload);
+ }
+ // didn't pass, not on our list, do nothing
+ }
+ }
+ else if (cmd == "add")
+ {
+ // should be no reason we'd ever get an add if we already have it
+ // if passes filter send an add message, else do nothing
+ assert(!wasFound);
+ if (passesFilter)
+ {
+ // not in our list, add it and say so
+ mItems.insert(pNotification);
+ onAdd(pNotification);
+ abortProcessing = mChanged(payload);
+ }
+ }
+ else if (cmd == "delete")
+ {
+ // if we have it in our list, pass on the delete, then delete it, else do nothing
+ if (wasFound)
+ {
+ onDelete(pNotification);
+ abortProcessing = mChanged(payload);
+ mItems.erase(pNotification);
+ }
+ }
+ return abortProcessing;
+}
+
+LLNotificationChannel::LLNotificationChannel(const Params& p)
+: LLNotificationChannelBase(p.filter()),
+ LLInstanceTracker<LLNotificationChannel, std::string>(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()),
+ mName(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString())
+{
+ for (const std::string& source : p.sources)
+ {
+ connectToChannel(source);
+ }
+}
+
+
+LLNotificationChannel::LLNotificationChannel(const std::string& name,
+ const std::string& parent,
+ LLNotificationFilter filter)
+: LLNotificationChannelBase(filter),
+ LLInstanceTracker<LLNotificationChannel, std::string>(name),
+ mName(name)
+{
+ // bind to notification broadcast
+ connectToChannel(parent);
+}
+
+LLNotificationChannel::~LLNotificationChannel()
+{
+ for (LLBoundListener &listener : mListeners)
+ {
+ listener.disconnect();
+ }
+}
+
+bool LLNotificationChannel::isEmpty() const
+{
+ return mItems.empty();
+}
+
+S32 LLNotificationChannel::size() const
+{
+ return mItems.size();
+}
+
+size_t LLNotificationChannel::size()
+{
+ return mItems.size();
+}
+
+void LLNotificationChannel::forEachNotification(NotificationProcess process)
+{
+ LLMutexLock lock(&mItemsMutex);
+ std::for_each(mItems.begin(), mItems.end(), process);
+}
+
+std::string LLNotificationChannel::summarize()
+{
+ std::string s("Channel '");
+ s += mName;
+ s += "'\n ";
+ LLMutexLock lock(&mItemsMutex);
+ for (LLNotificationChannel::Iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ s += (*it)->summarize();
+ s += "\n ";
+ }
+ return s;
+}
+
+void LLNotificationChannel::connectToChannel( const std::string& channel_name )
+{
+ if (channel_name.empty())
+ {
+ mListeners.push_back(LLNotifications::instance().connectChanged(
+ boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
+ }
+ else
+ {
+ mParents.push_back(channel_name);
+ LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name);
+ mListeners.push_back(p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
+ }
+}
+
+// ---
+// END OF LLNotificationChannel implementation
+// =========================================================
+
+
+// ============================================== ===========
+// LLNotifications implementation
+// ---
+LLNotifications::LLNotifications()
+: LLNotificationChannelBase(LLNotificationFilters::includeEverything),
+ mIgnoreAllNotifications(false)
+{
+ mListener.reset(new LLNotificationsListener(*this));
+ LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
+
+ // touch the instance tracker for notification channels, so that it will still be around in our destructor
+ LLInstanceTracker<LLNotificationChannel, std::string>::instanceCount();
+}
+
+void LLNotifications::clear()
+{
+ mDefaultChannels.clear();
+}
+
+// The expiration channel gets all notifications that are cancelled
+bool LLNotifications::expirationFilter(LLNotificationPtr pNotification)
+{
+ return pNotification->isCancelled() || pNotification->isRespondedTo();
+}
+
+bool LLNotifications::expirationHandler(const LLSD& payload)
+{
+ if (payload["sigtype"].asString() != "delete")
+ {
+ // anything added to this channel actually should be deleted from the master
+ cancel(find(payload["id"]));
+ return true; // don't process this item any further
+ }
+ return false;
+}
+
+bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
+{
+ if (!pNotif->hasUniquenessConstraints())
+ {
+ return true;
+ }
+
+ // checks against existing unique notifications
+ for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
+ existing_it != mUniqueNotifications.end();
+ ++existing_it)
+ {
+ LLNotificationPtr existing_notification = existing_it->second;
+ if (pNotif != existing_notification
+ && pNotif->isEquivalentTo(existing_notification))
+ {
+ if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD)
+ {
+ cancel(existing_notification);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool LLNotifications::uniqueHandler(const LLSD& payload)
+{
+ std::string cmd = payload["sigtype"];
+
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+ if (pNotif && pNotif->hasUniquenessConstraints())
+ {
+ if (cmd == "add")
+ {
+ // not a duplicate according to uniqueness criteria, so we keep it
+ // and store it for future uniqueness checks
+ mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif));
+ }
+ else if (cmd == "delete")
+ {
+ mUniqueNotifications.erase(pNotif->getName());
+ }
+ }
+
+ return false;
+}
+
+bool LLNotifications::failedUniquenessTest(const LLSD& payload)
+{
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+
+ std::string cmd = payload["sigtype"];
+
+ if (!pNotif || cmd != "add")
+ {
+ return false;
+ }
+
+ switch(pNotif->getCombineBehavior())
+ {
+ case LLNotification::REPLACE_WITH_NEW:
+ // Update the existing unique notification with the data from this particular instance...
+ // This guarantees that duplicate notifications will be collapsed to the one
+ // most recently triggered
+ for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
+ existing_it != mUniqueNotifications.end();
+ ++existing_it)
+ {
+ LLNotificationPtr existing_notification = existing_it->second;
+ if (pNotif != existing_notification
+ && pNotif->isEquivalentTo(existing_notification))
+ {
+ // copy notification instance data over to oldest instance
+ // of this unique notification and update it
+ existing_notification->updateFrom(pNotif);
+ // then delete the new one
+ cancel(pNotif);
+ }
+ }
+ break;
+ case LLNotification::COMBINE_WITH_NEW:
+ // Add to the existing unique notification with the data from this particular instance...
+ // This guarantees that duplicate notifications will be collapsed to the one
+ // most recently triggered
+ for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
+ existing_it != mUniqueNotifications.end();
+ ++existing_it)
+ {
+ LLNotificationPtr existing_notification = existing_it->second;
+ if (pNotif != existing_notification
+ && pNotif->isEquivalentTo(existing_notification))
+ {
+ // copy the notifications from the newest instance into the oldest
+ existing_notification->mCombinedNotifications.push_back(pNotif);
+ existing_notification->mCombinedNotifications.insert(existing_notification->mCombinedNotifications.end(),
+ pNotif->mCombinedNotifications.begin(), pNotif->mCombinedNotifications.end());
+
+ // pop up again
+ existing_notification->update();
+ }
+ }
+ break;
+ case LLNotification::KEEP_OLD:
+ break;
+ case LLNotification::CANCEL_OLD:
+ // already handled by filter logic
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
+{
+ return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName).get());
+}
+
+
+// this function is called once at construction time, after the object is constructed.
+void LLNotifications::initSingleton()
+{
+ loadTemplates();
+ loadVisibilityRules();
+ createDefaultChannels();
+}
+
+void LLNotifications::cleanupSingleton()
+{
+ clear();
+}
+
+void LLNotifications::createDefaultChannels()
+{
+ LL_INFOS("Notifications") << "Generating default notification channels" << LL_ENDL;
+ // now construct the various channels AFTER loading the notifications,
+ // because the history channel is going to rewrite the stored notifications file
+ mDefaultChannels.push_back(new LLNotificationChannel("Enabled", "",
+ !boost::bind(&LLNotifications::getIgnoreAllNotifications, this)));
+ mDefaultChannels.push_back(new LLNotificationChannel("Expiration", "Enabled",
+ boost::bind(&LLNotifications::expirationFilter, this, _1)));
+ mDefaultChannels.push_back(new LLNotificationChannel("Unexpired", "Enabled",
+ !boost::bind(&LLNotifications::expirationFilter, this, _1))); // use negated bind
+ mDefaultChannels.push_back(new LLNotificationChannel("Unique", "Unexpired",
+ boost::bind(&LLNotifications::uniqueFilter, this, _1)));
+ mDefaultChannels.push_back(new LLNotificationChannel("Ignore", "Unique",
+ filterIgnoredNotifications));
+ mDefaultChannels.push_back(new LLNotificationChannel("VisibilityRules", "Ignore",
+ boost::bind(&LLNotifications::isVisibleByRules, this, _1)));
+ mDefaultChannels.push_back(new LLNotificationChannel("Visible", "VisibilityRules",
+ &LLNotificationFilters::includeEverything));
+ mDefaultChannels.push_back(new LLPersistentNotificationChannel());
+
+ // connect action methods to these channels
+ getChannel("Enabled")->connectFailedFilter(&defaultResponse);
+ getChannel("Expiration")->connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));
+ // uniqueHandler slot should be added as first slot of the signal due to
+ // usage LLStopWhenHandled combiner in LLStandardSignal
+ getChannel("Unique")->connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
+ getChannel("Unique")->connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
+ getChannel("Ignore")->connectFailedFilter(&handleIgnoredNotification);
+ getChannel("VisibilityRules")->connectFailedFilter(&visibilityRuleMached);
+}
+
+
+LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
+{
+ if (mTemplates.count(name))
+ {
+ return mTemplates[name];
+ }
+ else
+ {
+ return mTemplates["MissingAlert"];
+ }
+}
+
+bool LLNotifications::templateExists(const std::string& name)
+{
+ return (mTemplates.count(name) != 0);
+}
+
+void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option)
+{
+ LLNotificationPtr temp_notify(new LLNotification(params));
+ LLSD response = temp_notify->getResponseTemplate();
+ LLSD selected_item = temp_notify->getForm()->getElement(option);
+
+ if (selected_item.isUndefined())
+ {
+ LL_WARNS("Notifications") << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL;
+ return;
+ }
+ response[selected_item["name"].asString()] = true;
+
+ temp_notify->respond(response);
+}
+
+LLNotifications::TemplateNames LLNotifications::getTemplateNames() const
+{
+ TemplateNames names;
+ for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it)
+ {
+ names.push_back(it->first);
+ }
+ return names;
+}
+
+typedef std::map<std::string, std::string> StringMap;
+void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
+{
+ // walk the list of attributes looking for replacements
+ for (LLXMLAttribList::iterator it=node->mAttributes.begin();
+ it != node->mAttributes.end(); ++it)
+ {
+ std::string value = it->second->getValue();
+ if (value[0] == '$')
+ {
+ value.erase(0, 1); // trim off the $
+ std::string replacement;
+ StringMap::const_iterator found = replacements.find(value);
+ if (found != replacements.end())
+ {
+ replacement = found->second;
+ LL_DEBUGS("Notifications") << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL;
+ it->second->setValue(replacement);
+ }
+ else
+ {
+ LL_WARNS("Notifications") << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL;
+ }
+ }
+ }
+
+ // now walk the list of children and call this recursively.
+ for (LLXMLNodePtr child = node->getFirstChild();
+ child.notNull(); child = child->getNextSibling())
+ {
+ replaceSubstitutionStrings(child, replacements);
+ }
+}
+
+void replaceFormText(LLNotificationForm::Params& form, const std::string& pattern, const std::string& replace)
+{
+ if (form.ignore.isProvided() && form.ignore.text() == pattern)
+ {
+ form.ignore.text = replace;
+ }
+
+ for (LLNotificationForm::FormElement& element : form.form_elements.elements)
+ {
+ if (element.button.isChosen() && element.button.text() == pattern)
+ {
+ element.button.text = replace;
+ }
+ }
+}
+
+void addPathIfExists(const std::string& new_path, std::vector<std::string>& paths)
+{
+ if (gDirUtilp->fileExists(new_path))
+ {
+ paths.push_back(new_path);
+ }
+}
+
+bool LLNotifications::loadTemplates()
+{
+ LL_INFOS("Notifications") << "Reading notifications template" << LL_ENDL;
+ // Passing findSkinnedFilenames(constraint=LLDir::ALL_SKINS) makes it
+ // output all relevant pathnames instead of just the ones from the most
+ // specific skin.
+ std::vector<std::string> search_paths =
+ gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", LLDir::ALL_SKINS);
+ if (search_paths.empty())
+ {
+ LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
+ LL_ERRS() << "Problem finding notifications.xml" << LL_ENDL;
+ }
+
+ std::string base_filename = search_paths.front();
+ LLXMLNodePtr root;
+ bool success = LLXMLNode::getLayeredXMLNode(root, search_paths);
+
+ if (!success || root.isNull() || !root->hasName( "notifications" ))
+ {
+ LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
+ LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL;
+ return false;
+ }
+
+ LLNotificationTemplate::Notifications params;
+ LLXUIParser parser;
+ parser.readXUI(root, params, base_filename);
+
+ if(!params.validateBlock())
+ {
+ LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
+ LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL;
+ return false;
+ }
+
+ mTemplates.clear();
+
+ for (const LLNotificationTemplate::GlobalString& string : params.strings)
+ {
+ mGlobalStrings[string.name] = string.value;
+ }
+
+ std::map<std::string, LLNotificationForm::Params> form_templates;
+
+ for (const LLNotificationTemplate::Template& notification_template : params.templates)
+ {
+ form_templates[notification_template.name] = notification_template.form;
+ }
+
+ for (LLNotificationTemplate::Params& notification : params.notifications)
+ {
+ if (notification.form_ref.form_template.isChosen())
+ {
+ // replace form contents from template
+ notification.form_ref.form = form_templates[notification.form_ref.form_template.name];
+ if(notification.form_ref.form_template.yes_text.isProvided())
+ {
+ replaceFormText(notification.form_ref.form, "$yestext", notification.form_ref.form_template.yes_text);
+ }
+ if(notification.form_ref.form_template.no_text.isProvided())
+ {
+ replaceFormText(notification.form_ref.form, "$notext", notification.form_ref.form_template.no_text);
+ }
+ if(notification.form_ref.form_template.cancel_text.isProvided())
+ {
+ replaceFormText(notification.form_ref.form, "$canceltext", notification.form_ref.form_template.cancel_text);
+ }
+ if(notification.form_ref.form_template.help_text.isProvided())
+ {
+ replaceFormText(notification.form_ref.form, "$helptext", notification.form_ref.form_template.help_text);
+ }
+ if(notification.form_ref.form_template.ignore_text.isProvided())
+ {
+ replaceFormText(notification.form_ref.form, "$ignoretext", notification.form_ref.form_template.ignore_text);
+ }
+ }
+ mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification));
+ }
+
+ LL_INFOS("Notifications") << "...done" << LL_ENDL;
+
+ return true;
+}
+
+bool LLNotifications::loadVisibilityRules()
+{
+ const std::string xml_filename = "notification_visibility.xml";
+ // Note that here we're looking for the "en" version, the default
+ // language, rather than the most localized version of this file.
+ std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, xml_filename);
+
+ LLNotificationVisibilityRule::Rules params;
+ LLSimpleXUIParser parser;
+ parser.readXUI(full_filename, params);
+
+ if(!params.validateBlock())
+ {
+ LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
+ LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL;
+ return false;
+ }
+
+ mVisibilityRules.clear();
+
+ for (const LLNotificationVisibilityRule::Rule& rule : params.rules)
+ {
+ mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(rule)));
+ }
+
+ return true;
+}
+
+// Add a simple notification (from XUI)
+void LLNotifications::addFromCallback(const LLSD& name)
+{
+ add(name.asString(), LLSD(), LLSD());
+}
+
+LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = name;
+ return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+}
+
+LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, const std::string& functor_name)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = functor_name;
+ return add(LLNotification::Params().name(name)
+ .substitutions(substitutions)
+ .payload(payload)
+ .functor(functor_p));
+}
+
+//virtual
+LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.function = functor;
+ return add(LLNotification::Params().name(name)
+ .substitutions(substitutions)
+ .payload(payload)
+ .functor(functor_p));
+}
+
+// generalized add function that takes a parameter block object for more complex instantiations
+LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
+{
+ LLNotificationPtr pNotif(new LLNotification(p));
+ add(pNotif);
+ return pNotif;
+}
+
+
+void LLNotifications::add(const LLNotificationPtr pNotif)
+{
+ if (pNotif == NULL) return;
+
+ // first see if we already have it -- if so, that's a problem
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ LL_ERRS() << "Notification added a second time to the master notification channel." << LL_ENDL;
+ }
+
+ updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif);
+}
+
+void LLNotifications::load(const LLNotificationPtr pNotif)
+{
+ if (pNotif == NULL) return;
+
+ // first see if we already have it -- if so, that's a problem
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ LL_ERRS() << "Notification loaded a second time to the master notification channel." << LL_ENDL;
+ }
+
+ updateItem(LLSD().with("sigtype", "load").with("id", pNotif->id()), pNotif);
+}
+
+void LLNotifications::cancel(LLNotificationPtr pNotif)
+{
+ if (pNotif == NULL || pNotif->isCancelled()) return;
+
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
+ }
+}
+
+void LLNotifications::cancelByName(const std::string& name)
+{
+ LLMutexLock lock(&mItemsMutex);
+ std::vector<LLNotificationPtr> notifs_to_cancel;
+ for (LLNotificationSet::iterator it=mItems.begin(), end_it = mItems.end();
+ it != end_it;
+ ++it)
+ {
+ LLNotificationPtr pNotif = *it;
+ if (pNotif->getName() == name)
+ {
+ notifs_to_cancel.push_back(pNotif);
+ }
+ }
+
+ for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
+ it != end_it;
+ ++it)
+ {
+ LLNotificationPtr pNotif = *it;
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
+ }
+}
+
+void LLNotifications::cancelByOwner(const LLUUID ownerId)
+{
+ LLMutexLock lock(&mItemsMutex);
+ std::vector<LLNotificationPtr> notifs_to_cancel;
+ for (LLNotificationSet::iterator it = mItems.begin(), end_it = mItems.end();
+ it != end_it;
+ ++it)
+ {
+ LLNotificationPtr pNotif = *it;
+ if (pNotif && pNotif->getPayload().get("owner_id").asUUID() == ownerId)
+ {
+ notifs_to_cancel.push_back(pNotif);
+ }
+ }
+
+ for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
+ it != end_it;
+ ++it)
+ {
+ LLNotificationPtr pNotif = *it;
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
+ }
+}
+
+void LLNotifications::update(const LLNotificationPtr pNotif)
+{
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ updateItem(LLSD().with("sigtype", "change").with("id", pNotif->id()), pNotif);
+ }
+}
+
+
+LLNotificationPtr LLNotifications::find(LLUUID uuid)
+{
+ LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid)));
+ LLNotificationSet::iterator it=mItems.find(target);
+ if (it == mItems.end())
+ {
+ LL_DEBUGS("Notifications") << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << LL_ENDL;
+ return LLNotificationPtr((LLNotification*)NULL);
+ }
+ else
+ {
+ return *it;
+ }
+}
+
+std::string LLNotifications::getGlobalString(const std::string& key) const
+{
+ GlobalStringMap::const_iterator it = mGlobalStrings.find(key);
+ if (it != mGlobalStrings.end())
+ {
+ return it->second;
+ }
+ else
+ {
+ // if we don't have the key as a global, return the key itself so that the error
+ // is self-diagnosing.
+ return key;
+ }
+}
+
+void LLNotifications::setIgnoreAllNotifications(bool setting)
+{
+ mIgnoreAllNotifications = setting;
+}
+bool LLNotifications::getIgnoreAllNotifications()
+{
+ return mIgnoreAllNotifications;
+}
+
+void LLNotifications::setIgnored(const std::string& name, bool ignored)
+{
+ LLNotificationTemplatePtr templatep = getTemplate(name);
+ templatep->mForm->setIgnored(ignored);
+}
+
+bool LLNotifications::getIgnored(const std::string& name)
+{
+ LLNotificationTemplatePtr templatep = getTemplate(name);
+ return (mIgnoreAllNotifications) || ( (templatep->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) && (templatep->mForm->getIgnored()) );
+}
+
+bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
+{
+ if(n->isRespondedTo())
+ {
+ // This avoids infinite recursion in the case where the filter calls respond()
+ return true;
+ }
+
+ VisibilityRuleList::iterator it;
+
+ for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++)
+ {
+ // An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule.
+ LL_DEBUGS("Notifications")
+ << "notification \"" << n->getName() << "\" "
+ << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, "
+ << "name = \"" << (*it)->mName << "\" "
+ << "tag = \"" << (*it)->mTag << "\" "
+ << "type = \"" << (*it)->mType << "\" "
+ << LL_ENDL;
+
+ if(!(*it)->mType.empty())
+ {
+ if((*it)->mType != n->getType())
+ {
+ // Type doesn't match, so skip this rule.
+ continue;
+ }
+ }
+
+ if(!(*it)->mTag.empty())
+ {
+ // check this notification's tag(s) against it->mTag and continue if no match is found.
+ if(!n->matchesTag((*it)->mTag))
+ {
+ // This rule's non-empty tag didn't match one of the notification's tags. Skip this rule.
+ continue;
+ }
+ }
+
+ if(!(*it)->mName.empty())
+ {
+ // check this notification's name against the notification's name and continue if no match is found.
+ if((*it)->mName != n->getName())
+ {
+ // This rule's non-empty name didn't match the notification. Skip this rule.
+ continue;
+ }
+ }
+
+ // If we got here, the rule matches. Don't evaluate subsequent rules.
+ if(!(*it)->mVisible)
+ {
+ // This notification is being hidden.
+
+ if((*it)->mResponse.empty())
+ {
+ // Response property is empty. Cancel this notification.
+ LL_DEBUGS("Notifications") << "cancelling notification " << n->getName() << LL_ENDL;
+
+ cancel(n);
+ }
+ else
+ {
+ // Response property is not empty. Return the specified response.
+ LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON);
+ // TODO: verify that the response template has an item with the correct name
+ response[(*it)->mResponse] = true;
+
+ LL_DEBUGS("Notifications") << "responding to notification " << n->getName() << " with response = " << response << LL_ENDL;
+
+ n->respond(response);
+ }
+
+ return false;
+ }
+
+ // If we got here, exit the loop and return true.
+ break;
+ }
+
+ LL_DEBUGS("Notifications") << "allowing notification " << n->getName() << LL_ENDL;
+
+ return true;
+}
+
+
+// ---
+// END OF LLNotifications implementation
+// =========================================================
+
+std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
+{
+ s << notification.summarize();
+ return s;
+}
+
+void LLPostponedNotification::lookupName(const LLUUID& id,
+ bool is_group)
+{
+ if (is_group)
+ {
+ gCacheName->getGroup(id,
+ boost::bind(&LLPostponedNotification::onGroupNameCache,
+ this, _1, _2, _3));
+ }
+ else
+ {
+ fetchAvatarName(id);
+ }
+}
+
+void LLPostponedNotification::onGroupNameCache(const LLUUID& id,
+ const std::string& full_name,
+ bool is_group)
+{
+ finalizeName(full_name);
+}
+
+void LLPostponedNotification::fetchAvatarName(const LLUUID& id)
+{
+ if (id.notNull())
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLPostponedNotification::onAvatarNameCache, this, _1, _2));
+ }
+}
+
+void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id,
+ const LLAvatarName& av_name)
+{
+ mAvatarNameCacheConnection.disconnect();
+
+ std::string name = av_name.getCompleteName();
+
+ // from PE merge - we should figure out if this is the right thing to do
+ if (name.empty())
+ {
+ LL_WARNS("Notifications") << "Empty name received for Id: " << agent_id << LL_ENDL;
+ name = SYSTEM_FROM;
+ }
+
+ finalizeName(name);
+}
+
+void LLPostponedNotification::finalizeName(const std::string& name)
+{
+ mName = name;
+ modifyNotificationParams();
+ LLNotifications::instance().add(mParams);
+ cleanup();
+}