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