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