Course Page
authorKyle Huckins <khuckins@catalyte.io>
Sun, 8 Dec 2019 00:07:26 +0000 (00:07 +0000)
committerKyle Huckins <khuckins@catalyte.io>
Thu, 9 Jan 2020 17:45:32 +0000 (17:45 +0000)
- Perl Module and Course Page displaying name and course
number of course, as well as names/roles of publicly viewable
members, course details, and a table of all materials associated
with the course.
- URL listed as eg/opac/course/[COURSE_ID]

Signed-off-by: Kyle Huckins <khuckins@catalyte.io>
 Changes to be committed:
modified:   Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
modified:   Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
new file:   Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Course.pm
new file:   Open-ILS/src/templates/opac/course.tt2
modified:   Open-ILS/src/templates/opac/css/style.css.tt2
new file:   Open-ILS/src/templates/opac/parts/course/body.tt2

Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Course.pm [new file with mode: 0644]
Open-ILS/src/templates/opac/course.tt2 [new file with mode: 0644]
Open-ILS/src/templates/opac/css/style.css.tt2
Open-ILS/src/templates/opac/parts/course/body.tt2 [new file with mode: 0644]

index 09e219e..6152e13 100644 (file)
@@ -1049,7 +1049,17 @@ __PACKAGE__->register_method(
     api_name        => 'open-ils.circ.course_materials.retrieve',
     signature       => q/
         Returns an array of course materials.
-        @params copy_id     : The id of the item who's materials we want to retrieve
+        @params args     : Supplied object to filter search.
+    /);
+
+__PACKAGE__->register_method(
+    method          => 'fetch_course_materials',
+    autoritative    => 1,
+    api_name        => 'open-ils.circ.course_materials.retrieve.fleshed',
+    signature       => q/
+        Returns an array of course materials, each fleshed out with information
+        from the item and the course_material object.
+        @params args     : Supplied object to filter search.
     /);
 
 __PACKAGE__->register_method(
@@ -1064,8 +1074,55 @@ __PACKAGE__->register_method(
 sub fetch_course_materials {
     my ($self, $conn, $args) = @_;
     my $e = new_editor();
+    my $materials = {};
+    my %items;
+
+    $materials->{list} = $e->search_asset_course_module_course_materials($args);
+    return $materials->{list} unless ($self->api_name =~ /\.fleshed/);
+
+    # If we want it fleshed out...
+    for my $course_material (@{$materials->{list}}) {
+        my $material = {};
+        $material->{id} = $course_material->id;
+        $material->{relationship} = $course_material->relationship;
+        $material->{record} = $course_material->record;
+        my $copy = $e->retrieve_asset_copy([
+            $course_material->item, {
+                flesh => 3, flesh_fields => {
+                    'acp' => ['call_number'],
+                    'acn' => ['record']
+                }
+            }
+        ]);
+
+        $material->{item_data} = $copy;
+        $material->{volume_data} = $copy->call_number;
+        $material->{record_data} = $copy->call_number->record;
+        $items{$course_material->item} = $material;
+    }
 
-    return $e->search_asset_course_module_course_materials($args);
+    my $targets = ();
+    for my $item (values %items) {
+        my $final_item = {};
+        my $mvr = $U->record_to_mvr($item->{record_data});
+        $final_item->{id} = $item->{id};
+        $final_item->{relationship} = $item->{relationship};
+        $final_item->{record} = $item->{record};
+        $final_item->{barcode} = $item->{item_data}->barcode;
+        $final_item->{circ_lib} = $item->{item_data}->circ_lib;
+        $final_item->{title} = $mvr->title;
+        $final_item->{call_number} = $item->{volume_data}->label;
+        $final_item->{location} = $e->retrieve_asset_copy_location(
+            $item->{item_data}->location
+        );
+        $final_item->{status} = $e->retrieve_config_copy_status(
+            $item->{item_data}->status
+        );
+
+        push @$targets, $final_item;
+    }
+
+    return $targets;
 }
 
 sub fetch_courses {
@@ -1083,6 +1140,61 @@ sub fetch_courses {
 }
 
 __PACKAGE__->register_method(
+    method          => 'fetch_course_users',
+    autoritative    => 1,
+    api_name        => 'open-ils.circ.course_users.retrieve',
+    signature       => q/
+        Returns an array of course users.
+        @params course_id: The id of the course we want to retrieve from
+    /);
+__PACKAGE__->register_method(
+    method          => 'fetch_course_users',
+    autoritative    => 1,
+    api_name        => 'open-ils.circ.course_users.retrieve.staff',
+    signature       => q/
+        Returns an array of course users.
+        @params course_id: The id of the course we want to retrieve from
+    /);
+
+sub fetch_course_users {
+    my ($self, $conn, $course_id) = @_;
+    my $e = new_editor();
+    my $filter = {};
+    my $users = {};
+    my %patrons;
+
+    $filter->{course} = $course_id;
+    $filter->{is_public} = 't'
+        unless ($self->api_name =~ /\.staff/) and $e->allowed('MANAGE_RESERVES');
+    
+    
+    $users->{list} =  $e->search_asset_course_module_course_users($filter);
+    for my $course_user (@{$users->{list}}) {
+        my $patron = {};
+        $patron->{id} = $course_user->id;
+        $patron->{usr_role} = $course_user->usr_role;
+        $patron->{patron_data} = $e->retrieve_actor_user($course_user->usr);
+        $patrons{$course_user->usr} = $patron;
+    }
+
+    my $targets = ();
+    for my $user (values %patrons) {
+        my $final_user = {};
+        $final_user->{id} = $user->{id};
+        $final_user->{usr_role} = $user->{usr_role};
+        $final_user->{first_given_name} = $user->{patron_data}->first_given_name;
+        $final_user->{family_name} = $user->{patron_data}->family_name;
+        $final_user->{pref_first_given_name} = $user->{patron_data}->pref_first_given_name;
+        $final_user->{pref_family_name} = $user->{patron_data}->pref_family_name;
+        
+        push @$targets, $final_user;
+    }
+
+    return $targets;
+
+}
+
+__PACKAGE__->register_method(
     method      => 'fetch_copy_tags',
     authoritative   => 1,
     api_name        => 'open-ils.circ.copy_tags.retrieve',
index 105c3bd..35f372a 100644 (file)
@@ -23,6 +23,7 @@ use OpenILS::WWW::EGCatLoader::Browse;
 use OpenILS::WWW::EGCatLoader::Library;
 use OpenILS::WWW::EGCatLoader::Search;
 use OpenILS::WWW::EGCatLoader::Record;
+use OpenILS::WWW::EGCatLoader::Course;
 use OpenILS::WWW::EGCatLoader::Container;
 use OpenILS::WWW::EGCatLoader::SMS;
 use OpenILS::WWW::EGCatLoader::Register;
@@ -149,6 +150,7 @@ sub load {
     return $self->load_record if $path =~ m|opac/record/\d|;
     return $self->load_cnbrowse if $path =~ m|opac/cnbrowse|;
     return $self->load_browse if $path =~ m|opac/browse|;
+    return $self->load_course if $path =~ m|opac/course|;
 
     return $self->load_mylist_add if $path =~ m|opac/mylist/add|;
     return $self->load_mylist_delete if $path =~ m|opac/mylist/delete|;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Course.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Course.pm
new file mode 100644 (file)
index 0000000..bd340ea
--- /dev/null
@@ -0,0 +1,42 @@
+package OpenILS::WWW::EGCatLoader;
+use strict; use warnings;
+use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_GONE HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST HTTP_NOT_FOUND);
+use OpenSRF::Utils::Logger qw/$logger/;
+use OpenILS::Utils::CStoreEditor qw/:funcs/;
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Application::AppUtils;
+use Net::HTTP::NB;
+use IO::Select;
+my $U = 'OpenILS::Application::AppUtils';
+
+sub load_course {
+    my $self = shift;
+    my $ctx = $self->ctx;
+
+    $ctx->{page} = 'course';
+    $ctx->{readonly} = $self->cgi->param('readonly');
+
+    my $course_id = $ctx->{page_args}->[0];
+
+    return Apache2::Const::HTTP_BAD_REQUEST
+        unless $course_id and $course_id =~ /^\d+$/;
+
+    $ctx->{course} = $U->simplereq(
+        'open-ils.circ',
+        'open-ils.circ.courses.retrieve',
+        [$course_id]
+    )->[0];
+    
+    $ctx->{instructors} = $U->simplereq(
+        'open-ils.circ',
+        'open-ils.circ.course_users.retrieve',
+        $course_id
+    );
+
+    $ctx->{course_materials} = $U->simplereq(
+        'open-ils.circ',
+        'open-ils.circ.course_materials.retrieve.fleshed',
+        {course => $course_id}
+    );
+    return Apache2::Const::OK;
+}
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac/course.tt2 b/Open-ILS/src/templates/opac/course.tt2
new file mode 100644 (file)
index 0000000..e53fc06
--- /dev/null
@@ -0,0 +1,16 @@
+[%- PROCESS "opac/parts/header.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Course Details: [_1] - [_2]", ctx.course.name, ctx.course.course_number);
+-%]
+    [%- INCLUDE "opac/parts/searchbar.tt2" %]
+    <h2 class="sr-only">[% l('Course Details') %]</h2>
+    <br class="clear-both" />
+    <div id="content-wrapper" class="content-wrapper-record-page">
+        <div id="main-content">
+            [% INCLUDE "opac/parts/course/body.tt2" %]
+            <div class="common-full-pad"></div>
+        </div>
+        <br class="clear-both" />
+    </div>
+[%- END %]
index 72c092f..cf1bfff 100644 (file)
@@ -672,7 +672,8 @@ div.format_icon {
     padding-top: 1.5em;
 }
 
-#rdetails_status td {
+#rdetails_status td,
+#course_material_table td {
     [% IF rtl == 't' -%]
     padding: 7px 13px 3px 0px;
     [%- ELSE %]
@@ -685,7 +686,8 @@ div.format_icon {
     white-space: normal;
 }
 
-#rdetails_status thead th {
+#rdetails_status thead th,
+#course_material_table thead th {
     [% IF rtl == 't' -%]
     padding: 13px 13px 13px 0px;
     text-align: right;
@@ -698,7 +700,8 @@ div.format_icon {
     font-weight: bold;
 }
 
-#rdetails_status tbody td {
+#rdetails_status tbody td,
+#course_material_table tbody td {
     [% IF rtl == 't' -%]
     padding-right: 13px;
     text-align: right;
@@ -707,7 +710,8 @@ div.format_icon {
     text-align: left;
     [%- END %]
 }
-#rdetails_status tbody td.copy_note {
+#rdetails_status tbody td.copy_note
+#course_material_table tbody {
     color: [% css_colors.primary %];
     text-wrap:normal;
     white-space:pre-wrap !important;
@@ -1864,7 +1868,7 @@ a.dash-link:hover { text-decoration: underline !important; }
     vertical-align: top;
     [% END -%]
 }
-.rdetail-author-div {
+.rdetail-author-div, .course-instructor-div {
     padding-bottom: 10px;
     display: inline-block;
 }
@@ -2643,7 +2647,7 @@ a.preflib_change {
     display: none;
 }
 
-.rdetail_authors_div {
+.rdetail_authors_div, .course_instructors_div {
     margin-bottom: 1em;
 }
 
@@ -2985,14 +2989,17 @@ a.preflib_change {
     #main-content {
         margin: 0 1px;
     }
-    #rdetails_status thead {
+    #rdetails_status thead,
+    #course_material_table thead {
         display: none;
     }
-    #rdetails_status tr {
+    #rdetails_status tr,
+    #course_material_table tr {
        display: block;
        margin-top: 3px;
     }
-    #rdetails_status td {
+    #rdetails_status td,
+    #course_material_table td {
        display: block;
        padding: 1px;
     }
@@ -3433,3 +3440,13 @@ label[for*=expert_]
 .carousel .glide__arrow--left {
     left: -5em;
 }
+.archived_course {
+    color: [% css_colors.text_badnews %];
+    font-weight: bold;
+}
+.course_details {
+    padding-top: 1em;
+}
+.course_details_div {
+    padding-bottom: 1em;
+}
\ No newline at end of file
diff --git a/Open-ILS/src/templates/opac/parts/course/body.tt2 b/Open-ILS/src/templates/opac/parts/course/body.tt2
new file mode 100644 (file)
index 0000000..cae56e2
--- /dev/null
@@ -0,0 +1,101 @@
+<div id='canvas_main' class='canvas'>
+  <div class="course_details">
+    <div>
+      <h1>
+        [% l(ctx.course.name) %] ([% ctx.course.course_number %])
+        [% IF ctx.course.is_archived == 't' %]
+        <span class="archived_course">
+          This course is inactive.
+        </span>
+        [% END %]
+      </h1>
+      [% IF ctx.instructors.size %]
+      <div class="course_instructors_div">
+        <strong>Course Instructors:</strong>
+        [%- FOR instructor IN ctx.instructors;
+          instructorString = '';
+          IF instructor.pref_family_name;
+            instructorString = instructorString _ instructor.pref_family_name _ ', ';
+          ELSE;
+            instructorString = instructorString _ instructor.family_name _ ', ';
+          END;
+          IF instructor.pref_first_given_name;
+            instructorString = instructorString _ instructor.pref_first_given_name;
+          ELSE;
+            instructorString = instructorString _ instructor.first_given_name;
+          END;
+          instructorString = instructorString _ ' (' _ l(instructor.usr_role) _ ')'; %]
+          <span class="course-instructor-div">[% instructorString %].</span>
+        [% END %]
+      </div>
+      [% END %]
+
+      <div class="course_details_div">
+        <h2>[% l('Course Details') %]</h2>
+        <div>
+          <span><strong>[% l('Course Title') %]: </strong></span>
+          <span>[% ctx.course.name %]</span>
+        </div>
+        <div>
+          <span><strong>[% l('Course Number') %]: </strong></span>
+          <span>[% ctx.course.course_number %]</span>
+        <div>
+        </div>
+          <span><strong>[% l('Section Number') %]: </strong></span>
+          <span>[% ctx.course.section_number %]</span>
+        </div>
+        <div>
+          [% 
+            owning_lib = ctx.get_aou(ctx.course.owning_lib);
+            lib_url = ctx.get_org_setting(owning_lib.id, 'lib.info_url');
+            prefer_external_url = ctx.get_org_setting(owning_lib.id, 'lib.prefer_external_url');
+            UNLESS lib_url && prefer_external_url;
+              lib_url = mkurl(ctx.opac_root _ '/library/' _ owning_lib.shortname, {}, 1);
+             END; 
+          %]
+          <span><strong>[% l('Owning Library') %]: </strong></span>
+          <span><a href="[% lib_url %]">[% owning_lib.name %]</a></span>
+        </div>
+      </div>
+      
+      [%- UNLESS ctx.course.is_archived == 't' %]
+      <div>
+        <span><h2>[% l('Course Materials') %]</h2></span>
+        <table class="table_no_border_space table_no_cell_pad table_no_border" width="100%" id="course_material_table">
+          <thead>
+            <tr>
+              <th scope="col">Location</th>
+              <th scope="col">Call Number</th>
+              <th scope="col">Title</th>
+              <th scope="col">Barcode</th>
+              <th scope="col">Relationship</th>
+              <th scope="col">Status</th>
+              <th scope="col">Shelving Location</th>
+            </tr>
+          </thead>
+          <tbody>
+            [% FOREACH copy_info IN ctx.course_materials %]
+              <tr>
+                <td>[%- INCLUDE "opac/parts/library_name_link.tt2"; -%]
+                    <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut">
+                    <meta property="price" content="0.00">
+                </td>
+                <td>[% copy_info.call_number %]</td>
+                <td>
+                  <a href="[% mkurl(ctx.opac_root _ '/record/' _ copy_info.record) %]">
+                    [% copy_info.title %]
+                  </a>
+                </td>
+                <td>[% copy_info.barcode %]</td>
+                <td>[% copy_info.relationship %]</td>
+                <td>[% copy_info.status.name %]</td>
+                <td>[% copy_info.location.name %]</td>
+              </tr>
+            [% END %]
+          </tbody>
+        </table>
+      </div>
+      [% END %]
+    </div>
+  </div>
+</div>