./sql/photos.sql
[code]CREATE TABLE album(
 albumid INT(6) PRIMARY KEY AUTO_INCREMENT,
 title VARCHAR(60) NULL,
 volgorde INT(6) NULL 
)ENGINE=innoDB;

CREATE TABLE photos(
 photoid INT(6) PRIMARY KEY AUTO_INCREMENT,
 title VARCHAR(60) NULL,
 volgorde INT(6) NULL,
 photo VARCHAR(100) NOT NULL,
 albumid INT(6) NULL,
 CONSTRAINT fk_album_albumid FOREIGN KEY (albumid) REFERENCES album (albumid),
 CONSTRAINT ak_photos_photo UNIQUE (photo)
)ENGINE=innoDB;[/code]

./organize.php
[code]
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>

	<head>
		<title>Organize Photos</title>
		<link rel="stylesheet" href="./css/organize.css" type="text/css" media="screen" />
 		<link rel="stylesheet" href="./css/uploadbox.css" type="text/css" media="screen" />
 		<link rel="stylesheet" href="./css/loginbox.css" type="text/css" media="screen" />
		<script src="./js/prototype.js" type="text/javascript"></script>
 		<script src="./js/scriptaculous.js" type="text/javascript"></script>
 		<script src="./js/organize.js" type="text/javascript"></script>
 		<script src="./js/uploadbox.js" type="text/javascript"></script>
 		<script src="./js/loginbox.js" type="text/javascript"></script>
	</head>
	
	<body>


<div id="delete" style="display: none;" class="top"><img alt="recycle" src="./images/recycle.png">Drop items here to delete them<img alt="recycle" src="./images/recycle.png"></div>
<div id="options" class="top">N3RD's Photo Organizer</div>

<div id="menu">
	<a id="addalbum" href="#">New Photoset</a>
	<ul id="photosets"></ul>	
</div>

<div id="all">
	<a id="upload" href="#">Upload</a>
	<ul id="listall"></ul>
</div>

<div id="footer">Copyright 2007 - Boaz den Besten</div>
	
<div id="container"><ul id="mylist"></ul></div>

<div class="center" id="systemWorking"><img src="./images/loading.gif" alt="loading"></div>
<div class="center" id="dropzone" style="display: none;">Drop here to add to the set</div>

<iframe id="uploader" name="uploader" style="display:none;"></iframe>


	</body>

</html>
[/code]

./photoset.php

[code]
<?php

try{
	
	require_once 'settings.php';
	
	if(isset($_GET['albumid']) && is_numeric($_GET['albumid'])){
		$sAlbum = 'albumid='.$_GET['albumid'];
	}else{
		$sAlbum = 'albumid IS NULL';
	}
	
	$oDOM = new DOMDocument();
	$oPDO = new PDO('mysql:host='.$aSetting['dbHost'].';dbname='.$aSetting['dbDatabase'], $aSetting['dbUsername'], $aSetting['dbPassword'], array(PDO::ATTR_PERSISTENT => true));
	$oPDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	
	function addNode($p_oParentNode, $p_sName, $p_sValue, $oDOM){
		$oNode = $oDOM->createElement($p_sName);
		$oNode->appendChild($oDOM->createTextNode($p_sValue));
		$p_oParentNode->appendChild($oNode);
	}
	
	$oDOMRoot = $oDOM->createElement('photos');
	
	if(isset($_GET['albumid']) && is_numeric($_GET['albumid'])){
		$oPDOStatement = $oPDO->prepare("SELECT albumid, title FROM album WHERE ".$sAlbum.";");
		$oPDOStatement->execute();
		$aRow = $oPDOStatement->fetch();
		addNode($oDOMRoot, 'albumid', $aRow['albumid'], $oDOM);
		addNode($oDOMRoot, 'title', $aRow['title'], $oDOM);
		
	}else{
		$sAlbum = 'albumid IS NULL';
		addNode($oDOMRoot, 'albumid', null, $oDOM);
		addNode($oDOMRoot, 'title', 'no album', $oDOM);
	}
	
	foreach($oPDO->query("SELECT photoid, title, photo FROM photos WHERE ".$sAlbum." ORDER BY volgorde ASC, photoid ASC;", PDO::FETCH_ASSOC) as $aRow){
		$oDOMPhoto = $oDOM->createElement('photo');
		addNode($oDOMPhoto, 'photoid', $aRow['photoid'], $oDOM);
		addNode($oDOMPhoto, 'title', $aRow['title'], $oDOM);
		addNode($oDOMPhoto, 'filename', $aRow['photo'], $oDOM);
		$oDOMRoot->appendChild($oDOMPhoto);
	}
	
	header('Content-type: application/xml');
	echo $oDOM->saveXML($oDOMRoot);

}catch(PDOException $e){
	echo 'Database error: '.$e->getCode();
}catch(Exception $e){
	echo 'Unkown error: '.$e->getCode();
}

?>
[/code]

./photoset_save.php

[code]
<?php

try{
	
	session_start();
	require_once 'settings.php';
	
	if(!checkLogin()){
		die('User not logged in.');
	}
	
	$oDb = new PDO('mysql:host='.$aSetting['dbHost'].';dbname='.$aSetting['dbDatabase'], $aSetting['dbUsername'], $aSetting['dbPassword']);
	
	if($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_POST['mylist']) && !empty($_POST['albumid'])){
		
		echo '<pre>';
		print_r($_POST);
		echo '</pre>';
		
		$oQuery = $oDb->prepare("UPDATE photos SET volgorde = :volgorde, albumid = :albumid WHERE photoid = :photoid");
			
		foreach($_POST['mylist'] as $iKey => $iValue){
			$oQuery->bindParam(':volgorde', $iKey, PDO::PARAM_INT);
			$oQuery->bindParam(':photoid', $iValue, PDO::PARAM_INT);
			$oQuery->bindParam(':albumid', $_POST['albumid'], PDO::PARAM_INT);
			$oQuery->execute();
		}
			
	}elseif($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_POST['listall'])){
		echo '<pre>';
		print_r($_POST);
		echo '</pre>';
		
		$oQuery = $oDb->prepare("UPDATE photos SET volgorde = NULL, albumid = NULL WHERE photoid = :photoid");
			
		foreach($_POST['listall'] as $iPhotoId){
			$oQuery->bindParam(':photoid', $iPhotoId, PDO::PARAM_INT);
			$oQuery->execute();
		}
	}elseif($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_POST['delete'])){
		echo '<pre>';
		print_r($_POST);
		echo '</pre>';
		
		$oQuery = $oDb->prepare("SELECT photo FROM photos WHERE photoid = :photoid");
		$oQuery->bindParam(':photoid', $_POST['delete'], PDO::PARAM_INT);
		$oQuery->execute();
		$aRow = $oQuery->fetch();
		
		$aLocations = array(
		'album/normal/',
		'album/small/',
		'album/thumb/'
		);
		
		foreach($aLocations as $sLocation){
			unlink($sLocation.$aRow['photo']);
		}
		
		$oQuery = $oDb->prepare("DELETE FROM photos WHERE photoid = :photoid");
			
		$oQuery->bindParam(':photoid', $_POST['delete'], PDO::PARAM_INT);
		$oQuery->execute();
	
	}
	
}catch(PDOException $e){
	echo 'Database error: '.$e->getCode();
}catch(Exception $e){
	echo 'Unkown error: '.$e->getCode();
}

?>
[/code]

./login.php

[code]
<?php

session_start();
require_once 'settings.php';

if($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_POST['username']) && !empty($_POST['password'])){
	
	$bFlag = false;
	foreach($aUsers as $aUser){
		if($aUser['username'] == $_POST['username'] && $aUser['password'] == $_POST['password']){
			$bFlag = true;
		}
	}
	if($bFlag){
		$_SESSION['login']['username'] = $_POST['username'];
		$_SESSION['login']['password'] = $_POST['password'];
		$_SESSION['login']['ip'] = $_SERVER['REMOTE_ADDR'];
		echo 'ok';
	}else{
		$_SESSION['login'] = null;
		echo 'fail';
	}
	
}else{
	echo checkLogin() ? 'ok' : 'fail';
}

?>
[/code]

./js/organize.js

[code]
// The application must know if a photoset is loaded:
var bPhotoSetLoaded = false;

// Need for the drag 'n scroll effect
Position.includeScrollOffsets = true;

// thnx ppk
function getNodeValue(obj, tag){
	return obj.getElementsByTagName(tag)[0].firstChild.nodeValue;
}

// -- Loading image

var myGlobalHandlers = {
	onCreate: function(){
		Element.show('systemWorking');
	},

	onComplete: function() {
		Element.hide('systemWorking');
	}
};
Ajax.Responders.register(myGlobalHandlers);

// -- End loading image

// -- Handle a drag event

Draggables.addObserver({
	onStart: function(){
		$('options').hide();
		$('delete').show();
		
		$('dropzone').show();
	},
	onEnd: function(){
		$('options').show();
		$('delete').hide();
		
		$('dropzone').hide();
	}
});

// -- Stop handeling a drag event

// PhotoSet laden
function loadPhotoSet(PhotoSetId){
	//Eerst oude photos verwijderen:
	var descendants = $('mylist').immediateDescendants();
	for(var i = 0; i < descendants.size(); i++){
		descendants[i].remove();
		
	}
	
	new Ajax.Request('photoset.php', {
		method: 'get',
		parameters: 'albumid='+PhotoSetId,
		onComplete: function(transport){
			
			var root = transport.responseXML.documentElement;
			var photos = root.getElementsByTagName('photo');
			var photoid = null;
			var filename = null;
			
			var item = null; 
			for(var i = 0; i < photos.length; i++)
			{
				photoid = getNodeValue(photos.item(i), 'photoid');

				filename = getNodeValue(photos.item(i), 'filename');

				item = Builder.node('li', {id: 'option_'+photoid}, [
					Builder.node('img', {src: 'album/small/'+filename, alt: 'album/small/'+filename})
				]);
					
				$('mylist').appendChild(item);
				
			}
			
			Sortable.create('mylist', {tag: 'li', scroll: window, constraint: false, containment: ['listall', 'mylist'], onUpdate: function() {
					new Ajax.Request('photoset_save.php', {
						method : 'post',
						parameters : Sortable.serialize('mylist')+'&albumid='+PhotoSetId,
						onComplete: function(){
							var img = $$('#photoset_'+PhotoSetId+' a img');
							//alert(img[0].src);
							var newImg = $$('#mylist li img');
							//alert(newImg[0].src);
							img[0].src = newImg[0].src;
						}
					});
				}
			});
		}
	});
	// -- scroll to top and show me the photos
	new Effect.ScrollTo(window);
	// -- let the app know a photoset is loaded
	bPhotoSetLoaded = true;
}

// Alles laden:
function loadAll(){
	//Eerst oude verwijderen:
	var descendants = $('listall').immediateDescendants();
	for(var i = 0; i < descendants.size(); i++){
		descendants[i].remove();
	}
	
	new Ajax.Request('photoset.php', {
		method: 'get',
		onComplete: function(transport){
			
			var root = transport.responseXML.documentElement;
			var photos = root.getElementsByTagName('photo');
			var photoid = null;
			var filename = null;
			
			var item = null; 
			for(var i = 0; i < photos.length; i++)
			{
				photoid = getNodeValue(photos.item(i), 'photoid');

				filename = getNodeValue(photos.item(i), 'filename');

				item = Builder.node('li', {id: 'all_'+photoid}, [
					Builder.node('img', {src: 'album/small/'+filename, alt: 'album/small/'+filename})
				]);
					
				$('listall').appendChild(item);			  			
			}
			
			Sortable.create('listall', {tag: 'li', scroll: window, constraint: false, containment: ['mylist', 'listall'], onUpdate: function() {
					new Ajax.Request('photoset_save.php', {
						method : 'post',
						parameters : Sortable.serialize('listall')
					});
				}
			});
		}
	});
	
}

// PhotoSets laden:
function loadPhotoSets(){
	//Eerst oude verwijderen:
	var descendants = $('photosets').immediateDescendants();
	for(var i = 0; i < descendants.size(); i++){
		descendants[i].remove();
	}
	
	new Ajax.Request('photosets.php', {
		method: 'get',
		onComplete: function(transport){
			
			var root = transport.responseXML.documentElement;
			var photosets = root.getElementsByTagName('photoset');
			var albumid = null;
			var title = null;
			var photo = null;
			
			sets = $('photosets');
			var item = null;
			for(var i = 0; i < photosets.length; i++)
			{
				albumid = getNodeValue(photosets.item(i), 'albumid');
				title = getNodeValue(photosets.item(i), 'title');
				photo = getNodeValue(photosets.item(i), 'photo');

				item = Builder.node('li', {id: 'photoset_'+albumid});
				
				var albumlink = Builder.node('a', {href: 'javascript: loadPhotoSet('+albumid+');', 'class': 'album'}, [
					Builder.node('img', {src: 'album/small/'+photo, alt: title, 'title': title}),
					Builder.node('br'),
					Builder.node('small', title)
				]);
				
				item.appendChild(albumlink);
				
				sets.appendChild(item);
				
			}
			
			
			$('menu').appendChild(sets);
			
			Sortable.create(sets, {tag: 'li', scroll: window, constraint: false, onUpdate: function(){
					new Ajax.Request('photosets_save.php', {
						method : 'post',
						parameters : Sortable.serialize('photosets')
					});
				}
			});
		}
	}); 			
}

// -- Create a new photoset
function newPhotoSet(event){
	// -- Een ajax request om het album te maken en vervolgens het last_instert_id terug te krijgen.
	var title = prompt('Wat is de titel van het nieuwe album?');
	if(title && title != null && title != 'null'){
		new Ajax.Request('photosets_save.php', {
			method: 'post',
			parameters: 'title='+title,
			onComplete: function(transport){
				var albumid = transport.responseText;
				loadPhotoSet(albumid);
				loadPhotoSets();
			}
		});
	}
	Event.stop(event);
}

Event.observe(window, 'load', function() {
	// -- Load all data
	loadPhotoSets();
	//loadPhotoSet(1);
	loadAll();
	// -- Set a new PhotoSet button:
	Event.observe('addalbum', 'click', newPhotoSet);
	
	// -- Make a dropzone for photos (new albums as special)
	Droppables.add('dropzone', {
		containment: 'listall', 
		onDrop: function(element){
			if(!bPhotoSetLoaded){
				alert('Select a photoset first.');
			}else{
				element.remove();
				$('mylist').appendChild(element);
			}
		}
	});
	
	// -- Make a allphoto dropzone:
	Droppables.add('all', {
		containment: 'mylist', 
		onDrop: function(element){
			element.remove();
			$('listall').appendChild(element);
		}
	});
	
	// -- Make a dropzone to delete photos AND photosets
	Droppables.add('delete', {
		containment: ['listall', 'mylist', 'photosets'],
		onDrop: function(element){
			var aPhotoInfo = element.id.split('_');
			var iPhotoId = aPhotoInfo[1];
			
			// Album verwijderen:				
			if(aPhotoInfo[0] == 'photoset' && confirm('Are you sure you want to delete this photoset?')){// Als het een photoset is die verwijderen:
				new Ajax.Request('photosets_save.php', {
					method: 'post',
					parameters: 'delete='+iPhotoId
				});
				// Geselecteerde photos verwijderen:
				var descendants = $('mylist').immediateDescendants();
				for(var i = 0; i < descendants.size(); i++){
					descendants[i].remove();
		
				}
				// Boolean zetten dat er geen album geselecteerd is:
				bPhotoSetLoaded = false;
				
				// Album verwijderen:
				Element.remove(element);
				
				// Alle photos opnieuw laden:
				loadAll();
			}
			
			// Photo verwijderen:
			if(aPhotoInfo[0] != 'photoset' && confirm('Are you sure you want to permanatly remove this photo?')){ // Anders is het een photo, deze verwijderen
				new Ajax.Request('photoset_save.php', {
					method: 'post',
					parameters: 'delete='+iPhotoId
				});
				Element.remove(element);
			}
		}
	});
});
[/code]