 StudentList = rclass
     displayName : "CourseEditor-StudentList"
     propTypes:
-        students   : rtypes.object.isRequired
-        user_map   : rtypes.object.isRequired
-        info       : rtypes.func.isRequired
-
-    render_last: (name, obj) ->
+        name     : rtypes.string.isRequired
+        flux     : rtypes.object.isRequired
+        students : rtypes.object.isRequired
+        user_map : rtypes.object.isRequired
+        info     : rtypes.func.isRequired
+
+    open: (type, assignment_id, student_id) ->
+        @props.flux.getActions(@props.name).open_assignment(type, assignment_id, student_id)
+
+    copy: (type, assignment_id, student_id) ->
+        @props.flux.getActions(@props.name).copy_assignment(type, assignment_id, student_id)
+
+    render_last: (name, obj, type, info) ->
+        open = => @open(type, info.assignment_id, info.student_id)
+        copy = => @copy(type, info.assignment_id, info.student_id)
         v = [<span key='name'>{name+': '}</span>]
         if obj?
             if obj.time?
                 v.push(<span key='time'>{new Date(obj.time).toLocaleString()}</span>)
+                v.push(<a key='open' href='' onClick={(e)=>e.preventDefault();open()}>(open)</a>)
             if obj.error
                 v.push(<span key='error' style={color:'red'}>{obj.error}</span>)
+            v.push(<a key="copy" href='' onClick={(e)=>e.preventDefault();copy()}>(re-copy)</a>)
         else
-            v.push("(not yet)") # TODO: put a button on it
+            v.push(<a key="copy" href='' onClick={(e)=>e.preventDefault();copy()}>(copy)</a>)
         return v
 
     render_student_info: (info) ->
         <Row >
             <Col md=4 key='last_assignment'>
-                {@render_last('Assigned', info.last_assignment)}
+                {@render_last('Assigned', info.last_assignment, 'assigned', info)}
             </Col>
             <Col md=4 key='collect'>
-                {@render_last('Collected', info.last_collect)}
+                {@render_last('Collected', info.last_collect, 'collected', info)}
             </Col>
             <Col md=4 key='return_graded'>
-                {@render_last('Returned', info.last_return_graded)}
+                {@render_last('Returned', info.last_return_graded, 'graded', info)}
             </Col>
         </Row>
 

        <Row  style={entry_style}>
            <Col sm=12>
                <Panel header={@render_more_header()}>
                    <AssignmentStudents flux={@props.flux} assignment={@props.assignment}
                                        students={@props.students} user_map={@props.user_map} />
                </Panel>
            </Col>

                        last_return_graded = syncdb.select_one(where:where).last_return_graded ? {}
                        last_return_graded[student.get('student_id')] = {time: misc.mswalltime(), error:err}
                        @_update(set:{last_return_graded:last_return_graded}, where:where)
                        cb?(err)

        # Copy the given assignment to all non-deleted students, doing 10 copies in parallel at once.
        return_assignment_to_all_students: (assignment, cb) =>
