view flys-client/src/main/java/de/intevation/flys/client/client/ui/ProjectList.java @ 4559:7f86ab48284c

Add a small close and add project button to the project list header
author Björn Ricks <bjoern.ricks@intevation.de>
date Fri, 16 Nov 2012 14:26:01 +0100
parents 03de5c424f95
children c1c4c95ae9e4
line wrap: on
line source
package de.intevation.flys.client.client.ui;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;

import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.Autofit;
import com.smartgwt.client.types.ListGridEditEvent;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.types.SortArrow;
import com.smartgwt.client.types.SortDirection;
import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.util.BooleanCallback;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.HoverCustomizer;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.CellClickEvent;
import com.smartgwt.client.widgets.grid.events.CellClickHandler;
import com.smartgwt.client.widgets.grid.events.CellDoubleClickEvent;
import com.smartgwt.client.widgets.grid.events.CellDoubleClickHandler;
import com.smartgwt.client.widgets.grid.events.EditCompleteEvent;
import com.smartgwt.client.widgets.grid.events.EditCompleteHandler;
import com.smartgwt.client.widgets.grid.events.HeaderDoubleClickEvent;
import com.smartgwt.client.widgets.grid.events.HeaderDoubleClickHandler;
import com.smartgwt.client.widgets.grid.events.RowContextClickEvent;
import com.smartgwt.client.widgets.grid.events.RowContextClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.menu.Menu;
import com.smartgwt.client.widgets.menu.MenuItem;
import com.smartgwt.client.widgets.menu.MenuItemSeparator;
import com.smartgwt.client.widgets.menu.events.ClickHandler;
import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent;

import de.intevation.flys.client.client.Config;
import de.intevation.flys.client.client.FLYS;
import de.intevation.flys.client.client.FLYSConstants;
import de.intevation.flys.client.client.event.CollectionChangeEvent;
import de.intevation.flys.client.client.event.CollectionChangeHandler;
import de.intevation.flys.client.client.event.FilterHandler;
import de.intevation.flys.client.client.event.RangeFilterEvent;
import de.intevation.flys.client.client.event.StringFilterEvent;
import de.intevation.flys.client.client.services.AddArtifactService;
import de.intevation.flys.client.client.services.AddArtifactServiceAsync;
import de.intevation.flys.client.client.services.ArtifactService;
import de.intevation.flys.client.client.services.ArtifactServiceAsync;
import de.intevation.flys.client.client.services.CreateCollectionService;
import de.intevation.flys.client.client.services.CreateCollectionServiceAsync;
import de.intevation.flys.client.client.services.DeleteCollectionService;
import de.intevation.flys.client.client.services.DeleteCollectionServiceAsync;
import de.intevation.flys.client.client.services.DescribeCollectionService;
import de.intevation.flys.client.client.services.DescribeCollectionServiceAsync;
import de.intevation.flys.client.client.services.GetArtifactService;
import de.intevation.flys.client.client.services.GetArtifactServiceAsync;
import de.intevation.flys.client.client.services.SetCollectionNameService;
import de.intevation.flys.client.client.services.SetCollectionNameServiceAsync;
import de.intevation.flys.client.client.services.SetCollectionTTLService;
import de.intevation.flys.client.client.services.SetCollectionTTLServiceAsync;
import de.intevation.flys.client.client.services.UserCollectionsService;
import de.intevation.flys.client.client.services.UserCollectionsServiceAsync;
import de.intevation.flys.client.shared.model.Artifact;
import de.intevation.flys.client.shared.model.Collection;
import de.intevation.flys.client.shared.model.CollectionItem;
import de.intevation.flys.client.shared.model.CollectionRecord;
import de.intevation.flys.client.shared.model.Recommendation;
import de.intevation.flys.client.shared.model.User;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;


/**
 * The project list shows a list of projects of a specific user.
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class ProjectList
extends      VLayout
implements   CollectionChangeHandler, EditCompleteHandler, FilterHandler
{
    /** Interval to refresh the user's projects.*/
    public static final int UPDATE_INTERVAL = 30000;

    /** Min Interval to refresh the user's projects.*/
    public static final int MIN_UPDATE_INTERVAL = 5000;

    /** The initial width of this panel.*/
    public static final int MIN_WIDTH = 300;

    /** The max length for new project names.*/
    public static final int MAX_NAME_LENGTH = 50;

    public static final String COLUMN_DATE_WIDTH = "100px";

    public static final String COLUMN_TITLE_WIDTH = "*";

    public static final String COLUMN_FAVORITE_WIDTH = "75px";

    /** The interface that provides i18n messages. */
    private final FLYSConstants messages = GWT.create(FLYSConstants.class);

    /** The UserService used to retrieve information about the current user. */
    protected UserCollectionsServiceAsync userCollectionsService =
        GWT.create(UserCollectionsService.class);

    /** The service used to set the name of a project.*/
    protected SetCollectionNameServiceAsync nameService =
        GWT.create(SetCollectionNameService.class);

    /** The service used to set the name of a project.*/
    protected SetCollectionTTLServiceAsync ttlService =
        GWT.create(SetCollectionTTLService.class);

    /** The service used to set the name of a project.*/
    protected DeleteCollectionServiceAsync deleteService =
        GWT.create(DeleteCollectionService.class);

    /** The DescribeCollectionService used to update the existing collection. */
    protected DescribeCollectionServiceAsync describeCollectionService =
        GWT.create(DescribeCollectionService.class);

    /** The ArtifactService used to communicate with the Artifact server. */
    protected ArtifactServiceAsync createArtifactService =
        GWT.create(ArtifactService.class);

    /** The ArtifactService used to communicate with the Artifact server. */
    protected CreateCollectionServiceAsync createCollectionService =
        GWT.create(CreateCollectionService.class);

    /** The AddArtifactService used to add an artifact to a collection. */
    protected AddArtifactServiceAsync addArtifactService =
        GWT.create(AddArtifactService.class);

    /** The GetArtifactService used to open an existing collection. */
    protected GetArtifactServiceAsync getArtifactService =
        GWT.create(GetArtifactService.class);

    /** A pointer to the FLYS instance.*/
    protected FLYS flys;

    /** The user whose projects should be displayed.*/
    protected User user;

    /** The grid that contains the project rows.*/
    protected ListGrid grid;

    /** All user collections.*/
    protected List<Collection> collections;

    /** The collections visible in the grid.*/
    protected List<Collection> filteredCollections;

    /** The collection to clone*/
    protected Collection cloneCollection;

    /** The message class that provides i18n strings.*/
    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);


    /**
     * The default constructor that creates a new ProjectList for a specific
     * user.
     *
     * @param user The user.
     */
    public ProjectList(FLYS flys, User user) {
        super();
        this.flys = flys;
        this.user = user;

        filteredCollections = new ArrayList<Collection>();
        collections = new ArrayList<Collection>();
        grid = new ListGrid();
        initGrid();
        init();
        initTimer();

        grid.addEditCompleteHandler(this);

        updateUserCollections();
    }


    protected void initGrid() {
        grid.setWidth100();
        grid.setAutoFitData(Autofit.VERTICAL);
        grid.setAutoFitMaxWidth(500);
        grid.setEmptyMessage(messages.no_projects());
        grid.setLoadingDataMessage(messages.load_projects());
        grid.setCanEdit(false);
        grid.setEditEvent(ListGridEditEvent.NONE);
        grid.setShowHeaderContextMenu(false);
        grid.setShowSortArrow(SortArrow.NONE);
        grid.setSortDirection(SortDirection.DESCENDING);
        grid.setSortField(0);
        grid.setSelectionType(SelectionStyle.SINGLE);
        grid.setCanReorderFields(false);
        grid.setLeaveScrollbarGap(false);
        grid.setBorder("0px");

        ListGridField date = buildDateField();
        ListGridField name = buildNameField();
        ListGridField fav  = buildFavoriteField();

        grid.setFields(date, name, fav);

        grid.addHeaderDoubleClickHandler(new HeaderDoubleClickHandler() {
            @Override
            public void onHeaderDoubleClick(HeaderDoubleClickEvent event) {
                // Cancel the event.
                return;
            }
        });

        // Add a handler to set / unset the favorite state of a project.
        grid.addCellClickHandler(new CellClickHandler() {
            @Override
            public void onCellClick(CellClickEvent event) {
                if (event.getColNum() != 2) {
                    return;
                }

                CollectionRecord r = (CollectionRecord) event.getRecord();
                Collection       c = r.getCollection();

                c.setTTL(c.getTTL() == 0 ? -1 : 0);
                updateCollectionTTL(c);
            }
        });

        // Add a handler to open a project.
        grid.addCellDoubleClickHandler(new CellDoubleClickHandler() {
            @Override
            public void onCellDoubleClick(CellDoubleClickEvent e) {
                CollectionRecord record = (CollectionRecord) e.getRecord();
                String uuid = record != null
                    ? record.getCollection().identifier()
                    : "";
                getFlys().openProject(uuid);
            }
        });

        // Add a handler to open a context menu.
        grid.addRowContextClickHandler(new RowContextClickHandler() {
            @Override
            public void onRowContextClick(RowContextClickEvent event) {
                CollectionRecord record = (CollectionRecord) event.getRecord();

                Menu menu = createContextMenu(record);
                grid.setContextMenu(menu);
                menu.showContextMenu();

                event.cancel();
            }
        });
    }


    /**
     * Initializes a repeating timer that updates the user's collections. The
     * interval is specified by the constant <i>UPDATE_INTERVAL</i>.
     */
    protected void initTimer() {
        Config config   = Config.getInstance();
        int    interval = config.getProjectListUpdateInterval();

        interval = interval > MIN_UPDATE_INTERVAL ? interval : UPDATE_INTERVAL;

        GWT.log("Update project list every " + interval + " milliseconds.");

        Timer t = new Timer() {
            @Override
            public void run() {
                updateUserCollections();
            }
        };

        t.scheduleRepeating(interval);
    }


    public FLYS getFlys() {
        return flys;
    }


    /**
     * Creates a new context menu that interacts with a CollectionRecord.
     *
     * @param record The selected record.
     *
     * @return the context menu with operations that interact with
     * <i>record</i>.
     */
    protected Menu createContextMenu(final CollectionRecord record) {
        Menu menu = new Menu();

        MenuItem open = new MenuItem(messages.open_project());
        open.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(MenuItemClickEvent evt) {
                getFlys().openProject(record.getCollection().identifier());
            }
        });

        MenuItem del = new MenuItem(messages.delete_project());
        del.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(MenuItemClickEvent evt) {
                SC.ask(messages.really_delete(), new BooleanCallback() {
                    @Override
                    public void execute(Boolean value) {
                        if (value) {
                            deleteCollection(record.getCollection());
                        }
                    }
                });
            }
        });

        MenuItem rename = new MenuItem(messages.rename_project());
        rename.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(MenuItemClickEvent evt) {
                int row = grid.getRecordIndex(record);
                grid.startEditing(row, 1, false);
            }
        });

        MenuItem clone = new MenuItem(messages.clone_project());
        clone.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(MenuItemClickEvent evt) {
                cloneProject(record.getCollection());
            }
        });

        menu.addItem(open);
        menu.addItem(rename);
        menu.addItem(clone);
        menu.addItem(new MenuItemSeparator());
        menu.addItem(del);

        return menu;
    }


    /**
     * The init() method handles the layout stuff for this widget.
     */
    protected void init() {
        setWidth(MIN_WIDTH);
        setMinWidth(MIN_WIDTH);
        setHeight100();
        setShowResizeBar(true);
        setShowEdges(false);
        setLayoutMargin(0);
        setLayoutAlign(VerticalAlignment.TOP);
        setOverflow(Overflow.AUTO);

        Label title = new Label(messages.projects());
        title.setHeight("20");
        title.setMargin(5);
        title.setWidth100();
        title.setStyleName("projectHeader");

        HLayout buttonWrapper = new HLayout();

        Button addButton = new Button("+");
        addButton.setStyleName("projectsAddButton");
        addButton.setTitle(messages.new_project());
        addButton.setWidth("30px");

        Button closeButton = new Button("X");
        closeButton.setStyleName("projectsCloseButton");
        closeButton.setTitle(messages.projectlist_close());
        closeButton.setWidth("30px");

        buttonWrapper.addMember(addButton);
        buttonWrapper.addMember(closeButton);
        buttonWrapper.setAlign(Alignment.RIGHT);
        buttonWrapper.setAutoWidth();

        HLayout titleWrapper = new HLayout();
        titleWrapper.setStyleName("bgBlueMid");
        titleWrapper.setWidth100();
        titleWrapper.setHeight("20px");
        titleWrapper.addMember(title);
        titleWrapper.addMember(buttonWrapper);

        Canvas gridWrapper = new Canvas();
        gridWrapper.setPadding(0);
        titleWrapper.setWidth100();
        gridWrapper.addChild(grid);

        TableFilter filter = new TableFilter();
        filter.setHeight("30px");
        filter.addFilterHandler(this);
        filter.setBorder("1px solid gray");

        addMember(titleWrapper);
        addMember(gridWrapper);
        addMember(filter);

        addButton.addClickHandler(
                new com.google.gwt.event.dom.client.ClickHandler() {

                    @Override
                    public void onClick(ClickEvent ev) {
                        flys.newProject();
                    }
                });

        closeButton.addClickHandler(
                new com.google.gwt.event.dom.client.ClickHandler() {

            @Override
            public void onClick(ClickEvent ev) {
                hide();
            }
        });
    }


    @Override
    public void onFilterCriteriaChanged(StringFilterEvent event) {
        String search = event.getFilter();
        if (search != null && search.length() > 0) {
            // Filter the records.
            filterCollections(search);
        }
        else {
            filteredCollections.clear();
            for(int i = 0; i < collections.size(); i++) {
                filteredCollections.add(collections.get(i));
            }
            updateGrid();
        }
    }


    @Override
    public void onFilterCriteriaChanged(RangeFilterEvent event) {
        //Empty. No Ranges to filter.
    }


    /** On collection change, update list (probably name change or similar). */
    @Override
    public void onCollectionChange(CollectionChangeEvent event) {
        if (event.getOldValue() == null) {
            updateUserCollections();
        }
    }


    @Override
    public void onEditComplete(EditCompleteEvent event) {
        if (event.getColNum() != 1) {
            return;
        }

        int row = event.getRowNum();

        CollectionRecord r = (CollectionRecord) grid.getRecord(row);
        Collection       c = r.getCollection();

        Map<?, ?> newValues = event.getNewValues();
        String name   = (String) newValues.get("name");

        int maxLength = getMaxNameLength();
        int length    = name != null ? name.length() : 0;

        if (length <= 0 || length > maxLength) {
            String msg = messages.project_name_too_long();
            msg        = msg.replace("$LEN", String.valueOf(maxLength));
            SC.warn(msg);

            ListGridRecord[] rs = grid.getRecords();
            rs[row] = (ListGridRecord) event.getOldRecord();
            grid.setRecords(rs);

            return;
        }

        c.setName(name);
        updateCollectionName(c);
    }


    /**
     * Set the name of the collection <i>c</i> to a new value. If the update
     * process succeeded, the project list is refreshed.
     *
     * @param c The Collection with a new name.
     */
    protected void updateCollectionName(final Collection c) {
        if (c == null) {
            return;
        }

        GWT.log("Update Collection name: " + c.identifier());
        GWT.log("=> New name = " + c.getName());

        nameService.setName(c, new AsyncCallback<Void>(){
            @Override
            public void onFailure(Throwable caught) {
                String msg = caught.getMessage();

                try {
                    SC.warn(messages.getString(msg));
                }
                catch (MissingResourceException mre) {
                    SC.warn(msg);
                }
            }

            @Override
            public void onSuccess(Void v) {
                updateUserCollections();
                if(flys.getWorkspace().hasView(c.identifier())) {
                    flys.getWorkspace().updateTitle(c.identifier(), c.getName());
                }
            }
        });
    }


    /**
     * Set the ttl of the collection <i>c</i> to a new value. If the update
     * process succeeded, the project list is refreshed.
     *
     * @param c The Collection with a new ttl.
     */
    protected void updateCollectionTTL(Collection c) {
        if (c == null) {
            return;
        }

        GWT.log("Update Collection TTL: " + c.identifier());
        GWT.log("=> New ttl = " + c.getTTL());

        ttlService.setTTL(c, new AsyncCallback<Void>() {
            @Override
            public void onFailure(Throwable caught) {
                String msg = caught.getMessage();

                try {
                    SC.warn(messages.getString(msg));
                }
                catch (MissingResourceException mre) {
                    SC.warn(msg);
                }
            }

            @Override
            public void onSuccess(Void v) {
                updateUserCollections();
            }
        });
    }


    /**
     * Delete the collection <i>c</i>.
     *
     * @param c The Collection that should be deleted.
     */
    public void deleteCollection(final Collection c) {
        if (c == null) {
            return;
        }

        GWT.log("Delete Collection: " + c.identifier());

        deleteService.delete(c, new AsyncCallback<Void>(){
            @Override
            public void onFailure(Throwable caught) {
                String msg = caught.getMessage();

                try {
                    SC.warn(messages.getString(msg));
                }
                catch (MissingResourceException mre) {
                    SC.warn(msg);
                }
            }

            @Override
            public void onSuccess(Void v) {
                flys.getWorkspace().destroyProject(c.identifier());
                updateUserCollections();
            }
        });
    }


    public void updateUserCollections() {
        GWT.log("==> ProjectList updates user collections!");

        Config config = Config.getInstance();
        String locale = config.getLocale();

        userCollectionsService.getUserCollections(locale, user.identifier(),
            new AsyncCallback<Collection[]>() {
                @Override
                public void onFailure(Throwable caught) {
                    String msg = caught.getMessage();

                    try {
                        SC.warn(messages.getString(msg));
                    }
                    catch (MissingResourceException mre) {
                        SC.warn(msg);
                    }
                }

                @Override
                public void onSuccess(Collection[] collections) {
                    int num = collections != null ? collections.length : 0;

                    GWT.log("Received " + num + " user collections.");

                    updateGridDataSource(collections);
                }
            }
        );
    }


    /**
     * Delete all entries in the ListGrid.
     */
    protected void clearGrid() {
        ListGridRecord[] records = grid.getRecords();

        for (ListGridRecord record: records) {
            grid.removeData(record);
        }
    }


    /**
     * Update the collections data source.
     *
     * First removes all collections to avoid duplicates, then add new entries.
     *
     * @param c Collections to set to the data source.
     */
    protected void updateGridDataSource (Collection[] c) {
        collections.clear();
        if(c == null) {
            clearGrid();
            return;
        }
        for (Collection coll : c) {
            this.collections.add(coll);
        }
        filterCollections("");
    }


    /**
     * Updates the ListGrid.
     */
    protected void updateGrid() {
        clearGrid();

        if (filteredCollections == null ||
            filteredCollections.size() == 0) {
            return;
        }

        for (Collection c: filteredCollections) {
            grid.addData(new CollectionRecord(c));
        }
    }


    /**
     * Filter for the user collections.
     *
     * @param search String to search for in collection names.
     */
    protected void filterCollections(String search) {
        // Clear the collection list.
        filteredCollections.clear();

        // Filter the list.
        for (int i = 0; i < collections.size(); i++) {
            String name;

            // Get the collection name.
            if (collections.get(i).getName().equals("") ||
                collections.get(i).getName() == null) {
                name = collections.get(i).identifier();
            }
            else {
                name = collections.get(i).getName();
            }

            name = name.toLowerCase();
            // Add a collection to the filtered list if the search string
            // matches.
            if (name.contains(search.toLowerCase())) {
                filteredCollections.add(collections.get(i));
            }
        }
        updateGrid();
    }


    public int getMaxNameLength() {
        return MAX_NAME_LENGTH;
    }


    /**
     * Builds the field in the grid that displays the creation time of a
     * project.
     *
     * @return the grid field.
     */
    protected ListGridField buildDateField() {
        ListGridField date = new ListGridField(
            "creationTime", messages.projectlist_creationTime());

        date.setType(ListGridFieldType.DATE);
        date.setCanEdit(false);

        date.setCellFormatter(new CellFormatter() {
            @Override
            public String format(Object value, ListGridRecord rec, int r, int c) {
                if (value == null) {
                    return null;
                }

                DateTimeFormat dtf = DateTimeFormat.getFormat(
                    messages.datetime_format());

                return dtf.format((Date)value);
            }
        });

        date.setWidth(COLUMN_DATE_WIDTH);
        date.setAlign(Alignment.CENTER);

        return date;
    }


    /**
     * Builds the field in the grid that displays the name of a project.
     *
     * @return the grid field.
     */
    protected ListGridField buildNameField() {
        ListGridField name = new ListGridField(
            "name", messages.projectlist_title());

        name.setType(ListGridFieldType.TEXT);
        name.setShowHover(true);
        name.setHoverCustomizer(new HoverCustomizer() {
            @Override
            public String hoverHTML(
                Object         value,
                ListGridRecord record,
                int            row,
                int            col)
            {
                CollectionRecord r = (CollectionRecord) record;
                Collection       c = r.getCollection();

                String name = r.getName();

                return name != null && name.length() > 0
                    ? name
                    : c.identifier();
            }
        });

        name.setWidth(COLUMN_TITLE_WIDTH);
        name.setAlign(Alignment.LEFT);

        return name;
    }


    protected ListGridField buildFavoriteField() {
        ListGridField fav = new ListGridField(
            "ttl", messages.projectlist_favorite());

        fav.setType(ListGridFieldType.IMAGE);
        String base = GWT.getHostPageBaseURL();
        fav.setImageURLPrefix(base + "images/");
        fav.setImageURLSuffix(".png");
        fav.setWidth(COLUMN_FAVORITE_WIDTH);
        fav.setAlign(Alignment.CENTER);
        fav.setCanEdit(false);

        return fav;
    }


    protected void cloneProject(Collection c) {
        Config config = Config.getInstance();
        String locale = config.getLocale();

        cloneCollection = c;

        describeCollectionService.describe(c.identifier(), locale,
            new AsyncCallback<Collection>() {
                @Override
                public void onFailure(Throwable caught) {
                    GWT.log("Could not DESCRIBE collection.");
                    SC.warn(messages.getString(caught.getMessage()));
                }


                @Override
                public void onSuccess(Collection newCollection) {
                    GWT.log("Successfully DESCRIBED collection.");
                    String uuid = getMasterArtifact(newCollection);
                    cloneArtifact(uuid, newCollection);
                }
            }
        );
    }


    /** Get master artifacts UUID of a collection. */
    protected String getMasterArtifact(Collection newCollection) {
        String uuid = newCollection.getItem(0).identifier();
        // The master artifact uuid.
        return uuid;
    }


    /** Clone artifact/create collection, using the refArtifacts factory. */
    protected void cloneArtifact(String uuid, Artifact refArtifact,
        final String locale) {
        Recommendation recommendation = new Recommendation(
            refArtifact.getName(),
            null,
            uuid,
            null);

        String factory = recommendation.getFactory();
        createArtifactService.create(
            locale, factory, recommendation,
            new AsyncCallback<Artifact>() {
                @Override
                public void onFailure(Throwable caught) {
                    GWT.log("Error loading recommendations: " +
                        caught.getMessage());
                }

                @Override
                public void onSuccess(Artifact artifact) {
                    GWT.log("Created new artifact: " + artifact.getUuid());
                    createCollection(artifact);
                }
            }
        );
    }


    /**
     * Clone a project (collection).
     */
    protected void cloneArtifact(final String uuid, Collection newCollection) {
        Config config       = Config.getInstance();
        final String locale = config.getLocale();

        // Find out which factory to use for cloning.
        CollectionItem master = newCollection.getItem(0);
        getArtifactService.getArtifact(
            locale,
            master.identifier(),
            master.hash(),
            new AsyncCallback<Artifact>() {
                @Override
                public void onFailure(Throwable caught) {
                    SC.warn(MSG.getString(caught.getMessage()));
                }

                @Override
                public void onSuccess(Artifact artifact) {
                    cloneArtifact(uuid, artifact, locale);
                }
        });
    }


    protected void createCollection(final Artifact artifact) {
        Config config        = Config.getInstance();
        final String locale  = config.getLocale();
        final String ownerid = user.identifier();

        createCollectionService.create(
            locale,
            ownerid,
            new AsyncCallback<Collection>() {
                @Override
                public void onFailure(Throwable caught) {
                    GWT.log("Could not create the new collection.");
                    SC.warn(messages.getString(caught.getMessage()));
                }

                @Override
                public void onSuccess(Collection collection) {
                    GWT.log("Successfully created a new collection.");
                    addArtifactToCollection(artifact, collection);
                }
            }
        );
    }


    protected void addArtifactToCollection(Artifact a, Collection c) {
        Config config       = Config.getInstance();
        final String locale = config.getLocale();

        addArtifactService.add(
            c, a, locale,
            new AsyncCallback<Collection>() {
                @Override
                public void onFailure(Throwable caught) {
                    GWT.log("An error occured while adding artifact.");
                    SC.warn(messages.getString(caught.getMessage()));
                }

                @Override
                public void onSuccess(Collection newColl) {
                    String name = cloneCollection.getName();
                    if(name == null || name.equals("")) {
                        name = cloneCollection.identifier();
                    }

                    newColl.setName(messages.copy_of() + ": " + name);

                    updateCollectionName(newColl);
                    if(cloneCollection.getTTL() == 0) {
                        newColl.setTTL(0);
                        updateCollectionTTL(newColl);
                    }
                    updateUserCollections();
                }
            }
        );
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org