PageRenderTime 169ms CodeModel.GetById 12ms app.highlight 144ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llui/llnotifications.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1809 lines | 1403 code | 242 blank | 164 comment | 227 complexity | 9cf9ab9dac2018b07815b61be17eb5b7 MD5 | raw file
   1/**
   2* @file llnotifications.cpp
   3* @brief Non-UI queue manager for keeping a prioritized list of notifications
   4*
   5* $LicenseInfo:firstyear=2008&license=viewerlgpl$
   6* Second Life Viewer Source Code
   7* Copyright (C) 2010, Linden Research, Inc.
   8* 
   9* This library is free software; you can redistribute it and/or
  10* modify it under the terms of the GNU Lesser General Public
  11* License as published by the Free Software Foundation;
  12* version 2.1 of the License only.
  13* 
  14* This library is distributed in the hope that it will be useful,
  15* but WITHOUT ANY WARRANTY; without even the implied warranty of
  16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17* Lesser General Public License for more details.
  18* 
  19* You should have received a copy of the GNU Lesser General Public
  20* License along with this library; if not, write to the Free Software
  21* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22* 
  23* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24* $/LicenseInfo$
  25*/
  26
  27#include "linden_common.h"
  28
  29#include "llnotifications.h"
  30#include "llnotificationtemplate.h"
  31#include "llnotificationvisibilityrule.h"
  32
  33#include "llavatarnamecache.h"
  34#include "llinstantmessage.h"
  35#include "llcachename.h"
  36#include "llxmlnode.h"
  37#include "lluictrl.h"
  38#include "lluictrlfactory.h"
  39#include "lldir.h"
  40#include "llsdserialize.h"
  41#include "lltrans.h"
  42#include "llnotificationslistener.h"
  43#include "llstring.h"
  44#include "llsdparam.h"
  45#include "llsdutil.h"
  46
  47#include <algorithm>
  48#include <boost/regex.hpp>
  49#include <boost/foreach.hpp>
  50
  51
  52const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
  53
  54void NotificationPriorityValues::declareValues()
  55{
  56	declare("low", NOTIFICATION_PRIORITY_LOW);
  57	declare("normal", NOTIFICATION_PRIORITY_NORMAL);
  58	declare("high", NOTIFICATION_PRIORITY_HIGH);
  59	declare("critical", NOTIFICATION_PRIORITY_CRITICAL);
  60}
  61
  62LLNotificationForm::FormElementBase::FormElementBase()
  63:	name("name")
  64{}
  65
  66LLNotificationForm::FormIgnore::FormIgnore()
  67:	text("text"),
  68	control("control"),
  69	invert_control("invert_control", false),
  70	save_option("save_option", false)
  71{}
  72
  73LLNotificationForm::FormButton::FormButton()
  74:	index("index"),
  75	text("text"),
  76	ignore("ignore"),
  77	is_default("default"),
  78	type("type")
  79{
  80	// set type here so it gets serialized
  81	type = "button";
  82}
  83
  84LLNotificationForm::FormInput::FormInput()
  85:	type("type"),
  86	text("text"),
  87	max_length_chars("max_length_chars"),
  88	width("width", 0),
  89	value("value")
  90{}
  91
  92LLNotificationForm::FormElement::FormElement()
  93:	button("button"),
  94	input("input")
  95{}
  96
  97LLNotificationForm::FormElements::FormElements()
  98:	elements("")
  99{}
 100
 101LLNotificationForm::Params::Params()
 102:	name("name"),
 103	ignore("ignore"),
 104	form_elements("")
 105{}
 106
 107// Local channel for persistent notifications
 108// Stores only persistent notifications.
 109// Class users can use connectChanged() to process persistent notifications
 110// (see LLNotificationStorage for example).
 111class LLPersistentNotificationChannel : public LLNotificationChannel
 112{
 113	LOG_CLASS(LLPersistentNotificationChannel);
 114public:
 115	LLPersistentNotificationChannel() :
 116		LLNotificationChannel("Persistent", "Visible", &notificationFilter, LLNotificationComparators::orderByUUID())
 117	{
 118	}
 119
 120private:
 121
 122	// The channel gets all persistent notifications except those that have been canceled
 123	static bool notificationFilter(LLNotificationPtr pNotification)
 124	{
 125		bool handle_notification = false;
 126
 127		handle_notification = pNotification->isPersistent()
 128			&& !pNotification->isCancelled();
 129
 130		return handle_notification;
 131	}
 132
 133	void onDelete(LLNotificationPtr pNotification)
 134	{
 135		// we want to keep deleted notifications in our log, otherwise some 
 136		// notifications will be lost on exit.
 137		mItems.insert(pNotification);
 138	}
 139};
 140
 141bool filterIgnoredNotifications(LLNotificationPtr notification)
 142{
 143	LLNotificationFormPtr form = notification->getForm();
 144	// Check to see if the user wants to ignore this alert
 145	return !notification->getForm()->getIgnored();
 146}
 147
 148bool handleIgnoredNotification(const LLSD& payload)
 149{
 150	if (payload["sigtype"].asString() == "add")
 151	{
 152		LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
 153		if (!pNotif) return false;
 154
 155		LLNotificationFormPtr form = pNotif->getForm();
 156		LLSD response;
 157		switch(form->getIgnoreType())
 158		{
 159		case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE:
 160			response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON);
 161			break;
 162		case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE:
 163			response = LLUI::sSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName());
 164			break;
 165		case LLNotificationForm::IGNORE_SHOW_AGAIN:
 166			break;
 167		default:
 168			return false;
 169		}
 170		pNotif->setIgnored(true);
 171		pNotif->respond(response);
 172		return true; 	// don't process this item any further
 173	}
 174	return false;
 175}
 176
 177bool defaultResponse(const LLSD& payload)
 178{
 179	if (payload["sigtype"].asString() == "add")
 180	{
 181		LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
 182		if (pNotif) 
 183		{
 184			// supply default response
 185			pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON));
 186		}
 187	}
 188	return false;
 189}
 190
 191bool visibilityRuleMached(const LLSD& payload)
 192{
 193	// This is needed because LLNotifications::isVisibleByRules may have cancelled the notification.
 194	// Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order.
 195	return true;
 196}
 197
 198
 199namespace LLNotificationFilters
 200{
 201	// a sample filter
 202	bool includeEverything(LLNotificationPtr p)
 203	{
 204		return true;
 205	}
 206};
 207
 208LLNotificationForm::LLNotificationForm()
 209:	mIgnore(IGNORE_NO)
 210{
 211}
 212
 213
 214LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p) 
 215:	mIgnore(IGNORE_NO),
 216	mInvertSetting(false) // ignore settings by default mean true=show, false=ignore
 217{
 218	if (p.ignore.isProvided())
 219	{
 220		mIgnoreMsg = p.ignore.text;
 221
 222		if (!p.ignore.save_option)
 223		{
 224			mIgnore = IGNORE_WITH_DEFAULT_RESPONSE;
 225		}
 226		else
 227		{
 228			// remember last option chosen by user and automatically respond with that in the future
 229			mIgnore = IGNORE_WITH_LAST_RESPONSE;
 230			LLUI::sSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
 231		}
 232
 233		BOOL show_notification = TRUE;
 234		if (p.ignore.control.isProvided())
 235		{
 236			mIgnoreSetting = LLUI::sSettingGroups["config"]->getControl(p.ignore.control);
 237			mInvertSetting = p.ignore.invert_control;
 238		}
 239		else
 240		{
 241			LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", TRUE);
 242			mIgnoreSetting = LLUI::sSettingGroups["ignores"]->getControl(name);
 243		}
 244	}
 245
 246	LLParamSDParser parser;
 247	parser.writeSD(mFormData, p.form_elements);
 248
 249	if (!mFormData.isArray())
 250	{
 251		// change existing contents to a one element array
 252		LLSD new_llsd_array = LLSD::emptyArray();
 253		new_llsd_array.append(mFormData);
 254		mFormData = new_llsd_array;
 255	}
 256
 257	for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray();
 258		it != end_it;
 259		++it)
 260	{
 261		// lift contents of form element up a level, since element type is already encoded in "type" param
 262		if (it->isMap() && it->beginMap() != it->endMap())
 263		{
 264			*it = it->beginMap()->second;
 265		}
 266	}
 267
 268	LL_DEBUGS("Notifications") << name << LL_ENDL;
 269	LL_DEBUGS("Notifications") << ll_pretty_print_sd(mFormData) << LL_ENDL;
 270}
 271
 272LLNotificationForm::LLNotificationForm(const LLSD& sd)
 273	: mIgnore(IGNORE_NO)
 274{
 275	if (sd.isArray())
 276	{
 277		mFormData = sd;
 278	}
 279	else
 280	{
 281		llwarns << "Invalid form data " << sd << llendl;
 282		mFormData = LLSD::emptyArray();
 283	}
 284}
 285
 286LLSD LLNotificationForm::asLLSD() const
 287{ 
 288	return mFormData; 
 289}
 290
 291LLSD LLNotificationForm::getElement(const std::string& element_name)
 292{
 293	for (LLSD::array_const_iterator it = mFormData.beginArray();
 294		it != mFormData.endArray();
 295		++it)
 296	{
 297		if ((*it)["name"].asString() == element_name) return (*it);
 298	}
 299	return LLSD();
 300}
 301
 302
 303bool LLNotificationForm::hasElement(const std::string& element_name)
 304{
 305	for (LLSD::array_const_iterator it = mFormData.beginArray();
 306		it != mFormData.endArray();
 307		++it)
 308	{
 309		if ((*it)["name"].asString() == element_name) return true;
 310	}
 311	return false;
 312}
 313
 314void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value)
 315{
 316	LLSD element;
 317	element["type"] = type;
 318	element["name"] = name;
 319	element["text"] = name;
 320	element["value"] = value;
 321	element["index"] = mFormData.size();
 322	mFormData.append(element);
 323}
 324
 325void LLNotificationForm::append(const LLSD& sub_form)
 326{
 327	if (sub_form.isArray())
 328	{
 329		for (LLSD::array_const_iterator it = sub_form.beginArray();
 330			it != sub_form.endArray();
 331			++it)
 332		{
 333			mFormData.append(*it);
 334		}
 335	}
 336}
 337
 338void LLNotificationForm::formatElements(const LLSD& substitutions)
 339{
 340	for (LLSD::array_iterator it = mFormData.beginArray();
 341		it != mFormData.endArray();
 342		++it)
 343	{
 344		// format "text" component of each form element
 345		if ((*it).has("text"))
 346		{
 347			std::string text = (*it)["text"].asString();
 348			LLStringUtil::format(text, substitutions);
 349			(*it)["text"] = text;
 350		}
 351		if ((*it)["type"].asString() == "text" && (*it).has("value"))
 352		{
 353			std::string value = (*it)["value"].asString();
 354			LLStringUtil::format(value, substitutions);
 355			(*it)["value"] = value;
 356		}
 357	}
 358}
 359
 360std::string LLNotificationForm::getDefaultOption()
 361{
 362	for (LLSD::array_const_iterator it = mFormData.beginArray();
 363		it != mFormData.endArray();
 364		++it)
 365	{
 366		if ((*it)["default"]) return (*it)["name"].asString();
 367	}
 368	return "";
 369}
 370
 371LLControlVariablePtr LLNotificationForm::getIgnoreSetting() 
 372{ 
 373	return mIgnoreSetting; 
 374}
 375
 376bool LLNotificationForm::getIgnored()
 377{
 378	bool show = true;
 379	if (mIgnore != LLNotificationForm::IGNORE_NO
 380		&& mIgnoreSetting) 
 381	{
 382		show = mIgnoreSetting->getValue().asBoolean();
 383		if (mInvertSetting) show = !show;
 384	}
 385
 386	return !show;
 387}
 388
 389void LLNotificationForm::setIgnored(bool ignored)
 390{
 391	if (mIgnoreSetting)
 392	{
 393		if (mInvertSetting) ignored = !ignored;
 394		mIgnoreSetting->setValue(!ignored);
 395	}
 396}
 397
 398LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Params& p)
 399:	mName(p.name),
 400	mType(p.type),
 401	mMessage(p.value),
 402	mLabel(p.label),
 403	mIcon(p.icon),
 404	mURL(p.url.value),
 405	mExpireSeconds(p.duration),
 406	mExpireOption(p.expire_option),
 407	mURLOption(p.url.option),
 408	mURLTarget(p.url.target),
 409	mUnique(p.unique.isProvided()),
 410	mPriority(p.priority),
 411	mPersist(p.persist),
 412	mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name())
 413{
 414	if (p.sound.isProvided()
 415		&& LLUI::sSettingGroups["config"]->controlExists(p.sound))
 416	{
 417		mSoundEffect = LLUUID(LLUI::sSettingGroups["config"]->getString(p.sound));
 418	}
 419
 420	BOOST_FOREACH(const LLNotificationTemplate::UniquenessContext& context, p.unique.contexts)
 421	{
 422		mUniqueContext.push_back(context.value);
 423	}
 424	
 425	lldebugs << "notification \"" << mName << "\": tag count is " << p.tags.size() << llendl;
 426	
 427	BOOST_FOREACH(const LLNotificationTemplate::Tag& tag, p.tags)
 428	{
 429		lldebugs << "    tag \"" << std::string(tag.value) << "\"" << llendl;
 430		mTags.push_back(tag.value);
 431	}
 432
 433	mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form));
 434}
 435
 436LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p)
 437{
 438	if (p.show.isChosen())
 439	{
 440		mType = p.show.type;
 441		mTag = p.show.tag;
 442		mName = p.show.name;
 443		mVisible = true;
 444	}
 445	else if (p.hide.isChosen())
 446	{
 447		mType = p.hide.type;
 448		mTag = p.hide.tag;
 449		mName = p.hide.name;
 450		mVisible = false;
 451	}
 452	else if (p.respond.isChosen())
 453	{
 454		mType = p.respond.type;
 455		mTag = p.respond.tag;
 456		mName = p.respond.name;
 457		mVisible = false;
 458		mResponse = p.respond.response;
 459	}
 460}
 461
 462LLNotification::LLNotification(const LLNotification::Params& p) : 
 463	mTimestamp(p.time_stamp), 
 464	mSubstitutions(p.substitutions),
 465	mPayload(p.payload),
 466	mExpiresAt(0),
 467	mTemporaryResponder(false),
 468	mRespondedTo(false),
 469	mPriority(p.priority),
 470	mCancelled(false),
 471	mIgnored(false),
 472	mResponderObj(NULL),
 473	mIsReusable(false)
 474{
 475	if (p.functor.name.isChosen())
 476	{
 477		mResponseFunctorName = p.functor.name;
 478	}
 479	else if (p.functor.function.isChosen())
 480	{
 481		mResponseFunctorName = LLUUID::generateNewID().asString();
 482		LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function());
 483
 484		mTemporaryResponder = true;
 485	}
 486	else if(p.functor.responder.isChosen())
 487	{
 488		mResponder = p.functor.responder;
 489	}
 490
 491	if(p.responder.isProvided())
 492	{
 493		mResponderObj = p.responder;
 494	}
 495
 496	mId.generate();
 497	init(p.name, p.form_elements);
 498}
 499
 500
 501LLNotification::LLNotification(const LLSD& sd) :
 502	mTemporaryResponder(false),
 503	mRespondedTo(false),
 504	mCancelled(false),
 505	mIgnored(false),
 506	mResponderObj(NULL),
 507	mIsReusable(false)
 508{ 
 509	mId.generate();
 510	mSubstitutions = sd["substitutions"];
 511	mPayload = sd["payload"]; 
 512	mTimestamp = sd["time"]; 
 513	mExpiresAt = sd["expiry"];
 514	mPriority = (ENotificationPriority)sd["priority"].asInteger();
 515	mResponseFunctorName = sd["responseFunctor"].asString();
 516	std::string templatename = sd["name"].asString();
 517	init(templatename, LLSD());
 518	// replace form with serialized version
 519	mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"]));
 520}
 521
 522
 523LLSD LLNotification::asLLSD()
 524{
 525	LLSD output;
 526	output["id"] = mId;
 527	output["name"] = mTemplatep->mName;
 528	output["form"] = getForm()->asLLSD();
 529	output["substitutions"] = mSubstitutions;
 530	output["payload"] = mPayload;
 531	output["time"] = mTimestamp;
 532	output["expiry"] = mExpiresAt;
 533	output["priority"] = (S32)mPriority;
 534	output["responseFunctor"] = mResponseFunctorName;
 535	output["reusable"] = mIsReusable;
 536
 537	if(mResponder)
 538	{
 539		output["responder"] = mResponder->asLLSD();
 540	}
 541
 542	return output;
 543}
 544
 545void LLNotification::update()
 546{
 547	LLNotifications::instance().update(shared_from_this());
 548}
 549
 550void LLNotification::updateFrom(LLNotificationPtr other)
 551{
 552	// can only update from the same notification type
 553	if (mTemplatep != other->mTemplatep) return;
 554
 555	// NOTE: do NOT change the ID, since it is the key to
 556	// this given instance, just update all the metadata
 557	//mId = other->mId;
 558
 559	mPayload = other->mPayload;
 560	mSubstitutions = other->mSubstitutions;
 561	mTimestamp = other->mTimestamp;
 562	mExpiresAt = other->mExpiresAt;
 563	mCancelled = other->mCancelled;
 564	mIgnored = other->mIgnored;
 565	mPriority = other->mPriority;
 566	mForm = other->mForm;
 567	mResponseFunctorName = other->mResponseFunctorName;
 568	mRespondedTo = other->mRespondedTo;
 569	mResponse = other->mResponse;
 570	mTemporaryResponder = other->mTemporaryResponder;
 571	mIsReusable = other->isReusable();
 572
 573	update();
 574}
 575
 576const LLNotificationFormPtr LLNotification::getForm()
 577{
 578	return mForm;
 579}
 580
 581void LLNotification::cancel()
 582{
 583	mCancelled = true;
 584}
 585
 586LLSD LLNotification::getResponseTemplate(EResponseTemplateType type)
 587{
 588	LLSD response = LLSD::emptyMap();
 589	for (S32 element_idx = 0;
 590		element_idx < mForm->getNumElements();
 591		++element_idx)
 592	{
 593		LLSD element = mForm->getElement(element_idx);
 594		if (element.has("name"))
 595		{
 596			response[element["name"].asString()] = element["value"];
 597		}
 598
 599		if ((type == WITH_DEFAULT_BUTTON) 
 600			&& element["default"].asBoolean())
 601		{
 602			response[element["name"].asString()] = true;
 603		}
 604	}
 605	return response;
 606}
 607
 608//static
 609S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response)
 610{
 611	LLNotificationForm form(notification["form"]);
 612
 613	for (S32 element_idx = 0;
 614		element_idx < form.getNumElements();
 615		++element_idx)
 616	{
 617		LLSD element = form.getElement(element_idx);
 618
 619		// only look at buttons
 620		if (element["type"].asString() == "button" 
 621			&& response[element["name"].asString()].asBoolean())
 622		{
 623			return element["index"].asInteger();
 624		}
 625	}
 626
 627	return -1;
 628}
 629
 630//static
 631std::string LLNotification::getSelectedOptionName(const LLSD& response)
 632{
 633	for (LLSD::map_const_iterator response_it = response.beginMap();
 634		response_it != response.endMap();
 635		++response_it)
 636	{
 637		if (response_it->second.isBoolean() && response_it->second.asBoolean())
 638		{
 639			return response_it->first;
 640		}
 641	}
 642	return "";
 643}
 644
 645
 646void LLNotification::respond(const LLSD& response)
 647{
 648	// *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()
 649	mRespondedTo = true;
 650	mResponse = response;
 651
 652	if(mResponder)
 653	{
 654		mResponder->handleRespond(asLLSD(), response);
 655	}
 656	else if (!mResponseFunctorName.empty())
 657	{
 658		// look up the functor
 659		LLNotificationFunctorRegistry::ResponseFunctor functor =
 660			LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
 661		// and then call it
 662		functor(asLLSD(), response);
 663	}
 664	else
 665	{
 666		// no registered responder
 667		return;
 668	}
 669
 670	if (mTemporaryResponder && !isReusable())
 671	{
 672		LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
 673		mResponseFunctorName = "";
 674		mTemporaryResponder = false;
 675	}
 676
 677	if (mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO)
 678	{
 679		mForm->setIgnored(mIgnored);
 680		if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
 681		{
 682			LLUI::sSettingGroups["ignores"]->setLLSD("Default" + getName(), response);
 683		}
 684	}
 685
 686	update();
 687}
 688
 689void LLNotification::respondWithDefault()
 690{
 691	respond(getResponseTemplate(WITH_DEFAULT_BUTTON));
 692}
 693
 694
 695const std::string& LLNotification::getName() const
 696{
 697	return mTemplatep->mName;
 698}
 699
 700const std::string& LLNotification::getIcon() const
 701{
 702	return mTemplatep->mIcon;
 703}
 704
 705
 706bool LLNotification::isPersistent() const
 707{
 708	return mTemplatep->mPersist;
 709}
 710
 711std::string LLNotification::getType() const
 712{
 713	return (mTemplatep ? mTemplatep->mType : "");
 714}
 715
 716S32 LLNotification::getURLOption() const
 717{
 718	return (mTemplatep ? mTemplatep->mURLOption : -1);
 719}
 720
 721S32 LLNotification::getURLOpenExternally() const
 722{
 723	return(mTemplatep? mTemplatep->mURLTarget == "_external": -1);
 724}
 725
 726bool LLNotification::hasUniquenessConstraints() const 
 727{ 
 728	return (mTemplatep ? mTemplatep->mUnique : false);
 729}
 730
 731bool LLNotification::matchesTag(const std::string& tag)
 732{
 733	bool result = false;
 734	
 735	if(mTemplatep)
 736	{
 737		std::list<std::string>::iterator it;
 738		for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++)
 739		{
 740			if((*it) == tag)
 741			{
 742				result = true;
 743				break;
 744			}
 745		}
 746	}
 747	
 748	return result;
 749}
 750
 751void LLNotification::setIgnored(bool ignore)
 752{
 753	mIgnored = ignore;
 754}
 755
 756void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
 757{
 758	if (mTemporaryResponder)
 759		// get rid of the old one
 760		LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
 761	mResponseFunctorName = responseFunctorName;
 762	mTemporaryResponder = false;
 763}
 764
 765void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb)
 766{
 767	if(mTemporaryResponder)
 768	{
 769		LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
 770	}
 771
 772	LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
 773}
 774
 775void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
 776{
 777	mResponder = responder;
 778}
 779
 780bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
 781{
 782	if (this->mTemplatep->mName != that->mTemplatep->mName) 
 783	{
 784		return false; // must have the same template name or forget it
 785	}
 786	if (this->mTemplatep->mUnique)
 787	{
 788		const LLSD& these_substitutions = this->getSubstitutions();
 789		const LLSD& those_substitutions = that->getSubstitutions();
 790		const LLSD& this_payload = this->getPayload();
 791		const LLSD& that_payload = that->getPayload();
 792
 793		// highlander bit sez there can only be one of these
 794		for (std::vector<std::string>::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end();
 795			it != end_it;
 796			++it)
 797		{
 798			// if templates differ in either substitution strings or payload with the given field name
 799			// then they are considered inequivalent
 800			// use of get() avoids converting the LLSD value to a map as the [] operator would
 801			if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString()
 802				|| this_payload.get(*it).asString() != that_payload.get(*it).asString())
 803			{
 804				return false;
 805			}
 806		}
 807		return true;
 808	}
 809
 810	return false; 
 811}
 812
 813void LLNotification::init(const std::string& template_name, const LLSD& form_elements)
 814{
 815	mTemplatep = LLNotifications::instance().getTemplate(template_name);
 816	if (!mTemplatep) return;
 817
 818	// add default substitutions
 819	const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs();
 820	for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin();
 821		 iter != default_args.end(); ++iter)
 822	{
 823		mSubstitutions[iter->first] = iter->second;
 824	}
 825	mSubstitutions["_URL"] = getURL();
 826	mSubstitutions["_NAME"] = template_name;
 827	// TODO: something like this so that a missing alert is sensible:
 828	//mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
 829
 830	mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
 831	mForm->append(form_elements);
 832
 833	// apply substitution to form labels
 834	mForm->formatElements(mSubstitutions);
 835
 836	mIgnored = mForm->getIgnored();
 837
 838	LLDate rightnow = LLDate::now();
 839	if (mTemplatep->mExpireSeconds)
 840	{
 841		mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds);
 842	}
 843
 844	if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED)
 845	{
 846		mPriority = mTemplatep->mPriority;
 847	}
 848}
 849
 850std::string LLNotification::summarize() const
 851{
 852	std::string s = "Notification(";
 853	s += getName();
 854	s += ") : ";
 855	s += mTemplatep ? mTemplatep->mMessage : "";
 856	// should also include timestamp and expiration time (but probably not payload)
 857	return s;
 858}
 859
 860std::string LLNotification::getMessage() const
 861{
 862	// all our callers cache this result, so it gives us more flexibility
 863	// to do the substitution at call time rather than attempting to 
 864	// cache it in the notification
 865	if (!mTemplatep)
 866		return std::string();
 867
 868	std::string message = mTemplatep->mMessage;
 869	LLStringUtil::format(message, mSubstitutions);
 870	return message;
 871}
 872
 873std::string LLNotification::getLabel() const
 874{
 875	std::string label = mTemplatep->mLabel;
 876	LLStringUtil::format(label, mSubstitutions);
 877	return (mTemplatep ? label : "");
 878}
 879
 880std::string LLNotification::getURL() const
 881{
 882	if (!mTemplatep)
 883		return std::string();
 884	std::string url = mTemplatep->mURL;
 885	LLStringUtil::format(url, mSubstitutions);
 886	return (mTemplatep ? url : "");
 887}
 888
 889// =========================================================
 890// LLNotificationChannel implementation
 891// ---
 892LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot)
 893{
 894	// when someone wants to connect to a channel, we first throw them
 895	// all of the notifications that are already in the channel
 896	// we use a special signal called "load" in case the channel wants to care
 897	// only about new notifications
 898	for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
 899	{
 900		slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
 901	}
 902	// and then connect the signal so that all future notifications will also be
 903	// forwarded.
 904	return mChanged.connect(slot);
 905}
 906
 907LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot)
 908{
 909	for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
 910	{
 911		slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
 912	}
 913	return mChanged.connect(slot, boost::signals2::at_front);
 914}
 915
 916LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot)
 917{
 918	// these two filters only fire for notifications added after the current one, because
 919	// they don't participate in the hierarchy.
 920	return mPassedFilter.connect(slot);
 921}
 922
 923LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot)
 924{
 925	return mFailedFilter.connect(slot);
 926}
 927
 928// external call, conforms to our standard signature
 929bool LLNotificationChannelBase::updateItem(const LLSD& payload)
 930{	
 931	// first check to see if it's in the master list
 932	LLNotificationPtr pNotification	 = LLNotifications::instance().find(payload["id"]);
 933	if (!pNotification)
 934		return false;	// not found
 935	
 936	return updateItem(payload, pNotification);
 937}
 938
 939
 940//FIX QUIT NOT WORKING
 941
 942
 943// internal call, for use in avoiding lookup
 944bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification)
 945{	
 946	std::string cmd = payload["sigtype"];
 947	LLNotificationSet::iterator foundItem = mItems.find(pNotification);
 948	bool wasFound = (foundItem != mItems.end());
 949	bool passesFilter = mFilter(pNotification);
 950	
 951	// first, we offer the result of the filter test to the simple
 952	// signals for pass/fail. One of these is guaranteed to be called.
 953	// If either signal returns true, the change processing is NOT performed
 954	// (so don't return true unless you know what you're doing!)
 955	bool abortProcessing = false;
 956	if (passesFilter)
 957	{
 958		abortProcessing = mPassedFilter(payload);
 959	}
 960	else
 961	{
 962		abortProcessing = mFailedFilter(payload);
 963	}
 964	
 965	if (abortProcessing)
 966	{
 967		return true;
 968	}
 969	
 970	if (cmd == "load")
 971	{
 972		// should be no reason we'd ever get a load if we already have it
 973		// if passes filter send a load message, else do nothing
 974		assert(!wasFound);
 975		if (passesFilter)
 976		{
 977			// not in our list, add it and say so
 978			mItems.insert(pNotification);
 979			abortProcessing = mChanged(payload);
 980			onLoad(pNotification);
 981		}
 982	}
 983	else if (cmd == "change")
 984	{
 985		// if it passes filter now and was found, we just send a change message
 986		// if it passes filter now and wasn't found, we have to add it
 987		// if it doesn't pass filter and wasn't found, we do nothing
 988		// if it doesn't pass filter and was found, we need to delete it
 989		if (passesFilter)
 990		{
 991			if (wasFound)
 992			{
 993				// it already existed, so this is a change
 994				// since it changed in place, all we have to do is resend the signal
 995				abortProcessing = mChanged(payload);
 996				onChange(pNotification);
 997			}
 998			else
 999			{
1000				// not in our list, add it and say so
1001				mItems.insert(pNotification);
1002				// our payload is const, so make a copy before changing it
1003				LLSD newpayload = payload;
1004				newpayload["sigtype"] = "add";
1005				abortProcessing = mChanged(newpayload);
1006				onChange(pNotification);
1007			}
1008		}
1009		else
1010		{
1011			if (wasFound)
1012			{
1013				// it already existed, so this is a delete
1014				mItems.erase(pNotification);
1015				// our payload is const, so make a copy before changing it
1016				LLSD newpayload = payload;
1017				newpayload["sigtype"] = "delete";
1018				abortProcessing = mChanged(newpayload);
1019				onChange(pNotification);
1020			}
1021			// didn't pass, not on our list, do nothing
1022		}
1023	}
1024	else if (cmd == "add")
1025	{
1026		// should be no reason we'd ever get an add if we already have it
1027		// if passes filter send an add message, else do nothing
1028		assert(!wasFound);
1029		if (passesFilter)
1030		{
1031			// not in our list, add it and say so
1032			mItems.insert(pNotification);
1033			abortProcessing = mChanged(payload);
1034			onAdd(pNotification);
1035		}
1036	}
1037	else if (cmd == "delete")
1038	{
1039		// if we have it in our list, pass on the delete, then delete it, else do nothing
1040		if (wasFound)
1041		{
1042			abortProcessing = mChanged(payload);
1043			// do not delete the notification to make LLChatHistory::appendMessage add notification panel to IM window
1044			if( ! pNotification->isReusable() )
1045			{
1046				mItems.erase(pNotification);
1047				onDelete(pNotification);
1048			}
1049		}
1050	}
1051	return abortProcessing;
1052}
1053
1054/* static */
1055LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name, 
1056															 const std::string& parent,
1057															 LLNotificationFilter filter, 
1058															 LLNotificationComparator comparator)
1059{
1060	// note: this is not a leak; notifications are self-registering.
1061	// This factory helps to prevent excess deletions by making sure all smart
1062	// pointers to notification channels come from the same source
1063	new LLNotificationChannel(name, parent, filter, comparator);
1064	return LLNotifications::instance().getChannel(name);
1065}
1066
1067
1068LLNotificationChannel::LLNotificationChannel(const std::string& name, 
1069											 const std::string& parent,
1070											 LLNotificationFilter filter, 
1071											 LLNotificationComparator comparator) : 
1072LLNotificationChannelBase(filter, comparator),
1073mName(name),
1074mParent(parent)
1075{
1076	// store myself in the channel map
1077	LLNotifications::instance().addChannel(LLNotificationChannelPtr(this));
1078	// bind to notification broadcast
1079	if (parent.empty())
1080	{
1081		LLNotifications::instance().connectChanged(
1082			boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
1083	}
1084	else
1085	{
1086		LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent);
1087		p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
1088	}
1089}
1090
1091
1092void LLNotificationChannel::setComparator(LLNotificationComparator comparator) 
1093{ 
1094	mComparator = comparator; 
1095	LLNotificationSet s2(mComparator);
1096	s2.insert(mItems.begin(), mItems.end());
1097	mItems.swap(s2);
1098	
1099	// notify clients that we've been resorted
1100	mChanged(LLSD().with("sigtype", "sort")); 
1101}
1102
1103bool LLNotificationChannel::isEmpty() const
1104{
1105	return mItems.empty();
1106}
1107
1108LLNotificationChannel::Iterator LLNotificationChannel::begin()
1109{
1110	return mItems.begin();
1111}
1112
1113LLNotificationChannel::Iterator LLNotificationChannel::end()
1114{
1115	return mItems.end();
1116}
1117
1118std::string LLNotificationChannel::summarize()
1119{
1120	std::string s("Channel '");
1121	s += mName;
1122	s += "'\n  ";
1123	for (LLNotificationChannel::Iterator it = begin(); it != end(); ++it)
1124	{
1125		s += (*it)->summarize();
1126		s += "\n  ";
1127	}
1128	return s;
1129}
1130
1131
1132// ---
1133// END OF LLNotificationChannel implementation
1134// =========================================================
1135
1136
1137// =========================================================
1138// LLNotifications implementation
1139// ---
1140LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything,
1141															   LLNotificationComparators::orderByUUID()),
1142									mIgnoreAllNotifications(false)
1143{
1144	LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
1145
1146    mListener.reset(new LLNotificationsListener(*this));
1147}
1148
1149
1150// The expiration channel gets all notifications that are cancelled
1151bool LLNotifications::expirationFilter(LLNotificationPtr pNotification)
1152{
1153	return pNotification->isCancelled() || pNotification->isRespondedTo();
1154}
1155
1156bool LLNotifications::expirationHandler(const LLSD& payload)
1157{
1158	if (payload["sigtype"].asString() != "delete")
1159	{
1160		// anything added to this channel actually should be deleted from the master
1161		cancel(find(payload["id"]));
1162		return true;	// don't process this item any further
1163	}
1164	return false;
1165}
1166
1167bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
1168{
1169	if (!pNotif->hasUniquenessConstraints())
1170	{
1171		return true;
1172	}
1173
1174	// checks against existing unique notifications
1175	for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
1176		existing_it != mUniqueNotifications.end();
1177		++existing_it)
1178	{
1179		LLNotificationPtr existing_notification = existing_it->second;
1180		if (pNotif != existing_notification 
1181			&& pNotif->isEquivalentTo(existing_notification))
1182		{
1183			return false;
1184		}
1185	}
1186
1187	return true;
1188}
1189
1190bool LLNotifications::uniqueHandler(const LLSD& payload)
1191{
1192	std::string cmd = payload["sigtype"];
1193
1194	LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
1195	if (pNotif && pNotif->hasUniquenessConstraints()) 
1196	{
1197		if (cmd == "add")
1198		{
1199			// not a duplicate according to uniqueness criteria, so we keep it
1200			// and store it for future uniqueness checks
1201			mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif));
1202		}
1203		else if (cmd == "delete")
1204		{
1205			mUniqueNotifications.erase(pNotif->getName());
1206		}
1207	}
1208
1209	return false;
1210}
1211
1212bool LLNotifications::failedUniquenessTest(const LLSD& payload)
1213{
1214	LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
1215	
1216	std::string cmd = payload["sigtype"];
1217
1218	if (!pNotif || cmd != "add")
1219	{
1220		return false;
1221	}
1222
1223	// Update the existing unique notification with the data from this particular instance...
1224	// This guarantees that duplicate notifications will be collapsed to the one
1225	// most recently triggered
1226	for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
1227		existing_it != mUniqueNotifications.end();
1228		++existing_it)
1229	{
1230		LLNotificationPtr existing_notification = existing_it->second;
1231		if (pNotif != existing_notification 
1232			&& pNotif->isEquivalentTo(existing_notification))
1233		{
1234			// copy notification instance data over to oldest instance
1235			// of this unique notification and update it
1236			existing_notification->updateFrom(pNotif);
1237			// then delete the new one
1238			cancel(pNotif);
1239		}
1240	}
1241
1242	return false;
1243}
1244
1245
1246void LLNotifications::addChannel(LLNotificationChannelPtr pChan)
1247{
1248	mChannels[pChan->getName()] = pChan;
1249}
1250
1251LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
1252{
1253	ChannelMap::iterator p = mChannels.find(channelName);
1254	if(p == mChannels.end())
1255	{
1256		llerrs << "Did not find channel named " << channelName << llendl;
1257		return LLNotificationChannelPtr();
1258	}
1259	return p->second;
1260}
1261
1262
1263// this function is called once at construction time, after the object is constructed.
1264void LLNotifications::initSingleton()
1265{
1266	loadTemplates();
1267	loadVisibilityRules();
1268	createDefaultChannels();
1269}
1270
1271void LLNotifications::createDefaultChannels()
1272{
1273	// now construct the various channels AFTER loading the notifications,
1274	// because the history channel is going to rewrite the stored notifications file
1275	LLNotificationChannel::buildChannel("Enabled", "",
1276		!boost::bind(&LLNotifications::getIgnoreAllNotifications, this));
1277	LLNotificationChannel::buildChannel("Expiration", "Enabled",
1278		boost::bind(&LLNotifications::expirationFilter, this, _1));
1279	LLNotificationChannel::buildChannel("Unexpired", "Enabled",
1280		!boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind
1281	LLNotificationChannel::buildChannel("Unique", "Unexpired",
1282		boost::bind(&LLNotifications::uniqueFilter, this, _1));
1283	LLNotificationChannel::buildChannel("Ignore", "Unique",
1284		filterIgnoredNotifications);
1285	LLNotificationChannel::buildChannel("VisibilityRules", "Ignore",
1286		boost::bind(&LLNotifications::isVisibleByRules, this, _1));
1287	LLNotificationChannel::buildChannel("Visible", "VisibilityRules",
1288		&LLNotificationFilters::includeEverything);
1289
1290	// create special persistent notification channel
1291	// this isn't a leak, don't worry about the empty "new"
1292	new LLPersistentNotificationChannel();
1293
1294	// connect action methods to these channels
1295	LLNotifications::instance().getChannel("Enabled")->
1296		connectFailedFilter(&defaultResponse);
1297	LLNotifications::instance().getChannel("Expiration")->
1298        connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));
1299	// uniqueHandler slot should be added as first slot of the signal due to
1300	// usage LLStopWhenHandled combiner in LLStandardSignal
1301	LLNotifications::instance().getChannel("Unique")->
1302        connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
1303	LLNotifications::instance().getChannel("Unique")->
1304        connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
1305	LLNotifications::instance().getChannel("Ignore")->
1306		connectFailedFilter(&handleIgnoredNotification);
1307	LLNotifications::instance().getChannel("VisibilityRules")->
1308		connectFailedFilter(&visibilityRuleMached);
1309}
1310
1311
1312LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
1313{
1314	if (mTemplates.count(name))
1315	{
1316		return mTemplates[name];
1317	}
1318	else
1319	{
1320		return mTemplates["MissingAlert"];
1321	}
1322}
1323
1324bool LLNotifications::templateExists(const std::string& name)
1325{
1326	return (mTemplates.count(name) != 0);
1327}
1328
1329void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option)
1330{
1331	LLNotificationPtr temp_notify(new LLNotification(params));
1332	LLSD response = temp_notify->getResponseTemplate();
1333	LLSD selected_item = temp_notify->getForm()->getElement(option);
1334	
1335	if (selected_item.isUndefined())
1336	{
1337		llwarns << "Invalid option" << option << " for notification " << (std::string)params.name << llendl;
1338		return;
1339	}
1340	response[selected_item["name"].asString()] = true;
1341
1342	temp_notify->respond(response);
1343}
1344
1345LLNotifications::TemplateNames LLNotifications::getTemplateNames() const
1346{
1347	TemplateNames names;
1348	for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it)
1349	{
1350		names.push_back(it->first);
1351	}
1352	return names;
1353}
1354
1355typedef std::map<std::string, std::string> StringMap;
1356void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
1357{
1358	// walk the list of attributes looking for replacements
1359	for (LLXMLAttribList::iterator it=node->mAttributes.begin();
1360		 it != node->mAttributes.end(); ++it)
1361	{
1362		std::string value = it->second->getValue();
1363		if (value[0] == '$')
1364		{
1365			value.erase(0, 1);	// trim off the $
1366			std::string replacement;
1367			StringMap::const_iterator found = replacements.find(value);
1368			if (found != replacements.end())
1369			{
1370				replacement = found->second;
1371				lldebugs << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << llendl;
1372				it->second->setValue(replacement);
1373			}
1374			else
1375			{
1376				llwarns << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << llendl;
1377			}
1378		}
1379	}
1380	
1381	// now walk the list of children and call this recursively.
1382	for (LLXMLNodePtr child = node->getFirstChild(); 
1383		 child.notNull(); child = child->getNextSibling())
1384	{
1385		replaceSubstitutionStrings(child, replacements);
1386	}
1387}
1388
1389void replaceFormText(LLNotificationForm::Params& form, const std::string& pattern, const std::string& replace)
1390{
1391	if (form.ignore.isProvided() && form.ignore.text() == pattern)
1392	{
1393		form.ignore.text = replace;
1394	}
1395
1396	BOOST_FOREACH(LLNotificationForm::FormElement& element, form.form_elements.elements)
1397	{
1398		if (element.button.isChosen() && element.button.text() == pattern)
1399		{
1400			element.button.text = replace;
1401		}
1402	}
1403}
1404
1405void addPathIfExists(const std::string& new_path, std::vector<std::string>& paths)
1406{
1407	if (gDirUtilp->fileExists(new_path))
1408	{
1409		paths.push_back(new_path);
1410	}
1411}
1412
1413bool LLNotifications::loadTemplates()
1414{
1415	std::vector<std::string> search_paths;
1416	
1417	std::string skin_relative_path = gDirUtilp->getDirDelimiter() + LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + "notifications.xml";
1418	std::string localized_skin_relative_path = gDirUtilp->getDirDelimiter() + LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + "notifications.xml";
1419
1420	addPathIfExists(gDirUtilp->getDefaultSkinDir() + skin_relative_path, search_paths);
1421	addPathIfExists(gDirUtilp->getDefaultSkinDir() + localized_skin_relative_path, search_paths);
1422	addPathIfExists(gDirUtilp->getSkinDir() + skin_relative_path, search_paths);
1423	addPathIfExists(gDirUtilp->getSkinDir() + localized_skin_relative_path, search_paths);
1424	addPathIfExists(gDirUtilp->getUserSkinDir() + skin_relative_path, search_paths);
1425	addPathIfExists(gDirUtilp->getUserSkinDir() + localized_skin_relative_path, search_paths);
1426
1427	std::string base_filename = search_paths.front();
1428	LLXMLNodePtr root;
1429	BOOL success  = LLXMLNode::getLayeredXMLNode(root, search_paths);
1430	
1431	if (!success || root.isNull() || !root->hasName( "notifications" ))
1432	{
1433		llerrs << "Problem reading UI Notifications file: " << base_filename << llendl;
1434		return false;
1435	}
1436
1437	LLNotificationTemplate::Notifications params;
1438	LLXUIParser parser;
1439	parser.readXUI(root, params, base_filename);
1440
1441	if(!params.validateBlock())
1442	{
1443		llerrs << "Problem reading UI Notifications file: " << base_filename << llendl;
1444		return false;
1445	}
1446
1447	mTemplates.clear();
1448
1449	BOOST_FOREACH(LLNotificationTemplate::GlobalString& string, params.strings)
1450	{
1451		mGlobalStrings[string.name] = string.value;
1452	}
1453
1454	std::map<std::string, LLNotificationForm::Params> form_templates;
1455
1456	BOOST_FOREACH(LLNotificationTemplate::Template& notification_template, params.templates)
1457	{
1458		form_templates[notification_template.name] = notification_template.form;
1459	}
1460
1461	BOOST_FOREACH(LLNotificationTemplate::Params& notification, params.notifications)
1462	{
1463		if (notification.form_ref.form_template.isChosen())
1464		{
1465			// replace form contents from template
1466			notification.form_ref.form = form_templates[notification.form_ref.form_template.name];
1467			if(notification.form_ref.form_template.yes_text.isProvided())
1468			{
1469				replaceFormText(notification.form_ref.form, "$yestext", notification.form_ref.form_template.yes_text);
1470			}
1471			if(notification.form_ref.form_template.no_text.isProvided())
1472			{
1473				replaceFormText(notification.form_ref.form, "$notext", notification.form_ref.form_template.no_text);
1474			}
1475			if(notification.form_ref.form_template.cancel_text.isProvided())
1476			{
1477				replaceFormText(notification.form_ref.form, "$canceltext", notification.form_ref.form_template.cancel_text);
1478			}
1479			if(notification.form_ref.form_template.ignore_text.isProvided())
1480			{
1481				replaceFormText(notification.form_ref.form, "$ignoretext", notification.form_ref.form_template.ignore_text);
1482			}
1483		}
1484		mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification));
1485	}
1486
1487	return true;
1488}
1489
1490bool LLNotifications::loadVisibilityRules()
1491{
1492	const std::string xml_filename = "notification_visibility.xml";
1493	std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename);
1494
1495	LLNotificationVisibilityRule::Rules params;
1496	LLSimpleXUIParser parser;
1497	parser.readXUI(full_filename, params);
1498
1499	if(!params.validateBlock())
1500	{
1501		llerrs << "Problem reading UI Notification Visibility Rules file: " << full_filename << llendl;
1502		return false;
1503	}
1504
1505	mVisibilityRules.clear();
1506
1507	BOOST_FOREACH(LLNotificationVisibilityRule::Rule& rule, params.rules)
1508	{
1509		mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(rule)));
1510	}
1511
1512	return true;
1513}
1514
1515// Add a simple notification (from XUI)
1516void LLNotifications::addFromCallback(const LLSD& name)
1517{
1518	add(name.asString(), LLSD(), LLSD());
1519}
1520
1521LLNotificationPtr LLNotifications::add(const std::string& name, 
1522									   const LLSD& substitutions, 
1523									   const LLSD& payload)
1524{
1525	LLNotification::Params::Functor functor_p;
1526	functor_p.name = name;
1527	return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));	
1528}
1529
1530LLNotificationPtr LLNotifications::add(const std::string& name, 
1531									   const LLSD& substitutions, 
1532									   const LLSD& payload, 
1533									   const std::string& functor_name)
1534{
1535	LLNotification::Params::Functor functor_p;
1536	functor_p.name = functor_name;
1537	return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));	
1538}
1539							  
1540//virtual
1541LLNotificationPtr LLNotifications::add(const std::string& name, 
1542										const LLSD& substitutions, 
1543										const LLSD& payload, 
1544										LLNotificationFunctorRegistry::ResponseFunctor functor)
1545{
1546	LLNotification::Params::Functor functor_p;
1547	functor_p.function = functor;
1548	return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));	
1549}
1550
1551// generalized add function that takes a parameter block object for more complex instantiations
1552LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
1553{
1554	LLNotificationPtr pNotif(new LLNotification(p));
1555	add(pNotif);
1556	return pNotif;
1557}
1558
1559
1560void LLNotifications::add(const LLNotificationPtr pNotif)
1561{
1562	if (pNotif == NULL) return;
1563
1564	// first see if we already have it -- if so, that's a problem
1565	LLNotificationSet::iterator it=mItems.find(pNotif);
1566	if (it != mItems.end())
1567	{
1568		llerrs << "Notification added a second time to the master notification channel." << llendl;
1569	}
1570
1571	updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif);
1572}
1573
1574void LLNotifications::cancel(LLNotificationPtr pNotif)
1575{
1576	if (pNotif == NULL || pNotif->isCancelled()) return;
1577
1578	LLNotificationSet::iterator it=mItems.find(pNotif);
1579	if (it == mItems.end())
1580	{
1581		llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl;
1582	}
1583	pNotif->cancel();
1584	updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
1585}
1586
1587void LLNotifications::cancelByName(const std::string& name)
1588{
1589	std::vector<LLNotificationPtr> notifs_to_cancel;
1590	for (LLNotificationSet::iterator it=mItems.begin(), end_it = mItems.end();
1591		it != end_it;
1592		++it)
1593	{
1594		LLNotificationPtr pNotif = *it;
1595		if (pNotif->getName() == name)
1596		{
1597			notifs_to_cancel.push_back(pNotif);
1598		}
1599	}
1600
1601	for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
1602		it != end_it;
1603		++it)
1604	{
1605		LLNotificationPtr pNotif = *it;
1606		pNotif->cancel();
1607		updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
1608	}
1609}
1610
1611void LLNotifications::update(const LLNotificationPtr pNotif)
1612{
1613	LLNotificationSet::iterator it=mItems.find(pNotif);
1614	if (it != mItems.end())
1615	{
1616		updateItem(LLSD().with("sigtype", "change").with("id", pNotif->id()), pNotif);
1617	}
1618}
1619
1620
1621LLNotificationPtr LLNotifications::find(LLUUID uuid)
1622{
1623	LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid));
1624	LLNotificationSet::iterator it=mItems.find(target);
1625	if (it == mItems.end())
1626	{
1627		LL_DEBUGS("Notifications") << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << llendl;
1628		return LLNotificationPtr((LLNotification*)NULL);
1629	}
1630	else
1631	{
1632		return *it;
1633	}
1634}
1635
1636void LLNotifications::forEachNotification(NotificationProcess process)
1637{
1638	std::for_each(mItems.begin(), mItems.end(), process);
1639}
1640
1641std::string LLNotifications::getGlobalString(const std::string& key) const
1642{
1643	GlobalStringMap::const_iterator it = mGlobalStrings.find(key);
1644	if (it != mGlobalStrings.end())
1645	{
1646		return it->second;
1647	}
1648	else
1649	{
1650		// if we don't have the key as a global, return the key itself so that the error
1651		// is self-diagnosing.
1652		return key;
1653	}
1654}
1655
1656void LLNotifications::setIgnoreAllNotifications(bool setting)
1657{
1658	mIgnoreAllNotifications = setting; 
1659}
1660bool LLNotifications::getIgnoreAllNotifications()
1661{
1662	return mIgnoreAllNotifications; 
1663}
1664													
1665bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
1666{
1667	if(n->isRespondedTo())
1668	{
1669		// This avoids infinite recursion in the case where the filter calls respond()
1670		return true;
1671	}
1672	
1673	VisibilityRuleList::iterator it;
1674	
1675	for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++)
1676	{
1677		// An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule.
1678		lldebugs 
1679			<< "notification \"" << n->getName() << "\" " 
1680			<< "testing against " << ((*it)->mVisible?"show":"hide") << " rule, "
1681			<< "name = \"" << (*it)->mName << "\" "
1682			<< "tag = \"" << (*it)->mTag << "\" "
1683			<< "type = \"" << (*it)->mType << "\" "
1684			<< llendl;
1685
1686		if(!(*it)->mType.empty())
1687		{
1688			if((*it)->mType != n->getType())
1689			{
1690				// Type doesn't match, so skip this rule.
1691				continue;
1692			}
1693		}
1694		
1695		if(!(*it)->mTag.empty())
1696		{
1697			// check this notification's tag(s) against it->mTag and continue if no match is found.
1698			if(!n->matchesTag((*it)->mTag))
1699			{
1700				// This rule's non-empty tag didn't match one of the notification's tags.  Skip this rule.
1701				continue;
1702			}
1703		}
1704
1705		if(!(*it)->mName.empty())
1706		{
1707			// check this notification's name against the notification's name and continue if no match is found.
1708			if((*it)->mName != n->getName())
1709			{
1710				// This rule's non-empty name didn't match the notification.  Skip this rule.
1711				continue;
1712			}
1713		}
1714		
1715		// If we got here, the rule matches.  Don't evaluate subsequent rules.
1716		if(!(*it)->mVisible)
1717		{
1718			// This notification is being hidden.
1719			
1720			if((*it)->mResponse.empty())
1721			{
1722				// Response property is empty.  Cancel this notification.
1723				lldebugs << "cancelling notification " << n->getName() << llendl;
1724
1725				cancel(n);
1726			}
1727			else
1728			{
1729				// Response property is not empty.  Return the specified response.
1730				LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON);
1731				// TODO: verify that the response template has an item with the correct name
1732				response[(*it)->mResponse] = true;
1733
1734				lldebugs << "responding to notification " << n->getName() << " with response = " << response << llendl;
1735				
1736				n->respond(response);
1737			}
1738
1739			return false;
1740		}
1741		
1742		// If we got here, exit the loop and return true.
1743		break;
1744	}
1745	
1746	lldebugs << "allowing notification " << n->getName() << llendl;
1747
1748	return true;
1749}
1750			
1751
1752// ---
1753// END OF LLNotifications implementation
1754// =========================================================
1755
1756std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
1757{
1758	s << notification.summarize();
1759	return s;
1760}
1761
1762//static
1763void LLPostponedNotification::lookupName(LLPostponedNotification* thiz,
1764										 const LLUUID& id,
1765										 bool is_group)
1766{
1767	if (is_group)
1768	{
1769		gCacheName->getGroup(id,
1770			boost::bind(&LLPostponedNotification::onGroupNameCache,
1771				thiz, _1, _2, _3));
1772	}
1773	else
1774	{
1775		LLAvatarNameCache::get(id,
1776			boost::bind(&LLPostponedNotification::onAvatarNameCache,
1777				thiz, _1, _2));
1778	}
1779}
1780
1781void LLPostponedNotification::onGroupNameCache(const LLUUID& id,
1782											   const std::string& full_name,
1783											   bool is_group)
1784{
1785	finalizeName(full_name);
1786}
1787
1788void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id,
1789												const LLAvatarName& av_name)
1790{
1791	std::string name = av_name.getCompleteName();
1792
1793	// from PE merge - we should figure out if this is the right thing to do
1794	if (name.empty())
1795	{
1796		llwarns << "Empty name received for Id: " << agent_id << llendl;
1797		name = SYSTEM_FROM;
1798	}
1799	
1800	finalizeName(name);
1801}
1802
1803void LLPostponedNotification::finalizeName(const std::string& name)
1804{
1805	mName = name;
1806	modifyNotificationParams();
1807	LLNotifications::instance().add(mParams);
1808	cleanup();
1809}