diff --git a/assets/js/Components/App.js b/assets/js/Components/App.js
index 6985d55b..9b4bab36 100644
--- a/assets/js/Components/App.js
+++ b/assets/js/Components/App.js
@@ -39,6 +39,7 @@ class App extends React.Component {
this.handleIssueSave = this.handleIssueSave.bind(this)
this.handleFileSave = this.handleFileSave.bind(this)
this.handleCourseRescan = this.handleCourseRescan.bind(this)
+ this.handleFullCourseRescan = this.handleFullCourseRescan.bind(this)
this.handleNewReport = this.handleNewReport.bind(this)
this.resizeFrame = this.resizeFrame.bind(this)
}
@@ -53,6 +54,7 @@ class App extends React.Component {
navigation={this.state.navigation}
handleNavigation={this.handleNavigation}
handleCourseRescan={this.handleCourseRescan}
+ handleFullCourseRescan={this.handleFullCourseRescan}
handleModal={this.handleModal} />
{(('welcome' !== this.state.navigation) && ('summary' !== this.state.navigation)) &&
@@ -148,6 +150,11 @@ class App extends React.Component {
return api.scanCourse(this.settings.course.id)
}
+ fullRescan() {
+ let api = new Api(this.settings)
+ return api.fullRescan(this.settings.course.id)
+ }
+
disableReview = () => {
return this.state.syncComplete && !this.state.disableReview
}
@@ -162,6 +169,16 @@ class App extends React.Component {
this.forceUpdate()
}
+ handleFullCourseRescan() {
+ if (this.state.hasNewReport) {
+ this.setState({ hasNewReport: false, syncComplete: false })
+ this.fullRescan()
+ .then((response) => response.json())
+ .then(this.handleNewReport)
+ }
+ this.forceUpdate()
+ }
+
handleNewReport(data) {
let report = this.state.report
let hasNewReport = this.state.hasNewReport
diff --git a/assets/js/Components/Header.js b/assets/js/Components/Header.js
index 5e860b80..ff5f492c 100644
--- a/assets/js/Components/Header.js
+++ b/assets/js/Components/Header.js
@@ -44,6 +44,7 @@ class Header extends React.Component {
{/*
this.handleMoreNav('settings')}>{this.props.t('menu.settings')} */}
{this.props.t('menu.scan_course')}
+ {this.props.t('menu.full_rescan')}
{this.props.t('menu.download_pdf')}
diff --git a/assets/js/Services/Api.js b/assets/js/Services/Api.js
index 3ace069d..330989c9 100644
--- a/assets/js/Services/Api.js
+++ b/assets/js/Services/Api.js
@@ -13,6 +13,7 @@ export default class Api {
adminCourses: '/api/admin/courses/account/{account}/term/{term}',
scanContent: '/api/sync/content/{contentItem}',
scanCourse: '/api/sync/{course}',
+ fullRescan: '/api/sync/rescan/{course}',
scanIssue: '/api/issues/{issue}/scan',
adminReport: '/api/admin/courses/{course}/reports/latest',
adminReportHistory: '/api/admin/reports/account/{account}/term/{term}',
@@ -233,6 +234,21 @@ export default class Api {
})
}
+ fullRescan(courseId)
+ {
+ const authToken = this.getAuthToken()
+ let url = `${this.apiUrl}${this.endpoints.fullRescan}`
+ url = url.replace('{course}', courseId)
+
+ return fetch(url, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-AUTH-TOKEN': authToken,
+ },
+ })
+ }
+
scanContent(contentId)
{
const authToken = this.getAuthToken()
diff --git a/src/Controller/SyncController.php b/src/Controller/SyncController.php
index d9157326..307201d7 100644
--- a/src/Controller/SyncController.php
+++ b/src/Controller/SyncController.php
@@ -71,6 +71,59 @@ public function requestSync(Course $course, LmsFetchService $lmsFetch)
return new JsonResponse($response);
}
+ #[Route('/api/sync/rescan/{course}', name: 'full_rescan')]
+ public function fullCourseRescan(Course $course, LmsFetchService $lmsFetch) {
+ $response = new ApiResponse();
+ $user = $this->getUser();
+ $reportArr = false;
+
+ try {
+ if (!$this->userHasCourseAccess($course)) {
+ throw new \Exception('msg.no_permissions');
+ }
+ if ($course->isDirty()) {
+ throw new \Exception('msg.course_scanning');
+ }
+ if (!$course->isActive()) {
+ $response->setData(0);
+ throw new \Exception('msg.sync.course_inactive');
+ }
+
+ $prevReport = $course->getPreviousReport();
+
+ $course->removeAllReports();
+
+ $lmsFetch->refreshLmsContent($course, $user);
+
+ $report = $course->getLatestReport();
+
+ if (!$report) {
+ throw new \Exception('msg.no_report_created');
+ }
+
+ $reportArr = $report->toArray();
+ $reportArr['files'] = $course->getFileItems();
+ $reportArr['issues'] = $course->getAllIssues();
+ $reportArr['contentItems'] = $course->getContentItems();
+
+ $response->setData($reportArr);
+
+ if ($prevReport && ($prevReport->getIssueCount() == $report->getIssueCount())) {
+ $response->addMessage('msg.no_new_content', 'success', 5000);
+ } else {
+ $response->addMessage('msg.new_content', 'success', 5000);
+ }
+ } catch (\Exception $e) {
+ if ('msg.course_scanning' === $e->getMessage()) {
+ $response->addMessage($e->getMessage(), 'info', 0, false);
+ } else {
+ $response->addMessage($e->getMessage(), 'error', 0);
+ }
+ }
+
+ return new JsonResponse($response);
+ }
+
#[Route('/api/sync/content/{contentItem}', name: 'content_sync', methods: ['GET'])]
public function requestContentSync(ContentItem $contentItem, LmsFetchService $lmsFetch, PhpAllyService $phpAlly)
{
diff --git a/src/Entity/Course.php b/src/Entity/Course.php
index 2e40a3a6..86d14c22 100644
--- a/src/Entity/Course.php
+++ b/src/Entity/Course.php
@@ -259,6 +259,12 @@ public function removeReport(Report $report): self
return $this;
}
+ public function removeAllReports(): self
+ {
+ $this->reports->clear();
+ return $this;
+ }
+
public function getPreviousReport(): ?Report
{
$reports = $this->reports->toArray();
diff --git a/translations/en.json b/translations/en.json
index fbc21735..816e1b07 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -124,6 +124,7 @@
"menu.admin": "Admin",
"menu.download_pdf": "Download PDF",
"menu.scan_course": "Rescan Course",
+ "menu.full_rescan": "Force Full Rescan",
"msg.no_permissions": "You do not have permission to access the specified course.",
"msg.course_scanning": "Course scanning...",
diff --git a/translations/es.json b/translations/es.json
index 38f4863c..001df103 100644
--- a/translations/es.json
+++ b/translations/es.json
@@ -124,6 +124,7 @@
"menu.admin": "Admin",
"menu.download_pdf": "Descargar PDF",
"menu.scan_course": "Volver a escanear Curso",
+ "menu.full_rescan": "Reiniciar escaneo completo",
"msg.no_permissions": "No tienes permiso para acceder al curso especificado.",
"msg.course_scanning": "Escaneando el curso...",