aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llfloaterbulkpermission.cpp
blob: 6721661b19822d490d0e0c7c851695d963692180 (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
/** 
 * @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=viewergpl$
 * 
 * Copyright (c) 2008-2009, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */


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

#include "roles_constants.h" // for GP_OBJECT_MANIPULATE


LLFloaterBulkPermission::LLFloaterBulkPermission(const LLSD& seed) : mDone(FALSE)
{
	mID.generate();
	LLUICtrlFactory::getInstance()->buildFloater(this,"floater_bulk_perms.xml");
	childSetEnabled("next_owner_transfer", gSavedSettings.getBOOL("BulkChangeNextOwnerCopy"));
	childSetAction("help", onHelpBtn, this);
	childSetAction("apply", onApplyBtn, this);
	childSetAction("close", onCloseBtn, this);
	childSetAction("check_all", onCheckAll, this);
	childSetAction("check_none", onUncheckAll, this);
	childSetCommitCallback("next_owner_copy", &onCommitCopy, this);
}

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


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

	//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.
		llwarns << "No inventory for " << mCurrentObjectID << llendl;
		nextObject();
	}
}

void LLFloaterBulkPermission::onApplyBtn(void* user_data)
{
	LLFloaterBulkPermission* self = static_cast<LLFloaterBulkPermission*>(user_data);
	self->doApply();
}

void LLFloaterBulkPermission::onHelpBtn(void* user_data)
{
	LLNotifications::instance().add("HelpBulkPermission");
}

void LLFloaterBulkPermission::onCloseBtn(void* user_data)
{
	LLFloaterBulkPermission* self = static_cast<LLFloaterBulkPermission*>(user_data);
	self->onClose(false);
}

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

BOOL LLFloaterBulkPermission::start()
{
	// note: number of top-level objects to modify is mObjectIDs.count().
	getChild<LLScrollListCtrl>("queue output")->addCommentText(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.count();
		//llinfos << "Objects left to process = " << count << llendl;
		mCurrentObjectID.setNull();
		if(count > 0)
		{
			successful_start = popNext();
			//llinfos << (successful_start ? "successful" : "unsuccessful") << llendl; 
		}
	} while((mObjectIDs.count() > 0) && !successful_start);

	if(isDone() && !mDone)
	{
		getChild<LLScrollListCtrl>("queue output")->addCommentText(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.count();
	if(mCurrentObjectID.isNull() && (count > 0))
	{
		mCurrentObjectID = mObjectIDs.get(0);
		//llinfos << "mCurrentID: " << mCurrentObjectID << llendl;
		mObjectIDs.remove(0);
		LLViewerObject* obj = gObjectList.findObject(mCurrentObjectID);
		if(obj)
		{
			//llinfos << "requesting inv for " << mCurrentObjectID << llendl;
			LLUUID* id = new LLUUID(mID);
			registerVOInventoryListener(obj,id);
			requestVOInventory();
			rv = TRUE;
		}
		else
		{
			llinfos<<"NULL LLViewerObject" <<llendl;
		}
	}

	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("BulkChangeIncludeLandmarks" , check);
	gSavedSettings.setBOOL("BulkChangeIncludeNotecards" , check);
	gSavedSettings.setBOOL("BulkChangeIncludeObjects"   , check);
	gSavedSettings.setBOOL("BulkChangeIncludeScripts"   , check);
	gSavedSettings.setBOOL("BulkChangeIncludeSounds"    , check);
	gSavedSettings.setBOOL("BulkChangeIncludeTextures"  , check);
}


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

	InventoryObjectList::const_iterator it = inv->begin();
	InventoryObjectList::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_LANDMARK  && gSavedSettings.getBOOL("BulkChangeIncludeLandmarks" )) ||
			( 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_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());
				U32 flags = new_item->getFlags();

				U32 desired_next_owner_perms = LLFloaterPerms::getNextOwnerPerms("BulkChange");
				U32 desired_everyone_perms = LLFloaterPerms::getEveryonePerms("BulkChange");
				U32 desired_group_perms = LLFloaterPerms::getGroupPerms("BulkChange");

				// If next owner permissions have changed (and this is an object)
				// then set the slam permissions flag so that they are applied on rez.
				if((perm.getMaskNextOwner() != desired_next_owner_perms)
				   && (new_item->getType() == LLAssetType::AT_OBJECT))
				{
					flags |= LLInventoryItem::II_FLAGS_OBJECT_SLAM_PERM;
				}
				// If everyone permissions have changed (and this is an object)
				// then set the overwrite everyone permissions flag so they
				// are applied on rez.
				if ((perm.getMaskEveryone() != desired_everyone_perms)
				    && (new_item->getType() == LLAssetType::AT_OBJECT))
				{
					flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
				}
				// If group permissions have changed (and this is an object)
				// then set the overwrite group permissions flag so they
				// are applied on rez.
				if ((perm.getMaskGroup() != desired_group_perms)
				    && (new_item->getType() == LLAssetType::AT_OBJECT))
				{
					flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
				}

				// 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
					)
				{
					perm.setMaskNext(desired_next_owner_perms);
					perm.setMaskEveryone(desired_everyone_perms);
					perm.setMaskGroup(desired_group_perms);
					new_item->setPermissions(perm); // here's the beef
					new_item->setFlags(flags); // and the tofu
					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->addCommentText(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)
{
	LLMemType mt(LLMemType::MTYPE_OBJECT);
	
	// 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());
}