int albumCounter = 0;
for (Album album : albums) {
String albumPrefix = sonePrefix + "/Albums/" + albumCounter++;
- configuration.getStringValue(albumPrefix + "/ID").setValue(album.getId());
+ configuration.getStringValue(albumPrefix + "/ID").setValue(album.getInternalId());
configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle());
configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription());
configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent().equals(sone.getRootAlbum()) ? null : album.getParent().getId());
}
String imagePrefix = sonePrefix + "/Images/" + imageCounter++;
configuration.getStringValue(imagePrefix + "/ID").setValue(image.getId());
- configuration.getStringValue(imagePrefix + "/Album").setValue(album.getId());
+ configuration.getStringValue(imagePrefix + "/Album").setValue(album.getInternalId());
configuration.getStringValue(imagePrefix + "/Key").setValue(image.getKey());
configuration.getStringValue(imagePrefix + "/Title").setValue(image.getTitle());
configuration.getStringValue(imagePrefix + "/Description").setValue(image.getDescription());
* @return The ID of this album
*/
String getId();
+ String getInternalId();
/**
* Returns the Sone this album belongs to.
--- /dev/null
+package net.pterodactylus.sone.data;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+
+import com.google.common.base.Charsets;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+
+/**
+ * Builds (practically) unique IDs by combining Sone and element IDs.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+@ThreadSafe
+public class IdBuilder {
+
+ private static final HashFunction HASH_FUNCTION = Hashing.sha256();
+ public static final int ID_STRING_LENGTH = HASH_FUNCTION.bits() / 4;
+
+ private final HashFunction sha256 = HASH_FUNCTION;
+
+ @Nonnull
+ public String buildId(@Nonnull String soneId, @Nonnull String id) {
+ return sha256.newHasher()
+ .putBytes(soneId.getBytes(Charsets.UTF_8))
+ .putBytes(id.getBytes(Charsets.UTF_8))
+ .hash().toString();
+ }
+
+}
import java.util.UUID;
import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.IdBuilder;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Sone;
*/
public class AlbumImpl implements Album {
+ private final IdBuilder idBuilder = new IdBuilder();
+
/** The ID of this album. */
private final String id;
@Override
public String getId() {
+ return idBuilder.buildId(sone.getId(), id);
+ }
+
+ @Override
+ public String getInternalId() {
return id;
}
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty;
+import net.pterodactylus.sone.data.IdBuilder;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.text.TextFilter;
import net.pterodactylus.sone.web.page.FreenetRequest;
}
String description = request.getHttpRequest().getPartAsStringFailsafe("description", 256).trim();
Sone currentSone = getCurrentSone(request.getToadletContext());
- String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
+ String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", IdBuilder.ID_STRING_LENGTH);
Album parent = webInterface.getCore().getAlbum(parentId);
if (parentId.equals("")) {
parent = currentSone.getRootAlbum();
package net.pterodactylus.sone.web;
import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.IdBuilder;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
- String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
+ String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", IdBuilder.ID_STRING_LENGTH);
Album album = webInterface.getCore().getAlbum(albumId);
if (album == null) {
throw new RedirectException("invalid.html");
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty;
+import net.pterodactylus.sone.data.IdBuilder;
import net.pterodactylus.sone.text.TextFilter;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.template.Template;
protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
- String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
+ String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", IdBuilder.ID_STRING_LENGTH);
Album album = webInterface.getCore().getAlbum(albumId);
if (album == null) {
throw new RedirectException("invalid.html");
import javax.imageio.stream.ImageInputStream;
import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.IdBuilder;
import net.pterodactylus.sone.data.Image.Modifier.ImageTitleMustNotBeEmpty;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.data.TemporaryImage;
super.processTemplate(request, templateContext);
if (request.getMethod() == Method.POST) {
Sone currentSone = getCurrentSone(request.getToadletContext());
- String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
+ String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", IdBuilder.ID_STRING_LENGTH);
Album parent = webInterface.getCore().getAlbum(parentId);
if (parent == null) {
throw new RedirectException("noPermission.html");
<albums>
<%/first>
<album>
- <id><%album.id|xml></id>
+ <id><%album.internalId|xml></id>
<%if !album.parent.root>
- <parent><%album.parent.id|xml></parent>
+ <parent><%album.parent.internalId|xml></parent>
<%/if>
<title><%album.title|xml></title>
<description><%album.description|xml></description>
--- /dev/null
+package net.pterodactylus.sone.data;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link IdBuilderTest}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class IdBuilderTest {
+
+ private static final String SONE_ID = "~Yp72VX0c6FLDvgIzip5wIvaGIIrjKcKvnX~pTaMKXs";
+ private static final String ELEMENT_ID = "88CC70AE-E853-4EEE-B245-E4C55F40DDDF";
+ private static final String EXPECTED_ID = "139a629a13f6a2c4191fb19ecead7e57335ea3deb2a971b88d5e004378c4daad";
+
+ private final IdBuilder idBuilder = new IdBuilder();
+
+ @Test
+ public void idBuilderBuildsCorrectIds() {
+ assertThat(idBuilder.buildId(SONE_ID, ELEMENT_ID), is(EXPECTED_ID));
+ }
+
+}
@Test
public void testBasicAlbumFunctionality() {
- Album newAlbum = new AlbumImpl(mock(Sone.class));
+ Album newAlbum = new AlbumImpl(when(mock(Sone.class).getId()).thenReturn(SONE_ID).<Sone>getMock());
assertThat(memoryDatabase.getAlbum(newAlbum.getId()), is(Optional.<Album>absent()));
memoryDatabase.storeAlbum(newAlbum);
assertThat(memoryDatabase.getAlbum(newAlbum.getId()), is(of(newAlbum)));