if '-' in n or '_' in n:
raise BadRequest('Bad characters in group name')
groups[n] = v.replace(' ', '').split(',')
- if '' in groups[n]:
+ while '' in groups[n]:
groups[n].remove('')
return meta, groups
+++ /dev/null
-#!/usr/bin/env python
-
-import sys
-import os
-import time
-import logging
-import hashlib
-import binascii
-
-from os import makedirs, unlink
-from os.path import isdir, realpath, exists, join
-from binascii import hexlify
-
-from backends.lib.hashfiler.context_file import ContextFile
-
-class HashMap(list):
-
- def __init__(self, blocksize, blockhash):
- super(HashMap, self).__init__()
- self.blocksize = blocksize
- self.blockhash = blockhash
-
- def _hash_raw(self, v):
- h = hashlib.new(self.blockhash)
- h.update(v)
- return h.digest()
-
- def hash(self):
- if len(self) == 0:
- return self._hash_raw('')
- if len(self) == 1:
- return self.__getitem__(0)
-
- h = list(self)
- s = 2
- while s < len(h):
- s = s * 2
- h += [('\x00' * len(h[0]))] * (s - len(h))
- while len(h) > 1:
- h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
- return h[0]
-
-class Mapper(object):
- """Mapper.
- Required contstructor parameters: mappath, namelen.
- """
-
- mappath = None
- namelen = None
-
- def __init__(self, **params):
- self.params = params
- self.namelen = params['namelen']
- mappath = realpath(params['mappath'])
- if not isdir(mappath):
- if not exists(mappath):
- makedirs(mappath)
- else:
- raise ValueError("Variable mappath '%s' is not a directory" % (mappath,))
- self.mappath = mappath
-
- def _get_rear_map(self, maphash, create=0):
- filename = hexlify(maphash)
- dir = join(self.mappath, filename[0:2], filename[2:4], filename[4:6])
- if not exists(dir):
- makedirs(dir)
- name = join(dir, filename)
- return ContextFile(name, create)
-
- def _check_rear_map(self, maphash):
- filename = hexlify(maphash)
- dir = join(self.mappath, filename[0:2], filename[2:4], filename[4:6])
- name = join(dir, filename)
- return exists(name)
-
- def map_retr(self, maphash, blkoff=0, nr=100000000000000):
- """Return as a list, part of the hashes map of an object
- at the given block offset.
- By default, return the whole hashes map.
- """
- namelen = self.namelen
- hashes = ()
-
- with self._get_rear_map(maphash, 0) as rmap:
- if rmap:
- hashes = list(rmap.sync_read_chunks(namelen, nr, blkoff))
- return hashes
-
- def map_stor(self, maphash, hashes=(), blkoff=0, create=1):
- """Store hashes in the given hashes map."""
- namelen = self.namelen
- if self._check_rear_map(maphash):
- return
- with self._get_rear_map(maphash, 1) as rmap:
- rmap.sync_write_chunks(namelen, blkoff, hashes, None)
-
-if __name__ == "__main__":
- map = HashMap(4 * 1024 * 1024, 'sha256')
-
- namelen = len(map.hash())
- params = {'mappath': os.path.join('data/pithos/maps-new'),
- 'namelen': namelen}
- mapper = Mapper(**params)
-
- files = os.listdir('data/pithos/maps')
- for f in files:
- with ContextFile(os.path.join('data/pithos/maps', f), 0) as rmap:
- hashes = list(rmap.sync_read_chunks(namelen, 100000000000000, 0))
- map[:] = hashes
- hash = map.hash()
- mapper.map_stor(hash, hashes)
- print 'update versions set hash=\'%s\' where serial=%s;' % (hexlify(hash), int(f, 16))
-
*/
@DefaultStringValue("")
String version();
+
+ @DefaultStringValue("X-Auth-Token")
+ String authTokenCookie();
+
+ @DefaultStringValue("_shibsession_")
+ String shibSessionCookiePrefix();
}
authCookie=_pithos2_a
cookieSeparator=|
apiPath=/v1/
-version=2.0
\ No newline at end of file
+version=2.0
+authTokenCookie=X-Auth-Token
+shibSessionCookiePrefix=_shibsession_
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2011 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+package gr.grnet.pithos.web.client;
+
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.DialogBox;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HasHorizontalAlignment;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+
+
+/**
+ * A dialog box that displays the user credentials for use in other client
+ * applications.
+ */
+public class CredentialsDialog extends DialogBox {
+
+ private final String WIDTH_FIELD = "35em";
+ private final String WIDTH_TEXT = "42em";
+
+ /**
+ * The widget constructor.
+ */
+ public CredentialsDialog(final Pithos app, final MessagePanel.Images images) {
+ // Set the dialog's caption.
+ setText("User Credentials");
+ setAnimationEnabled(true);
+ // A VerticalPanel that contains the 'about' label and the 'OK' button.
+ VerticalPanel outer = new VerticalPanel();
+ // Create the text and set a style name so we can style it with CSS.
+ HTML text = new HTML("<p>These are the user credentials that are " +
+ "required for interacting with Pithos+");
+ text.setStyleName("pithos-credentialsText");
+ text.setWidth(WIDTH_TEXT);
+ outer.add(text);
+ FlexTable table = new FlexTable();
+ table.setText(0, 0, "Username");
+ table.setText(1, 0, "Token");
+ TextBox username = new TextBox();
+ username.setText(app.getUsername());
+ username.setReadOnly(true);
+ username.setWidth(WIDTH_FIELD);
+ username.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ Pithos.enableIESelection();
+ ((TextBox) event.getSource()).selectAll();
+ Pithos.preventIESelection();
+ }
+
+ });
+ table.setWidget(0, 1, username);
+
+ TextBox tokenBox = new TextBox();
+ tokenBox.setText(app.getToken());
+ tokenBox.setReadOnly(true);
+ tokenBox.setWidth(WIDTH_FIELD);
+ tokenBox.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ Pithos.enableIESelection();
+ ((TextBox) event.getSource()).selectAll();
+ Pithos.preventIESelection();
+ }
+
+ });
+ table.setWidget(1, 1, tokenBox);
+
+ table.getFlexCellFormatter().setStyleName(0, 0, "props-labels");
+ table.getFlexCellFormatter().setStyleName(0, 1, "props-values");
+ table.getFlexCellFormatter().setStyleName(1, 0, "props-labels");
+ table.getFlexCellFormatter().setStyleName(1, 1, "props-values");
+ outer.add(table);
+
+ // Create the 'OK' button, along with a listener that hides the dialog
+ // when the button is clicked.
+ Button confirm = new Button("Close", new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ hide();
+ }
+ });
+ outer.add(confirm);
+ outer.setCellHorizontalAlignment(confirm, HasHorizontalAlignment.ALIGN_CENTER);
+ outer.setSpacing(8);
+ setWidget(outer);
+ }
+
+ @Override
+ protected void onPreviewNativeEvent(NativePreviewEvent preview) {
+ super.onPreviewNativeEvent(preview);
+ NativeEvent evt = preview.getNativeEvent();
+ if (evt.getType().equals("keydown"))
+ // Use the popup's key preview hooks to close the dialog when
+ // either enter or escape is pressed.
+ switch (evt.getKeyCode()) {
+ case KeyCodes.KEY_ENTER:
+ case KeyCodes.KEY_ESCAPE:
+ hide();
+ break;
+ }
+ }
+}
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
*
* @param newImages the image bundle passed on by the parent object
*/
- public FileContextMenu(final Pithos app, Images newImages, TreeView selectedTree, Folder selectedFolder, List<File> selectedFiles) {
+ public FileContextMenu(final Pithos app, Images newImages, TreeView selectedTree, Folder selectedFolder, final List<File> selectedFiles) {
// The popup's constructor's argument is a boolean specifying that it
// auto-close itself when the user clicks outside of it.
super(true);
contextMenu.addItem(new MenuItem("<span>" + AbstractImagePrototype.create(newImages.viewText()).getHTML() + " Properties</span>", true, new PropertiesCommand(app, this, selectedFiles, images, 0)));
}
- if (selectedFiles.size() == 1)
- contextMenu.addItem(new MenuItem("<span><a class='hidden-link' href='" + app.getApiPath() + selectedFiles.get(0).getOwner() + selectedFiles.get(0).getUri() + "?X-Auth-Token=" + app.getToken() + "' target='_blank'>" + AbstractImagePrototype.create(newImages.download()).getHTML() + " Download</a></span>", true, (Command) null));
+ contextMenu.addItem(new MenuItem("<span>" + AbstractImagePrototype.create(newImages.download()).getHTML() + " Download</span>", true, new Command() {
+
+ @Override
+ public void execute() {
+ for (File f : selectedFiles)
+ Window.open(app.getApiPath() + f.getOwner() + f.getUri() + "?X-Auth-Token=" + app.getToken(), "_blank", "");
+ }
+ }));
add(contextMenu);
}
outer.setWidth("100%");
topPanel = new TopPanel(this, Pithos.images);
- topPanel.setWidth("100%");
+ topPanel.setWidth("75%");
outer.add(topPanel);
-
- messagePanel.setWidth("100%");
+ outer.setCellHorizontalAlignment(topPanel, HasHorizontalAlignment.ALIGN_CENTER);
+
+ messagePanel.setWidth("75%");
messagePanel.setVisible(false);
outer.add(messagePanel);
outer.setCellHorizontalAlignment(messagePanel, HasHorizontalAlignment.ALIGN_CENTER);
groupTreeView = new GroupTreeView(groupTreeViewModel);
trees = new VerticalPanel();
+ trees.setWidth("100%");
+ VerticalPanel uploadButtonPanel = new VerticalPanel();
upload = new Button("Upload File", new ClickHandler() {
@Override
public void onClick(@SuppressWarnings("unused") ClickEvent event) {
new UploadFileCommand(Pithos.this, null, getSelection()).execute();
}
});
+ uploadButtonPanel.add(upload);
+ uploadButtonPanel.setWidth("100%");
+ uploadButtonPanel.setHeight("60px");
+ uploadButtonPanel.setCellHorizontalAlignment(upload, HasHorizontalAlignment.ALIGN_CENTER);
+ uploadButtonPanel.setCellVerticalAlignment(upload, HasVerticalAlignment.ALIGN_MIDDLE);
upload.addStyleName("pithos-uploadButton");
- trees.add(upload);
+ trees.add(uploadButtonPanel);
HorizontalPanel treeHeader = new HorizontalPanel();
treeHeader.addStyleName("pithos-treeHeader");
+ treeHeader.setWidth("100%");
+ treeHeader.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
HorizontalPanel statistics = new HorizontalPanel();
statistics.add(new HTML("Total Objects: "));
totalFiles = new HTML();
// Add the left and right panels to the split panel.
splitPanel.setLeftWidget(trees);
splitPanel.setRightWidget(inner);
- splitPanel.setSplitPosition("25%");
+ splitPanel.setSplitPosition("35%");
splitPanel.setSize("100%", "100%");
splitPanel.addStyleName("pithos-splitPanel");
+ splitPanel.setWidth("75%");
outer.add(splitPanel);
+ outer.setCellHorizontalAlignment(splitPanel, HasHorizontalAlignment.ALIGN_CENTER);
statusPanel = new StatusPanel();
+ statusPanel.setWidth("75%");
outer.add(statusPanel);
-
+ outer.setCellHorizontalAlignment(statusPanel, HasHorizontalAlignment.ALIGN_CENTER);
// Hook the window resize event, so that we can adjust the UI.
Window.addResizeHandler(this);
}
});
}
+
+ public void logoff() {
+ Configuration conf = (Configuration) GWT.create(Configuration.class);
+ Cookies.removeCookie(conf.authCookie());
+ Cookies.removeCookie(conf.authTokenCookie(), "/");
+ for (String s: Cookies.getCookieNames())
+ if (s.startsWith(conf.shibSessionCookiePrefix()))
+ Cookies.removeCookie(s, "/");
+ Window.Location.assign(Window.Location.getPath());
+ }
}
import java.util.List;
import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
*
* @param newImages the image bundle passed on by the parent object
*/
- public ToolsMenu(Pithos app, Images newImages, TreeView selectedTree, Folder folder, List<File> files) {
+ public ToolsMenu(final Pithos app, Images newImages, TreeView selectedTree, Folder folder, final List<File> files) {
// The popup's constructor's argument is a boolean specifying that it
// auto-close itself when the user clicks outside of it.
super(true);
if (properties != null)
contextMenu.addItem(properties);
}
- if (files != null && files.size() == 1)
- contextMenu.addItem(new MenuItem("<span><a class='hidden-link' href='" + app.getApiPath() + files.get(0).getOwner() + files.get(0).getUri() + "?X-Auth-Token=" + app.getToken() + "' target='_blank'>" + AbstractImagePrototype.create(newImages.download()).getHTML() + " Download</a></span>", true, (Command) null));
+ if (files != null) {
+ contextMenu.addItem(new MenuItem("<span>" + AbstractImagePrototype.create(newImages.download()).getHTML() + " Download</span>", true, new Command() {
+
+ @Override
+ public void execute() {
+ for (File f: files)
+ Window.open(app.getApiPath() + files.get(0).getOwner() + files.get(0).getUri() + "?X-Auth-Token=" + app.getToken(), "_blank", "");
+ }
+ }));
+ }
}
}
else {
package gr.grnet.pithos.web.client;
import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
*/
public static final boolean DONE = false;
- private Pithos app;
+ Pithos app;
/**
* An image bundle for this widgets images.
@Source("gr/grnet/pithos/resources/pithos2-logo.png")
ImageResource pithosLogo();
+
+ @Source("gr/grnet/pithos/resources/desc.png")
+ ImageResource downArrow();
}
/**
HorizontalPanel outer = new HorizontalPanel();
outer.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
-// outer.setSpacing(2);
outer.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
outer.setStyleName("pithos-topPanel");
MenuBar username = new MenuBar();
username.setStyleName("pithos-usernameMenu");
- MenuItem userItem = new MenuItem(_app.getUsername(), new MenuBar(true));
- userItem.addStyleName("pithos-usernameMenu");
+ username.setAutoOpen(true);
+
+ MenuBar userItemMenu = new MenuBar(true);
+ userItemMenu.addItem(new MenuItem("API token", new Command() {
+
+ @Override
+ public void execute() {
+ new CredentialsDialog(app, images).center();
+ }
+ }));
+ userItemMenu.addItem(new MenuItem("Log off", new Command() {
+
+ @Override
+ public void execute() {
+ app.logoff();
+ }
+ }));
+
+ SafeHtmlBuilder sb = new SafeHtmlBuilder();
+ sb.append(SafeHtmlUtils.fromSafeConstant(_app.getUsername()));
+ sb.appendHtmlConstant(AbstractImagePrototype.create(images.downArrow()).getHTML());
+ MenuItem userItem = new MenuItem(_app.getUsername(), userItemMenu);
username.addItem(userItem);
outer.add(username);
outer.setCellHorizontalAlignment(username, HasHorizontalAlignment.ALIGN_RIGHT);
-
+ Image downArrow = AbstractImagePrototype.create(images.downArrow()).createImage();
+ outer.add(downArrow);
+ outer.setCellHorizontalAlignment(downArrow, HasHorizontalAlignment.ALIGN_LEFT);
initWidget(outer);
}
}
JSONArray o = array.get(i).isArray();
if (o != null) {
int num = (int) o.get(0).isNumber().doubleValue();
- Date date = new Date((long) o.get(1).isNumber().doubleValue());
+ Date date = new Date((long) (o.get(1).isNumber().doubleValue() * 1000)); //Convert to millis
Version v = new Version(num, date);
versions.add(v);
}
font-family: "Lucida Grande","Lucida Sans Unicode",Arial,Verdana,sans-serif;
margin: 8px;
margin-top: 3px;
+ background: url(images/background.png) repeat-x;
}
a {
.pithos-topPanel {
background-color: #4085a5;
font-size: 80%;
+ height: 60px;
}
.pithos-usernameMenu {
font-size: 90%;
vertical-align: middle;
font-weight: normal;
+ height:145px;
}
.pithos-List {
padding: 6px;
}
+.pithos-treeHeader {
+ background-color: #74aec9;
+}
+
.pithos-splitPanel {
- border: 1px solid white;
- background: url(images/background.png) repeat-x;
+ background: url(images/white50.png);
}
/* Use the background color for the splitter. */
.gwt-HorizontalSplitPanel .hsplitter {
cursor: move;
border: 0px;
- background: #bec8e6;
+ background: url(images/background.png) repeat-x;
}
.pithos-tag {
font-size: 120%;
text-align: center;
height: 40px;
- width: 100%;
+ width: 300px;
}
.pithos-rightSide {
text-align: center;
color: white;
}
+
+.pithos-credentialsText {
+ width: 24em;
+ /* Restore the padding we remove when overriding the gwt-DialogBox style */
+ padding: 3px;
+}