Page 1 of 3

Gantt - copy/paste tasks

Posted: Wed Dec 13, 2017 2:06 pm
by Dev_Coppelis
Hi,
I would like to check if a feature is already available (as I assume it is a must have function) :
• From the advanced view, the possibility to copy/paste a parent task (or a group of tasks), including sub tasks and all the related data (dates, resources, relations, ..).

BR

Re: Gantt - copy/paste tasks

Posted: Wed Dec 13, 2017 2:49 pm
by Maxim Gorkovsky
Hello.
There is a plugin allowing to copy/paste various data, but it cannot create new records: clipboard. It is enabled by default in our advanced demo.
I made a feature request for plugin with such functionality: https://app.assembla.com/spaces/bryntum/tickets/5331

Re: Gantt - copy/paste tasks

Posted: Wed Dec 13, 2017 3:08 pm
by Dev_Coppelis
Hi,
thanks for creating feature request.
Please note that this copy/paste should include sub tasks.
Any idea on when this is supposed to be available ?

Re: Gantt - copy/paste tasks

Posted: Wed Dec 13, 2017 3:41 pm
by Maxim Gorkovsky
I'm afraid I cannot say when exactly we will start on this feature, we have a lot on our plate now. You can subscribe to the ticket to get updates. We have some drafts of similar feature, so should not take very long.
Also there is always option to prioritize features by contacting sales at bryntum.com

Do you expect copy action to copy everything related to task, i.e. children, dependencies and resource assignments?

Re: Gantt - copy/paste tasks

Posted: Wed Dec 13, 2017 3:54 pm
by Dev_Coppelis
ok thanks for the information.

Yes copy everything related to task (like MS Project) .

There is also a related feature : When importing MS project file, to insert the content at the current position on the Gantt (currently this action deletes existing tasks).

Need to create a request for this too ?

Re: Gantt - copy/paste tasks

Posted: Wed Dec 13, 2017 4:11 pm
by Maxim Gorkovsky
I am not sure if this is worthy of feature request. This task should be pretty simple. Try following:
1) See #importData method of the Importer plugin
2) Instead of loading data into stores (dependencies, resources,..), append it. See this doc: https://docs.sencha.com/extjs/6.5.1/clas ... d-loadData
3) Instead of setting root use node.appendChild where node is task record that you want to import to
4) Avoid setting project calendar and do not set newRoot id to 'root'

This should do the trick.

Re: Gantt - copy/paste tasks

Posted: Fri Jan 12, 2018 4:12 pm
by Dev_Coppelis
Hi,
Any news about this copy/paste feature ?
Maxim Gorkovsky wrote:Hello.
There is a plugin allowing to copy/paste various data, but it cannot create new records: clipboard. It is enabled by default in our advanced demo.
I made a feature request for plugin with such functionality: https://app.assembla.com/spaces/bryntum/tickets/5331

Re: Gantt - copy/paste tasks

Posted: Fri Jan 12, 2018 5:15 pm
by Terence
It's not prioritised or in the pipeline yet. If you want to speed things up, I suggest to contact sales@bryntum.com

Re: Gantt - copy/paste tasks

Posted: Wed Jan 24, 2018 5:09 pm
by Dev_Coppelis
Maxim Gorkovsky wrote:I am not sure if this is worthy of feature request. This task should be pretty simple. Try following:
1) See #importData method of the Importer plugin
2) Instead of loading data into stores (dependencies, resources,..), append it. See this doc: https://docs.sencha.com/extjs/6.5.1/clas ... d-loadData
3) Instead of setting root use node.appendChild where node is task record that you want to import to
4) Avoid setting project calendar and do not set newRoot id to 'root'

This should do the trick.
Hi Maxim,
i changed the loading data into stores (dependencies, resources) to append but i don't know how to get the id of task to append Child.
Kindly find the below code
Ext.define('Gnt.examples.advanced.overrides.Importer', {
    extend: 'Ext.AbstractPlugin',

    alias: 'plugin.msproject_importer',

    taskStore: null,
    dependencyStore: null,
    assignmentStore: null,
    resourceStore: null,
    calendarManager: null,

    taskMap: null,
    resourceMap: null,

    syncBlock: function () {
        return false;
    },

    init: function (gantt) {
        this.taskStore = gantt.taskStore;
        this.dependencyStore = gantt.dependencyStore;
        this.resourceStore = gantt.resourceStore;
        this.assignmentStore = gantt.assignmentStore;
        this.calendarManager = this.taskStore.calendarManager;

        this.taskModelIdProperty = this.taskStore.model.prototype.idProperty;
        this.resourceModelIdProperty = this.resourceStore.model.prototype.idProperty;
        this.assignmentModelIdProperty = this.assignmentStore.model.prototype.idProperty;
        this.assignmentModelTaskIdProperty = this.assignmentStore.model.prototype.taskIdField;
        this.assignmentModelResourceIdProperty = this.assignmentStore.model.prototype.resourceIdField;
        this.dependencyModelIdProperty = this.dependencyStore.model.prototype.idProperty;
        this.dependencyModelFromProperty = this.dependencyStore.model.prototype.fromField;
        this.dependencyModelToProperty = this.dependencyStore.model.prototype.toField;

        // if we have CalendarManager onboard we do calendars importing as well
        if (this.calendarManager) {
            this.calendarModelIdProperty = this.calendarManager.model.prototype.idProperty;
            this.taskModelCalendarIdProperty = this.taskStore.model.prototype.CalendarIdField;
            this.resourceModelCalendarIdProperty = this.resourceStore.model.prototype.CalendarIdField;
        }
    },

    /*
     * @param {Object} data A custom data set with 'tasks', 'dependencies', 'assignments' and 'resources' properties.
     * */
    importData: function (data) {
        this.calendarMap = {};
        this.taskMap = {};
        this.resourceMap = {};
        this.projectCalendar = null;

        var taskStore = this.taskStore;

        taskStore.on('beforesync', this.syncBlock);

        taskStore.suspendEarlyDatesResetNotification();
        taskStore.suspendLateDatesResetNotification();

        // import calendars if we have CalendarManager
        this.calendarManager && this.processCalendars(data);

        var tasks = this.getTaskTree(Ext.isArray(data.tasks) ? data.tasks : [data.tasks]);

        this.processResources(data);
        this.processDependencies(data);
        this.processAssignments(data);

        var newRoot = Ext.isArray(data.tasks) ? {} : tasks[0];
        newRoot.children = tasks;

        if (newRoot.isNode) {
            // TODO: set root node id to "root" to comply w/ server side demos
            newRoot.setId('root');
            // seems extjs doesn't track root id change and children keep parentId intact
            Ext.Array.each(newRoot.childNodes, function (node) { node.data.parentId = 'root'; });

            // if instance is passed then getTreeStore will return null
            // https://www.sencha.com/forum/showthread.php?297640
            newRoot.join(taskStore);
        }

        // set project calendar if it's provided
        // we set project calendar in silent mode to not readjust all the tasks
        this.projectCalendar && taskStore.setCalendar(this.calendarMap[this.projectCalendar].getCalendar(), true);
        
        taskStore.setRoot(newRoot);

        taskStore.un('beforesync', this.syncBlock);

        taskStore.resumeEarlyDatesResetNotification();
        taskStore.resumeLateDatesResetNotification();
    },

    /* RESOURCES */
    processResources: function (data) {
        var resources = [];

        Ext.Array.map(data.resources, this.processResource, this);

        this.resourceStore.loadData(resources, true);
    },

    processResource: function (resData) {
        var id = resData[this.resourceModelIdProperty];
        delete resData[this.resourceModelIdProperty];

        resData[this.resourceModelCalendarIdProperty] = this.calendarMap[resData[this.resourceModelCalendarIdProperty]];

        var resource = new this.resourceStore.model(resData);

        this.resourceMap[id] = resource;
        return resource;
    },
    /* EOF RESOURCES */

    /* DEPENDENCIES */
    processDependencies: function (data) {
        var deps = Ext.Array.map(data.dependencies, this.processDependency, this);

        this.dependencyStore.loadData(deps, true);
    },

    processDependency: function (depData) {
        var fromId = depData[this.dependencyModelFromProperty];
        var toId = depData[this.dependencyModelToProperty];
        delete depData[this.dependencyModelFromProperty];
        delete depData[this.dependencyModelToProperty];
        delete depData[this.dependencyModelIdProperty];
        var dep = new this.dependencyStore.model(depData);

        dep.setSourceTask(this.taskMap[fromId]);
        dep.setTargetTask(this.taskMap[toId]);

        return dep;
    },
    /* EOF DEPENDENCIES */

    /* ASSIGNMENTS */
    processAssignments: function (data) {
        Ext.Array.each(data.assignments, this.processAssignment, this);
    },

    processAssignment: function (asData) {
        var resourceId = asData[this.assignmentModelResourceIdProperty];
        var taskId = asData[this.assignmentModelTaskIdProperty];
        delete asData[this.assignmentModelIdProperty];
        delete asData[this.assignmentModelResourceIdProperty];
        delete asData[this.assignmentModelTaskIdProperty];

        this.taskMap[taskId].assign(this.resourceMap[resourceId], asData.Units);
    },
    /* EOF ASSIGNMENTS */

    /* TASKS  */
    getTaskTree: function (tasks) {
        return Ext.Array.map(tasks, this.processTask, this);
    },

    processTask: function (data) {
        var id = data[this.taskModelIdProperty];
        var children = data.children;

        delete data.children;
        delete data[this.taskModelIdProperty];

        data[this.taskModelCalendarIdProperty] = this.calendarMap[data[this.taskModelCalendarIdProperty]];

        var t = new this.taskStore.model(data);
        t.taskStore = this.taskStore;

        if (children) {
            t.appendChild(this.getTaskTree(children));
        }

        t._Id = id;
        this.taskMap[t._Id] = t;

        return t;
    },
    /* EOF TASKS  */

    /* CALENDARS */
    processCalendarChildren: function (children) {
        return Ext.Array.map(children, this.processCalendar, this);
    },

    processCalendar: function (data) {
        var id = data[this.calendarModelIdProperty];
        var children = data.children;

        delete data.children;
        delete data[this.calendarModelIdProperty];

        var t = new this.calendarManager.model(data);

        if (children) {
            t.appendChild(this.processCalendarChildren(children));
        }

        t._Id = id;
        this.calendarMap[t._Id] = t;

        return t;
    },

    // Entry point of calendars loading
    processCalendars: function (data) {
        var calendarManager = this.calendarManager;

        var metaData = data.calendars.metaData;

        delete data.calendars.metaData;

        var processed = this.processCalendarChildren([data.calendars]),
            newRoot = processed[0];

        if (newRoot.isNode) {
            // TODO: set root node id to "root" to comply w/ server side demos
            newRoot.setId('root');
            // seems extjs doesn't track root id change and children keep parentId intact
            Ext.Array.each(newRoot.childNodes, function (node) { node.data.parentId = 'root'; });

            // if instance is passed then getTreeStore will return null
            // https://www.sencha.com/forum/showthread.php?297640
            newRoot.join(calendarManager);
        }

        calendarManager.setRoot(newRoot);

        // remember passed project calendar identifier ..we will set it later after tasks are loaded
        this.projectCalendar = metaData && metaData.projectCalendar;
    }
    /* EOF CALENDARS */

});

Re: Gantt - copy/paste tasks

Posted: Wed Jan 24, 2018 5:39 pm
by mats
That's a bit too much code to review I'm afraid. To get the task id:
task.getId();