PageRenderTime 51ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llui/llnotificationslistener.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 354 lines | 264 code | 18 blank | 72 comment | 28 complexity | 57e82be59c1c5f91d82b2136643464de MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llnotificationslistener.cpp
  3. * @author Brad Kittenbrink
  4. * @date 2009-07-08
  5. * @brief Implementation for llnotificationslistener.
  6. *
  7. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. #include "linden_common.h"
  29. #include "llnotificationslistener.h"
  30. #include "llnotifications.h"
  31. #include "llnotificationtemplate.h"
  32. #include "llsd.h"
  33. #include "llui.h"
  34. LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) :
  35. LLEventAPI("LLNotifications",
  36. "LLNotifications listener to (e.g.) pop up a notification"),
  37. mNotifications(notifications)
  38. {
  39. add("requestAdd",
  40. "Add a notification with specified [\"name\"], [\"substitutions\"] and [\"payload\"].\n"
  41. "If optional [\"reply\"] specified, arrange to send user response on that LLEventPump.",
  42. &LLNotificationsListener::requestAdd);
  43. add("listChannels",
  44. "Post to [\"reply\"] a map of info on existing channels",
  45. &LLNotificationsListener::listChannels,
  46. LLSD().with("reply", LLSD()));
  47. add("listChannelNotifications",
  48. "Post to [\"reply\"] an array of info on notifications in channel [\"channel\"]",
  49. &LLNotificationsListener::listChannelNotifications,
  50. LLSD().with("reply", LLSD()).with("channel", LLSD()));
  51. add("respond",
  52. "Respond to notification [\"uuid\"] with data in [\"response\"]",
  53. &LLNotificationsListener::respond,
  54. LLSD().with("uuid", LLSD()));
  55. add("cancel",
  56. "Cancel notification [\"uuid\"]",
  57. &LLNotificationsListener::cancel,
  58. LLSD().with("uuid", LLSD()));
  59. add("ignore",
  60. "Ignore future notification [\"name\"]\n"
  61. "(from <notification name= > in notifications.xml)\n"
  62. "according to boolean [\"ignore\"].\n"
  63. "If [\"name\"] is omitted or undefined, [un]ignore all future notifications.\n"
  64. "Note that ignored notifications are not forwarded unless intercepted before\n"
  65. "the \"Ignore\" channel.",
  66. &LLNotificationsListener::ignore);
  67. add("forward",
  68. "Forward to [\"pump\"] future notifications on channel [\"channel\"]\n"
  69. "according to boolean [\"forward\"]. When enabled, only types matching\n"
  70. "[\"types\"] are forwarded, as follows:\n"
  71. "omitted or undefined: forward all notifications\n"
  72. "string: forward only the specific named [sig]type\n"
  73. "array of string: forward any notification matching any named [sig]type.\n"
  74. "When boolean [\"respond\"] is true, we auto-respond to each forwarded\n"
  75. "notification.",
  76. &LLNotificationsListener::forward,
  77. LLSD().with("channel", LLSD()));
  78. }
  79. // This is here in the .cpp file so we don't need the definition of class
  80. // Forwarder in the header file.
  81. LLNotificationsListener::~LLNotificationsListener()
  82. {
  83. }
  84. void LLNotificationsListener::requestAdd(const LLSD& event_data) const
  85. {
  86. if(event_data.has("reply"))
  87. {
  88. mNotifications.add(event_data["name"],
  89. event_data["substitutions"],
  90. event_data["payload"],
  91. boost::bind(&LLNotificationsListener::NotificationResponder,
  92. this,
  93. event_data["reply"].asString(),
  94. _1, _2
  95. )
  96. );
  97. }
  98. else
  99. {
  100. mNotifications.add(event_data["name"],
  101. event_data["substitutions"],
  102. event_data["payload"]);
  103. }
  104. }
  105. void LLNotificationsListener::NotificationResponder(const std::string& reply_pump,
  106. const LLSD& notification,
  107. const LLSD& response) const
  108. {
  109. LLSD reponse_event;
  110. reponse_event["notification"] = notification;
  111. reponse_event["response"] = response;
  112. LLEventPumps::getInstance()->obtain(reply_pump).post(reponse_event);
  113. }
  114. void LLNotificationsListener::listChannels(const LLSD& params) const
  115. {
  116. LLReqID reqID(params);
  117. LLSD response(reqID.makeResponse());
  118. for (LLNotifications::ChannelMap::const_iterator cmi(mNotifications.mChannels.begin()),
  119. cmend(mNotifications.mChannels.end());
  120. cmi != cmend; ++cmi)
  121. {
  122. LLSD channelInfo;
  123. channelInfo["parent"] = cmi->second->getParentChannelName();
  124. response[cmi->first] = channelInfo;
  125. }
  126. LLEventPumps::instance().obtain(params["reply"]).post(response);
  127. }
  128. void LLNotificationsListener::listChannelNotifications(const LLSD& params) const
  129. {
  130. LLReqID reqID(params);
  131. LLSD response(reqID.makeResponse());
  132. LLNotificationChannelPtr channel(mNotifications.getChannel(params["channel"]));
  133. if (channel)
  134. {
  135. LLSD notifications(LLSD::emptyArray());
  136. for (LLNotificationChannel::Iterator ni(channel->begin()), nend(channel->end());
  137. ni != nend; ++ni)
  138. {
  139. notifications.append(asLLSD(*ni));
  140. }
  141. response["notifications"] = notifications;
  142. }
  143. LLEventPumps::instance().obtain(params["reply"]).post(response);
  144. }
  145. void LLNotificationsListener::respond(const LLSD& params) const
  146. {
  147. LLNotificationPtr notification(mNotifications.find(params["uuid"]));
  148. if (notification)
  149. {
  150. notification->respond(params["response"]);
  151. }
  152. }
  153. void LLNotificationsListener::cancel(const LLSD& params) const
  154. {
  155. LLNotificationPtr notification(mNotifications.find(params["uuid"]));
  156. if (notification)
  157. {
  158. mNotifications.cancel(notification);
  159. }
  160. }
  161. void LLNotificationsListener::ignore(const LLSD& params) const
  162. {
  163. // Calling a method named "ignore", but omitting its "ignore" Boolean
  164. // argument, should by default cause something to be ignored. Explicitly
  165. // pass ["ignore"] = false to cancel ignore.
  166. bool ignore = true;
  167. if (params.has("ignore"))
  168. {
  169. ignore = params["ignore"].asBoolean();
  170. }
  171. // This method can be used to affect either a single notification name or
  172. // all future notifications. The two use substantially different mechanisms.
  173. if (params["name"].isDefined())
  174. {
  175. // ["name"] was passed: ignore just that notification
  176. LLNotificationTemplatePtr templatep = mNotifications.getTemplate(params["name"]);
  177. if (templatep)
  178. {
  179. templatep->mForm->setIgnored(ignore);
  180. }
  181. }
  182. else
  183. {
  184. // no ["name"]: ignore all future notifications
  185. mNotifications.setIgnoreAllNotifications(ignore);
  186. }
  187. }
  188. class LLNotificationsListener::Forwarder: public LLEventTrackable
  189. {
  190. LOG_CLASS(LLNotificationsListener::Forwarder);
  191. public:
  192. Forwarder(LLNotifications& llnotifications, const std::string& channel):
  193. mNotifications(llnotifications),
  194. mRespond(false)
  195. {
  196. // Connect to the specified channel on construction. Because
  197. // LLEventTrackable is a base, we should automatically disconnect when
  198. // destroyed.
  199. LLNotificationChannelPtr channelptr(llnotifications.getChannel(channel));
  200. if (channelptr)
  201. {
  202. // Insert our processing as a "passed filter" listener. This way
  203. // we get to run before all the "changed" listeners, and we get to
  204. // swipe it (hide it from the other listeners) if desired.
  205. channelptr->connectPassedFilter(boost::bind(&Forwarder::handle, this, _1));
  206. }
  207. }
  208. void setPumpName(const std::string& name) { mPumpName = name; }
  209. void setTypes(const LLSD& types) { mTypes = types; }
  210. void setRespond(bool respond) { mRespond = respond; }
  211. private:
  212. bool handle(const LLSD& notification) const;
  213. bool matchType(const LLSD& filter, const std::string& type) const;
  214. LLNotifications& mNotifications;
  215. std::string mPumpName;
  216. LLSD mTypes;
  217. bool mRespond;
  218. };
  219. void LLNotificationsListener::forward(const LLSD& params)
  220. {
  221. std::string channel(params["channel"]);
  222. // First decide whether we're supposed to start forwarding or stop it.
  223. // Default to true.
  224. bool forward = true;
  225. if (params.has("forward"))
  226. {
  227. forward = params["forward"].asBoolean();
  228. }
  229. if (! forward)
  230. {
  231. // This is a request to stop forwarding notifications on the specified
  232. // channel. The rest of the params don't matter.
  233. // Because mForwarders contains scoped_ptrs, erasing the map entry
  234. // DOES delete the heap Forwarder object. Because Forwarder derives
  235. // from LLEventTrackable, destroying it disconnects it from the
  236. // channel.
  237. mForwarders.erase(channel);
  238. return;
  239. }
  240. // From here on, we know we're being asked to start (or modify) forwarding
  241. // on the specified channel. Find or create an appropriate Forwarder.
  242. ForwarderMap::iterator
  243. entry(mForwarders.insert(ForwarderMap::value_type(channel, ForwarderMap::mapped_type())).first);
  244. if (! entry->second)
  245. {
  246. entry->second.reset(new Forwarder(mNotifications, channel));
  247. }
  248. // Now, whether this Forwarder is brand-new or not, update it with the new
  249. // request info.
  250. Forwarder& fwd(*entry->second);
  251. fwd.setPumpName(params["pump"]);
  252. fwd.setTypes(params["types"]);
  253. fwd.setRespond(params["respond"]);
  254. }
  255. bool LLNotificationsListener::Forwarder::handle(const LLSD& notification) const
  256. {
  257. LL_INFOS("LLNotificationsListener") << "handle(" << notification << ")" << LL_ENDL;
  258. if (notification["sigtype"].asString() == "delete")
  259. {
  260. LL_INFOS("LLNotificationsListener") << "ignoring delete" << LL_ENDL;
  261. // let other listeners see the "delete" operation
  262. return false;
  263. }
  264. LLNotificationPtr note(mNotifications.find(notification["id"]));
  265. if (! note)
  266. {
  267. LL_INFOS("LLNotificationsListener") << notification["id"] << " not found" << LL_ENDL;
  268. return false;
  269. }
  270. if (! matchType(mTypes, note->getType()))
  271. {
  272. LL_INFOS("LLNotificationsListener") << "didn't match types " << mTypes << LL_ENDL;
  273. // We're not supposed to intercept this particular notification. Let
  274. // other listeners process it.
  275. return false;
  276. }
  277. LL_INFOS("LLNotificationsListener") << "sending via '" << mPumpName << "'" << LL_ENDL;
  278. // This is a notification we care about. Forward it through specified
  279. // LLEventPump.
  280. LLEventPumps::instance().obtain(mPumpName).post(asLLSD(note));
  281. // Are we also being asked to auto-respond?
  282. if (mRespond)
  283. {
  284. LL_INFOS("LLNotificationsListener") << "should respond" << LL_ENDL;
  285. note->respond(LLSD::emptyMap());
  286. // Did that succeed in removing the notification? Only cancel() if
  287. // it's still around -- otherwise we get an LL_ERRS crash!
  288. note = mNotifications.find(notification["id"]);
  289. if (note)
  290. {
  291. LL_INFOS("LLNotificationsListener") << "respond() didn't clear, canceling" << LL_ENDL;
  292. mNotifications.cancel(note);
  293. }
  294. }
  295. // If we've auto-responded to this notification, then it's going to be
  296. // deleted. Other listeners would get the change operation, try to look it
  297. // up and be baffled by lookup failure. So when we auto-respond, suppress
  298. // this notification: don't pass it to other listeners.
  299. return mRespond;
  300. }
  301. bool LLNotificationsListener::Forwarder::matchType(const LLSD& filter, const std::string& type) const
  302. {
  303. // Decide whether this notification matches filter:
  304. // undefined: forward all notifications
  305. if (filter.isUndefined())
  306. {
  307. return true;
  308. }
  309. // array of string: forward any notification matching any named type
  310. if (filter.isArray())
  311. {
  312. for (LLSD::array_const_iterator ti(filter.beginArray()), tend(filter.endArray());
  313. ti != tend; ++ti)
  314. {
  315. if (ti->asString() == type)
  316. {
  317. return true;
  318. }
  319. }
  320. // Didn't match any entry in the array
  321. return false;
  322. }
  323. // string: forward only the specific named type
  324. return (filter.asString() == type);
  325. }
  326. LLSD LLNotificationsListener::asLLSD(LLNotificationPtr note)
  327. {
  328. LLSD notificationInfo(note->asLLSD());
  329. // For some reason the following aren't included in LLNotification::asLLSD().
  330. notificationInfo["summary"] = note->summarize();
  331. notificationInfo["id"] = note->id();
  332. notificationInfo["type"] = note->getType();
  333. notificationInfo["message"] = note->getMessage();
  334. notificationInfo["label"] = note->getLabel();
  335. return notificationInfo;
  336. }