summaryrefslogtreecommitdiff
path: root/indra/newview/llfloaterbulkpermission.cpp
blob: 243c5461698c631174566eb35037fe37b266074b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
/**
 * @file llfloaterbulkpermissions.cpp
 * @author Michelle2 Zenovka
 * @brief A floater which allows task inventory item's properties to be changed on mass.
 *
 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */


#include "llviewerprecompiledheaders.h"
#include "llfloaterbulkpermission.h"
#include "llfloaterperms.h" // for utilities
#include "llagent.h"
#include "llchat.h"
#include "llinventorydefines.h"
#include "llviewerwindow.h"
#include "llviewerobject.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
#include "llviewercontrol.h"
#include "llviewerinventory.h"
#include "llviewerobject.h"
#include "llviewerregion.h"
#include "llresmgr.h"
#include "llbutton.h"
#include "lldir.h"
#include "llviewerstats.h"
#include "lluictrlfactory.h"
#include "llselectmgr.h"
#include "llcheckboxctrl.h"

#include "roles_constants.h" // for GP_OBJECT_MANIPULATE


LLFloaterBulkPermission::LLFloaterBulkPermission(const LLSD& seed)
:   LLFloater(seed),
    mDone(FALSE)
{
    mID.generate();
    mCommitCallbackRegistrar.add("BulkPermission.Ok",       boost::bind(&LLFloaterBulkPermission::onOkBtn, this));
    mCommitCallbackRegistrar.add("BulkPermission.Apply",    boost::bind(&LLFloaterBulkPermission::onApplyBtn, this));
    mCommitCallbackRegistrar.add("BulkPermission.Close",    boost::bind(&LLFloaterBulkPermission::onCloseBtn, this));
    mCommitCallbackRegistrar.add("BulkPermission.CheckAll", boost::bind(&LLFloaterBulkPermission::onCheckAll, this));
    mCommitCallbackRegistrar.add("BulkPermission.UncheckAll",   boost::bind(&LLFloaterBulkPermission::onUncheckAll, this));
    mCommitCallbackRegistrar.add("BulkPermission.CommitCopy",   boost::bind(&LLFloaterBulkPermission::onCommitCopy, this));
}

BOOL LLFloaterBulkPermission::postBuild()
{
    mBulkChangeIncludeAnimations = gSavedSettings.getBOOL("BulkChangeIncludeAnimations");
    mBulkChangeIncludeBodyParts = gSavedSettings.getBOOL("BulkChangeIncludeBodyParts");
    mBulkChangeIncludeClothing = gSavedSettings.getBOOL("BulkChangeIncludeClothing");
    mBulkChangeIncludeGestures = gSavedSettings.getBOOL("BulkChangeIncludeGestures");
    mBulkChangeIncludeNotecards = gSavedSettings.getBOOL("BulkChangeIncludeNotecards");
    mBulkChangeIncludeObjects = gSavedSettings.getBOOL("BulkChangeIncludeObjects");
    mBulkChangeIncludeScripts = gSavedSettings.getBOOL("BulkChangeIncludeScripts");
    mBulkChangeIncludeSounds = gSavedSettings.getBOOL("BulkChangeIncludeSounds");
    mBulkChangeIncludeTextures = gSavedSettings.getBOOL("BulkChangeIncludeTextures");
    mBulkChangeIncludeSettings = gSavedSettings.getBOOL("BulkChangeIncludeSettings");
    mBulkChangeIncludeMaterials = gSavedSettings.getBOOL("BulkChangeIncludeMaterials");

    mBulkChangeShareWithGroup = gSavedSettings.getBOOL("BulkChangeShareWithGroup");
    mBulkChangeEveryoneCopy = gSavedSettings.getBOOL("BulkChangeEveryoneCopy");
    mBulkChangeNextOwnerModify = gSavedSettings.getBOOL("BulkChangeNextOwnerModify");
    mBulkChangeNextOwnerCopy = gSavedSettings.getBOOL("BulkChangeNextOwnerCopy");
    mBulkChangeNextOwnerTransfer = gSavedSettings.getBOOL("BulkChangeNextOwnerTransfer");

    // fix invalid permissions case (in case initial settings were generated by a viewer affected by MAINT-3339)
    if( !mBulkChangeNextOwnerTransfer && !mBulkChangeEveryoneCopy)
    {
        mBulkChangeNextOwnerTransfer = true;
    }
    return TRUE;
}

void LLFloaterBulkPermission::doApply()
{
    // Inspects a stream of selected object contents and adds modifiable ones to the given array.
    class ModifiableGatherer : public LLSelectedNodeFunctor
    {
    public:
        ModifiableGatherer(std::vector<LLUUID>& q) : mQueue(q) { mQueue.reserve(32); }
        virtual bool apply(LLSelectNode* node)
        {
            if( node->allowOperationOnNode(PERM_MODIFY, GP_OBJECT_MANIPULATE) )
            {
                mQueue.push_back(node->getObject()->getID());
            }
            return true;
        }
    private:
        std::vector<LLUUID>& mQueue;
    };
    LLScrollListCtrl* list = getChild<LLScrollListCtrl>("queue output");
    list->deleteAllItems();
    ModifiableGatherer gatherer(mObjectIDs);
    LLSelectMgr::getInstance()->getSelection()->applyToNodes(&gatherer);
    if(mObjectIDs.empty())
    {
        list->setCommentText(getString("nothing_to_modify_text"));
    }
    else
    {
        mDone = FALSE;
        if (!start())
        {
            LL_WARNS() << "Unexpected bulk permission change failure." << LL_ENDL;
        }
    }
}


// This is the callback method for the viewer object currently being
// worked on.
// NOT static, virtual!
void LLFloaterBulkPermission::inventoryChanged(LLViewerObject* viewer_object,
                                             LLInventoryObject::object_list_t* inv,
                                             S32,
                                             void* q_id)
{
    //LL_INFOS() << "changed object: " << viewer_object->getID() << LL_ENDL;

    //Remove this listener from the object since its
    //listener callback is now being executed.

    //We remove the listener here because the function
    //removeVOInventoryListener removes the listener from a ViewerObject
    //which it internally stores.

    //If we call this further down in the function, calls to handleInventory
    //and nextObject may update the interally stored viewer object causing
    //the removal of the incorrect listener from an incorrect object.

    //Fixes SL-6119:Recompile scripts fails to complete
    removeVOInventoryListener();

    if (viewer_object && inv && (viewer_object->getID() == mCurrentObjectID) )
    {
        handleInventory(viewer_object, inv);
    }
    else
    {
        // something went wrong...
        // note that we're not working on this one, and move onto the
        // next object in the list.
        LL_WARNS() << "No inventory for " << mCurrentObjectID << LL_ENDL;
        nextObject();
    }
}

void LLFloaterBulkPermission::onOkBtn()
{
    doApply();
    closeFloater();
}

void LLFloaterBulkPermission::onApplyBtn()
{
    doApply();
}

void LLFloaterBulkPermission::onCloseBtn()
{
    gSavedSettings.setBOOL("BulkChangeIncludeAnimations", mBulkChangeIncludeAnimations);
    gSavedSettings.setBOOL("BulkChangeIncludeBodyParts", mBulkChangeIncludeBodyParts);
    gSavedSettings.setBOOL("BulkChangeIncludeClothing", mBulkChangeIncludeClothing);
    gSavedSettings.setBOOL("BulkChangeIncludeGestures", mBulkChangeIncludeGestures);
    gSavedSettings.setBOOL("BulkChangeIncludeNotecards", mBulkChangeIncludeNotecards);
    gSavedSettings.setBOOL("BulkChangeIncludeObjects", mBulkChangeIncludeObjects);
    gSavedSettings.setBOOL("BulkChangeIncludeScripts", mBulkChangeIncludeScripts);
    gSavedSettings.setBOOL("BulkChangeIncludeSounds", mBulkChangeIncludeSounds);
    gSavedSettings.setBOOL("BulkChangeIncludeTextures", mBulkChangeIncludeTextures);
    gSavedSettings.setBOOL("BulkChangeIncludeSettings", mBulkChangeIncludeSettings);
    gSavedSettings.setBOOL("BulkChangeIncludeMaterials", mBulkChangeIncludeMaterials);

    gSavedSettings.setBOOL("BulkChangeShareWithGroup", mBulkChangeShareWithGroup);
    gSavedSettings.setBOOL("BulkChangeEveryoneCopy", mBulkChangeEveryoneCopy);
    gSavedSettings.setBOOL("BulkChangeNextOwnerModify", mBulkChangeNextOwnerModify);
    gSavedSettings.setBOOL("BulkChangeNextOwnerCopy", mBulkChangeNextOwnerCopy);
    gSavedSettings.setBOOL("BulkChangeNextOwnerTransfer", mBulkChangeNextOwnerTransfer);
    closeFloater();
}

//static
void LLFloaterBulkPermission::onCommitCopy()
{
    // Implements fair use
    BOOL copyable = gSavedSettings.getBOOL("BulkChangeNextOwnerCopy");
    if(!copyable)
    {
        gSavedSettings.setBOOL("BulkChangeNextOwnerTransfer", TRUE);
    }
    LLCheckBoxCtrl* xfer =getChild<LLCheckBoxCtrl>("next_owner_transfer");
    xfer->setEnabled(copyable);
}

BOOL LLFloaterBulkPermission::start()
{
    // note: number of top-level objects to modify is mObjectIDs.size().
    getChild<LLScrollListCtrl>("queue output")->setCommentText(getString("start_text"));
    return nextObject();
}

// Go to the next object and start if found. Returns false if no objects left, true otherwise.
BOOL LLFloaterBulkPermission::nextObject()
{
    S32 count;
    BOOL successful_start = FALSE;
    do
    {
        count = mObjectIDs.size();
        //LL_INFOS() << "Objects left to process = " << count << LL_ENDL;
        mCurrentObjectID.setNull();
        if(count > 0)
        {
            successful_start = popNext();
            //LL_INFOS() << (successful_start ? "successful" : "unsuccessful") << LL_ENDL;
        }
    } while((mObjectIDs.size() > 0) && !successful_start);

    if(isDone() && !mDone)
    {
        getChild<LLScrollListCtrl>("queue output")->setCommentText(getString("done_text"));
        mDone = TRUE;
    }
    return successful_start;
}

// Pop the top object off of the queue.
// Return TRUE if the queue has started, otherwise FALSE.
BOOL LLFloaterBulkPermission::popNext()
{
    // get the head element from the container, and attempt to get its inventory.
    BOOL rv = FALSE;
    S32 count = mObjectIDs.size();
    if(mCurrentObjectID.isNull() && (count > 0))
    {
        mCurrentObjectID = mObjectIDs.at(0);
        //LL_INFOS() << "mCurrentID: " << mCurrentObjectID << LL_ENDL;
        mObjectIDs.erase(mObjectIDs.begin());
        LLViewerObject* obj = gObjectList.findObject(mCurrentObjectID);
        if(obj)
        {
            //LL_INFOS() << "requesting inv for " << mCurrentObjectID << LL_ENDL;
            LLUUID* id = new LLUUID(mID);
            registerVOInventoryListener(obj,id);
            requestVOInventory();
            rv = TRUE;
        }
        else
        {
            LL_INFOS()<<"NULL LLViewerObject" <<LL_ENDL;
        }
    }

    return rv;
}


void LLFloaterBulkPermission::doCheckUncheckAll(BOOL check)
{
    gSavedSettings.setBOOL("BulkChangeIncludeAnimations", check);
    gSavedSettings.setBOOL("BulkChangeIncludeBodyParts" , check);
    gSavedSettings.setBOOL("BulkChangeIncludeClothing"  , check);
    gSavedSettings.setBOOL("BulkChangeIncludeGestures"  , check);
    gSavedSettings.setBOOL("BulkChangeIncludeNotecards" , check);
    gSavedSettings.setBOOL("BulkChangeIncludeObjects"   , check);
    gSavedSettings.setBOOL("BulkChangeIncludeScripts"   , check);
    gSavedSettings.setBOOL("BulkChangeIncludeSounds"    , check);
    gSavedSettings.setBOOL("BulkChangeIncludeTextures"  , check);
    gSavedSettings.setBOOL("BulkChangeIncludeSettings"  , check);
    gSavedSettings.setBOOL("BulkChangeIncludeMaterials"  , check);
}


void LLFloaterBulkPermission::handleInventory(LLViewerObject* viewer_obj, LLInventoryObject::object_list_t* inv)
{
    LLScrollListCtrl* list = getChild<LLScrollListCtrl>("queue output");

    LLInventoryObject::object_list_t::const_iterator it = inv->begin();
    LLInventoryObject::object_list_t::const_iterator end = inv->end();
    for ( ; it != end; ++it)
    {
        LLAssetType::EType asstype = (*it)->getType();
        if(
            ( asstype == LLAssetType::AT_ANIMATION && gSavedSettings.getBOOL("BulkChangeIncludeAnimations")) ||
            ( asstype == LLAssetType::AT_BODYPART  && gSavedSettings.getBOOL("BulkChangeIncludeBodyParts" )) ||
            ( asstype == LLAssetType::AT_CLOTHING  && gSavedSettings.getBOOL("BulkChangeIncludeClothing"  )) ||
            ( asstype == LLAssetType::AT_GESTURE   && gSavedSettings.getBOOL("BulkChangeIncludeGestures"  )) ||
            ( asstype == LLAssetType::AT_NOTECARD  && gSavedSettings.getBOOL("BulkChangeIncludeNotecards" )) ||
            ( asstype == LLAssetType::AT_OBJECT    && gSavedSettings.getBOOL("BulkChangeIncludeObjects"   )) ||
            ( asstype == LLAssetType::AT_LSL_TEXT  && gSavedSettings.getBOOL("BulkChangeIncludeScripts"   )) ||
            ( asstype == LLAssetType::AT_SOUND     && gSavedSettings.getBOOL("BulkChangeIncludeSounds"    )) ||
            ( asstype == LLAssetType::AT_SETTINGS  && gSavedSettings.getBOOL("BulkChangeIncludeSettings"  )) ||
            ( asstype == LLAssetType::AT_MATERIAL  && gSavedSettings.getBOOL("BulkChangeIncludeMaterials")) ||
            ( asstype == LLAssetType::AT_TEXTURE   && gSavedSettings.getBOOL("BulkChangeIncludeTextures"  )))
        {
            LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());

            if (object)
            {
                LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
                LLViewerInventoryItem* new_item = (LLViewerInventoryItem*)item;
                LLPermissions perm(new_item->getPermissions());

                // chomp the inventory name so it fits in the scroll window nicely
                // and the user can see the [OK]
                std::string invname;
                invname=item->getName().substr(0,item->getName().size() < 30 ? item->getName().size() : 30 );

                LLUIString status_text = getString("status_text");
                status_text.setArg("[NAME]", invname.c_str());
                // Check whether we appear to have the appropriate permissions to change permission on this item.
                // Although the server will disallow any forbidden changes, it is a good idea to guess correctly
                // so that we can warn the user. The risk of getting this check wrong is therefore the possibility
                // of incorrectly choosing to not attempt to make a valid change.
                //
                // Trouble is this is extremely difficult to do and even when we know the results
                // it is difficult to design the best messaging. Therefore in this initial implementation
                // we'll always try to set the requested permissions and consider all cases successful
                // and perhaps later try to implement a smarter, friendlier solution. -MG
                if(true
                    //gAgent.allowOperation(PERM_MODIFY, perm, GP_OBJECT_MANIPULATE) // for group and everyone masks
                    //|| something else // for next owner perms
                    )
                {
                    U32 mask_next = LLFloaterPerms::getNextOwnerPerms("BulkChange");
                    if (asstype == LLAssetType::AT_SETTINGS)
                    {
                        mask_next |= PERM_COPY;
                    }
                    perm.setMaskNext(mask_next);
                    perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("BulkChange"));
                    perm.setMaskGroup(LLFloaterPerms::getGroupPerms("BulkChange"));
                    new_item->setPermissions(perm); // here's the beef
                    updateInventory(object,new_item,TASK_INVENTORY_ITEM_KEY,FALSE);
                    //status_text.setArg("[STATUS]", getString("status_ok_text"));
                    status_text.setArg("[STATUS]", "");
                }
                else
                {
                    //status_text.setArg("[STATUS]", getString("status_bad_text"));
                    status_text.setArg("[STATUS]", "");
                }

                list->setCommentText(status_text.getString());

                //TODO if we are an object inside an object we should check a recuse flag and if set
                //open the inventory of the object and recurse - Michelle2 Zenovka

                //  if(recurse &&  ( (*it)->getType() == LLAssetType::AT_OBJECT && processObject))
                //  {
                //      I think we need to get the UUID of the object inside the inventory
                //      call item->fetchFromServer();
                //      we need a call back to say item has arrived *sigh*
                //      we then need to do something like
                //      LLUUID* id = new LLUUID(mID);
                //      registerVOInventoryListener(obj,id);
                //      requestVOInventory();
                //  }
            }
        }
    }

    nextObject();
}


// Avoid inventory callbacks etc by just fire and forgetting the message with the permissions update
// we could do this via LLViewerObject::updateInventory but that uses inventory call backs and buggers
// us up and we would have a dodgy item iterator

void LLFloaterBulkPermission::updateInventory(LLViewerObject* object, LLViewerInventoryItem* item, U8 key, bool is_new)
{
    // This slices the object into what we're concerned about on the viewer.
    // The simulator will take the permissions and transfer ownership.
    LLPointer<LLViewerInventoryItem> task_item =
        new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(),
                                  item->getAssetUUID(), item->getType(),
                                  item->getInventoryType(),
                                  item->getName(), item->getDescription(),
                                  item->getSaleInfo(),
                                  item->getFlags(),
                                  item->getCreationDate());
    task_item->setTransactionID(item->getTransactionID());
    LLMessageSystem* msg = gMessageSystem;
    msg->newMessageFast(_PREHASH_UpdateTaskInventory);
    msg->nextBlockFast(_PREHASH_AgentData);
    msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
    msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
    msg->nextBlockFast(_PREHASH_UpdateData);
    msg->addU32Fast(_PREHASH_LocalID, object->mLocalID);
    msg->addU8Fast(_PREHASH_Key, key);
    msg->nextBlockFast(_PREHASH_InventoryData);
    task_item->packMessage(msg);
    msg->sendReliable(object->getRegion()->getHost());
}