summaryrefslogtreecommitdiff
path: root/indra/llui/llnotifications.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/llnotifications.cpp')
-rwxr-xr-x[-rw-r--r--]indra/llui/llnotifications.cpp1466
1 files changed, 920 insertions, 546 deletions
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 86989012ee..de14391d1f 100644..100755
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -2,186 +2,115 @@
* @file llnotifications.cpp
* @brief Non-UI queue manager for keeping a prioritized list of notifications
*
-* $LicenseInfo:firstyear=2008&license=viewergpl$
-*
-* Copyright (c) 2008-2009, Linden Research, Inc.
-*
+* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
-* The source code in this file ("Source Code") is provided by Linden Lab
-* to you under the terms of the GNU General Public License, version 2.0
-* ("GPL"), unless you have obtained a separate licensing agreement
-* ("Other License"), formally executed by you and Linden Lab. Terms of
-* the GPL can be found in doc/GPL-license.txt in this distribution, or
-* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+* 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.
*
-* There are special exceptions to the terms and conditions of the GPL as
-* it is applied to this Source Code. View the full text of the exception
-* in the file doc/FLOSS-exception.txt in this software distribution, or
-* online at
-* http://secondlifegrid.net/programs/open_source/licensing/flossexception
+* 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.
*
-* By copying, modifying or distributing this software, you acknowledge
-* that you have read and understood your obligations described above,
-* and agree to abide by those obligations.
+* 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
*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
+* 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 "llnotificationslistener.h"
+#include "llstring.h"
+#include "llsdparam.h"
+#include "llsdutil.h"
#include <algorithm>
#include <boost/regex.hpp>
+#include <boost/foreach.hpp>
const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
-// local channel for notification history
-class LLNotificationHistoryChannel : public LLNotificationChannel
+void NotificationPriorityValues::declareValues()
{
- LOG_CLASS(LLNotificationHistoryChannel);
-public:
- LLNotificationHistoryChannel(const std::string& filename) :
- LLNotificationChannel("History", "Visible", &historyFilter, LLNotificationComparators::orderByUUID()),
- mFileName(filename)
- {
- connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler, this, _1));
- loadPersistentNotifications();
- }
-
-private:
- bool historyHandler(const LLSD& payload)
- {
- // we ignore "load" messages, but rewrite the persistence file on any other
- std::string sigtype = payload["sigtype"];
- if (sigtype != "load")
- {
- savePersistentNotifications();
- }
- return false;
- }
-
- // The history channel gets all notifications except those that have been cancelled
- static bool historyFilter(LLNotificationPtr pNotification)
- {
- return !pNotification->isCancelled();
- }
-
- void savePersistentNotifications()
- {
- /* NOTE: As of 2009-11-09 the reload of notifications on startup does not
- work, and has not worked for months. Skip saving notifications until the
- read can be fixed, because this hits the disk once per notification and
- causes log spam. James
-
- llinfos << "Saving open notifications to " << mFileName << llendl;
-
- llofstream notify_file(mFileName.c_str());
- if (!notify_file.is_open())
- {
- llwarns << "Failed to open " << mFileName << llendl;
- return;
- }
+ declare("low", NOTIFICATION_PRIORITY_LOW);
+ declare("normal", NOTIFICATION_PRIORITY_NORMAL);
+ declare("high", NOTIFICATION_PRIORITY_HIGH);
+ declare("critical", NOTIFICATION_PRIORITY_CRITICAL);
+}
- LLSD output;
- output["version"] = NOTIFICATION_PERSIST_VERSION;
- LLSD& data = output["data"];
+LLNotificationForm::FormElementBase::FormElementBase()
+: name("name"),
+ enabled("enabled", true)
+{}
- for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
- {
- if (!LLNotifications::instance().templateExists((*it)->getName())) continue;
+LLNotificationForm::FormIgnore::FormIgnore()
+: text("text"),
+ control("control"),
+ invert_control("invert_control", false),
+ save_option("save_option", false)
+{}
- // only store notifications flagged as persisting
- LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate((*it)->getName());
- if (!templatep->mPersist) continue;
+LLNotificationForm::FormButton::FormButton()
+: index("index"),
+ text("text"),
+ ignore("ignore"),
+ is_default("default"),
+ type("type")
+{
+ // set type here so it gets serialized
+ type = "button";
+}
- data.append((*it)->asLLSD());
- }
+LLNotificationForm::FormInput::FormInput()
+: type("type"),
+ text("text"),
+ max_length_chars("max_length_chars"),
+ width("width", 0),
+ value("value")
+{}
- LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
- formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
- */
- }
+LLNotificationForm::FormElement::FormElement()
+: button("button"),
+ input("input")
+{}
- void loadPersistentNotifications()
- {
- llinfos << "Loading open notifications from " << mFileName << llendl;
+LLNotificationForm::FormElements::FormElements()
+: elements("")
+{}
- llifstream notify_file(mFileName.c_str());
- if (!notify_file.is_open())
- {
- llwarns << "Failed to open " << mFileName << llendl;
- return;
- }
+LLNotificationForm::Params::Params()
+: name("name"),
+ ignore("ignore"),
+ form_elements("")
+{}
- LLSD input;
- LLPointer<LLSDParser> parser = new LLSDXMLParser();
- if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
- {
- llwarns << "Failed to parse open notifications" << llendl;
- return;
- }
- if (input.isUndefined()) return;
- std::string version = input["version"];
- if (version != NOTIFICATION_PERSIST_VERSION)
- {
- llwarns << "Bad open notifications version: " << version << llendl;
- return;
- }
- LLSD& data = input["data"];
- if (data.isUndefined()) return;
-
- LLNotifications& instance = LLNotifications::instance();
- for (LLSD::array_const_iterator notification_it = data.beginArray();
- notification_it != data.endArray();
- ++notification_it)
- {
- instance.add(LLNotificationPtr(new LLNotification(*notification_it)));
- }
- }
-
- //virtual
- void onDelete(LLNotificationPtr pNotification)
- {
- // we want to keep deleted notifications in our log
- mItems.insert(pNotification);
-
- return;
- }
-
-private:
- std::string mFileName;
-};
bool filterIgnoredNotifications(LLNotificationPtr notification)
{
- // filter everything if we are to ignore ALL
- if(LLNotifications::instance().getIgnoreAllNotifications())
- {
- return false;
- }
-
LLNotificationFormPtr form = notification->getForm();
// Check to see if the user wants to ignore this alert
- if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO)
- {
- return LLUI::sSettingGroups["ignores"]->getBOOL(notification->getName());
- }
-
- return true;
+ return !notification->getForm()->getIgnored();
}
bool handleIgnoredNotification(const LLSD& payload)
@@ -213,6 +142,28 @@ bool handleIgnoredNotification(const LLSD& payload)
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
@@ -223,66 +174,71 @@ namespace LLNotificationFilters
};
LLNotificationForm::LLNotificationForm()
-: mFormData(LLSD::emptyArray()),
- mIgnore(IGNORE_NO)
+: 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 LLXMLNodePtr xml_node)
-: mFormData(LLSD::emptyArray()),
- mIgnore(IGNORE_NO)
+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 (!xml_node->hasName("form"))
+ if (p.ignore.isProvided())
{
- llwarns << "Bad xml node for form: " << xml_node->getName() << llendl;
- }
- LLXMLNodePtr child = xml_node->getFirstChild();
- while(child)
- {
- child = LLNotifications::instance().checkForXMLTemplate(child);
+ mIgnoreMsg = p.ignore.text;
- LLSD item_entry;
- std::string element_name = child->getName()->mString;
+ if (!p.ignore.save_option)
+ {
+ mIgnore = IGNORE_WITH_DEFAULT_RESPONSE;
+ }
+ else
+ {
+ // remember last option chosen by user and automatically respond with that in the future
+ mIgnore = IGNORE_WITH_LAST_RESPONSE;
+ LLUI::sSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
+ }
- if (element_name == "ignore" )
+ BOOL show_notification = TRUE;
+ if (p.ignore.control.isProvided())
{
- bool save_option = false;
- child->getAttribute_bool("save_option", save_option);
- if (!save_option)
- {
- mIgnore = IGNORE_WITH_DEFAULT_RESPONSE;
- }
- else
- {
- // remember last option chosen by user and automatically respond with that in the future
- mIgnore = IGNORE_WITH_LAST_RESPONSE;
- LLUI::sSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
- }
- child->getAttributeString("text", mIgnoreMsg);
- BOOL show_notification = TRUE;
- LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Ignore notification with this name", TRUE);
+ mIgnoreSetting = LLUI::sSettingGroups["config"]->getControl(p.ignore.control);
+ mInvertSetting = p.ignore.invert_control;
}
else
{
- // flatten xml form entry into single LLSD map with type==name
- item_entry["type"] = element_name;
- const LLXMLAttribList::iterator attrib_end = child->mAttributes.end();
- for(LLXMLAttribList::iterator attrib_it = child->mAttributes.begin();
- attrib_it != attrib_end;
- ++attrib_it)
- {
- item_entry[std::string(attrib_it->second->getName()->mString)] = attrib_it->second->getValue();
- }
- item_entry["value"] = child->getTextContents();
- mFormData.append(item_entry);
+ LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", LLControlVariable::PERSIST_NONDFT);
+ mIgnoreSetting = LLUI::sSettingGroups["ignores"]->getControl(name);
}
+ }
+
+ LLParamSDParser parser;
+ parser.writeSD(mFormData, p.form_elements);
- child = child->getNextSibling();
+ 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())
{
@@ -290,7 +246,7 @@ LLNotificationForm::LLNotificationForm(const LLSD& sd)
}
else
{
- llwarns << "Invalid form data " << sd << llendl;
+ LL_WARNS() << "Invalid form data " << sd << LL_ENDL;
mFormData = LLSD::emptyArray();
}
}
@@ -312,7 +268,7 @@ LLSD LLNotificationForm::getElement(const std::string& element_name)
}
-bool LLNotificationForm::hasElement(const std::string& element_name)
+bool LLNotificationForm::hasElement(const std::string& element_name) const
{
for (LLSD::array_const_iterator it = mFormData.beginArray();
it != mFormData.endArray();
@@ -323,7 +279,48 @@ bool LLNotificationForm::hasElement(const std::string& element_name)
return false;
}
-void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value)
+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;
@@ -331,6 +328,7 @@ void LLNotificationForm::addElement(const std::string& type, const std::string&
element["text"] = name;
element["value"] = value;
element["index"] = mFormData.size();
+ element["enabled"] = enabled;
mFormData.append(element);
}
@@ -380,27 +378,117 @@ std::string LLNotificationForm::getDefaultOption()
return "";
}
-LLNotificationTemplate::LLNotificationTemplate() :
- mExpireSeconds(0),
- mExpireOption(-1),
- mURLOption(-1),
- mURLOpenExternally(-1),
- mUnique(false),
- mPriority(NOTIFICATION_PRIORITY_NORMAL)
+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)
{
- mForm = LLNotificationFormPtr(new LLNotificationForm());
+ if (mIgnoreSetting)
+ {
+ if (mInvertSetting) ignored = !ignored;
+ mIgnoreSetting->setValue(!ignored);
+ }
}
-LLNotification::LLNotification(const LLNotification::Params& p) :
+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),
+ 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),
+ mSoundName("")
+{
+ if (p.sound.isProvided()
+ && LLUI::sSettingGroups["config"]->controlExists(p.sound))
+ {
+ mSoundName = p.sound;
+ }
+
+ BOOST_FOREACH(const LLNotificationTemplate::UniquenessContext& context, p.unique.contexts)
+ {
+ mUniqueContext.push_back(context.value);
+ }
+
+ LL_DEBUGS() << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL;
+
+ BOOST_FOREACH(const LLNotificationTemplate::Tag& tag, p.tags)
+ {
+ LL_DEBUGS() << " 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(0),
+ mExpiresAt(p.expiry),
mTemporaryResponder(false),
mRespondedTo(false),
mPriority(p.priority),
mCancelled(false),
- mIgnored(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())
{
@@ -413,43 +501,62 @@ LLNotification::LLNotification(const LLNotification::Params& p) :
mTemporaryResponder = true;
}
+ else if(p.functor.responder.isChosen())
+ {
+ mResponder = p.functor.responder;
+ }
+
+ if(p.responder.isProvided())
+ {
+ mResponderObj = p.responder;
+ }
- mId.generate();
init(p.name, p.form_elements);
}
-LLNotification::LLNotification(const LLSD& sd) :
- mTemporaryResponder(false),
- mRespondedTo(false),
- mCancelled(false),
- mIgnored(false)
+LLSD LLNotification::asLLSD(bool excludeTemplateElements)
{
- mId.generate();
- mSubstitutions = sd["substitutions"];
- mPayload = sd["payload"];
- mTimestamp = sd["time"];
- mExpiresAt = sd["expiry"];
- mPriority = (ENotificationPriority)sd["priority"].asInteger();
- mResponseFunctorName = sd["responseFunctor"].asString();
- std::string templatename = sd["name"].asString();
- init(templatename, LLSD());
- // replace form with serialized version
- mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"]));
-}
-
+ 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 LLNotification::asLLSD()
-{
LLSD output;
- output["name"] = mTemplatep->mName;
- output["form"] = getForm()->asLLSD();
- output["substitutions"] = mSubstitutions;
- output["payload"] = mPayload;
- output["time"] = mTimestamp;
- output["expiry"] = mExpiresAt;
- output["priority"] = (S32)mPriority;
- output["responseFunctor"] = mResponseFunctorName;
+ parser.writeSD(output, p);
return output;
}
@@ -477,6 +584,7 @@ void LLNotification::updateFrom(LLNotificationPtr other)
mForm = other->mForm;
mResponseFunctorName = other->mResponseFunctorName;
mRespondedTo = other->mRespondedTo;
+ mResponse = other->mResponse;
mTemporaryResponder = other->mTemporaryResponder;
update();
@@ -554,13 +662,28 @@ std::string LLNotification::getSelectedOptionName(const LLSD& response)
void LLNotification::respond(const LLSD& response)
{
+ // *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()
mRespondedTo = true;
- // look up the functor
- LLNotificationFunctorRegistry::ResponseFunctor functor =
- LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
- // and then call it
- functor(asLLSD(), response);
-
+ 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
+ {
+ // no registered responder
+ return;
+ }
+
if (mTemporaryResponder)
{
LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
@@ -570,8 +693,7 @@ void LLNotification::respond(const LLSD& response)
if (mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO)
{
- BOOL show_notification = mIgnored ? FALSE : TRUE;
- LLUI::sSettingGroups["ignores"]->setBOOL(getName(), show_notification);
+ mForm->setIgnored(mIgnored);
if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
{
LLUI::sSettingGroups["ignores"]->setLLSD("Default" + getName(), response);
@@ -581,6 +703,68 @@ void LLNotification::respond(const LLSD& 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::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;
@@ -595,19 +779,19 @@ void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
mTemporaryResponder = false;
}
-bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const
+void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb)
{
- for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin();
- required_fields_it != required_fields.end();
- required_fields_it++)
+ if(mTemporaryResponder)
{
- std::string required_field_name = *required_fields_it;
- if( ! getPayload().has(required_field_name))
- {
- return false; // a required field was not found
- }
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
}
- return true; // all required fields were found
+
+ LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
+}
+
+void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
+{
+ mResponder = responder;
}
bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
@@ -618,11 +802,28 @@ bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
}
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
- return
- this->payloadContainsAll(that->mTemplatep->mUniqueContext) &&
- that->payloadContainsAll(this->mTemplatep->mUniqueContext);
+ 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;
}
@@ -644,11 +845,13 @@ void LLNotification::init(const std::string& template_name, const LLSD& form_ele
//mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
- mForm->append(form_elements);
+ mForm->append(form_elements);
// apply substitution to form labels
mForm->formatElements(mSubstitutions);
+ mIgnored = mForm->getIgnored();
+
LLDate rightnow = LLDate::now();
if (mTemplatep->mExpireSeconds)
{
@@ -684,6 +887,16 @@ std::string LLNotification::getMessage() const
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;
@@ -700,6 +913,49 @@ std::string LLNotification::getURL() const
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::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
// ---
@@ -760,7 +1016,7 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
std::string cmd = payload["sigtype"];
LLNotificationSet::iterator foundItem = mItems.find(pNotification);
bool wasFound = (foundItem != mItems.end());
- bool passesFilter = mFilter(pNotification);
+ 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.
@@ -769,10 +1025,12 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
bool abortProcessing = false;
if (passesFilter)
{
+ onFilterPass(pNotification);
abortProcessing = mPassedFilter(payload);
}
else
{
+ onFilterFail(pNotification);
abortProcessing = mFailedFilter(payload);
}
@@ -790,8 +1048,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// not in our list, add it and say so
mItems.insert(pNotification);
- abortProcessing = mChanged(payload);
onLoad(pNotification);
+ abortProcessing = mChanged(payload);
}
}
else if (cmd == "change")
@@ -806,18 +1064,18 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// it already existed, so this is a change
// since it changed in place, all we have to do is resend the signal
- abortProcessing = mChanged(payload);
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);
- onChange(pNotification);
}
}
else
@@ -826,11 +1084,11 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// 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);
- onChange(pNotification);
}
// didn't pass, not on our list, do nothing
}
@@ -844,8 +1102,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// not in our list, add it and say so
mItems.insert(pNotification);
- abortProcessing = mChanged(payload);
onAdd(pNotification);
+ abortProcessing = mChanged(payload);
}
}
else if (cmd == "delete")
@@ -853,61 +1111,35 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
// 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);
- onDelete(pNotification);
}
}
return abortProcessing;
}
-/* static */
-LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name,
- const std::string& parent,
- LLNotificationFilter filter,
- LLNotificationComparator comparator)
+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())
{
- // note: this is not a leak; notifications are self-registering.
- // This factory helps to prevent excess deletions by making sure all smart
- // pointers to notification channels come from the same source
- new LLNotificationChannel(name, parent, filter, comparator);
- return LLNotifications::instance().getChannel(name);
+ BOOST_FOREACH(const std::string& source, p.sources)
+ {
+ connectToChannel(source);
+ }
}
LLNotificationChannel::LLNotificationChannel(const std::string& name,
const std::string& parent,
- LLNotificationFilter filter,
- LLNotificationComparator comparator) :
-LLNotificationChannelBase(filter, comparator),
-mName(name),
-mParent(parent)
-{
- // store myself in the channel map
- LLNotifications::instance().addChannel(LLNotificationChannelPtr(this));
+ LLNotificationFilter filter)
+: LLNotificationChannelBase(filter),
+ LLInstanceTracker<LLNotificationChannel, std::string>(name),
+ mName(name)
+{
// bind to notification broadcast
- if (parent.empty())
- {
- LLNotifications::instance().connectChanged(
- boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
- }
- else
- {
- LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent);
- p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
- }
-}
-
-
-void LLNotificationChannel::setComparator(LLNotificationComparator comparator)
-{
- mComparator = comparator;
- LLNotificationSet s2(mComparator);
- s2.insert(mItems.begin(), mItems.end());
- mItems.swap(s2);
-
- // notify clients that we've been resorted
- mChanged(LLSD().with("sigtype", "sort"));
+ connectToChannel(parent);
}
bool LLNotificationChannel::isEmpty() const
@@ -915,6 +1147,11 @@ bool LLNotificationChannel::isEmpty() const
return mItems.empty();
}
+S32 LLNotificationChannel::size() const
+{
+ return mItems.size();
+}
+
LLNotificationChannel::Iterator LLNotificationChannel::begin()
{
return mItems.begin();
@@ -925,6 +1162,11 @@ LLNotificationChannel::Iterator LLNotificationChannel::end()
return mItems.end();
}
+size_t LLNotificationChannel::size()
+{
+ return mItems.size();
+}
+
std::string LLNotificationChannel::summarize()
{
std::string s("Channel '");
@@ -938,24 +1180,44 @@ std::string LLNotificationChannel::summarize()
return s;
}
+void LLNotificationChannel::connectToChannel( const std::string& channel_name )
+{
+ if (channel_name.empty())
+ {
+ LLNotifications::instance().connectChanged(
+ boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ }
+ else
+ {
+ mParents.push_back(channel_name);
+ LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name);
+ p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ }
+}
// ---
// END OF LLNotificationChannel implementation
// =========================================================
-// =========================================================
+// ============================================== ===========
// LLNotifications implementation
// ---
-LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything,
- LLNotificationComparators::orderByUUID()),
- mIgnoreAllNotifications(false)
+LLNotifications::LLNotifications()
+: LLNotificationChannelBase(LLNotificationFilters::includeEverything),
+ mIgnoreAllNotifications(false)
{
+ mListener.reset(new LLNotificationsListener(*this));
LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
- mListener.reset(new LLNotificationsListener(*this));
+ // 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)
@@ -990,7 +1252,15 @@ bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
if (pNotif != existing_notification
&& pNotif->isEquivalentTo(existing_notification))
{
- return false;
+ if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD)
+ {
+ cancel(existing_notification);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
}
@@ -999,16 +1269,18 @@ bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
bool LLNotifications::uniqueHandler(const LLSD& payload)
{
+ std::string cmd = payload["sigtype"];
+
LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
if (pNotif && pNotif->hasUniquenessConstraints())
{
- if (payload["sigtype"].asString() == "add")
+ 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 (payload["sigtype"].asString() == "delete")
+ else if (cmd == "delete")
{
mUniqueNotifications.erase(pNotif->getName());
}
@@ -1021,45 +1293,50 @@ bool LLNotifications::failedUniquenessTest(const LLSD& payload)
{
LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
- if (!pNotif || !pNotif->hasUniquenessConstraints())
+ std::string cmd = payload["sigtype"];
+
+ if (!pNotif || cmd != "add")
{
return false;
}
- // checks against existing unique notifications
- for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
- existing_it != mUniqueNotifications.end();
- ++existing_it)
+ switch(pNotif->getCombineBehavior())
{
- LLNotificationPtr existing_notification = existing_it->second;
- if (pNotif != existing_notification
- && pNotif->isEquivalentTo(existing_notification))
+ 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)
{
- // copy notification instance data over to oldest instance
- // of this unique notification and update it
- existing_notification->updateFrom(pNotif);
- // then delete the new one
- pNotif->cancel();
+ 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::KEEP_OLD:
+ break;
+ case LLNotification::CANCEL_OLD:
+ // already handled by filter logic
+ break;
+ default:
+ break;
}
return false;
}
-
-void LLNotifications::addChannel(LLNotificationChannelPtr pChan)
-{
- mChannels[pChan->getName()] = pChan;
-}
-
LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
{
- ChannelMap::iterator p = mChannels.find(channelName);
- if(p == mChannels.end())
- {
- llerrs << "Did not find channel named " << channelName << llendl;
- }
- return p->second;
+ return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName));
}
@@ -1067,6 +1344,7 @@ LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelN
void LLNotifications::initSingleton()
{
loadTemplates();
+ loadVisibilityRules();
createDefaultChannels();
}
@@ -1074,49 +1352,39 @@ void LLNotifications::createDefaultChannels()
{
// now construct the various channels AFTER loading the notifications,
// because the history channel is going to rewrite the stored notifications file
- LLNotificationChannel::buildChannel("Expiration", "",
- boost::bind(&LLNotifications::expirationFilter, this, _1));
- LLNotificationChannel::buildChannel("Unexpired", "",
- !boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind
- LLNotificationChannel::buildChannel("Unique", "Unexpired",
- boost::bind(&LLNotifications::uniqueFilter, this, _1));
- LLNotificationChannel::buildChannel("Ignore", "Unique",
- filterIgnoredNotifications);
- LLNotificationChannel::buildChannel("Visible", "Ignore",
- &LLNotificationFilters::includeEverything);
-
- // create special history channel
- //std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
- // use ^^^ when done debugging notifications serialization
- std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_USER_SETTINGS, "open_notifications.xml" );
- // this isn't a leak, don't worry about the empty "new"
- new LLNotificationHistoryChannel(notifications_log_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
+ LLNotifications::instance().getChannel("Enabled")->
+ connectFailedFilter(&defaultResponse);
LLNotifications::instance().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
LLNotifications::instance().getChannel("Unique")->
connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
-// failedUniquenessTest slot isn't necessary
-// LLNotifications::instance().getChannel("Unique")->
-// connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
+ LLNotifications::instance().getChannel("Unique")->
+ connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
LLNotifications::instance().getChannel("Ignore")->
connectFailedFilter(&handleIgnoredNotification);
+ LLNotifications::instance().getChannel("VisibilityRules")->
+ connectFailedFilter(&visibilityRuleMached);
}
-bool LLNotifications::addTemplate(const std::string &name,
- LLNotificationTemplatePtr theTemplate)
-{
- if (mTemplates.count(name))
- {
- llwarns << "LLNotifications -- attempted to add template '" << name << "' twice." << llendl;
- return false;
- }
- mTemplates[name] = theTemplate;
- return true;
-}
LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
{
@@ -1135,11 +1403,6 @@ bool LLNotifications::templateExists(const std::string& name)
return (mTemplates.count(name) != 0);
}
-void LLNotifications::clearTemplates()
-{
- mTemplates.clear();
-}
-
void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option)
{
LLNotificationPtr temp_notify(new LLNotification(params));
@@ -1148,7 +1411,7 @@ void LLNotifications::forceResponse(const LLNotification::Params& params, S32 op
if (selected_item.isUndefined())
{
- llwarns << "Invalid option" << option << " for notification " << (std::string)params.name << llendl;
+ LL_WARNS() << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL;
return;
}
response[selected_item["name"].asString()] = true;
@@ -1169,7 +1432,6 @@ LLNotifications::TemplateNames LLNotifications::getTemplateNames() const
typedef std::map<std::string, std::string> StringMap;
void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
{
- //llwarns << "replaceSubstitutionStrings" << llendl;
// walk the list of attributes looking for replacements
for (LLXMLAttribList::iterator it=node->mAttributes.begin();
it != node->mAttributes.end(); ++it)
@@ -1183,13 +1445,12 @@ void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
if (found != replacements.end())
{
replacement = found->second;
- //llwarns << "replaceSubstituionStrings: value: " << value << " repl: " << replacement << llendl;
-
+ LL_DEBUGS() << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL;
it->second->setValue(replacement);
}
else
{
- llwarns << "replaceSubstituionStrings FAILURE: value: " << value << " repl: " << replacement << llendl;
+ LL_WARNS() << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL;
}
}
}
@@ -1202,227 +1463,167 @@ void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
}
}
-// private to this file
-// returns true if the template request was invalid and there's nothing else we
-// can do with this node, false if you should keep processing (it may have
-// replaced the contents of the node referred to)
-LLXMLNodePtr LLNotifications::checkForXMLTemplate(LLXMLNodePtr item)
+void replaceFormText(LLNotificationForm::Params& form, const std::string& pattern, const std::string& replace)
{
- if (item->hasName("usetemplate"))
+ if (form.ignore.isProvided() && form.ignore.text() == pattern)
+ {
+ form.ignore.text = replace;
+ }
+
+ BOOST_FOREACH(LLNotificationForm::FormElement& element, form.form_elements.elements)
{
- std::string replacementName;
- if (item->getAttributeString("name", replacementName))
+ if (element.button.isChosen() && element.button.text() == pattern)
{
- StringMap replacements;
- for (LLXMLAttribList::const_iterator it=item->mAttributes.begin();
- it != item->mAttributes.end(); ++it)
- {
- replacements[it->second->getName()->mString] = it->second->getValue();
- }
- if (mXmlTemplates.count(replacementName))
- {
- item=LLXMLNode::replaceNode(item, mXmlTemplates[replacementName]);
-
- // walk the nodes looking for $(substitution) here and replace
- replaceSubstitutionStrings(item, replacements);
- }
- else
- {
- llwarns << "XML template lookup failure on '" << replacementName << "' " << llendl;
- }
+ element.button.text = replace;
}
}
- return item;
+}
+
+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()
{
- const std::string xml_filename = "notifications.xml";
+ LL_INFOS() << "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 = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
-
+ BOOL success = LLXMLNode::getLayeredXMLNode(root, search_paths);
+
if (!success || root.isNull() || !root->hasName( "notifications" ))
{
- llerrs << "Problem reading UI Notifications file: " << xml_filename << llendl;
+ LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL;
return false;
}
-
- clearTemplates();
-
- for (LLXMLNodePtr item = root->getFirstChild();
- item.notNull(); item = item->getNextSibling())
+
+ LLNotificationTemplate::Notifications params;
+ LLXUIParser parser;
+ parser.readXUI(root, params, base_filename);
+
+ if(!params.validateBlock())
{
- // we do this FIRST so that item can be changed if we
- // encounter a usetemplate -- we just replace the
- // current xml node and keep processing
- item = checkForXMLTemplate(item);
-
- if (item->hasName("global"))
- {
- std::string global_name;
- if (item->getAttributeString("name", global_name))
- {
- mGlobalStrings[global_name] = item->getTextContents();
- }
- continue;
- }
-
- if (item->hasName("template"))
- {
- // store an xml template; templates must have a single node (can contain
- // other nodes)
- std::string name;
- item->getAttributeString("name", name);
- LLXMLNodePtr ptr = item->getFirstChild();
- mXmlTemplates[name] = ptr;
- continue;
- }
-
- if (!item->hasName("notification"))
- {
- llwarns << "Unexpected entity " << item->getName()->mString <<
- " found in " << xml_filename << llendl;
- continue;
- }
-
- // now we know we have a notification entry, so let's build it
- LLNotificationTemplatePtr pTemplate(new LLNotificationTemplate());
+ LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL;
+ return false;
+ }
- if (!item->getAttributeString("name", pTemplate->mName))
- {
- llwarns << "Unable to parse notification with no name" << llendl;
- continue;
- }
-
- //llinfos << "Parsing " << pTemplate->mName << llendl;
-
- pTemplate->mMessage = item->getTextContents();
- pTemplate->mDefaultFunctor = pTemplate->mName;
- item->getAttributeString("type", pTemplate->mType);
- item->getAttributeString("icon", pTemplate->mIcon);
- item->getAttributeString("label", pTemplate->mLabel);
- item->getAttributeU32("duration", pTemplate->mExpireSeconds);
- item->getAttributeU32("expireOption", pTemplate->mExpireOption);
-
- std::string priority;
- item->getAttributeString("priority", priority);
- pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL;
- if (!priority.empty())
- {
- if (priority == "low") pTemplate->mPriority = NOTIFICATION_PRIORITY_LOW;
- if (priority == "normal") pTemplate->mPriority = NOTIFICATION_PRIORITY_NORMAL;
- if (priority == "high") pTemplate->mPriority = NOTIFICATION_PRIORITY_HIGH;
- if (priority == "critical") pTemplate->mPriority = NOTIFICATION_PRIORITY_CRITICAL;
- }
-
- item->getAttributeString("functor", pTemplate->mDefaultFunctor);
+ mTemplates.clear();
- BOOL persist = false;
- item->getAttributeBOOL("persist", persist);
- pTemplate->mPersist = persist;
-
- std::string sound;
- item->getAttributeString("sound", sound);
- if (!sound.empty())
+ BOOST_FOREACH(LLNotificationTemplate::GlobalString& string, params.strings)
+ {
+ mGlobalStrings[string.name] = string.value;
+ }
+
+ std::map<std::string, LLNotificationForm::Params> form_templates;
+
+ BOOST_FOREACH(LLNotificationTemplate::Template& notification_template, params.templates)
+ {
+ form_templates[notification_template.name] = notification_template.form;
+ }
+
+ BOOST_FOREACH(LLNotificationTemplate::Params& notification, params.notifications)
+ {
+ if (notification.form_ref.form_template.isChosen())
{
- // test for bad sound effect name / missing effect
- if (LLUI::sSettingGroups["config"]->controlExists(sound))
+ // 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())
{
- pTemplate->mSoundEffect =
- LLUUID(LLUI::sSettingGroups["config"]->getString(sound));
+ replaceFormText(notification.form_ref.form, "$yestext", notification.form_ref.form_template.yes_text);
}
- else
+ if(notification.form_ref.form_template.no_text.isProvided())
{
- llwarns << "Unknown sound effect control name " << sound
- << llendl;
+ replaceFormText(notification.form_ref.form, "$notext", notification.form_ref.form_template.no_text);
}
- }
-
- for (LLXMLNodePtr child = item->getFirstChild();
- !child.isNull(); child = child->getNextSibling())
- {
- child = checkForXMLTemplate(child);
-
- // <url>
- if (child->hasName("url"))
+ if(notification.form_ref.form_template.cancel_text.isProvided())
{
- pTemplate->mURL = child->getTextContents();
- child->getAttributeU32("option", pTemplate->mURLOption);
- child->getAttributeU32("openexternally", pTemplate->mURLOpenExternally);
+ replaceFormText(notification.form_ref.form, "$canceltext", notification.form_ref.form_template.cancel_text);
}
-
- if (child->hasName("unique"))
- {
- pTemplate->mUnique = true;
- for (LLXMLNodePtr formitem = child->getFirstChild();
- !formitem.isNull(); formitem = formitem->getNextSibling())
- {
- if (formitem->hasName("context"))
- {
- std::string key;
- formitem->getAttributeString("key", key);
- pTemplate->mUniqueContext.push_back(key);
- //llwarns << "adding " << key << " to unique context" << llendl;
- }
- else
- {
- llwarns << "'unique' has unrecognized subelement "
- << formitem->getName()->mString << llendl;
- }
- }
- }
-
- // <form>
- if (child->hasName("form"))
+ 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())
{
- pTemplate->mForm = LLNotificationFormPtr(new LLNotificationForm(pTemplate->mName, child));
+ replaceFormText(notification.form_ref.form, "$ignoretext", notification.form_ref.form_template.ignore_text);
}
}
- addTemplate(pTemplate->mName, pTemplate);
+ mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification));
}
-
- //std::ostringstream ostream;
- //root->writeToOstream(ostream, "\n ");
- //llwarns << ostream.str() << llendl;
-
+
+ LL_INFOS() << "...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())
+ {
+ LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL;
+ return false;
+ }
+
+ mVisibilityRules.clear();
+
+ BOOST_FOREACH(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(LLNotification::Params().name(name.asString()));
+ add(name.asString(), LLSD(), LLSD());
}
-LLNotificationPtr LLNotifications::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload)
+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)
+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));
+ 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)
+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));
+ 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
@@ -1436,11 +1637,13 @@ LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
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())
{
- llerrs << "Notification added a second time to the master notification channel." << llendl;
+ LL_ERRS() << "Notification added a second time to the master notification channel." << LL_ENDL;
}
updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif);
@@ -1448,13 +1651,38 @@ void LLNotifications::add(const LLNotificationPtr pNotif)
void LLNotifications::cancel(LLNotificationPtr pNotif)
{
+ if (pNotif == NULL || pNotif->isCancelled()) return;
+
LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it == mItems.end())
+ if (it != mItems.end())
+ {
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
+ }
+}
+
+void LLNotifications::cancelByName(const std::string& name)
+{
+ 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)
{
- llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl;
+ LLNotificationPtr pNotif = *it;
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
}
- pNotif->cancel();
- updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
}
void LLNotifications::update(const LLNotificationPtr pNotif)
@@ -1469,11 +1697,11 @@ void LLNotifications::update(const LLNotificationPtr pNotif)
LLNotificationPtr LLNotifications::find(LLUUID uuid)
{
- LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid));
+ LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid)));
LLNotificationSet::iterator it=mItems.find(target);
if (it == mItems.end())
{
- llwarns << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << llendl;
+ LL_DEBUGS("Notifications") << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << LL_ENDL;
return LLNotificationPtr((LLNotification*)NULL);
}
else
@@ -1511,6 +1739,93 @@ bool LLNotifications::getIgnoreAllNotifications()
return mIgnoreAllNotifications;
}
+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()
+ << "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() << "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() << "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() << "allowing notification " << n->getName() << LL_ENDL;
+
+ return true;
+}
+
+
// ---
// END OF LLNotifications implementation
// =========================================================
@@ -1521,3 +1836,62 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
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() << "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();
+}