/**
* PODD is an OWL ontology database used for scientific project management
*
* Copyright (C) 2009-2013 The University Of Queensland
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.podd.resources;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import org.openrdf.OpenRDFException;
import org.openrdf.model.Model;
import org.openrdf.model.URI;
import org.openrdf.model.impl.LinkedHashModel;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.Rio;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.ByteArrayRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.Variant;
import org.restlet.resource.Get;
import org.restlet.resource.Post;
import org.restlet.resource.ResourceException;
import org.restlet.security.User;
import com.github.ansell.restletutils.SesameRealmConstants;
import com.github.podd.restlet.PoddAction;
import com.github.podd.restlet.PoddSesameRealm;
import com.github.podd.restlet.PoddWebServiceApplication;
import com.github.podd.restlet.RestletUtils;
import com.github.podd.utils.PODD;
import com.github.podd.utils.PoddUser;
import com.github.podd.utils.PoddUserStatus;
import com.github.podd.utils.PoddWebConstants;
/**
*
* User Edit resource to modify PODD User details, except password changes. Password changes should
* be made using the {@link UserPasswordResourceImpl}.
*
* @author kutila
*
*/
public class UserEditResourceImpl extends AbstractUserResourceImpl
{
/**
* Handle an HTTP POST request submitting RDF data to edit an existing PoddUser.
*/
@Post("rdf|rj|json|ttl")
public Representation editUserRdf(final Representation entity, final Variant variant) throws ResourceException
{
this.log.info("editUserRdf");
final String requestedUserIdentifier = this.getUserParameter();
final PoddAction action =
this.getAction(requestedUserIdentifier, PoddAction.OTHER_USER_EDIT, PoddAction.CURRENT_USER_EDIT);
if(requestedUserIdentifier == null)
{
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Did not specify user to edit");
}
this.log.info("requesting edit user: {}", requestedUserIdentifier);
final User user = this.getRequest().getClientInfo().getUser();
this.log.info("authenticated user: {}", user);
// check authentication first
this.checkAuthentication(action);
final PoddSesameRealm nextRealm = ((PoddWebServiceApplication)this.getApplication()).getRealm();
final PoddUser poddUser = nextRealm.findUser(requestedUserIdentifier);
if(poddUser == null)
{
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "User not found");
}
URI userUri = null;
try
{
// - get input stream with RDF content
final InputStream inputStream = entity.getStream();
final RDFFormat inputFormat =
Rio.getParserFormatForMIMEType(entity.getMediaType().getName(), RDFFormat.RDFXML);
final Model modifiedUserModel = Rio.parse(inputStream, "", inputFormat);
// - create PoddUser with edited details
this.mergeModelWithUser(modifiedUserModel, poddUser);
// modify User record in the Realm
userUri = nextRealm.updateUser(poddUser);
this.log.debug("Updated User <{}>", poddUser);
// - check the User was successfully added to the Realm
final PoddUser findUser = nextRealm.findUser(poddUser.getIdentifier());
if(findUser == null)
{
throw new ResourceException(Status.SERVER_ERROR_INTERNAL, "Failed to add user");
}
}
catch(final IOException e)
{
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "There was a problem with the input", e);
}
catch(final OpenRDFException e)
{
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "There was a problem with the input", e);
}
// - prepare response
final ByteArrayOutputStream output = new ByteArrayOutputStream(8096);
final RDFFormat outputFormat =
Rio.getWriterFormatForMIMEType(variant.getMediaType().getName(), RDFFormat.RDFXML);
try
{
final Model model = new LinkedHashModel();
model.add(userUri, SesameRealmConstants.OAS_USERIDENTIFIER, PODD.VF.createLiteral(poddUser.getIdentifier()));
Rio.write(model, output, outputFormat);
}
catch(final OpenRDFException e)
{
throw new ResourceException(Status.SERVER_ERROR_INTERNAL, "Could not create response");
}
return new ByteArrayRepresentation(output.toByteArray(), MediaType.valueOf(outputFormat.getDefaultMIMEType()));
}
/**
* Handle an HTTP GET request to display the Edit User page in HTML
*
* FIXME: incomplete, initial untested code
*/
@Get
public Representation getUserEditPageHtml(final Representation entity) throws ResourceException
{
this.log.info("editUserHtml");
final String requestedUserIdentifier = this.getUserParameter();
final PoddAction action =
this.getAction(requestedUserIdentifier, PoddAction.OTHER_USER_EDIT, PoddAction.CURRENT_USER_EDIT);
if(requestedUserIdentifier == null)
{
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Did not specify user to edit");
}
this.log.info("requesting edit user: {}", requestedUserIdentifier);
final User user = this.getRequest().getClientInfo().getUser();
this.log.info("authenticated user: {}", user);
// Even though this page only displays user information, since the
// intention is
// to modify user information, the Action is considered as a
// "User Edit".
this.checkAuthentication(action);
final Map<String, Object> dataModel = RestletUtils.getBaseDataModel(this.getRequest());
dataModel.put("contentTemplate", "editUser.html.ftl");
dataModel.put("pageTitle", "Edit PODD User");
dataModel.put("title", "Edit User");
dataModel.put("authenticatedUsername", user.getIdentifier());
final PoddSesameRealm realm = ((PoddWebServiceApplication)this.getApplication()).getRealm();
final PoddUser poddUser = realm.findUser(requestedUserIdentifier);
if(poddUser == null)
{
throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "User not found.");
}
else
{
dataModel.put("requestedUser", poddUser);
final PoddUserStatus[] statuses = PoddUserStatus.values();
dataModel.put("statusList", statuses);
}
return RestletUtils.getHtmlRepresentation(
this.getPoddApplication().getPropertyUtil()
.get(PoddWebConstants.PROPERTY_TEMPLATE_BASE, PoddWebConstants.DEFAULT_TEMPLATE_BASE),
dataModel, MediaType.TEXT_HTML, this.getPoddApplication().getTemplateConfiguration());
}
/**
* Helper method to update the {@link PoddUser} with information in the given {@link Model}.
*
* @param model
* @return
* @throws ResourceException
* if mandatory data is missing.
*/
private void mergeModelWithUser(final Model model, final PoddUser currentUser)
{
// User identifier and email are fixed and cannot be changed
// Password change is not allowed from this User Edit Service. Print a
// warning.
final String password = model.filter(null, SesameRealmConstants.OAS_USERSECRET, null).objectString();
if(password != null)
{
this.log.warn("Attempting to change password via User Edit Service. Disallowed.");
}
final String firstName = model.filter(null, SesameRealmConstants.OAS_USERFIRSTNAME, null).objectString();
if(firstName != null)
{
currentUser.setFirstName(firstName);
}
final String lastName = model.filter(null, SesameRealmConstants.OAS_USERLASTNAME, null).objectString();
if(lastName != null)
{
currentUser.setLastName(lastName);
}
final URI homePage = model.filter(null, PODD.PODD_USER_HOMEPAGE, null).objectURI();
if(homePage != null)
{
currentUser.setHomePage(homePage);
}
final String organization = model.filter(null, PODD.PODD_USER_ORGANIZATION, null).objectString();
if(organization != null)
{
currentUser.setOrganization(organization);
}
final String orcidID = model.filter(null, PODD.PODD_USER_ORCID, null).objectString();
if(orcidID != null)
{
currentUser.setOrcid(orcidID);
}
final String title = model.filter(null, PODD.PODD_USER_TITLE, null).objectString();
if(title != null)
{
currentUser.setTitle(title);
}
final String phone = model.filter(null, PODD.PODD_USER_PHONE, null).objectString();
if(phone != null)
{
currentUser.setPhone(phone);
}
final String address = model.filter(null, PODD.PODD_USER_ADDRESS, null).objectString();
if(address != null)
{
currentUser.setAddress(address);
}
final String position = model.filter(null, PODD.PODD_USER_POSITION, null).objectString();
if(position != null)
{
currentUser.setPosition(position);
}
// TODO: no longer seems such a good idea! Simply updating the Status if
// sent seems better.
PoddUserStatus status = PoddUserStatus.INACTIVE;
final URI statusUri = model.filter(null, PODD.PODD_USER_STATUS, null).objectURI();
if(statusUri != null)
{
status = PoddUserStatus.getUserStatusByUri(statusUri);
}
else
{
this.log.warn("User Status was not sent. Setting the User status to INACTIVE");
}
currentUser.setUserStatus(status);
}
}