llnotifications.cpp 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547
  1. /**
  2. * @file llnotifications.cpp
  3. * @brief Non-UI queue manager for keeping a prioritized list of notifications
  4. *
  5. * $LicenseInfo:firstyear=2008&license=viewergpl$
  6. *
  7. * Copyright (c) 2008-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include <algorithm>
  34. #include <regex>
  35. #include "llnotifications.h"
  36. #include "lldir.h"
  37. #include "llsdserialize.h"
  38. #include "lluictrlfactory.h"
  39. LLNotifications gNotifications;
  40. const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
  41. const std::string TEMPLATES_FILE = "notifications.xml";
  42. const std::string PERSISTENT_NOTIF_XML_FILE =
  43. "open_notifications_coolvlviewer.xml";
  44. // Local channel for notification history
  45. class LLNotificationHistoryChannel : public LLNotificationChannel
  46. {
  47. protected:
  48. LOG_CLASS(LLNotificationHistoryChannel);
  49. public:
  50. LLNotificationHistoryChannel(const std::string& filename)
  51. : LLNotificationChannel("History", "Visible", &historyFilter,
  52. LLNotificationComparators::orderByUUID()),
  53. mFileName(filename)
  54. {
  55. connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler,
  56. this, _1));
  57. loadPersistentNotifications();
  58. }
  59. private:
  60. bool historyHandler(const LLSD& payload)
  61. {
  62. // We ignore "load" messages, but rewrite the persistence file on any
  63. // other
  64. std::string sigtype = payload["sigtype"];
  65. if (sigtype != "load")
  66. {
  67. savePersistentNotifications();
  68. }
  69. return false;
  70. }
  71. // The history channel gets all notifications except those that have been
  72. // cancelled
  73. LL_INLINE static bool historyFilter(LLNotificationPtr notifp)
  74. {
  75. return notifp && !notifp->isCancelled();
  76. }
  77. void savePersistentNotifications()
  78. {
  79. LL_DEBUGS("Notifications") << "Saving open notifications to "
  80. << mFileName << LL_ENDL;
  81. llofstream notify_file(mFileName.c_str());
  82. if (!notify_file.is_open())
  83. {
  84. llwarns << "Failed to open " << mFileName << llendl;
  85. return;
  86. }
  87. LLSD output;
  88. output["version"] = NOTIFICATION_PERSIST_VERSION;
  89. LLSD& data = output["data"];
  90. for (LLNotificationSet::iterator it = mItems.begin(),
  91. end = mItems.end();
  92. it != end; ++it)
  93. {
  94. LLNotificationPtr n = *it;
  95. if (n && gNotifications.templateExists(n->getName()))
  96. {
  97. // Only store notifications flagged as persisting
  98. LLNotificationTemplatePtr t = gNotifications.getTemplate(n->getName());
  99. if (t && t->mPersist)
  100. {
  101. data.append(n->asLLSD());
  102. }
  103. }
  104. }
  105. LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
  106. formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
  107. }
  108. void loadPersistentNotifications()
  109. {
  110. llinfos << "Loading open notifications from " << mFileName << llendl;
  111. llifstream notify_file(mFileName.c_str());
  112. if (!notify_file.is_open())
  113. {
  114. llwarns << "Failed to open " << mFileName << llendl;
  115. return;
  116. }
  117. LLSD input;
  118. LLPointer<LLSDParser> parser = new LLSDXMLParser();
  119. if (parser->parse(notify_file, input,
  120. LLSDSerialize::SIZE_UNLIMITED) < 0)
  121. {
  122. llwarns << "Failed to parse open notifications" << llendl;
  123. return;
  124. }
  125. if (input.isUndefined()) return;
  126. std::string version = input["version"];
  127. if (version != NOTIFICATION_PERSIST_VERSION)
  128. {
  129. llwarns << "Bad open notifications version: " << version << llendl;
  130. return;
  131. }
  132. LLSD& data = input["data"];
  133. if (data.isUndefined()) return;
  134. for (LLSD::array_const_iterator it = data.beginArray(),
  135. end = data.endArray();
  136. it != end; ++it)
  137. {
  138. gNotifications.add(LLNotificationPtr(new LLNotification(*it)));
  139. }
  140. }
  141. //virtual
  142. void onDelete(LLNotificationPtr notifp)
  143. {
  144. // We want to keep deleted notifications in our log
  145. mItems.insert(notifp);
  146. }
  147. private:
  148. std::string mFileName;
  149. };
  150. bool filterIgnoredNotifications(LLNotificationPtr notification)
  151. {
  152. LLNotificationFormPtr form = notification->getForm();
  153. // Check to see if the user wants to ignore this alert
  154. if (form && form->getIgnoreType() != LLNotificationForm::IGNORE_NO)
  155. {
  156. LLControlGroup* config = LLUI::sConfigGroup;
  157. return config && config->getWarning(notification->getName());
  158. }
  159. return true;
  160. }
  161. bool handleIgnoredNotification(const LLSD& payload)
  162. {
  163. if (payload["sigtype"].asString() != "add")
  164. {
  165. return false;
  166. }
  167. LLNotificationPtr notifp = gNotifications.find(payload["id"].asUUID());
  168. if (!notifp)
  169. {
  170. return false;
  171. }
  172. LLNotificationFormPtr form = notifp->getForm();
  173. LLSD response;
  174. switch (form->getIgnoreType())
  175. {
  176. case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE:
  177. response =
  178. notifp->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON);
  179. break;
  180. case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE:
  181. if (LLUI::sIgnoresGroup)
  182. {
  183. std::string cname = "Default" + notifp->getName();
  184. response = LLUI::sIgnoresGroup->getLLSD(cname.c_str());
  185. }
  186. if (response.isUndefined() || !response.isMap() ||
  187. response.beginMap() == response.endMap())
  188. {
  189. // Invalid saved response: let's use something that we can
  190. // trust
  191. response =
  192. notifp->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON);
  193. }
  194. break;
  195. case LLNotificationForm::IGNORE_SHOW_AGAIN:
  196. break;
  197. default:
  198. return false;
  199. }
  200. notifp->setIgnored(true);
  201. notifp->respond(response);
  202. return true; // Do not process this item any further
  203. }
  204. namespace LLNotificationFilters
  205. {
  206. // a sample filter
  207. bool includeEverything(LLNotificationPtr p)
  208. {
  209. return true;
  210. }
  211. };
  212. LLNotificationForm::LLNotificationForm()
  213. : mFormData(LLSD::emptyArray()),
  214. mIgnore(IGNORE_NO)
  215. {
  216. }
  217. LLNotificationForm::LLNotificationForm(const std::string& name,
  218. const LLXMLNodePtr xml_node)
  219. : mFormData(LLSD::emptyArray()),
  220. mIgnore(IGNORE_NO)
  221. {
  222. if (!xml_node->hasName("form"))
  223. {
  224. llwarns << "Bad xml node for form: " << xml_node->getName() << llendl;
  225. }
  226. LLControlGroup* ignores = LLUI::sIgnoresGroup;
  227. LLXMLNodePtr child = xml_node->getFirstChild();
  228. std::string cname;
  229. while (child)
  230. {
  231. child = gNotifications.checkForXMLTemplate(child);
  232. LLSD item_entry;
  233. std::string element_name = child->getName()->mString;
  234. if (element_name == "ignore" && ignores)
  235. {
  236. bool save_option = false;
  237. child->getAttributeBool("save_option", save_option);
  238. if (!save_option)
  239. {
  240. mIgnore = IGNORE_WITH_DEFAULT_RESPONSE;
  241. }
  242. else
  243. {
  244. // remember last option chosen by user and automatically
  245. // respond with that in the future
  246. mIgnore = IGNORE_WITH_LAST_RESPONSE;
  247. cname = "Default" + name;
  248. ignores->declareLLSD(cname.c_str(), "",
  249. "Default response for notification " +
  250. name);
  251. }
  252. child->getAttributeString("text", mIgnoreMsg);
  253. ignores->addWarning(name);
  254. }
  255. else
  256. {
  257. // Flatten xml form entry into single LLSD map with type==name
  258. item_entry["type"] = element_name;
  259. for (LLXMLAttribList::iterator it = child->mAttributes.begin(),
  260. end = child->mAttributes.end();
  261. it != end; ++it)
  262. {
  263. if (it->second) // Paranoia
  264. {
  265. std::string name(it->second->getName()->mString);
  266. item_entry[name] = it->second->getValue();
  267. }
  268. }
  269. item_entry["value"] = child->getTextContents();
  270. mFormData.append(item_entry);
  271. }
  272. child = child->getNextSibling();
  273. }
  274. }
  275. LLNotificationForm::LLNotificationForm(const LLSD& sd)
  276. {
  277. if (sd.isArray())
  278. {
  279. mFormData = sd;
  280. }
  281. else
  282. {
  283. llwarns << "Invalid form data " << sd << llendl;
  284. mFormData = LLSD::emptyArray();
  285. }
  286. }
  287. LLSD LLNotificationForm::getElement(const std::string& element_name)
  288. {
  289. for (LLSD::array_const_iterator it = mFormData.beginArray(),
  290. end = mFormData.endArray();
  291. it != end; ++it)
  292. {
  293. if ((*it)["name"].asString() == element_name)
  294. {
  295. return *it;
  296. }
  297. }
  298. return LLSD();
  299. }
  300. bool LLNotificationForm::hasElement(const std::string& element_name)
  301. {
  302. for (LLSD::array_const_iterator it = mFormData.beginArray(),
  303. end = mFormData.endArray();
  304. it != end; ++it)
  305. {
  306. if ((*it)["name"].asString() == element_name)
  307. {
  308. return true;
  309. }
  310. }
  311. return false;
  312. }
  313. void LLNotificationForm::addElement(const std::string& type,
  314. 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. void LLNotificationForm::append(const LLSD& sub_form)
  325. {
  326. if (sub_form.isArray())
  327. {
  328. for (LLSD::array_const_iterator it = sub_form.beginArray(),
  329. end = sub_form.endArray();
  330. it != end; ++it)
  331. {
  332. mFormData.append(*it);
  333. }
  334. }
  335. }
  336. void LLNotificationForm::formatElements(const LLSD& substitutions)
  337. {
  338. for (LLSD::array_iterator it = mFormData.beginArray(),
  339. end = mFormData.endArray();
  340. it != end; ++it)
  341. {
  342. // format "text" component of each form element
  343. if (it->has("text"))
  344. {
  345. std::string text = (*it)["text"].asString();
  346. text = LLNotification::format(text, substitutions);
  347. (*it)["text"] = text;
  348. }
  349. if ((*it)["type"].asString() == "text" && it->has("value"))
  350. {
  351. std::string value = (*it)["value"].asString();
  352. value = LLNotification::format(value, substitutions);
  353. (*it)["value"] = value;
  354. }
  355. }
  356. }
  357. std::string LLNotificationForm::getDefaultOption()
  358. {
  359. for (LLSD::array_const_iterator it = mFormData.beginArray(),
  360. end = mFormData.endArray();
  361. it != end; ++it)
  362. {
  363. if ((*it)["default"])
  364. {
  365. return (*it)["name"].asString();
  366. }
  367. }
  368. return "";
  369. }
  370. LLNotificationTemplate::LLNotificationTemplate()
  371. : mExpireSeconds(0),
  372. mExpireOption(-1),
  373. mURLOption(-1),
  374. mUnique(false),
  375. mPriority(NOTIFICATION_PRIORITY_NORMAL)
  376. {
  377. mForm = LLNotificationFormPtr(new LLNotificationForm());
  378. }
  379. LLNotification::LLNotification(const LLNotification::Params& p)
  380. : mTimestamp(p.timestamp),
  381. mSubstitutions(p.substitutions),
  382. mPayload(p.payload),
  383. mExpiresAt(0),
  384. mResponseFunctorName(p.functor_name),
  385. mTemporaryResponder(p.mTemporaryResponder),
  386. mRespondedTo(false),
  387. mPriority(p.priority),
  388. mCancelled(false),
  389. mIgnored(false)
  390. {
  391. mId.generate();
  392. init(p.name, p.form_elements);
  393. }
  394. LLNotification::LLNotification(const LLSD& sd)
  395. : mTemporaryResponder(false),
  396. mRespondedTo(false),
  397. mCancelled(false),
  398. mIgnored(false)
  399. {
  400. mId.generate();
  401. mSubstitutions = sd["substitutions"];
  402. mPayload = sd["payload"];
  403. mTimestamp = sd["time"];
  404. mExpiresAt = sd["expiry"];
  405. mPriority = (ENotificationPriority)sd["priority"].asInteger();
  406. mResponseFunctorName = sd["responseFunctor"].asString();
  407. std::string templatename = sd["name"].asString();
  408. init(templatename, LLSD());
  409. // replace form with serialized version
  410. mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"]));
  411. }
  412. LLSD LLNotification::asLLSD()
  413. {
  414. LLSD output;
  415. output["name"] = mTemplatep->mName;
  416. output["form"] = getForm()->asLLSD();
  417. output["substitutions"] = mSubstitutions;
  418. output["payload"] = mPayload;
  419. output["time"] = mTimestamp;
  420. output["expiry"] = mExpiresAt;
  421. output["priority"] = (S32)mPriority;
  422. output["responseFunctor"] = mResponseFunctorName;
  423. return output;
  424. }
  425. void LLNotification::update()
  426. {
  427. gNotifications.update(shared_from_this());
  428. }
  429. void LLNotification::updateFrom(LLNotificationPtr other)
  430. {
  431. // can only update from the same notification type
  432. if (mTemplatep != other->mTemplatep) return;
  433. // NOTE: do NOT change the ID, since it is the key to
  434. // this given instance, just update all the metadata
  435. //mId = other->mId;
  436. mPayload = other->mPayload;
  437. mSubstitutions = other->mSubstitutions;
  438. mTimestamp = other->mTimestamp;
  439. mExpiresAt = other->mExpiresAt;
  440. mCancelled = other->mCancelled;
  441. mIgnored = other->mIgnored;
  442. mPriority = other->mPriority;
  443. mForm = other->mForm;
  444. mResponseFunctorName = other->mResponseFunctorName;
  445. mRespondedTo = other->mRespondedTo;
  446. mTemporaryResponder = other->mTemporaryResponder;
  447. update();
  448. }
  449. LLSD LLNotification::getResponseTemplate(EResponseTemplateType type)
  450. {
  451. LLSD response = LLSD::emptyMap();
  452. for (S32 element_idx = 0; element_idx < mForm->getNumElements();
  453. ++element_idx)
  454. {
  455. LLSD element = mForm->getElement(element_idx);
  456. if (element.has("name"))
  457. {
  458. response[element["name"].asString()] = element["value"];
  459. }
  460. if (type == WITH_DEFAULT_BUTTON && element["default"].asBoolean())
  461. {
  462. response[element["name"].asString()] = true;
  463. }
  464. }
  465. return response;
  466. }
  467. //static
  468. S32 LLNotification::getSelectedOption(const LLSD& notification,
  469. const LLSD& response)
  470. {
  471. LLNotificationForm form(notification["form"]);
  472. for (S32 element_idx = 0; element_idx < form.getNumElements();
  473. ++element_idx)
  474. {
  475. LLSD element = form.getElement(element_idx);
  476. // only look at buttons
  477. if (element["type"].asString() == "button"
  478. && response[element["name"].asString()].asBoolean())
  479. {
  480. return element["index"].asInteger();
  481. }
  482. }
  483. return -1;
  484. }
  485. //static
  486. std::string LLNotification::getSelectedOptionName(const LLSD& response)
  487. {
  488. for (LLSD::map_const_iterator response_it = response.beginMap();
  489. response_it != response.endMap(); ++response_it)
  490. {
  491. if (response_it->second.isBoolean() && response_it->second.asBoolean())
  492. {
  493. return response_it->first;
  494. }
  495. }
  496. return "";
  497. }
  498. void LLNotification::respond(const LLSD& response, bool save)
  499. {
  500. mRespondedTo = true;
  501. // Call the functor
  502. LLNotificationFunctorRegistry::ResponseFunctor functor =
  503. LLNotificationFunctorRegistry::getInstance()->getFunctor(mResponseFunctorName);
  504. functor(asLLSD(), response);
  505. if (mTemporaryResponder)
  506. {
  507. LLNotificationFunctorRegistry::getInstance()->unregisterFunctor(mResponseFunctorName);
  508. mResponseFunctorName = "";
  509. mTemporaryResponder = false;
  510. }
  511. if (save)
  512. {
  513. LLControlGroup* ignores = LLUI::sIgnoresGroup;
  514. if (ignores && mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO)
  515. {
  516. ignores->setWarning(getName(), !mIgnored);
  517. if (mIgnored &&
  518. mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
  519. {
  520. std::string cname = "Default" + getName();
  521. ignores->setLLSD(cname.c_str(), response);
  522. }
  523. }
  524. }
  525. update();
  526. }
  527. void LLNotification::setResponseFunctor(const std::string& functor_name)
  528. {
  529. if (mTemporaryResponder)
  530. {
  531. // Get rid of the old one
  532. LLNotificationFunctorRegistry::getInstance()->unregisterFunctor(mResponseFunctorName);
  533. }
  534. mResponseFunctorName = functor_name;
  535. mTemporaryResponder = false;
  536. }
  537. bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const
  538. {
  539. for (std::vector<std::string>::const_iterator
  540. required_fields_it = required_fields.begin();
  541. required_fields_it != required_fields.end(); ++required_fields_it)
  542. {
  543. std::string required_field_name = *required_fields_it;
  544. if (!getPayload().has(required_field_name))
  545. {
  546. return false; // a required field was not found
  547. }
  548. }
  549. return true; // all required fields were found
  550. }
  551. bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
  552. {
  553. if (this->mTemplatep->mName != that->mTemplatep->mName)
  554. {
  555. return false; // must have the same template name or forget it
  556. }
  557. if (this->mTemplatep->mUnique)
  558. {
  559. // Highlander bit set there can only be one of these
  560. return this->payloadContainsAll(that->mTemplatep->mUniqueContext) &&
  561. that->payloadContainsAll(this->mTemplatep->mUniqueContext);
  562. }
  563. return false;
  564. }
  565. void LLNotification::init(const std::string& template_name,
  566. const LLSD& form_elements)
  567. {
  568. mTemplatep = gNotifications.getTemplate(template_name);
  569. if (!mTemplatep) return;
  570. // Add default substitutions
  571. // *TODO: change this to read from the translatable strings file
  572. mSubstitutions["SECOND_LIFE"] = "Second Life";
  573. mSubstitutions["_URL"] = getURL();
  574. mSubstitutions["_NAME"] = template_name;
  575. mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
  576. mForm->append(form_elements);
  577. // Apply substitution to form labels
  578. mForm->formatElements(mSubstitutions);
  579. LLDate rightnow = LLDate::now();
  580. if (mTemplatep->mExpireSeconds)
  581. {
  582. mExpiresAt = LLDate(rightnow.secondsSinceEpoch() +
  583. mTemplatep->mExpireSeconds);
  584. }
  585. if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED)
  586. {
  587. mPriority = mTemplatep->mPriority;
  588. }
  589. }
  590. std::string LLNotification::summarize() const
  591. {
  592. std::string s = "Notification(" + getName() + ") : ";
  593. if (mTemplatep)
  594. {
  595. s += mTemplatep->mMessage;
  596. }
  597. // should also include timestamp and expiration time (but probably not
  598. // payload)
  599. return s;
  600. }
  601. //static
  602. std::string LLNotification::format(const std::string& s,
  603. const LLSD& substitutions)
  604. {
  605. if (s.empty() || !substitutions.isMap() ||
  606. s.find('[') == std::string::npos)
  607. {
  608. return s;
  609. }
  610. std::string::const_iterator start = s.begin();
  611. std::string::const_iterator end = s.end();
  612. std::ostringstream output;
  613. try
  614. {
  615. // Match strings like [NAME]
  616. const std::regex key("\\[([0-9_A-Z]+)\\]");
  617. std::smatch match;
  618. while (std::regex_search(start, end, match, key))
  619. {
  620. bool found_replacement = false;
  621. std::string replacement;
  622. // See if we have a replacement for the bracketed string (without
  623. // the brackets) test first using has() because if we just look up
  624. // with operator[] we get back an empty string even if the value is
  625. // missing. We want to distinguish between missing replacements and
  626. // deliberately empty replacement strings.
  627. if (substitutions.has(std::string(match[1].first,
  628. match[1].second)))
  629. {
  630. replacement = substitutions[std::string(match[1].first,
  631. match[1].second)].asString();
  632. found_replacement = true;
  633. }
  634. // If not, see if there is one WITH brackets
  635. else if (substitutions.has(std::string(match[0].first,
  636. match[0].second)))
  637. {
  638. replacement = substitutions[std::string(match[0].first,
  639. match[0].second)].asString();
  640. found_replacement = true;
  641. }
  642. if (found_replacement)
  643. {
  644. output << std::string(start, match[0].first) << replacement;
  645. }
  646. else
  647. {
  648. // We had no replacement, so leave the string we searched for,
  649. // so that it gets noticed by QA: "stuff [NAME_NOT_FOUND]" is
  650. // output.
  651. output << std::string(start, match[0].second);
  652. }
  653. // Update search position
  654. start = match[0].second;
  655. }
  656. }
  657. catch (std::regex_error& e)
  658. {
  659. llwarns << "Regex error: " << e.what() << llendl;
  660. }
  661. // Send the remainder of the string (with no further matches for bracketed
  662. // names)
  663. output << std::string(start, end);
  664. return output.str();
  665. }
  666. std::string LLNotification::getMessage() const
  667. {
  668. // All our callers cache this result, so it gives us more flexibility to do
  669. // the substitution at call time rather than attempting to cache it in the
  670. // notification
  671. return mTemplatep ? format(mTemplatep->mMessage, mSubstitutions) : "";
  672. }
  673. std::string LLNotification::getLabel() const
  674. {
  675. return mTemplatep ? format(mTemplatep->mLabel, mSubstitutions) : "";
  676. }
  677. ///////////////////////////////////////////////////////////////////////////////
  678. // LLNotificationChannel implementation
  679. ///////////////////////////////////////////////////////////////////////////////
  680. void LLNotificationChannelBase::connectChanged(const LLStandardNotificationSignal::slot_type& slot)
  681. {
  682. // When someone wants to connect to a channel, we first throw them all of
  683. // the notifications that are already in the channel we use a special
  684. // signal called "load" in case the channel wants to care only about new
  685. // notifications
  686. for (LLNotificationSet::iterator it = mItems.begin(), end = mItems.end();
  687. it != end; ++it)
  688. {
  689. LLNotificationPtr n = *it;
  690. if (n) // Paranoia
  691. {
  692. slot(LLSD().with("sigtype", "load").with("id", n->getID()));
  693. }
  694. }
  695. // And then connect the signal so that all future notifications will also
  696. // be forwarded.
  697. mChanged.connect(slot);
  698. }
  699. void LLNotificationChannelBase::connectPassedFilter(const LLStandardNotificationSignal::slot_type& slot)
  700. {
  701. // These two filters only fire for notifications added after the current
  702. // one, because they do not participate in the hierarchy.
  703. mPassedFilter.connect(slot);
  704. }
  705. void LLNotificationChannelBase::connectFailedFilter(const LLStandardNotificationSignal::slot_type& slot)
  706. {
  707. mFailedFilter.connect(slot);
  708. }
  709. // External call, conforms to our standard signature
  710. bool LLNotificationChannelBase::updateItem(const LLSD& payload)
  711. {
  712. // First check to see if it is in the master list
  713. LLNotificationPtr notifp = gNotifications.find(payload["id"]);
  714. return notifp && updateItem(payload, notifp);
  715. }
  716. // FIX QUIT NOT WORKING
  717. // Internal call, for use in avoiding lookup
  718. bool LLNotificationChannelBase::updateItem(const LLSD& payload,
  719. LLNotificationPtr notifp)
  720. {
  721. std::string cmd = payload["sigtype"];
  722. bool was_found = mItems.find(notifp) != mItems.end();
  723. bool passes_filter = mFilter(notifp);
  724. // First, we offer the result of the filter test to the simple signals for
  725. // pass/fail. One of these is guaranteed to be called. If either signal
  726. // returns true, the change processing is NOT performed (so do not return
  727. // true unless you know what you're doing !).
  728. bool abort_processing = false;
  729. if (passes_filter)
  730. {
  731. abort_processing = mPassedFilter(payload);
  732. }
  733. else
  734. {
  735. abort_processing = mFailedFilter(payload);
  736. }
  737. if (abort_processing)
  738. {
  739. return true;
  740. }
  741. if (cmd == "load")
  742. {
  743. // Should be no reason we'd ever get a load if we already have it if
  744. // passes filter send a load message, else do nothing
  745. llassert(!was_found);
  746. if (passes_filter)
  747. {
  748. // not in our list, add it and say so
  749. mItems.insert(notifp);
  750. abort_processing = mChanged(payload);
  751. onLoad(notifp);
  752. }
  753. }
  754. else if (cmd == "change")
  755. {
  756. // If it passes the filter now and was found, we just send a change
  757. // message.
  758. // If it passes the filter now and was not found, we have to add it.
  759. // If it does not pass the filter and was not found, we do nothing.
  760. // If it does not pass the filter and was found, we need to delete it.
  761. if (passes_filter)
  762. {
  763. if (was_found)
  764. {
  765. // It already existed, so this is a change since it changed in
  766. // place, all we have to do is resend the signal
  767. abort_processing = mChanged(payload);
  768. onChange(notifp);
  769. }
  770. else
  771. {
  772. // Not in our list, add it and say so
  773. mItems.insert(notifp);
  774. // Our payload is const, so make a copy before changing it
  775. LLSD newpayload = payload;
  776. newpayload["sigtype"] = "add";
  777. abort_processing = mChanged(newpayload);
  778. onChange(notifp);
  779. }
  780. }
  781. else if (was_found)
  782. {
  783. // It already existed, so this is a delete
  784. mItems.erase(notifp);
  785. // Our payload is const, so make a copy before changing it
  786. LLSD newpayload = payload;
  787. newpayload["sigtype"] = "delete";
  788. abort_processing = mChanged(newpayload);
  789. onChange(notifp);
  790. }
  791. // Did not pass, not on our list, do nothing
  792. }
  793. else if (cmd == "add")
  794. {
  795. // Should be no reason we would ever get an add if we already have it;
  796. // if passes the filter send an add message, else do nothing
  797. llassert(!was_found);
  798. if (passes_filter)
  799. {
  800. // Not in our list, add it and say so
  801. mItems.insert(notifp);
  802. abort_processing = mChanged(payload);
  803. onAdd(notifp);
  804. }
  805. }
  806. else if (cmd == "delete")
  807. {
  808. // If we have it in our list, pass on the delete, then delete it, else
  809. // do nothing
  810. if (was_found)
  811. {
  812. abort_processing = mChanged(payload);
  813. mItems.erase(notifp);
  814. onDelete(notifp);
  815. }
  816. }
  817. return abort_processing;
  818. }
  819. //static
  820. LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name,
  821. const std::string& parent,
  822. LLNotificationFilter filter,
  823. LLNotificationComparator comparator)
  824. {
  825. // Note: this is not a leak; notifications are self-registering. This
  826. // factory helps to prevent excess deletions by making sure all smart
  827. // pointers to notification channels come from the same source
  828. new LLNotificationChannel(name, parent, filter, comparator);
  829. return gNotifications.getChannel(name);
  830. }
  831. LLNotificationChannel::LLNotificationChannel(const std::string& name,
  832. const std::string& parent,
  833. LLNotificationFilter filter,
  834. LLNotificationComparator comparator)
  835. : LLNotificationChannelBase(filter, comparator),
  836. mName(name),
  837. mParent(parent)
  838. {
  839. // Store myself in the channel map
  840. gNotifications.addChannel(LLNotificationChannelPtr(this));
  841. // Bind to notification broadcast
  842. if (parent.empty())
  843. {
  844. gNotifications.connectChanged(boost::bind(&LLNotificationChannelBase::updateItem,
  845. this, _1));
  846. }
  847. else
  848. {
  849. LLNotificationChannelPtr p = gNotifications.getChannel(parent);
  850. LLStandardNotificationSignal::slot_type f =
  851. boost::bind(&LLNotificationChannelBase::updateItem, this, _1);
  852. p->connectChanged(f);
  853. }
  854. }
  855. void LLNotificationChannel::setComparator(LLNotificationComparator comparator)
  856. {
  857. mComparator = comparator;
  858. LLNotificationSet s2(mComparator);
  859. s2.insert(mItems.begin(), mItems.end());
  860. mItems.swap(s2);
  861. // Notify clients that we've been resorted
  862. mChanged(LLSD().with("sigtype", "sort"));
  863. }
  864. std::string LLNotificationChannel::summarize()
  865. {
  866. std::string s = "Channel '" + mName + "'\n ";
  867. for (LLNotificationChannel::Iterator it = begin(); it != end(); ++it)
  868. {
  869. s += (*it)->summarize();
  870. s += "\n ";
  871. }
  872. return s;
  873. }
  874. ///////////////////////////////////////////////////////////////////////////////
  875. // LLNotifications implementation
  876. ///////////////////////////////////////////////////////////////////////////////
  877. LLNotifications::LLNotifications()
  878. : LLNotificationChannelBase(LLNotificationFilters::includeEverything,
  879. LLNotificationComparators::orderByUUID())
  880. {
  881. }
  882. // Must be called once before notifications are used.
  883. void LLNotifications::initClass()
  884. {
  885. llinfos << "initializing..." << llendl;
  886. loadTemplates();
  887. createDefaultChannels();
  888. }
  889. // The expiration channel gets all notifications that are cancelled
  890. bool LLNotifications::expirationFilter(LLNotificationPtr notifp)
  891. {
  892. return notifp->isCancelled() || notifp->isRespondedTo();
  893. }
  894. bool LLNotifications::expirationHandler(const LLSD& payload)
  895. {
  896. if (payload["sigtype"].asString() != "delete")
  897. {
  898. // Anything added to this channel actually should be deleted from the
  899. // master
  900. cancel(find(payload["id"]));
  901. return true; // Do not process this item any further
  902. }
  903. return false;
  904. }
  905. bool LLNotifications::uniqueFilter(LLNotificationPtr notifp)
  906. {
  907. if (!notifp->hasUniquenessConstraints())
  908. {
  909. return true;
  910. }
  911. // Check against existing unique notifications
  912. for (LLNotificationMap::iterator
  913. it = mUniqueNotifications.find(notifp->getName()),
  914. end = mUniqueNotifications.end();
  915. it != end; ++it)
  916. {
  917. LLNotificationPtr existing_notification = it->second;
  918. if (notifp != existing_notification &&
  919. notifp->isEquivalentTo(existing_notification))
  920. {
  921. return false;
  922. }
  923. }
  924. return true;
  925. }
  926. bool LLNotifications::uniqueHandler(const LLSD& payload)
  927. {
  928. LLNotificationPtr notifp = gNotifications.find(payload["id"].asUUID());
  929. if (notifp && notifp->hasUniquenessConstraints())
  930. {
  931. if (payload["sigtype"].asString() == "add")
  932. {
  933. // Not a duplicate according to uniqueness criteria, so we keep it
  934. // and store it for future uniqueness checks
  935. mUniqueNotifications.insert(std::make_pair(notifp->getName(),
  936. notifp));
  937. }
  938. else if (payload["sigtype"].asString() == "delete")
  939. {
  940. mUniqueNotifications.erase(notifp->getName());
  941. }
  942. }
  943. return false;
  944. }
  945. bool LLNotifications::failedUniquenessTest(const LLSD& payload)
  946. {
  947. LLNotificationPtr notifp = gNotifications.find(payload["id"].asUUID());
  948. if (!notifp || !notifp->hasUniquenessConstraints())
  949. {
  950. return false;
  951. }
  952. // Check against existing unique notifications
  953. for (LLNotificationMap::iterator
  954. it = mUniqueNotifications.find(notifp->getName()),
  955. end = mUniqueNotifications.end();
  956. it != end; ++it)
  957. {
  958. LLNotificationPtr existing_notification = it->second;
  959. if (existing_notification && notifp != existing_notification &&
  960. notifp->isEquivalentTo(existing_notification))
  961. {
  962. // Copy notification instance data over to oldest instance of this
  963. // unique notification and update it
  964. existing_notification->updateFrom(notifp);
  965. // Then delete the new one
  966. notifp->cancel();
  967. }
  968. }
  969. return false;
  970. }
  971. void LLNotifications::addChannel(LLNotificationChannelPtr pChan)
  972. {
  973. mChannels[pChan->getName()] = pChan;
  974. }
  975. LLNotificationChannelPtr LLNotifications::getChannel(const std::string& chan_name)
  976. {
  977. ChannelMap::iterator p = mChannels.find(chan_name);
  978. if (p == mChannels.end())
  979. {
  980. llerrs << "Did not find channel named " << chan_name << llendl;
  981. }
  982. return p->second;
  983. }
  984. void LLNotifications::createDefaultChannels()
  985. {
  986. // Now construct the various channels AFTER loading the notifications,
  987. // because the history channel is going to rewrite the stored notifications
  988. // file
  989. LLNotificationChannel::buildChannel("Expiration", "",
  990. boost::bind(&LLNotifications::expirationFilter,
  991. this, _1));
  992. LLNotificationChannel::buildChannel("Unexpired", "",
  993. !boost::bind(&LLNotifications::expirationFilter,
  994. this, _1)); // use negated bind
  995. LLNotificationChannel::buildChannel("Unique", "Unexpired",
  996. boost::bind(&LLNotifications::uniqueFilter, this, _1));
  997. LLNotificationChannel::buildChannel("Ignore", "Unique",
  998. filterIgnoredNotifications);
  999. LLNotificationChannel::buildChannel("Visible", "Ignore",
  1000. &LLNotificationFilters::includeEverything);
  1001. // Create special history channel
  1002. std::string filename = gDirUtil.getFullPath(LL_PATH_USER_SETTINGS,
  1003. PERSISTENT_NOTIF_XML_FILE);
  1004. // This is not a leak, do not worry about the empty "new"
  1005. new LLNotificationHistoryChannel(filename);
  1006. // Connect action methods to these channels
  1007. getChannel("Expiration")->connectChanged(boost::bind(&LLNotifications::expirationHandler,
  1008. this, _1));
  1009. getChannel("Unique")->connectChanged(boost::bind(&LLNotifications::uniqueHandler,
  1010. this, _1));
  1011. getChannel("Unique")->connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest,
  1012. this, _1));
  1013. getChannel("Ignore")->connectFailedFilter(&handleIgnoredNotification);
  1014. }
  1015. bool LLNotifications::addTemplate(const std::string &name,
  1016. LLNotificationTemplatePtr theTemplate)
  1017. {
  1018. if (mTemplates.count(name))
  1019. {
  1020. llwarns << "LLNotifications -- attempted to add template '"
  1021. << name << "' twice." << llendl;
  1022. return false;
  1023. }
  1024. mTemplates[name] = theTemplate;
  1025. return true;
  1026. }
  1027. LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
  1028. {
  1029. return mTemplates.count(name) ? mTemplates[name]
  1030. : mTemplates["MissingAlert"];
  1031. }
  1032. void LLNotifications::forceResponse(const LLNotification::Params& params,
  1033. S32 option)
  1034. {
  1035. LLNotificationPtr temp_notify(new LLNotification(params));
  1036. LLSD response = temp_notify->getResponseTemplate();
  1037. LLSD selected_item = temp_notify->getForm()->getElement(option);
  1038. if (selected_item.isUndefined())
  1039. {
  1040. llwarns << "Invalid option " << option << " for notification "
  1041. << (std::string)params.name << llendl;
  1042. return;
  1043. }
  1044. response[selected_item["name"].asString()] = true;
  1045. temp_notify->respond(response, false);
  1046. }
  1047. LLNotifications::TemplateNames LLNotifications::getTemplateNames() const
  1048. {
  1049. TemplateNames names;
  1050. for (TemplateMap::const_iterator it = mTemplates.begin(),
  1051. end = mTemplates.end();
  1052. it != end; ++it)
  1053. {
  1054. names.emplace_back(it->first);
  1055. }
  1056. return names;
  1057. }
  1058. typedef std::map<std::string, std::string> StringMap;
  1059. void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
  1060. {
  1061. // walk the list of attributes looking for replacements
  1062. for (LLXMLAttribList::iterator it = node->mAttributes.begin(),
  1063. end = node->mAttributes.end();
  1064. it != end; ++it)
  1065. {
  1066. if (!it->second) continue; // Paranoia
  1067. std::string value = it->second->getValue();
  1068. if (value[0] == '$')
  1069. {
  1070. value.erase(0, 1); // trim off the $
  1071. std::string replacement;
  1072. StringMap::const_iterator found = replacements.find(value);
  1073. if (found != replacements.end())
  1074. {
  1075. replacement = found->second;
  1076. LL_DEBUGS("Notifications") << "Value: " << value
  1077. << " - Replacement: "
  1078. << replacement << LL_ENDL;
  1079. it->second->setValue(replacement);
  1080. }
  1081. else
  1082. {
  1083. llwarns << "Substitution Failure ! Value: "
  1084. << value << " - Replacement: " << replacement
  1085. << llendl;
  1086. }
  1087. }
  1088. }
  1089. // now walk the list of children and call this recursively.
  1090. for (LLXMLNodePtr child = node->getFirstChild();
  1091. child.notNull(); child = child->getNextSibling())
  1092. {
  1093. replaceSubstitutionStrings(child, replacements);
  1094. }
  1095. }
  1096. // Private to this file.
  1097. // Returns true if the template request was invalid and there is nothing else
  1098. // we can do with this node, false if you should keep processing (it may have
  1099. // replaced the contents of the node referred to)
  1100. LLXMLNodePtr LLNotifications::checkForXMLTemplate(LLXMLNodePtr item)
  1101. {
  1102. if (item.notNull() && item->hasName("usetemplate"))
  1103. {
  1104. std::string replacement;
  1105. if (item->getAttributeString("name", replacement))
  1106. {
  1107. StringMap replacements;
  1108. for (LLXMLAttribList::const_iterator
  1109. it = item->mAttributes.begin(),
  1110. end = item->mAttributes.end();
  1111. it != end; ++it)
  1112. {
  1113. if (it->second) // Paranoia
  1114. {
  1115. std::string name = it->second->getName()->mString;
  1116. replacements[name] = it->second->getValue();
  1117. }
  1118. }
  1119. if (mXmlTemplates.count(replacement))
  1120. {
  1121. item = LLXMLNode::replaceNode(item,
  1122. mXmlTemplates[replacement]);
  1123. // walk the nodes looking for $(substitution) here and replace
  1124. replaceSubstitutionStrings(item, replacements);
  1125. }
  1126. else
  1127. {
  1128. llwarns << "XML template lookup failure on: " << replacement
  1129. << llendl;
  1130. }
  1131. }
  1132. }
  1133. return item;
  1134. }
  1135. bool LLNotifications::loadTemplates()
  1136. {
  1137. LLXMLNodePtr root;
  1138. bool success = LLUICtrlFactory::getLayeredXMLNode(TEMPLATES_FILE, root);
  1139. if (!success || root.isNull() || !root->hasName("notifications"))
  1140. {
  1141. llerrs << "Problem reading UI Notifications file: " << TEMPLATES_FILE
  1142. << llendl;
  1143. return false;
  1144. }
  1145. clearTemplates();
  1146. for (LLXMLNodePtr item = root->getFirstChild(); item.notNull();
  1147. item = item->getNextSibling())
  1148. {
  1149. // We do this FIRST so that item can be changed if we encounter a
  1150. // usetemplate; we just replace the current xml node and keep
  1151. // processing.
  1152. item = checkForXMLTemplate(item);
  1153. if (item->hasName("global"))
  1154. {
  1155. std::string global_name;
  1156. if (item->getAttributeString("name", global_name))
  1157. {
  1158. mGlobalStrings[global_name] = item->getTextContents();
  1159. }
  1160. continue;
  1161. }
  1162. if (item->hasName("template"))
  1163. {
  1164. // store an xml template; templates must have a single node (can
  1165. // contain other nodes)
  1166. std::string name;
  1167. item->getAttributeString("name", name);
  1168. LLXMLNodePtr ptr = item->getFirstChild();
  1169. mXmlTemplates[name] = ptr;
  1170. continue;
  1171. }
  1172. if (!item->hasName("notification"))
  1173. {
  1174. llwarns << "Unexpected entity " << item->getName()->mString <<
  1175. " found in " << TEMPLATES_FILE << llendl;
  1176. continue;
  1177. }
  1178. // now we know we have a notification entry, so let's build it
  1179. LLNotificationTemplatePtr templatep(new LLNotificationTemplate());
  1180. if (!item->getAttributeString("name", templatep->mName))
  1181. {
  1182. llwarns << "Unable to parse notification with no name" << llendl;
  1183. continue;
  1184. }
  1185. LL_DEBUGS("Notifications") << "Parsing " << templatep->mName
  1186. << LL_ENDL;
  1187. templatep->mMessage = item->getTextContents();
  1188. templatep->mDefaultFunctor = templatep->mName;
  1189. item->getAttributeString("type", templatep->mType);
  1190. item->getAttributeString("icon", templatep->mIcon);
  1191. item->getAttributeString("label", templatep->mLabel);
  1192. item->getAttributeU32("duration", templatep->mExpireSeconds);
  1193. item->getAttributeU32("expireOption", templatep->mExpireOption);
  1194. std::string priority;
  1195. item->getAttributeString("priority", priority);
  1196. templatep->mPriority = NOTIFICATION_PRIORITY_NORMAL;
  1197. if (!priority.empty())
  1198. {
  1199. if (priority == "low")
  1200. {
  1201. templatep->mPriority = NOTIFICATION_PRIORITY_LOW;
  1202. }
  1203. else if (priority == "high")
  1204. {
  1205. templatep->mPriority = NOTIFICATION_PRIORITY_HIGH;
  1206. }
  1207. else if (priority == "critical")
  1208. {
  1209. templatep->mPriority = NOTIFICATION_PRIORITY_CRITICAL;
  1210. }
  1211. }
  1212. item->getAttributeString("functor", templatep->mDefaultFunctor);
  1213. bool persist = false;
  1214. item->getAttributeBool("persist", persist);
  1215. templatep->mPersist = persist;
  1216. std::string sound;
  1217. item->getAttributeString("sound", sound);
  1218. if (!sound.empty() && LLUI::sConfigGroup)
  1219. {
  1220. // TODO: test for bad sound effect name / missing effect
  1221. templatep->mSoundEffect = LLUUID(LLUI::sConfigGroup->getString(sound.c_str()));
  1222. }
  1223. for (LLXMLNodePtr child = item->getFirstChild(); !child.isNull();
  1224. child = child->getNextSibling())
  1225. {
  1226. child = checkForXMLTemplate(child);
  1227. // <url>
  1228. if (child->hasName("url"))
  1229. {
  1230. templatep->mURL = child->getTextContents();
  1231. child->getAttributeU32("option", templatep->mURLOption);
  1232. }
  1233. if (child->hasName("unique"))
  1234. {
  1235. templatep->mUnique = true;
  1236. for (LLXMLNodePtr formitem = child->getFirstChild();
  1237. !formitem.isNull(); formitem = formitem->getNextSibling())
  1238. {
  1239. if (formitem->hasName("context"))
  1240. {
  1241. std::string key;
  1242. formitem->getAttributeString("key", key);
  1243. templatep->mUniqueContext.emplace_back(key);
  1244. LL_DEBUGS("Notifications") << "adding " << key
  1245. << " to unique context"
  1246. << LL_ENDL;
  1247. }
  1248. else
  1249. {
  1250. llwarns << "'unique' has unrecognized sub-element "
  1251. << formitem->getName()->mString << llendl;
  1252. }
  1253. }
  1254. }
  1255. // <form>
  1256. if (child->hasName("form"))
  1257. {
  1258. templatep->mForm =
  1259. LLNotificationFormPtr(new LLNotificationForm(templatep->mName,
  1260. child));
  1261. }
  1262. }
  1263. addTemplate(templatep->mName, templatep);
  1264. }
  1265. return true;
  1266. }
  1267. // we provide a couple of simple add notification functions so that it is
  1268. // reasonable to create notifications in one line
  1269. LLNotificationPtr LLNotifications::add(const std::string& name,
  1270. const LLSD& substitutions,
  1271. const LLSD& payload)
  1272. {
  1273. return add(LLNotification::Params(name).substitutions(substitutions).payload(payload));
  1274. }
  1275. LLNotificationPtr LLNotifications::add(const std::string& name,
  1276. const LLSD& substitutions,
  1277. const LLSD& payload,
  1278. const std::string& functor_name)
  1279. {
  1280. return add(LLNotification::Params(name).substitutions(substitutions).payload(payload).functor_name(functor_name));
  1281. }
  1282. LLNotificationPtr LLNotifications::add(const std::string& name,
  1283. const LLSD& substitutions,
  1284. const LLSD& payload,
  1285. LLNotificationFunctorRegistry::ResponseFunctor functor)
  1286. {
  1287. return add(LLNotification::Params(name).substitutions(substitutions).payload(payload).functor(functor));
  1288. }
  1289. // Generalized add method that takes a parameter block object for more complex
  1290. // instantiations
  1291. LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
  1292. {
  1293. LLNotificationPtr notifp(new LLNotification(p));
  1294. add(notifp);
  1295. return notifp;
  1296. }
  1297. void LLNotifications::add(const LLNotificationPtr notifp)
  1298. {
  1299. // First see if we already have it: if so, this is a problem
  1300. LLNotificationSet::iterator it = mItems.find(notifp);
  1301. if (it != mItems.end())
  1302. {
  1303. llwarns << "Attempted to add notification '" << notifp->getName()
  1304. << "' (existing notification id: " << notifp->getID()
  1305. << ") a second time to the master notification channel !"
  1306. << llendl;
  1307. llassert(false);
  1308. return;
  1309. }
  1310. updateItem(LLSD().with("sigtype", "add").with("id", notifp->getID()),
  1311. notifp);
  1312. }
  1313. void LLNotifications::cancel(LLNotificationPtr notifp)
  1314. {
  1315. LLNotificationSet::iterator it = mItems.find(notifp);
  1316. if (it == mItems.end())
  1317. {
  1318. llwarns << "Attempted to delete inexistent notification." << llendl;
  1319. llassert(false);
  1320. return;
  1321. }
  1322. updateItem(LLSD().with("sigtype", "delete").with("id", notifp->getID()),
  1323. notifp);
  1324. notifp->cancel();
  1325. }
  1326. void LLNotifications::update(const LLNotificationPtr notifp)
  1327. {
  1328. LLNotificationSet::iterator it = mItems.find(notifp);
  1329. if (it != mItems.end())
  1330. {
  1331. updateItem(LLSD().with("sigtype", "change").with("id",
  1332. notifp->getID()),
  1333. notifp);
  1334. }
  1335. }
  1336. LLNotificationPtr LLNotifications::find(LLUUID uuid)
  1337. {
  1338. LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid));
  1339. LLNotificationSet::iterator it = mItems.find(target);
  1340. if (it == mItems.end())
  1341. {
  1342. LL_DEBUGS("Notifications") << "Cannot find notification '" << uuid
  1343. << LL_ENDL;
  1344. return LLNotificationPtr(NULL);
  1345. }
  1346. else
  1347. {
  1348. return *it;
  1349. }
  1350. }
  1351. void LLNotifications::forEachNotification(NotificationProcess process)
  1352. {
  1353. std::for_each(mItems.begin(), mItems.end(), process);
  1354. }
  1355. const std::string& LLNotifications::getGlobalString(const std::string& key) const
  1356. {
  1357. GlobalStringMap::const_iterator it = mGlobalStrings.find(key);
  1358. if (it != mGlobalStrings.end())
  1359. {
  1360. return it->second;
  1361. }
  1362. // If we do not have the key as a global, return the key itself so that the
  1363. // error is self-diagnosing.
  1364. return key;
  1365. }
  1366. std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
  1367. {
  1368. s << notification.summarize();
  1369. return s;
  1370. }