</actions>
</permacrud>
</class>
- <class id="acmcm" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::course_module_course_materials" oils_persist:tablename="asset.course_module_course_materials" reporter:label="Course Materials">
+ <class id="acmcm" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::course_module_course_materials" oils_persist:tablename="asset.course_module_course_materials" reporter:label="Course Materials" oils_persist:field_safe="true">
<fields oils_persist:primary="id" oils_persist:sequence="asset.course_module_course_materials_id_seq">
<field reporter:label="ID" name="id" reporter:datatype="id" />
<field reporter:label="Course" name="course" reporter:datatype="link" />
--- /dev/null
+<eg-string #deleteFailedString i18n-text text="Disassociation of Course Material failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Disassociation of Course Material succeeded"></eg-string>
+<eg-string #successString i18n-text text="Association of Course Material succeeded"></eg-string>
+<eg-string #failedString i18n-text text="Association of Course Material failed or was not allowed"></eg-string>
+<eg-string #differentLibraryString i18n-text text="Material exists at a different library"></eg-string>
+
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title" i18n>Course Users</h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div class="row mt-4">
+ <div class="col-md-5">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <span class="input-group-text" i18n>User Role</span>
+ </div>
+ <input type="text" [(ngModel)]="userRoleInput" />
+ </div>
+ </div>
+ <div class="row justify-content-center mt-3">
+ </div>
+ <div class="col-md-6">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text">
+ <span i18n>User</span>
+ </div>
+ </div>
+ <input type="text" [(ngModel)]="new_usr" />
+ </div>
+ </div>
+ </div>
+ <div class="col-md-0">
+ <button class="btn btn-outline-dark" (click)="associateUsers(userRoleInput)" i18n [disabled]="!new_usr || !userRoleInput">Add User</button>
+ </div>
+ <div class="mt-3">
+ <eg-grid #usersGrid [dataSource]="gridDataSource">
+ <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelected($event)">
+ </eg-grid-toolbar-action>
+ <eg-grid-column label="id" [index]=true [hidden]="true" i18n-label></eg-grid-column>
+ <eg-grid-column label="User Role" name="_usr_role" i18n-label></eg-grid-column>
+ <eg-grid-column label="First Name" name="first_given_name" i18n-label></eg-grid-column>
+ <eg-grid-column label="Second Name" name="second_given_name" i18n-label></eg-grid-column>
+ <eg-grid-column label="Last Name" name="family_name" i18n-label></eg-grid-column>
+ <eg-grid-column label="Prefix" name="pref_prefix" [hidden]="true" i18n-label></eg-grid-column>
+ <eg-grid-column label="Preferred First Name" name="pref_first_given_name"[hidden]="true" i18n-label></eg-grid-column>
+ <eg-grid-column label="Preferred Second Name" name="pref_second_given_name"[hidden]="true" i18n-label></eg-grid-column>
+ <eg-grid-column label="Preferred Family Name" name="pref_family_name"[hidden]="true" i18n-label></eg-grid-column>
+ <eg-grid-column label="Preferred Suffix" name="pref_suffix" [hidden]="true" i18n-label></eg-grid-column>
+ </eg-grid>
+ </div>
+ </div>
\ No newline at end of file
--- /dev/null
+import {Component, Input, ViewChild, OnInit, TemplateRef} from '@angular/core';\r
+import {Observable, Observer, of} from 'rxjs';\r
+import {DialogComponent} from '@eg/share/dialog/dialog.component';\r
+import {AuthService} from '@eg/core/auth.service';\r
+import {NetService} from '@eg/core/net.service';\r
+import {EventService} from '@eg/core/event.service';\r
+import {OrgService} from '@eg/core/org.service';\r
+import {PcrudService} from '@eg/core/pcrud.service';\r
+import {Pager} from '@eg/share/util/pager';\r
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';\r
+import {GridDataSource} from '@eg/share/grid/grid';\r
+import {GridComponent} from '@eg/share/grid/grid.component';\r
+import {IdlObject, IdlService} from '@eg/core/idl.service';\r
+import {StringComponent} from '@eg/share/string/string.component';\r
+import {ToastService} from '@eg/share/toast/toast.service';\r
+import {CourseService} from '@eg/staff/share/course.service';\r
+\r
+@Component({\r
+ selector: 'eg-course-associate-users-dialog',\r
+ templateUrl: './course-associate-users.component.html'\r
+})\r
+\r
+export class CourseAssociateUsersComponent extends DialogComponent {\r
+\r
+ @ViewChild('usersGrid', {static: true}) usersGrid: GridComponent;\r
+ @ViewChild('deleteFailedString', { static: true }) deleteFailedString: StringComponent;\r
+ @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent;\r
+ @ViewChild('successString', { static: true }) successString: StringComponent;\r
+ @ViewChild('failedString', { static: true }) failedString: StringComponent;\r
+ @ViewChild('differentLibraryString', { static: true }) differentLibraryString: StringComponent;\r
+ @Input() table_name = "Course Users";\r
+ @Input() userRoleInput: String;\r
+ \r
+ idl_class = "acmcu";\r
+ new_usr:any;\r
+ currentCourse: IdlObject;\r
+ users: any[];\r
+ gridDataSource: GridDataSource;\r
+\r
+ constructor(\r
+ private auth: AuthService,\r
+ private idl: IdlService,\r
+ private net: NetService,\r
+ private pcrud: PcrudService,\r
+ private org: OrgService,\r
+ private evt: EventService,\r
+ private modal: NgbModal,\r
+ private toast: ToastService,\r
+ private courseSvc: CourseService\r
+ ) {\r
+ super(modal);\r
+ this.gridDataSource = new GridDataSource();\r
+ }\r
+\r
+ ngOnInit() {\r
+ }\r
+\r
+ /**\r
+ * Takes the user id and creates a course user based around it.\r
+ * @param user_input The inputted user Id.\r
+ */\r
+ associateUsers(user_input) {\r
+ if (user_input) {\r
+ let user = this.idl.create('acmcu');\r
+ user.course(this.currentCourse.id());\r
+ user.usr(this.new_usr);\r
+ user.usr_role(user_input);\r
+ this.pcrud.create(user).subscribe(\r
+ val => {\r
+ console.debug('created: ' + val);\r
+ this.successString.current().then(str => this.toast.success(str));\r
+ }, err => {\r
+ this.failedString.current().then(str => this.toast.danger(str));\r
+ })\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Delete a user based on the id selected from the grid.\r
+ * @param users \r
+ */\r
+ deleteSelected(users) {\r
+ let user_ids = [];\r
+ users.forEach(user => {\r
+ this.gridDataSource.data.splice(this.gridDataSource.data.indexOf(user, 0), 1);\r
+ user_ids.push(user.id())\r
+ });\r
+ this.pcrud.remove(users).subscribe(user => {\r
+ this.pcrud.autoApply(user).subscribe(\r
+ val => {\r
+ console.debug('deleted: ' + val);\r
+ this.deleteSuccessString.current().then(str => this.toast.success(str));\r
+ },\r
+ err => {\r
+ this.deleteFailedString.current()\r
+ .then(str => this.toast.danger(str));\r
+ }\r
+ );\r
+ });\r
+ }\r
+\r
+}
\ No newline at end of file
<eg-course-associate-material-dialog #courseMaterialDialog>
</eg-course-associate-material-dialog>
+
+<eg-course-associate-users-dialog #courseUserDialog>
+</eg-course-associate-users-dialog>
+
<div class="w-100 mt-2 mb-2">
<eg-grid #grid idlClass={{idl_class}}
[dataSource]="grid_source"
</eg-grid-toolbar-action>
<eg-grid-toolbar-action label="Archive Selected" i18n-label (onClick)="archiveSelected($event)">
</eg-grid-toolbar-action>
+ <eg-grid-toolbar-action label="View Users" i18n-label (onClick)="openUsersDialog($event)">
+ </eg-grid-toolbar-action>
</eg-grid>
</div>
import {CourseAssociateMaterialComponent
} from './course-associate-material.component';
+import {CourseAssociateUsersComponent
+ } from './course-associate-users.component';
+
@Component({
templateUrl: './course-list.component.html'
})
@ViewChild('archiveSuccessString', { static: true }) archiveSuccessString: StringComponent;
@ViewChild('courseMaterialDialog', {static: true})
private courseMaterialDialog: CourseAssociateMaterialComponent;
+ @ViewChild('courseUserDialog', {static: true})
+ private courseUserDialog: CourseAssociateUsersComponent;
+
@Input() sort_field: string;
@Input() idl_class = "acmc";
@Input() dialog_size: 'sm' | 'lg' = 'lg';
limit: pager.limit,
order_by: orderBy
};
- return this.pcrud.retrieveAll(this.idl_class, searchOps, {fleshSelectors: true});
+ return this.pcrud.retrieveAll(this.idl_class, searchOps, {fleshSelectors: true})
};
}
});
}
+ /**
+ * Uses the course id to fetch the different users associated with that course.
+ * @param course The course id
+ * @param currentMaterials
+ */
+ fetchCourseUsers(course, currentMaterials): Promise<any> {
+ return new Promise((resolve, reject) => {
+ this.pcrud.search('acmcu', {course: course}).subscribe(res => {
+ if(res) this.fleshUserDetails(res.usr(), res.usr_role());
+ }, err => {
+ reject(err);
+ }, () => resolve(this.courseUserDialog.gridDataSource.data));
+ });
+ }
+
+ /**
+ * Takes the user id from the course table, and cross references that with the user table,
+ * to find the right data.
+ * @param userId The user id that is to be cross referenced.
+ * @param usr_role The user role that is to be added to the grid.
+ */
+ fleshUserDetails(userId, usr_role) {
+ return new Promise((resolve, reject) => {
+ this.pcrud.search("au", {id:userId}).subscribe(res => {
+ if (res) {
+ let user = res;
+ user._usr_role = usr_role;
+ this.courseUserDialog.gridDataSource.data.push(user);
+ }
+ }, err => {
+ reject(err);
+ }, () => resolve(this.courseMaterialDialog.gridDataSource.data));
+ });
+ }
+
fleshItemDetails(itemId, relationship): Promise<any> {
return new Promise((resolve, reject) => {
this.net.request(
});
});
}
+
+ /**
+ * Opens the user dialog component using the course id
+ * @param course
+ */
+ openUsersDialog(course) {
+ let currentUsers = []
+ this.courseUserDialog.gridDataSource.data = [];
+ this.fetchCourseUsers(course[0].id(), currentUsers).then(res => {
+ this.courseUserDialog.currentCourse = course[0];
+ this.courseUserDialog.users = currentUsers;
+ this.courseUserDialog.open({size: 'lg'}).subscribe(res => {
+ console.log(res);
+ });
+ });
+ }
}
import {CourseListComponent} from './course-list.component';
import {CoursePageComponent} from './course-page.component';
import {CourseAssociateMaterialComponent} from './course-associate-material.component';
+import {CourseAssociateUsersComponent} from './course-associate-users.component';
import {CourseReservesRoutingModule} from './routing.module';
import {ItemLocationSelectModule} from '@eg/share/item-location-select/item-location-select.module';
declarations: [
CourseListComponent,
CoursePageComponent,
- CourseAssociateMaterialComponent
+ CourseAssociateMaterialComponent,
+ CourseAssociateUsersComponent
],
imports: [
StaffCommonModule,
});
}
+ disassociateUsers(user) {
+ return new Promise((resolve, reject) => {
+ let user_ids = [];
+ let course_library_hash = {};
+ user.forEach(course => {
+ user_ids.push(course.id());
+ course_library_hash[course.id()] = course.owning_lib();
+ });
+ this.pcrud.search('acmcu', {user: user_ids}).subscribe(user => {
+ user.course(user_ids);
+ this.pcrud.autoApply(user).subscribe(res => {
+ console.log(res);
+ }, err => {
+ reject(err);
+ }, () => {
+ resolve(user);
+ });
+ }, err => {
+ reject(err)
+ }, () => {
+ resolve(user_ids);
+ });
+ });
+ }
+
resetItemFields(material, course_lib) {
this.pcrud.retrieve('acp', material.item(),
{flesh: 3, flesh_fields: {acp: ['call_number']}}).subscribe(copy => {