From 067ff3312946e9c42bcb760d73d2ada7e20c07d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81kos=20Hermann?= Date: Thu, 7 Mar 2024 14:27:26 +0100 Subject: [PATCH 01/23] Version bump --- apps/student/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/student/build.gradle b/apps/student/build.gradle index c487133e6d..815299e864 100644 --- a/apps/student/build.gradle +++ b/apps/student/build.gradle @@ -50,8 +50,8 @@ android { applicationId "com.instructure.candroid" minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 259 - versionName = '7.1.0' + versionCode = 260 + versionName = '7.2.0' vectorDrawables.useSupportLibrary = true multiDexEnabled = true From 640730867233e4a5ffcb38ee9d66ef7447ac7c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81kos=20Hermann?= Date: Thu, 7 Mar 2024 15:40:49 +0100 Subject: [PATCH 02/23] stub failing test --- .../java/com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt index 29efacabea..243bcc03c1 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt @@ -21,7 +21,9 @@ import androidx.test.espresso.Espresso import com.instructure.canvas.espresso.E2E import com.instructure.canvas.espresso.FeatureCategory import com.instructure.canvas.espresso.Priority +import com.instructure.canvas.espresso.ReleaseExclude import com.instructure.canvas.espresso.SecondaryFeatureCategory +import com.instructure.canvas.espresso.Stub import com.instructure.canvas.espresso.TestCategory import com.instructure.canvas.espresso.TestMetaData import com.instructure.canvasapi2.utils.toApiString @@ -52,6 +54,7 @@ class ScheduleE2ETest : StudentTest() { @JvmField var globalTimeout: Timeout = Timeout.millis(600000) // //TODO: workaround for that sometimes this test is running infinite time because of scrollToElement does not find an element. + @Stub @E2E @Test @TestMetaData(Priority.MANDATORY, FeatureCategory.CANVAS_FOR_ELEMENTARY, TestCategory.E2E, SecondaryFeatureCategory.SCHEDULE) From 08c410d5fccea1697a9cd4b9ebcee956e7997d4a Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Fri, 22 Mar 2024 14:54:35 +0100 Subject: [PATCH 03/23] Extend ModulesE2ETest with Unpublishable module item testing. (#2383) --- .../teacher/ui/e2e/ModulesE2ETest.kt | 43 +++++++++---------- .../teacher/ui/pages/ModulesPage.kt | 11 +++++ 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt index b0aaaa1a5a..2b2338650c 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt @@ -12,6 +12,7 @@ import com.instructure.dataseeding.api.DiscussionTopicsApi import com.instructure.dataseeding.api.ModulesApi import com.instructure.dataseeding.api.PagesApi import com.instructure.dataseeding.api.QuizzesApi +import com.instructure.dataseeding.api.SubmissionsApi import com.instructure.dataseeding.model.ModuleItemTypes import com.instructure.dataseeding.model.SubmissionType import com.instructure.dataseeding.util.days @@ -203,11 +204,15 @@ class ModulesE2ETest : TeacherComposeTest() { Log.d(PREPARATION_TAG, "Seeding data.") val data = seedData(students = 1, teachers = 1, courses = 1) val teacher = data.teachersList[0] + val student = data.studentsList[0] val course = data.coursesList[0] Log.d(PREPARATION_TAG, "Seeding 'Text Entry' assignment for '${course.name}' course.") val assignment = AssignmentsApi.createAssignment(course.id, teacher.token, withDescription = true, submissionTypes = listOf(SubmissionType.ONLINE_TEXT_ENTRY), dueAt = 1.days.fromNow.iso8601) + Log.d(PREPARATION_TAG,"Submit '${assignment.name}' assignment for '${student.name}' student.") + SubmissionsApi.seedAssignmentSubmission(course.id, student.token, assignment.id, submissionSeedsList = listOf(SubmissionsApi.SubmissionSeedInfo(amount = 1, submissionType = SubmissionType.ONLINE_TEXT_ENTRY))) + Log.d(PREPARATION_TAG, "Seeding another 'Text Entry' assignment for '${course.name}' course.") val assignment2 = AssignmentsApi.createAssignment(course.id, teacher.token, withDescription = true, submissionTypes = listOf(SubmissionType.ONLINE_TEXT_ENTRY), dueAt = 1.days.fromNow.iso8601) @@ -261,6 +266,7 @@ class ModulesE2ETest : TeacherComposeTest() { moduleListPage.assertModuleIsDisplayed(module2.name) moduleListPage.assertModuleNotPublished(module2.name) moduleListPage.assertModuleItemIsPublished(assignment.name) + moduleListPage.assertModuleStatusIconAlpha(assignment.name, 0.5f) moduleListPage.assertModuleItemIsPublished(quiz.title) moduleListPage.assertModuleItemIsPublished(discussionTopic.title) moduleListPage.assertModuleItemNotPublished(testPage.title) @@ -303,14 +309,16 @@ class ModulesE2ETest : TeacherComposeTest() { progressPage.assertProgressPageTitle(R.string.allModulesAndItems) progressPage.clickDone() - Log.d(STEP_TAG, "Assert that the proper snack bar text is displayed and the '${module.name}' module and all of it's items became unpublished.") + Log.d(STEP_TAG, "Assert that the proper snack bar text is displayed and the '${module.name}' module and all of it's items (except '${assignment.name}' assignment) became unpublished.") moduleListPage.assertSnackbarText(R.string.allModulesAndAllItemsUnpublished) moduleListPage.assertModuleNotPublished(module.name) - moduleListPage.assertModuleItemNotPublished(assignment.name) moduleListPage.assertModuleItemNotPublished(quiz.title) moduleListPage.assertModuleItemNotPublished(testPage.title) moduleListPage.assertModuleItemNotPublished(discussionTopic.title) + Log.d(STEP_TAG, "Assert that the '${assignment.name}' assignment remained published because it's unpublishable since it has a submission already.") + moduleListPage.assertModuleItemIsPublished(assignment.name) + Log.d(STEP_TAG, "Assert that '${module2.name}' module and all of it's items became unpublished.") moduleListPage.assertModuleNotPublished(module2.name) moduleListPage.assertModuleItemNotPublished(assignment2.name) @@ -330,7 +338,7 @@ class ModulesE2ETest : TeacherComposeTest() { Log.d(STEP_TAG, "Assert that the proper snack bar text is displayed and only the '${module.name}' module became published, but it's items remaining unpublished.") moduleListPage.assertSnackbarText(R.string.onlyModulesPublished) moduleListPage.assertModuleIsPublished(module.name) - moduleListPage.assertModuleItemNotPublished(assignment.name) + moduleListPage.assertModuleItemIsPublished(assignment.name) moduleListPage.assertModuleItemNotPublished(quiz.title) moduleListPage.assertModuleItemNotPublished(testPage.title) moduleListPage.assertModuleItemNotPublished(discussionTopic.title) @@ -373,14 +381,16 @@ class ModulesE2ETest : TeacherComposeTest() { progressPage.assertProgressPageTitle(R.string.selectedModulesAndItems) progressPage.clickDone() - Log.d(STEP_TAG, "Assert that the proper snack bar text is displayed and the '${module.name}' module and all of it's items became unpublished.") + Log.d(STEP_TAG, "Assert that the proper snack bar text is displayed and the '${module.name}' module and all of it's items (except '${assignment.name}' assignment) became unpublished.") moduleListPage.assertSnackbarText(R.string.moduleAndAllItemsUnpublished) moduleListPage.assertModuleNotPublished(module.name) - moduleListPage.assertModuleItemNotPublished(assignment.name) moduleListPage.assertModuleItemNotPublished(quiz.title) moduleListPage.assertModuleItemNotPublished(testPage.title) moduleListPage.assertModuleItemNotPublished(discussionTopic.title) + Log.d(STEP_TAG, "Assert that the '${assignment.name}' assignment remained published because it's unpublishable since it has a submission already.") + moduleListPage.assertModuleItemIsPublished(assignment.name) + Log.d(STEP_TAG, "Click on '${module.name}' module overflow.") moduleListPage.clickItemOverflow(module.name) @@ -390,24 +400,15 @@ class ModulesE2ETest : TeacherComposeTest() { device.waitForWindowUpdate(null, 3000) device.waitForIdle() - Log.d(STEP_TAG, "Assert that only the '${module.name}' module became published, but it's items remaining unpublished.") + Log.d(STEP_TAG, "Assert that only the '${module.name}' module became published, but it's items (except '${assignment.name}' assignment) remaining unpublished.") moduleListPage.assertModuleIsPublished(module.name) - moduleListPage.assertModuleItemNotPublished(assignment.name) + moduleListPage.assertModuleItemIsPublished(assignment.name) moduleListPage.assertModuleItemNotPublished(quiz.title) moduleListPage.assertModuleItemNotPublished(testPage.title) moduleListPage.assertModuleItemNotPublished(discussionTopic.title) //Bottom layer - One module item - Log.d(STEP_TAG, "Click on '${assignment.name}' assignment's overflow menu and publish it. Confirm the publish via the publish dialog.") - moduleListPage.clickItemOverflow(assignment.name) - moduleListPage.clickOnText(R.string.publishModuleItemAction) - moduleListPage.clickOnText(R.string.publishDialogPositiveButton) - - Log.d(STEP_TAG, "Assert that the 'Item published' snack bar has displayed and the '${assignment.name}' assignment became published.") - moduleListPage.assertSnackbarText(R.string.moduleItemPublished) - moduleListPage.assertModuleItemIsPublished(assignment.name) - Log.d(STEP_TAG, "Click on '${quiz.title}' quiz's overflow menu and publish it. Confirm the publish via the publish dialog.") moduleListPage.clickItemOverflow(quiz.title) moduleListPage.clickOnText(R.string.publishModuleItemAction) @@ -435,14 +436,10 @@ class ModulesE2ETest : TeacherComposeTest() { moduleListPage.assertSnackbarText(R.string.moduleItemPublished) moduleListPage.assertModuleItemIsPublished(discussionTopic.title) - Log.d(STEP_TAG, "Click on '${assignment.name}' assignment's overflow menu and unpublish it. Confirm the unpublish via the unpublish dialog.") + Log.d(STEP_TAG, "Try to click on '${assignment.name}' assignment's overflow menu (in order to unpublish it). Assert that a snack bar with a proper text will be displayed that it cannot be unpublished since it has student submissions.") moduleListPage.clickItemOverflow(assignment.name) - moduleListPage.clickOnText(R.string.unpublishModuleItemAction) - moduleListPage.clickOnText(R.string.unpublishDialogPositiveButton) - - Log.d(STEP_TAG, "Assert that the 'Item unpublished' snack bar has displayed and the '${assignment.name}' assignment became unpublished.") - moduleListPage.assertSnackbarText(R.string.moduleItemUnpublished) - moduleListPage.assertModuleItemNotPublished(assignment.name) + moduleListPage.assertSnackbarContainsText(assignment.name) + moduleListPage.assertModuleItemIsPublished(assignment.name) Log.d(STEP_TAG, "Click on '${quiz.title}' quiz's overflow menu and unpublish it. Confirm the unpublish via the unpublish dialog.") moduleListPage.clickItemOverflow(quiz.title) diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ModulesPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ModulesPage.kt index 5bf9682bad..e4db2a6609 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ModulesPage.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ModulesPage.kt @@ -5,6 +5,7 @@ import androidx.test.espresso.matcher.ViewMatchers.hasSibling import androidx.test.espresso.matcher.ViewMatchers.withChild import com.instructure.canvas.espresso.containsTextCaseInsensitive import com.instructure.espresso.RecyclerViewItemCountAssertion +import com.instructure.espresso.ViewAlphaAssertion import com.instructure.espresso.assertDisplayed import com.instructure.espresso.assertHasContentDescription import com.instructure.espresso.assertNotDisplayed @@ -113,6 +114,16 @@ class ModulesPage : BasePage() { ) } + /** + * Assert module status icon alpha value. + * + * @param moduleItemName The name of the module item. + * @param expectedAlphaValue The expected alpha (float) value. + */ + fun assertModuleStatusIconAlpha(moduleItemName: String, expectedAlphaValue: Float) { + onView(withId(R.id.moduleItemStatusIcon) + withParent(withId(R.id.statusWrapper) + hasSibling(withId(R.id.moduleItemTitle) + withText(moduleItemName)))).check(ViewAlphaAssertion(expectedAlphaValue)) + } + /** * Clicks on the collapse/expand icon. */ From a5111ddc152e1391379041c740e47a864ca89ad6 Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Sat, 23 Mar 2024 12:46:03 +0100 Subject: [PATCH 04/23] Fix master compilation error (#2389) * Fix compilation error on master (statusWrapper id changed to publishActions) * Increase wait time for Done status (sometimes API is too slow) --- .../java/com/instructure/teacher/ui/pages/ModulesPage.kt | 2 +- .../java/com/instructure/teacher/ui/pages/ProgressPage.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ModulesPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ModulesPage.kt index e4db2a6609..8b1cd6c598 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ModulesPage.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ModulesPage.kt @@ -121,7 +121,7 @@ class ModulesPage : BasePage() { * @param expectedAlphaValue The expected alpha (float) value. */ fun assertModuleStatusIconAlpha(moduleItemName: String, expectedAlphaValue: Float) { - onView(withId(R.id.moduleItemStatusIcon) + withParent(withId(R.id.statusWrapper) + hasSibling(withId(R.id.moduleItemTitle) + withText(moduleItemName)))).check(ViewAlphaAssertion(expectedAlphaValue)) + onView(withId(R.id.moduleItemStatusIcon) + withParent(withId(R.id.publishActions) + hasSibling(withId(R.id.moduleItemTitle) + withText(moduleItemName)))).check(ViewAlphaAssertion(expectedAlphaValue)) } /** diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ProgressPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ProgressPage.kt index 9a952b34aa..252f377e50 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ProgressPage.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ProgressPage.kt @@ -33,7 +33,7 @@ class ProgressPage(private val composeTestRule: ComposeTestRule) : BasePage() { fun clickDone() { composeTestRule.waitForIdle() - composeTestRule.waitUntilExactlyOneExists(hasText("Done"), 10000) + composeTestRule.waitUntilExactlyOneExists(hasText("Done"), 20000) composeTestRule.onNodeWithText("Done").performClick() } From d5fd97af95c398e102ee727c9cb5e53a484947d1 Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Thu, 4 Apr 2024 10:01:27 +0200 Subject: [PATCH 05/23] [MBL-17420][Teacher] - Test File module item update (#2393) --- .../student/ui/e2e/DiscussionsE2ETest.kt | 4 +- .../e2e/offline/OfflineDiscussionsE2ETest.kt | 4 +- .../teacher/ui/ModuleListPageTest.kt | 6 +- .../teacher/ui/e2e/ModulesE2ETest.kt | 126 ++++++++++++++++++ .../ui/pages/UpdateFilePermissionsPage.kt | 48 ++++++- .../teacher/ui/utils/TeacherTestExtensions.kt | 2 +- .../com/instructure/espresso/TestingUtils.kt | 13 +- 7 files changed, 190 insertions(+), 13 deletions(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DiscussionsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DiscussionsE2ETest.kt index aa5efe83d0..cd1c0ae152 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DiscussionsE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DiscussionsE2ETest.kt @@ -26,7 +26,7 @@ import com.instructure.canvas.espresso.TestCategory import com.instructure.canvas.espresso.TestMetaData import com.instructure.dataseeding.api.DiscussionTopicsApi import com.instructure.espresso.ViewUtils -import com.instructure.espresso.getCurrentDateInCanvasFormat +import com.instructure.espresso.getDateInCanvasFormat import com.instructure.student.ui.utils.StudentTest import com.instructure.student.ui.utils.seedData import com.instructure.student.ui.utils.tokenLogin @@ -154,7 +154,7 @@ class DiscussionsE2ETest: StudentTest() { discussionListPage.assertUnreadReplyCount(newTopicName, 0) Log.d(STEP_TAG, "Assert that the due date is the current date (in the expected format).") - val currentDate = getCurrentDateInCanvasFormat() + val currentDate = getDateInCanvasFormat() discussionListPage.assertDueDate(newTopicName, currentDate) } diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/offline/OfflineDiscussionsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/offline/OfflineDiscussionsE2ETest.kt index 2fccbc67e4..37fad33e9d 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/offline/OfflineDiscussionsE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/offline/OfflineDiscussionsE2ETest.kt @@ -26,7 +26,7 @@ import com.instructure.canvas.espresso.TestCategory import com.instructure.canvas.espresso.TestMetaData import com.instructure.canvas.espresso.checkToastText import com.instructure.dataseeding.api.DiscussionTopicsApi -import com.instructure.espresso.getCurrentDateInCanvasFormat +import com.instructure.espresso.getDateInCanvasFormat import com.instructure.student.R import com.instructure.student.ui.e2e.offline.utils.OfflineTestUtils import com.instructure.student.ui.utils.StudentTest @@ -127,7 +127,7 @@ class OfflineDiscussionsE2ETest : StudentTest() { discussionListPage.assertUnreadReplyCount(discussion1.title, 0) Log.d(STEP_TAG, "Assert that the due date is the current date (in the expected format).") - val currentDate = getCurrentDateInCanvasFormat() + val currentDate = getDateInCanvasFormat() discussionListPage.assertDueDate(discussion1.title, currentDate) Log.d(STEP_TAG, "Click on the Search (magnifying glass) icon and the '${discussion1.title}' discussion's title into the search input field.") diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/ModuleListPageTest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/ModuleListPageTest.kt index 6dea81f0a7..5bcdec9eab 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/ModuleListPageTest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/ModuleListPageTest.kt @@ -322,7 +322,7 @@ class ModuleListPageTest : TeacherComposeTest() { updateFilePermissionsPage.swipeUpBottomSheet() updateFilePermissionsPage.clickUnpublishRadioButton() - updateFilePermissionsPage.clickSaveButton() + updateFilePermissionsPage.clickUpdateButton() moduleListPage.assertModuleItemNotPublished(fileFolder.displayName.orEmpty()) } @@ -353,7 +353,7 @@ class ModuleListPageTest : TeacherComposeTest() { updateFilePermissionsPage.swipeUpBottomSheet() updateFilePermissionsPage.clickPublishRadioButton() - updateFilePermissionsPage.clickSaveButton() + updateFilePermissionsPage.clickUpdateButton() moduleListPage.assertModuleItemIsPublished(fileFolder.displayName.orEmpty()) } @@ -384,7 +384,7 @@ class ModuleListPageTest : TeacherComposeTest() { updateFilePermissionsPage.swipeUpBottomSheet() updateFilePermissionsPage.clickHideRadioButton() - updateFilePermissionsPage.clickSaveButton() + updateFilePermissionsPage.clickUpdateButton() moduleListPage.assertModuleItemHidden(fileFolder.displayName.orEmpty()) } diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt index 2b2338650c..daabbaaef9 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt @@ -9,20 +9,24 @@ import com.instructure.canvas.espresso.TestCategory import com.instructure.canvas.espresso.TestMetaData import com.instructure.dataseeding.api.AssignmentsApi import com.instructure.dataseeding.api.DiscussionTopicsApi +import com.instructure.dataseeding.api.FileFolderApi import com.instructure.dataseeding.api.ModulesApi import com.instructure.dataseeding.api.PagesApi import com.instructure.dataseeding.api.QuizzesApi import com.instructure.dataseeding.api.SubmissionsApi +import com.instructure.dataseeding.model.FileUploadType import com.instructure.dataseeding.model.ModuleItemTypes import com.instructure.dataseeding.model.SubmissionType import com.instructure.dataseeding.util.days import com.instructure.dataseeding.util.fromNow import com.instructure.dataseeding.util.iso8601 +import com.instructure.espresso.getCustomDateCalendar import com.instructure.teacher.R import com.instructure.teacher.ui.utils.TeacherComposeTest import com.instructure.teacher.ui.utils.openOverflowMenu import com.instructure.teacher.ui.utils.seedData import com.instructure.teacher.ui.utils.tokenLogin +import com.instructure.teacher.ui.utils.uploadTextFile import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Test @@ -469,4 +473,126 @@ class ModulesE2ETest : TeacherComposeTest() { moduleListPage.assertModuleItemNotPublished(discussionTopic.title) } + @E2E + @Test + @TestMetaData(Priority.IMPORTANT, FeatureCategory.MODULES, TestCategory.E2E) + fun testFileModuleItemUpdateE2E() { + + Log.d(PREPARATION_TAG, "Seeding data.") + val data = seedData(students = 1, teachers = 1, courses = 1) + val teacher = data.teachersList[0] + val course = data.coursesList[0] + + Log.d(PREPARATION_TAG, "Seeding a module for '${course.name}' course. It starts as unpublished.") + val module = ModulesApi.createModule(course.id, teacher.token) + + Log.d(PREPARATION_TAG, "Get the root folder of the course.") + val courseRootFolder = FileFolderApi.getCourseRootFolder(course.id, teacher.token) + + Log.d(PREPARATION_TAG, "Create a (text) file within the root folder (so the 'Files' tab file list) of the '${course.name}' course.") + val testTextFile = uploadTextFile(courseRootFolder.id, token = teacher.token, fileUploadType = FileUploadType.COURSE_FILE) + + Log.d(PREPARATION_TAG, "Create another (text) file within the root folder (so the 'Files' tab file list) of the '${course.name}' course.") + val testTextFile2 = uploadTextFile(courseRootFolder.id, token = teacher.token, fileUploadType = FileUploadType.COURSE_FILE) + + Log.d(PREPARATION_TAG, "Associate '${testTextFile.fileName}' (course) file with module: '${module.id}'.") + ModulesApi.createModuleItem( + course.id, + teacher.token, + module.id, + moduleItemTitle = testTextFile.fileName, + moduleItemType = ModuleItemTypes.FILE.stringVal, + contentId = testTextFile.id.toString() + ) + + Log.d(PREPARATION_TAG, "Associate '${testTextFile2.fileName}' (course) file with module: '${module.id}'.") + ModulesApi.createModuleItem( + course.id, + teacher.token, + module.id, + moduleItemTitle = testTextFile2.fileName, + moduleItemType = ModuleItemTypes.FILE.stringVal, + contentId = testTextFile2.id.toString() + ) + + Log.d(STEP_TAG, "Login with user: '${teacher.name}', login id: '${teacher.loginId}'. Assert that '${course.name}' course is displayed on the Dashboard.") + tokenLogin(teacher) + dashboardPage.waitForRender() + + Log.d(STEP_TAG, "Open '${course.name}' course and navigate to Modules Page.") + dashboardPage.openCourse(course.name) + courseBrowserPage.openModulesTab() + + Log.d(STEP_TAG, "Assert that both, the '${testTextFile.fileName}' and '${testTextFile2.fileName}' files are published.") + moduleListPage.assertModuleItemIsPublished(testTextFile.fileName) + moduleListPage.assertModuleItemIsPublished(testTextFile2.fileName) + + Log.d(STEP_TAG, "Click on the 'more menu' of the '${testTextFile.fileName}' file.") + moduleListPage.clickItemOverflow(testTextFile.fileName) + + Log.d(STEP_TAG, "Assert that by default, on the Update File Permissions Page the 'Published' radio button is checked within the 'Availability' section.") + updateFilePermissionsPage.assertFilePublished() + + Log.d(STEP_TAG, "Assert that the 'Visibility' section radio buttons are enabled and the 'Inherit from course' radio button is checked by default within the 'Visibility' section.") + updateFilePermissionsPage.assertVisibilityEnabled() + updateFilePermissionsPage.assertFileVisibilityInherit() + + Log.d(STEP_TAG, "Click on the 'Unpublish' radio button and assert that the 'Visibility' section radio buttons became disabled.") + updateFilePermissionsPage.clickUnpublishRadioButton() + updateFilePermissionsPage.assertVisibilityDisabled() + + Log.d(STEP_TAG, "Click on the 'Update' button and assert on the Module List Page that the '${testTextFile.fileName}' file became unpublished.") + updateFilePermissionsPage.clickUpdateButton() + moduleListPage.assertModuleItemNotPublished(testTextFile.fileName) + + Log.d(STEP_TAG, "Click on the 'more menu' of the '${testTextFile.fileName}' file.") + moduleListPage.clickItemOverflow(testTextFile.fileName) + + Log.d(STEP_TAG, "Assert that the 'Unpublished' radio button is checked because of the previous modifications.") + updateFilePermissionsPage.assertFileUnpublished() + + Log.d(STEP_TAG, "Click on the 'Only available with link' (aka. 'Hide') radio button and click on the 'Update' button to save the changes.") + updateFilePermissionsPage.clickHideRadioButton() + updateFilePermissionsPage.clickUpdateButton() + + Log.d(STEP_TAG, "Assert that the '${testTextFile.fileName}' file module item became hidden.") + moduleListPage.assertModuleItemHidden(testTextFile.fileName) + + Log.d(STEP_TAG, "Click on the 'more menu' of the '${testTextFile.fileName}' file.") + moduleListPage.clickItemOverflow(testTextFile.fileName) + + Log.d(STEP_TAG, "Assert that the 'Hidden' radio button is checked because of the previous modifications.") + updateFilePermissionsPage.assertFileHidden() + + Log.d(STEP_TAG, "Click on the 'Schedule availability' (aka. 'Scheduled') radio button without setting any 'From' and 'Until' dates and click on the 'Update' button to save the changes.") + updateFilePermissionsPage.clickScheduleRadioButton() + updateFilePermissionsPage.clickUpdateButton() + + Log.d(STEP_TAG, "Assert that the '${testTextFile.fileName}' file is published since that is the expected behaviour if we does not select any dates for schedule.") + moduleListPage.assertModuleItemIsPublished(testTextFile.fileName) + + Log.d(STEP_TAG, "Click on the 'more menu' of the '${testTextFile.fileName}' file.") + moduleListPage.clickItemOverflow(testTextFile.fileName) + + Log.d(STEP_TAG, "Assert that by default, on the Update File Permissions Page the 'Published' radio button is checked within the 'Availability' section.") + updateFilePermissionsPage.assertFilePublished() + + Log.d(STEP_TAG, "Click on the 'Schedule availability' (aka. 'Scheduled') radio button, set some dates and click on the 'Update' button to save the changes.") + updateFilePermissionsPage.clickScheduleRadioButton() + + Log.d(PREPARATION_TAG, "Create a calendar with 3 days ago and another one with 3 days later.") + val unlockDateCalendar = getCustomDateCalendar(-3) + val lockDateCalendar = getCustomDateCalendar(3) + + Log.d(STEP_TAG, "Set the 'From' and 'Until' dates (and times) as well. These are coming from the calendars which has been previously created.") + updateFilePermissionsPage.setFromDateTime(unlockDateCalendar) + updateFilePermissionsPage.setUntilDateTime(lockDateCalendar) + + Log.d(STEP_TAG, "Click on the 'Update' button to save the changes.") + updateFilePermissionsPage.clickUpdateButton() + + Log.d(STEP_TAG, "Assert that the '${testTextFile.fileName}' file module item is scheduled.") + moduleListPage.assertModuleItemScheduled(testTextFile.fileName) + } + } \ No newline at end of file diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/UpdateFilePermissionsPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/UpdateFilePermissionsPage.kt index 798d03d596..e3c9f2d060 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/UpdateFilePermissionsPage.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/UpdateFilePermissionsPage.kt @@ -18,6 +18,13 @@ package com.instructure.teacher.ui.pages +import android.widget.DatePicker +import android.widget.NumberPicker +import android.widget.TimePicker +import androidx.databinding.adapters.TextViewBindingAdapter.setText +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.contrib.PickerActions +import androidx.test.espresso.matcher.ViewMatchers.withClassName import com.instructure.espresso.OnViewWithId import com.instructure.espresso.assertChecked import com.instructure.espresso.assertDisabled @@ -26,18 +33,27 @@ import com.instructure.espresso.assertEnabled import com.instructure.espresso.assertHasText import com.instructure.espresso.assertNotDisplayed import com.instructure.espresso.click +import com.instructure.espresso.getDateInCanvasFormat import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.onViewWithId import com.instructure.espresso.page.onViewWithText +import com.instructure.espresso.page.waitForViewWithClassName import com.instructure.espresso.page.waitForViewWithId +import com.instructure.espresso.page.withId +import com.instructure.espresso.replaceText import com.instructure.espresso.scrollTo import com.instructure.espresso.swipeUp +import com.instructure.espresso.typeText import com.instructure.teacher.R +import org.hamcrest.Matchers import java.text.SimpleDateFormat +import java.util.Calendar import java.util.Date class UpdateFilePermissionsPage : BasePage() { - private val saveButton by OnViewWithId(R.id.updateButton) + private val updateButton by OnViewWithId(R.id.updateButton) private val publishRadioButton by OnViewWithId(R.id.publish) private val unpublishRadioButton by OnViewWithId(R.id.unpublish) private val hideRadioButton by OnViewWithId(R.id.hide) @@ -84,8 +100,8 @@ class UpdateFilePermissionsPage : BasePage() { publicRadioButton.assertChecked() } - fun clickSaveButton() { - saveButton.click() + fun clickUpdateButton() { + updateButton.click() } fun clickPublishRadioButton() { @@ -100,6 +116,10 @@ class UpdateFilePermissionsPage : BasePage() { waitForViewWithId(R.id.hide).click() } + fun clickScheduleRadioButton() { + waitForViewWithId(R.id.schedule).click() + } + fun assertScheduleLayoutDisplayed() { scheduleLayout.assertDisplayed() } @@ -144,4 +164,26 @@ class UpdateFilePermissionsPage : BasePage() { onViewWithText(R.string.edit_permissions).swipeUp() Thread.sleep(1000) } + + fun setFromDateTime(calendar: Calendar) { + availableFromDate.perform(click()) + waitForViewWithClassName(Matchers.equalTo(DatePicker::class.java.name)).perform(PickerActions.setDate(calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH))) + onViewWithId(android.R.id.button1).click() + availableFromTime.perform(click()) + onView(withClassName(Matchers.equalTo(TimePicker::class.java.name))) + .perform(PickerActions.setTime(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))) + onViewWithId(android.R.id.button1).click() + } + + fun setUntilDateTime(calendar: Calendar) { + availableUntilDate.perform(click()) + waitForViewWithClassName(Matchers.equalTo(DatePicker::class.java.name)).perform(PickerActions.setDate(calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH))) + onViewWithId(android.R.id.button1).click() + availableUntilTime.perform(click()) + onView(withClassName(Matchers.equalTo(TimePicker::class.java.name))) + .perform(PickerActions.setTime(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))) + onViewWithId(android.R.id.button1).click() + } } \ No newline at end of file diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherTestExtensions.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherTestExtensions.kt index 37ecc53877..91337ad3f7 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherTestExtensions.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherTestExtensions.kt @@ -279,7 +279,7 @@ fun TeacherTest.seedAssignmentSubmission( return SubmissionsApi.seedAssignmentSubmission(courseId, studentToken, assignmentId, commentSeeds, submissionSeeds) } -fun TeacherTest.uploadTextFile(courseId: Long, assignmentId: Long, token: String, fileUploadType: FileUploadType): AttachmentApiModel { +fun TeacherTest.uploadTextFile(courseId: Long, assignmentId: Long? = null, token: String, fileUploadType: FileUploadType): AttachmentApiModel { // Create the file val file = File( diff --git a/automation/espresso/src/main/kotlin/com/instructure/espresso/TestingUtils.kt b/automation/espresso/src/main/kotlin/com/instructure/espresso/TestingUtils.kt index 42fe0e5770..3ef1601647 100644 --- a/automation/espresso/src/main/kotlin/com/instructure/espresso/TestingUtils.kt +++ b/automation/espresso/src/main/kotlin/com/instructure/espresso/TestingUtils.kt @@ -43,14 +43,23 @@ fun capitalizeFirstLetter(inputText: String): String { @RequiresApi(Build.VERSION_CODES.O) -fun getCurrentDateInCanvasFormat(): String { - val expectedDate = LocalDateTime.now() +fun getDateInCanvasFormat(date: LocalDateTime? = null): String { + val expectedDate = date ?: LocalDateTime.now() val monthString = capitalizeFirstLetter(expectedDate.month.name.take(3)) val dayString = expectedDate.dayOfMonth val yearString = expectedDate.year return "$monthString $dayString, $yearString" } +fun getCustomDateCalendar(dayDiffFromToday: Int): Calendar { + val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")) + cal.add(Calendar.DATE, dayDiffFromToday) + cal.set(Calendar.HOUR_OF_DAY, 10) + cal.set(Calendar.MINUTE, 1) + cal.set(Calendar.SECOND, 1) + return cal +} + fun retry( times: Int = 3, delay: Long = 1000, From 58a8f1068f5436c793c60834107af72796856369 Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Thu, 4 Apr 2024 10:01:47 +0200 Subject: [PATCH 06/23] Test previous and next arrows in Student ModulesE2ETest. (#2397) --- .../student/ui/e2e/ModulesE2ETest.kt | 60 +++++++++++++++++-- .../student/ui/pages/AssignmentDetailsPage.kt | 3 +- .../student/ui/pages/DiscussionDetailsPage.kt | 3 +- .../student/ui/pages/GoToQuizPage.kt | 42 +++++++++++++ .../student/ui/pages/PageDetailsPage.kt | 37 ++++++++++++ .../student/ui/utils/StudentTest.kt | 9 ++- 6 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 apps/student/src/androidTest/java/com/instructure/student/ui/pages/GoToQuizPage.kt create mode 100644 apps/student/src/androidTest/java/com/instructure/student/ui/pages/PageDetailsPage.kt diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ModulesE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ModulesE2ETest.kt index cf15715843..ad88ae8ba2 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ModulesE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ModulesE2ETest.kt @@ -89,12 +89,12 @@ class ModulesE2ETest: StudentTest() { Log.d(PREPARATION_TAG,"Associate '${assignment2.name}' assignment with '${module2.name}' module.") ModulesApi.createModuleItem(course.id, teacher.token, module2.id, assignment2.name, ModuleItemTypes.ASSIGNMENT.stringVal, contentId = assignment2.id.toString()) - Log.d(PREPARATION_TAG,"Associate '${page1.title}' page with '${module2.name}' module.") - ModulesApi.createModuleItem(course.id, teacher.token, module2.id, page1.title, ModuleItemTypes.PAGE.stringVal, pageUrl = page1.url) - Log.d(PREPARATION_TAG,"Associate '${discussionTopic1.title}' discussion topic with '${module2.name}' module.") ModulesApi.createModuleItem(course.id, teacher.token, module2.id, discussionTopic1.title, ModuleItemTypes.DISCUSSION.stringVal, contentId = discussionTopic1.id.toString()) + Log.d(PREPARATION_TAG,"Associate '${quiz1.title}' page with '${module2.name}' module.") + ModulesApi.createModuleItem(course.id, teacher.token, module2.id, page1.title, ModuleItemTypes.PAGE.stringVal, pageUrl = page1.url) + Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.") tokenLogin(student) dashboardPage.waitForRender() @@ -149,9 +149,61 @@ class ModulesE2ETest: StudentTest() { modulesPage.clickOnModuleExpandCollapseIcon(module2.name) modulesPage.assertModulesAndItemsCount(7) // 2 modules titles, 2 module items in first module, 3 items in second module - Log.d(STEP_TAG, "Assert that '${assignment1.name}' module item is displayed and open it. Assert that the Assignment Details page is displayed with the corresponding assignment title.") + Log.d(STEP_TAG, "Assert that '${assignment1.name}' module item is displayed and open it. Assert that the Assignment Details page is displayed with the corresponding assignment name: '${assignment1.name}'.") modulesPage.assertAndClickModuleItem(module1.name, assignment1.name, true) assignmentDetailsPage.assertPageObjects() assignmentDetailsPage.assertAssignmentTitle(assignment1.name) + + Log.d(STEP_TAG, "Assert that the module name, '${module1.name}' is displayed at the bottom.") + assignmentDetailsPage.moduleItemInteractions.assertModuleNameDisplayed(module1.name) + + Log.d(STEP_TAG, "Assert that the previous arrow button is not displayed because the user is on the first module item's details page, but the next arrow button is displayed.") + assignmentDetailsPage.moduleItemInteractions.assertPreviousArrowNotDisplayed() + assignmentDetailsPage.moduleItemInteractions.assertNextArrowDisplayed() + + Log.d(STEP_TAG, "Click on the next arrow button and assert that the '${quiz1.title}' quiz module item's 'Go To Quiz' page is displayed.") + assignmentDetailsPage.moduleItemInteractions.clickOnNextArrow() + goToQuizPage.assertQuizTitle(quiz1.title) + + Log.d(STEP_TAG, "Assert that the module name, '${module1.name}' is displayed at the bottom.") + goToQuizPage.moduleItemInteractions.assertModuleNameDisplayed(module1.name) + + Log.d(STEP_TAG, "Assert that both the previous and the next buttons are displayed (since we are not at the first or the last module item details page).") + goToQuizPage.moduleItemInteractions.assertPreviousArrowDisplayed() + goToQuizPage.moduleItemInteractions.assertNextArrowDisplayed() + + Log.d(STEP_TAG, "Click on the next arrow button and assert that the Assignment Details Page is displayed with the corresponding assignment name: '${assignment2.name}'.") + goToQuizPage.moduleItemInteractions.clickOnNextArrow() + assignmentDetailsPage.assertPageObjects() + assignmentDetailsPage.assertAssignmentTitle(assignment2.name) + + Log.d(STEP_TAG, "Assert that the second module name, '${module2.name}' is displayed at the bottom since we can navigate even between modules with these arrows.") + assignmentDetailsPage.moduleItemInteractions.assertModuleNameDisplayed(module2.name) + + Log.d(STEP_TAG, "Assert that both the previous and the next buttons are displayed (since we are not at the first or the last module item details page, even if it's a first item of a module, but of the second module).") + assignmentDetailsPage.moduleItemInteractions.assertPreviousArrowDisplayed() + assignmentDetailsPage.moduleItemInteractions.assertNextArrowDisplayed() + + Log.d(STEP_TAG, "Click on the next arrow button and assert that the '${discussionTopic1.title}' discussion topic module item's details page is displayed.") + assignmentDetailsPage.moduleItemInteractions.clickOnNextArrow() + discussionDetailsPage.assertTitleText(discussionTopic1.title) + + Log.d(STEP_TAG, "Assert that the second module name, '${module2.name}' is displayed at the bottom.") + discussionDetailsPage.moduleItemInteractions.assertModuleNameDisplayed(module2.name) + + Log.d(STEP_TAG, "Assert that both the previous and the next buttons are displayed.") + discussionDetailsPage.moduleItemInteractions.assertPreviousArrowDisplayed() + discussionDetailsPage.moduleItemInteractions.assertNextArrowDisplayed() + + Log.d(STEP_TAG, "Click on the next arrow button and assert that the '${page1.url}' page module item's details page is displayed.") + discussionDetailsPage.moduleItemInteractions.clickOnNextArrow() + pageDetailsPage.webAssertPageUrl(page1.url) + + Log.d(STEP_TAG, "Assert that the second module name, '${module2.name}' is displayed at the bottom.") + pageDetailsPage.moduleItemInteractions.assertModuleNameDisplayed(module2.name) + + Log.d(STEP_TAG, "Assert that the previous arrow button is displayed but the next arrow button is not displayed because the user is on the last module item's details page.") + pageDetailsPage.moduleItemInteractions.assertPreviousArrowDisplayed() + pageDetailsPage.moduleItemInteractions.assertNextArrowNotDisplayed() } } \ No newline at end of file diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/AssignmentDetailsPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/AssignmentDetailsPage.kt index 871d533d39..b001e424f2 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/AssignmentDetailsPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/AssignmentDetailsPage.kt @@ -38,6 +38,7 @@ import com.instructure.canvas.espresso.stringContainsTextCaseInsensitive import com.instructure.canvas.espresso.waitForMatcherWithSleeps import com.instructure.canvasapi2.models.Assignment import com.instructure.dataseeding.model.SubmissionType +import com.instructure.espresso.ModuleItemInteractions import com.instructure.espresso.OnViewWithId import com.instructure.espresso.assertContainsText import com.instructure.espresso.assertDisplayed @@ -66,7 +67,7 @@ import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.anything import org.hamcrest.Matchers.not -open class AssignmentDetailsPage : BasePage(R.id.assignmentDetailsPage) { +open class AssignmentDetailsPage(val moduleItemInteractions: ModuleItemInteractions) : BasePage(R.id.assignmentDetailsPage) { val toolbar by OnViewWithId(R.id.toolbar) val points by OnViewWithId(R.id.points) val date by OnViewWithId(R.id.dueDateTextView) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DiscussionDetailsPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DiscussionDetailsPage.kt index 026f1f3f52..63bc20c3ff 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DiscussionDetailsPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DiscussionDetailsPage.kt @@ -36,6 +36,7 @@ import com.instructure.canvas.espresso.withCustomConstraints import com.instructure.canvas.espresso.withElementRepeat import com.instructure.canvasapi2.models.DiscussionEntry import com.instructure.canvasapi2.models.DiscussionTopicHeader +import com.instructure.espresso.ModuleItemInteractions import com.instructure.espresso.OnViewWithId import com.instructure.espresso.assertDisplayed import com.instructure.espresso.assertGone @@ -56,7 +57,7 @@ import org.hamcrest.Matchers.containsString import org.junit.Assert.assertTrue -class DiscussionDetailsPage : BasePage(R.id.discussionDetailsPage) { +class DiscussionDetailsPage(val moduleItemInteractions: ModuleItemInteractions) : BasePage(R.id.discussionDetailsPage) { private val discussionTopicTitle by OnViewWithId(R.id.discussionTopicTitle) private val replyButton by OnViewWithId(R.id.replyToDiscussionTopic) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/GoToQuizPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/GoToQuizPage.kt new file mode 100644 index 0000000000..9d6cc3af2c --- /dev/null +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/GoToQuizPage.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.instructure.student.ui.pages + +import com.instructure.espresso.ModuleItemInteractions +import com.instructure.espresso.OnViewWithText +import com.instructure.espresso.assertDisplayed +import com.instructure.espresso.click +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.withAncestor +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withText +import com.instructure.student.R + +class GoToQuizPage(val moduleItemInteractions: ModuleItemInteractions) : BasePage() { + + private val goToQuizButton by OnViewWithText(R.string.goToQuiz) + + fun clickGoToQuizButton() { + goToQuizButton.click() + } + + fun assertQuizTitle(expectedTitle: String) { + onView(withId(R.id.quizTitle) + withText(expectedTitle) + withAncestor(R.id.quizInfoContainer)).assertDisplayed() + } +} diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/PageDetailsPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/PageDetailsPage.kt new file mode 100644 index 0000000000..81b06aaf67 --- /dev/null +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/PageDetailsPage.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.instructure.student.ui.pages + +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.web.assertion.WebViewAssertions.webMatches +import androidx.test.espresso.web.model.Atoms +import androidx.test.espresso.web.sugar.Web.onWebView +import com.instructure.espresso.ModuleItemInteractions +import com.instructure.espresso.page.BasePage +import com.instructure.student.R +import org.hamcrest.Matchers.allOf +import org.hamcrest.Matchers.containsString + +class PageDetailsPage(val moduleItemInteractions: ModuleItemInteractions) : BasePage() { + + fun webAssertPageUrl(pageUrl: String) { + onWebView(allOf(withId(R.id.contentWebView), isDisplayed())) + .check(webMatches(Atoms.getCurrentUrl(), containsString(pageUrl))) + } + +} \ No newline at end of file diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentTest.kt index e72cd5bd6b..2bdebb30ac 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentTest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentTest.kt @@ -35,6 +35,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.instructure.canvas.espresso.CanvasTest import com.instructure.espresso.InstructureActivityTestRule +import com.instructure.espresso.ModuleItemInteractions import com.instructure.espresso.Searchable import com.instructure.espresso.swipeRight import com.instructure.pandautils.utils.Const @@ -62,6 +63,7 @@ import com.instructure.student.ui.pages.ElementaryCoursePage import com.instructure.student.ui.pages.ElementaryDashboardPage import com.instructure.student.ui.pages.FileListPage import com.instructure.student.ui.pages.FileUploadPage +import com.instructure.student.ui.pages.GoToQuizPage import com.instructure.student.ui.pages.GradesPage import com.instructure.student.ui.pages.GroupBrowserPage import com.instructure.student.ui.pages.HelpPage @@ -78,6 +80,7 @@ import com.instructure.student.ui.pages.ModuleProgressionPage import com.instructure.student.ui.pages.ModulesPage import com.instructure.student.ui.pages.NewMessagePage import com.instructure.student.ui.pages.NotificationPage +import com.instructure.student.ui.pages.PageDetailsPage import com.instructure.student.ui.pages.PageListPage import com.instructure.student.ui.pages.PairObserverPage import com.instructure.student.ui.pages.PandaAvatarPage @@ -150,7 +153,7 @@ abstract class StudentTest : CanvasTest() { */ val annotationCommentListPage = AnnotationCommentListPage() val announcementListPage = AnnouncementListPage(Searchable(R.id.search, R.id.search_src_text, R.id.search_close_btn)) - val assignmentDetailsPage = AssignmentDetailsPage() + val assignmentDetailsPage = AssignmentDetailsPage(ModuleItemInteractions(R.id.moduleName, R.id.next_item, R.id.prev_item)) val assignmentListPage = AssignmentListPage(Searchable(R.id.search, R.id.search_src_text)) val bookmarkPage = BookmarkPage() val calendarEventPage = CalendarEventPage() @@ -163,7 +166,7 @@ abstract class StudentTest : CanvasTest() { val courseGradesPage = CourseGradesPage() val dashboardPage = DashboardPage() val leftSideNavigationDrawerPage = LeftSideNavigationDrawerPage() - val discussionDetailsPage = DiscussionDetailsPage() + val discussionDetailsPage = DiscussionDetailsPage(ModuleItemInteractions(R.id.moduleName, R.id.next_item, R.id.prev_item)) val discussionListPage = DiscussionListPage(Searchable(R.id.search, R.id.search_src_text, R.id.search_close_btn)) val allCoursesPage = AllCoursesPage() val fileListPage = FileListPage(Searchable(R.id.search, R.id.queryInput, R.id.clearButton, R.id.backButton)) @@ -181,6 +184,7 @@ abstract class StudentTest : CanvasTest() { val newMessagePage = NewMessagePage() val notificationPage = NotificationPage() val pageListPage = PageListPage(Searchable(R.id.search, R.id.search_src_text, R.id.search_close_btn)) + val pageDetailsPage = PageDetailsPage(ModuleItemInteractions(R.id.moduleName, R.id.next_item, R.id.prev_item)) val pairObserverPage = PairObserverPage() val pandaAvatarPage = PandaAvatarPage() val peopleListPage = PeopleListPage() @@ -190,6 +194,7 @@ abstract class StudentTest : CanvasTest() { val qrLoginPage = QRLoginPage() val quizListPage = QuizListPage() val quizTakingPage = QuizTakingPage() + val goToQuizPage = GoToQuizPage(ModuleItemInteractions(R.id.moduleName, R.id.next_item, R.id.prev_item)) val remoteConfigSettingsPage = RemoteConfigSettingsPage() val settingsPage = SettingsPage() val submissionDetailsPage = SubmissionDetailsPage() From 48c6060827383f611ebc3c687c70ed7b1c9afa6c Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:35:32 +0200 Subject: [PATCH 07/23] [MBL-17460][Student][Teacher] Edit nickname on course cards shows unexpected error if doing it twice (#2402) refs: MBL-17460 affects: Student, Teacher release note: none * Disable positive button when no nickname is typed. * Removed unused xml. --- .../student/fragment/DashboardFragment.kt | 2 +- .../teacher/activities/InitActivity.kt | 2 +- .../dialog/EditCourseNicknameDialog.kt | 90 ------------------- .../res/layout/dialog_course_nickname.xml | 29 ------ .../dialogs}/EditCourseNicknameDialog.kt | 14 ++- .../res/layout/dialog_course_nickname.xml | 0 6 files changed, 14 insertions(+), 123 deletions(-) delete mode 100644 apps/teacher/src/main/java/com/instructure/teacher/dialog/EditCourseNicknameDialog.kt delete mode 100644 apps/teacher/src/main/res/layout/dialog_course_nickname.xml rename {apps/student/src/main/java/com/instructure/student/dialog => libs/pandautils/src/main/java/com/instructure/pandautils/dialogs}/EditCourseNicknameDialog.kt (85%) rename {apps/student => libs/pandautils}/src/main/res/layout/dialog_course_nickname.xml (100%) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/DashboardFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/DashboardFragment.kt index 2ba2396090..9498e39d20 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/DashboardFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/DashboardFragment.kt @@ -63,7 +63,7 @@ import com.instructure.student.databinding.CourseGridRecyclerRefreshLayoutBindin import com.instructure.student.databinding.FragmentCourseGridBinding import com.instructure.student.decorations.VerticalGridSpacingDecoration import com.instructure.student.dialog.ColorPickerDialog -import com.instructure.student.dialog.EditCourseNicknameDialog +import com.instructure.pandautils.dialogs.EditCourseNicknameDialog import com.instructure.student.events.CoreDataFinishedLoading import com.instructure.student.events.CourseColorOverlayToggledEvent import com.instructure.student.events.ShowGradesToggledEvent diff --git a/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt b/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt index 0e550b3576..d801f2b45e 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt @@ -57,6 +57,7 @@ import com.instructure.loginapi.login.dialog.MasqueradingDialog import com.instructure.loginapi.login.tasks.LogoutTask import com.instructure.pandautils.activities.BasePresenterActivity import com.instructure.pandautils.binding.viewBinding +import com.instructure.pandautils.dialogs.EditCourseNicknameDialog import com.instructure.pandautils.dialogs.RatingDialog import com.instructure.pandautils.features.help.HelpDialogFragment import com.instructure.pandautils.features.inbox.list.InboxFragment @@ -73,7 +74,6 @@ import com.instructure.teacher.R import com.instructure.teacher.databinding.ActivityInitBinding import com.instructure.teacher.databinding.NavigationDrawerBinding import com.instructure.teacher.dialog.ColorPickerDialog -import com.instructure.teacher.dialog.EditCourseNicknameDialog import com.instructure.teacher.events.CourseUpdatedEvent import com.instructure.teacher.events.ToDoListUpdatedEvent import com.instructure.teacher.factory.InitActivityPresenterFactory diff --git a/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditCourseNicknameDialog.kt b/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditCourseNicknameDialog.kt deleted file mode 100644 index 0c865c89dd..0000000000 --- a/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditCourseNicknameDialog.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.instructure.teacher.dialog - -/* - * Copyright (C) 2017 - present Instructure, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -import android.app.Dialog -import android.os.Bundle -import android.view.View -import android.view.WindowManager -import android.view.inputmethod.EditorInfo -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatDialog -import androidx.appcompat.app.AppCompatDialogFragment -import androidx.appcompat.widget.AppCompatEditText -import androidx.fragment.app.FragmentManager -import com.instructure.canvasapi2.models.Course -import com.instructure.pandautils.analytics.SCREEN_VIEW_EDIT_COURSE_NICKNAME -import com.instructure.pandautils.analytics.ScreenView -import com.instructure.pandautils.utils.* -import com.instructure.teacher.R -import java.util.Locale -import kotlin.properties.Delegates - -@ScreenView(SCREEN_VIEW_EDIT_COURSE_NICKNAME) -class EditCourseNicknameDialog : AppCompatDialogFragment() { - - private var mEditNicknameCallback: (String) -> Unit by Delegates.notNull() - - init { - retainInstance = true - } - - companion object { - fun getInstance(manager: FragmentManager, course: Course, callback: (String) -> Unit) : EditCourseNicknameDialog { - manager.dismissExisting() - val dialog = EditCourseNicknameDialog() - val args = Bundle() - args.putParcelable(Const.COURSE, course) - dialog.arguments = args - dialog.mEditNicknameCallback = callback - return dialog - } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val course : Course = nonNullArgs.get(Const.COURSE) as Course - val view = View.inflate(requireActivity(), R.layout.dialog_course_nickname, null) - val editCourseNicknameEditText = view.findViewById(R.id.newCourseNickname) - editCourseNicknameEditText.setText(course.name) - ViewStyler.themeEditText(requireContext(), editCourseNicknameEditText, ThemePrefs.brandColor) - editCourseNicknameEditText.inputType = EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS - editCourseNicknameEditText.selectAll() - - val nameDialog = AlertDialog.Builder(requireActivity()) - .setCancelable(true) - .setTitle(getString(R.string.edit_course_nickname)) - .setView(view) - .setPositiveButton(getString(android.R.string.ok).uppercase(Locale.getDefault())) { _, _ -> - mEditNicknameCallback(editCourseNicknameEditText.text.toString()) - } - .setNegativeButton(getString(android.R.string.cancel).uppercase(Locale.getDefault()), null) - .create() - nameDialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) - - nameDialog.setOnShowListener { - nameDialog.getButton(AppCompatDialog.BUTTON_POSITIVE).setTextColor(ThemePrefs.textButtonColor) - nameDialog.getButton(AppCompatDialog.BUTTON_NEGATIVE).setTextColor(ThemePrefs.textButtonColor) - } - return nameDialog - } - - override fun onDestroyView() { - // Fix for rotation bug - dialog?.let { if (retainInstance) it.setDismissMessage(null) } - super.onDestroyView() - } -} diff --git a/apps/teacher/src/main/res/layout/dialog_course_nickname.xml b/apps/teacher/src/main/res/layout/dialog_course_nickname.xml deleted file mode 100644 index 1d4e8329db..0000000000 --- a/apps/teacher/src/main/res/layout/dialog_course_nickname.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - diff --git a/apps/student/src/main/java/com/instructure/student/dialog/EditCourseNicknameDialog.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/dialogs/EditCourseNicknameDialog.kt similarity index 85% rename from apps/student/src/main/java/com/instructure/student/dialog/EditCourseNicknameDialog.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/dialogs/EditCourseNicknameDialog.kt index 32f6d89a17..96a72202cb 100644 --- a/apps/student/src/main/java/com/instructure/student/dialog/EditCourseNicknameDialog.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/dialogs/EditCourseNicknameDialog.kt @@ -1,4 +1,4 @@ -package com.instructure.student.dialog +package com.instructure.pandautils.dialogs /* * Copyright (C) 2017 - present Instructure, Inc. @@ -27,10 +27,10 @@ import androidx.appcompat.app.AppCompatDialogFragment import androidx.appcompat.widget.AppCompatEditText import androidx.fragment.app.FragmentManager import com.instructure.canvasapi2.models.Course +import com.instructure.pandautils.R import com.instructure.pandautils.analytics.SCREEN_VIEW_EDIT_COURSE_NICKNAME import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.utils.* -import com.instructure.student.R import java.util.Locale import kotlin.properties.Delegates @@ -75,6 +75,16 @@ class EditCourseNicknameDialog : AppCompatDialogFragment() { .create() nameDialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) + editCourseNicknameEditText.onTextChanged { + if (course.originalName == null && it.isEmpty()) { + nameDialog.getButton(Dialog.BUTTON_POSITIVE).isEnabled = false + nameDialog.getButton(Dialog.BUTTON_POSITIVE).setTextColor(ThemePrefs.increaseAlpha(ThemePrefs.textButtonColor, 128)) + } else { + nameDialog.getButton(Dialog.BUTTON_POSITIVE).isEnabled = true + nameDialog.getButton(Dialog.BUTTON_POSITIVE).setTextColor(ThemePrefs.textButtonColor) + } + } + nameDialog.setOnShowListener { nameDialog.getButton(AppCompatDialog.BUTTON_POSITIVE).setTextColor(ThemePrefs.textButtonColor) nameDialog.getButton(AppCompatDialog.BUTTON_NEGATIVE).setTextColor(ThemePrefs.textButtonColor) diff --git a/apps/student/src/main/res/layout/dialog_course_nickname.xml b/libs/pandautils/src/main/res/layout/dialog_course_nickname.xml similarity index 100% rename from apps/student/src/main/res/layout/dialog_course_nickname.xml rename to libs/pandautils/src/main/res/layout/dialog_course_nickname.xml From 45ce4f5c1114554742f140cf4886d426f1272ca4 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:35:59 +0200 Subject: [PATCH 08/23] [MBL-17441][Student] Inbox filter string can be barely seen after changing from dark to light mode #2401 refs: MBL-17441 affects: Student release note: none --- .../instructure/pandautils/features/inbox/list/InboxFragment.kt | 2 ++ libs/pandautils/src/main/res/layout/fragment_inbox.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/inbox/list/InboxFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/inbox/list/InboxFragment.kt index 6934e27622..18cc9cd62a 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/inbox/list/InboxFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/inbox/list/InboxFragment.kt @@ -324,6 +324,8 @@ class InboxFragment : Fragment(), NavigationCallbacks, FragmentInteractions { ViewStyler.themeToolbarColored(requireActivity(), binding.editToolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor) binding.toolbarWrapper.setBackgroundColor(ThemePrefs.primaryColor) binding.addMessage.backgroundTintList = ViewStyler.makeColorStateListForButton() + binding.scopeFilterText.setTextColor(ThemePrefs.textButtonColor) + binding.scopeFilterIcon.setColorFilter(ThemePrefs.textButtonColor) inboxRouter.attachNavigationIcon(binding.toolbar) } diff --git a/libs/pandautils/src/main/res/layout/fragment_inbox.xml b/libs/pandautils/src/main/res/layout/fragment_inbox.xml index 43947bd07b..cc82d34096 100644 --- a/libs/pandautils/src/main/res/layout/fragment_inbox.xml +++ b/libs/pandautils/src/main/res/layout/fragment_inbox.xml @@ -157,6 +157,7 @@ tools:text="Inbox" /> Date: Mon, 8 Apr 2024 11:36:34 +0200 Subject: [PATCH 09/23] [MBL-17438][Student] Graded Group Discussions Fail to Launch from To-Do List for Students Outside the First Group #2399 refs: MBL-17438 affects: Student release note: Fixed a bug where some group discussions wouldn't open from the To Do list. --- .../instructure/student/fragment/ToDoListFragment.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/ToDoListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/ToDoListFragment.kt index 0ffb0d1a1d..e223a0b533 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/ToDoListFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/ToDoListFragment.kt @@ -168,12 +168,10 @@ class ToDoListFragment : ParentFragment() { when { toDo?.assignment != null -> { // Launch assignment details fragment. if (toDo.assignment!!.discussionTopicHeader != null) { - val groupTopic = toDo.assignment!!.discussionTopicHeader!!.groupTopicChildren.firstOrNull() - if (groupTopic == null) { // Launch discussion details fragment - RouteMatcher.route(requireActivity(), DiscussionRouterFragment.makeRoute(toDo.canvasContext!!, toDo.assignment!!.discussionTopicHeader!!)) - } else { // Launch discussion details fragment with the group - RouteMatcher.route(requireActivity(), DiscussionRouterFragment.makeRoute(CanvasContext.emptyGroupContext(groupTopic.groupId), groupTopic.id)) - } + RouteMatcher.route( + requireActivity(), + DiscussionRouterFragment.makeRoute(toDo.canvasContext!!, toDo.assignment!!.discussionTopicHeader!!) + ) } else { // Launch assignment details fragment. RouteMatcher.route(requireActivity(), AssignmentDetailsFragment.makeRoute(toDo.canvasContext!!, toDo.assignment!!.id)) From 3ffa33fd2f26e2a8d003e1e95d9ea287b41ab19a Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:48:57 +0200 Subject: [PATCH 10/23] [MBL-17462][Teacher] All submissions page filter displays twice the Course's name and if uncheck it the 'Clear filter' button remains there and does nothing #2405 refs: MBL-17462 affects: Teacher release note: none --- .../teacher/fragments/AssignmentSubmissionListFragment.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentSubmissionListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentSubmissionListFragment.kt index c6e78b5daa..b75c46a017 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentSubmissionListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentSubmissionListFragment.kt @@ -205,7 +205,9 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< when (presenter.getFilter()) { SubmissionListFilter.ALL -> { filterTitle.setText(R.string.all_submissions) - clearFilterTextView.setGone() + if (presenter.getSectionFilterText().isEmpty()) { + clearFilterTextView.setGone() + } } SubmissionListFilter.LATE -> filterTitle.setText(R.string.submitted_late) SubmissionListFilter.MISSING -> filterTitle.setText(R.string.havent_submitted_yet) @@ -236,9 +238,6 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< presenter.setSections(canvasContexts) updateFilterTitle() - - filterTitle.text = filterTitle.text.toString().plus(presenter.getSectionFilterText()) - clearFilterTextView.setVisible() return } From 6e9331a16de2f03cd2f4690b59583fa16bee0553 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:49:20 +0200 Subject: [PATCH 11/23] [MBL-17461][Teacher] Navigating back to the first manually opened module item after orientation change #2403 refs: MBL-17461 affects: Teacher release note: none --- .../progression/ModuleProgressionFragment.kt | 1 + .../progression/ModuleProgressionViewModel.kt | 16 +++++- .../ModuleProgressionViewModelTest.kt | 52 +++++++++++++++---- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionFragment.kt index f37c697003..b23f39ccab 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionFragment.kt @@ -89,6 +89,7 @@ class ModuleProgressionFragment : Fragment() { addOnPageChangeListener(object : SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { setupCarousel(data, position) + viewModel.setCurrentPosition(position) } }) } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionViewModel.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionViewModel.kt index 93536f68cd..4238c96910 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionViewModel.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionViewModel.kt @@ -55,6 +55,8 @@ class ModuleProgressionViewModel @Inject constructor( get() = _events private val _events = MutableLiveData>() + private var currentPosition = -1 + fun loadData(canvasContext: CanvasContext, moduleItemIdParam: Long, assetType: String, assetId: String) { viewModelScope.tryLaunch { _state.postValue(ViewState.Loading) @@ -82,7 +84,13 @@ class ModuleProgressionViewModel @Inject constructor( .map { createModuleItemViewData(it, isDiscussionRedesignEnabled) to it } .filter { it.first != null } - val initialPosition = items.indexOfFirst { it.second.id == moduleItemId }.coerceAtLeast(0) + val position = if (currentPosition == -1) { + val initialPosition = items.indexOfFirst { it.second.id == moduleItemId }.coerceAtLeast(0) + currentPosition = initialPosition + initialPosition + } else { + currentPosition + } val moduleNames = items.map { item -> modules.find { item.second.moduleId == it.id }?.name.orEmpty() @@ -92,7 +100,7 @@ class ModuleProgressionViewModel @Inject constructor( ModuleProgressionViewData( items.map { it.first!! }, moduleNames, - initialPosition, + position, canvasContext.textAndIconColor ) ) @@ -114,4 +122,8 @@ class ModuleProgressionViewModel @Inject constructor( Type.File.name -> ModuleItemViewData.File(item.url.orEmpty()) else -> null } + + fun setCurrentPosition(position: Int) { + currentPosition = position + } } diff --git a/apps/teacher/src/test/java/com/instructure/teacher/unit/modules/progression/ModuleProgressionViewModelTest.kt b/apps/teacher/src/test/java/com/instructure/teacher/unit/modules/progression/ModuleProgressionViewModelTest.kt index 770e36351d..c4857d2dba 100644 --- a/apps/teacher/src/test/java/com/instructure/teacher/unit/modules/progression/ModuleProgressionViewModelTest.kt +++ b/apps/teacher/src/test/java/com/instructure/teacher/unit/modules/progression/ModuleProgressionViewModelTest.kt @@ -50,6 +50,7 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Assert +import org.junit.Assert.* import org.junit.Before import org.junit.Rule import org.junit.Test @@ -104,8 +105,8 @@ class ModuleProgressionViewModelTest { viewModel.loadData(Course(id = 1L), 1L, ModuleItemAsset.MODULE_ITEM.name, "") - Assert.assertEquals(ViewState.Error(expected), viewModel.state.value) - Assert.assertEquals(expected, (viewModel.state.value as? ViewState.Error)?.errorMessage) + assertEquals(ViewState.Error(expected), viewModel.state.value) + assertEquals(expected, (viewModel.state.value as? ViewState.Error)?.errorMessage) } @Test @@ -118,8 +119,8 @@ class ModuleProgressionViewModelTest { viewModel.loadData(Course(id = 1L), 1L, ModuleItemAsset.MODULE_ITEM.name, "") - Assert.assertEquals(ViewState.Success, viewModel.state.value) - Assert.assertEquals(expected, viewModel.data.value) + assertEquals(ViewState.Success, viewModel.state.value) + assertEquals(expected, viewModel.data.value) } @Test @@ -136,8 +137,8 @@ class ModuleProgressionViewModelTest { viewModel.loadData(Course(id = 1L), -1, ModuleItemAsset.ASSIGNMENT.name, "1") - Assert.assertEquals(ViewState.Success, viewModel.state.value) - Assert.assertEquals(expected, viewModel.data.value) + assertEquals(ViewState.Success, viewModel.state.value) + assertEquals(expected, viewModel.data.value) } @Test @@ -186,11 +187,11 @@ class ModuleProgressionViewModelTest { verify { Uri.parse("externalUri1") } verify { Uri.parse("externalUri2") } verify(exactly = 2) { mockUriBuilder.appendQueryParameter("display", "borderless") } - Assert.assertEquals(expected, viewModel.data.value) + assertEquals(expected, viewModel.data.value) } @Test - fun `Initial position calculated correctly`() { + fun `Position calculated correctly`() { coEvery { repository.getModulesWithItems(any()) } returns listOf( ModuleObject( id = 1L, name = "Module 1", items = listOf( @@ -208,8 +209,37 @@ class ModuleProgressionViewModelTest { viewModel.loadData(Course(id = 1L), 3L, ModuleItemAsset.MODULE_ITEM.name, "") - Assert.assertEquals(ViewState.Success, viewModel.state.value) - Assert.assertEquals(2, viewModel.data.value?.initialPosition) + assertEquals(ViewState.Success, viewModel.state.value) + assertEquals(2, viewModel.data.value?.initialPosition) + } + + @Test + fun `Position uses current position after reload when position was changed`() { + coEvery { repository.getModulesWithItems(any()) } returns listOf( + ModuleObject( + id = 1L, name = "Module 1", items = listOf( + ModuleItem(id = 1L, type = "Page", pageUrl = "pageUrl", moduleId = 1L), + ModuleItem(id = 2L, type = "Assignment", contentId = 1L, moduleId = 1L) + ) + ), + ModuleObject( + id = 2L, name = "Module 2", items = listOf( + ModuleItem(id = 3L, type = "Discussion", contentId = 2L, moduleId = 2L), + ModuleItem(id = 4L, type = "Quiz", contentId = 3L, moduleId = 2L) + ) + ) + ) + + viewModel.loadData(Course(id = 1L), 3L, ModuleItemAsset.MODULE_ITEM.name, "") + + assertEquals(ViewState.Success, viewModel.state.value) + assertEquals(2, viewModel.data.value?.initialPosition) + + viewModel.setCurrentPosition(3) + + viewModel.loadData(Course(id = 1L), 3L, ModuleItemAsset.MODULE_ITEM.name, "") + + assertEquals(3, viewModel.data.value?.initialPosition) } @Test @@ -220,6 +250,6 @@ class ModuleProgressionViewModelTest { viewModel.loadData(Course(id = 1L), -1, ModuleItemAsset.ASSIGNMENT.name, "1") - Assert.assertEquals(expected, viewModel.events.value?.peekContent()) + assertEquals(expected, viewModel.events.value?.peekContent()) } } From a18488b2c60ebf854d996b7163447ef0fa0627f8 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:15:47 +0200 Subject: [PATCH 12/23] [MBL-17375][Student] Investigate why submission is not in SpeedGrader #2406 refs: MBL-17375 affects: Student release note: none --- .../pandautils/features/file/upload/worker/FileUploadWorker.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt index 4799e6bf9f..b5c511c861 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt @@ -42,6 +42,7 @@ import com.instructure.pandautils.utils.orDefault import com.instructure.pandautils.utils.toJson import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import java.lang.IllegalStateException import java.util.* @HiltWorker @@ -127,7 +128,7 @@ class FileUploadWorker @AssistedInject constructor( updateSubmissionComplete(notificationId) attemptId = it.attempt Result.success() - } ?: Result.retry() + } ?: throw IllegalStateException("Failed to attach file to submission") } ACTION_MESSAGE_ATTACHMENTS -> { updateNotificationComplete(notificationId) From 18d7e2742b15f6f6ade72fd69b282bd27eb1b5dc Mon Sep 17 00:00:00 2001 From: Akos Hermann <72087159+hermannakos@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:36:49 +0200 Subject: [PATCH 13/23] [MBL-17469][Student][Parent] Add missing tag to On Paper and No Submssion assignments (#2407) Test plan: See ticket. refs: MBL-17469 affects: Student, Parent release note: Fixed a bug where Missing status was not visible for certain assignments. --- apps/flutter_parent/lib/models/assignment.dart | 2 +- .../screens/assignments/assignment_details_screen.dart | 9 +++++---- .../assignments/details/AssignmentDetailsViewModel.kt | 9 ++++++--- .../com/instructure/student/holders/GradeViewHolder.kt | 2 +- .../java/com/instructure/canvasapi2/models/Assignment.kt | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/apps/flutter_parent/lib/models/assignment.dart b/apps/flutter_parent/lib/models/assignment.dart index 6175b081eb..3947713bdf 100644 --- a/apps/flutter_parent/lib/models/assignment.dart +++ b/apps/flutter_parent/lib/models/assignment.dart @@ -145,7 +145,7 @@ abstract class Assignment implements Built { SubmissionStatus getStatus({required String? studentId}) { final submission = this.submission(studentId); - if (!isSubmittable()) { + if (!isSubmittable() && submission == null) { return SubmissionStatus.NONE; } else if (submission?.isLate == true) { return SubmissionStatus.LATE; diff --git a/apps/flutter_parent/lib/screens/assignments/assignment_details_screen.dart b/apps/flutter_parent/lib/screens/assignments/assignment_details_screen.dart index 9043221d75..2814bad688 100644 --- a/apps/flutter_parent/lib/screens/assignments/assignment_details_screen.dart +++ b/apps/flutter_parent/lib/screens/assignments/assignment_details_screen.dart @@ -142,7 +142,8 @@ class _AssignmentDetailsScreenState extends State { final assignment = snapshot.data!.assignment!; final submission = assignment.submission(_currentStudent?.id); final fullyLocked = assignment.isFullyLocked; - final showStatus = assignment.isSubmittable() || submission?.isGraded() == true; + final missing = submission?.missing == true; + final showStatus = missing || assignment.isSubmittable() || submission?.isGraded() == true; final submitted = submission?.submittedAt != null; final submittedColor = submitted ? ParentTheme.of(context)?.successColor : textTheme.bodySmall?.color; @@ -172,9 +173,9 @@ class _AssignmentDetailsScreenState extends State { if (showStatus) SizedBox(width: 8), if (showStatus) Text( - !submitted - ? l10n.assignmentNotSubmittedLabel - : submission?.isGraded() == true ? l10n.assignmentGradedLabel : l10n.assignmentSubmittedLabel, + missing ? l10n.assignmentMissingSubmittedLabel : + !submitted ? l10n.assignmentNotSubmittedLabel : + submission?.isGraded() == true ? l10n.assignmentGradedLabel : l10n.assignmentSubmittedLabel, style: textTheme.bodySmall?.copyWith( color: submittedColor, ), diff --git a/apps/student/src/main/java/com/instructure/student/features/assignments/details/AssignmentDetailsViewModel.kt b/apps/student/src/main/java/com/instructure/student/features/assignments/details/AssignmentDetailsViewModel.kt index 00ea9846cc..603dc0c104 100644 --- a/apps/student/src/main/java/com/instructure/student/features/assignments/details/AssignmentDetailsViewModel.kt +++ b/apps/student/src/main/java/com/instructure/student/features/assignments/details/AssignmentDetailsViewModel.kt @@ -281,7 +281,7 @@ class AssignmentDetailsViewModel @Inject constructor( val assignmentState = AssignmentUtils2.getAssignmentState(assignment, assignment.submission, false) // Don't mark LTI assignments as missing when overdue as they usually won't have a real submission for it - val isMissing = assignment.submission?.missing.orDefault() || (assignment.turnInType != Assignment.TurnInType.EXTERNAL_TOOL + val isMissing = assignment.isMissing() || (assignment.turnInType != Assignment.TurnInType.EXTERNAL_TOOL && assignment.dueAt != null && assignmentState == AssignmentUtils2.ASSIGNMENT_STATE_MISSING) @@ -308,8 +308,11 @@ class AssignmentDetailsViewModel @Inject constructor( val submittedStatusIcon = if (assignment.isSubmitted) R.drawable.ic_complete_solid else R.drawable.ic_no // Submission Status under title - We only show Graded or nothing at all for PAPER/NONE - val submissionStatusVisible = assignmentState == AssignmentUtils2.ASSIGNMENT_STATE_GRADED - || (assignment.turnInType != Assignment.TurnInType.ON_PAPER && assignment.turnInType != Assignment.TurnInType.NONE) + val submissionStatusVisible = + assignmentState == AssignmentUtils2.ASSIGNMENT_STATE_GRADED + || assignmentState == AssignmentUtils2.ASSIGNMENT_STATE_MISSING + || assignmentState == AssignmentUtils2.ASSIGNMENT_STATE_GRADED_MISSING + || (assignment.turnInType != Assignment.TurnInType.ON_PAPER && assignment.turnInType != Assignment.TurnInType.NONE) if (assignment.isLocked) { val lockedMessage = if (assignment.lockInfo?.contextModule != null) { diff --git a/apps/student/src/main/java/com/instructure/student/holders/GradeViewHolder.kt b/apps/student/src/main/java/com/instructure/student/holders/GradeViewHolder.kt index 2bd4e88500..4567300a2f 100644 --- a/apps/student/src/main/java/com/instructure/student/holders/GradeViewHolder.kt +++ b/apps/student/src/main/java/com/instructure/student/holders/GradeViewHolder.kt @@ -99,7 +99,7 @@ class GradeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { } date.setVisible(date.text.isNotBlank()) - if (assignment.isMissing() && !isEdit && assignment.submission?.grade == null) { + if (assignment.isMissing() && !isEdit) { submissionState.text = context.getString(R.string.missingAssignment) submissionState.setTextColor(ContextCompat.getColor(context, R.color.textDanger)) submissionState.setVisible() diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/models/Assignment.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/models/Assignment.kt index 4fa9e37b98..e6236d28bd 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/models/Assignment.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/models/Assignment.kt @@ -229,7 +229,7 @@ data class Assignment( override fun describeContents(): Int = 0 fun isMissing(): Boolean { - return !isSubmitted && dueDate?.before(Date()) ?: false + return submission?.missing == true || (!isSubmitted && dueDate?.before(Date()) ?: false && submission?.grade == null) } companion object { From 72eda170f9212e8561e779e267075b79441883e4 Mon Sep 17 00:00:00 2001 From: Akos Hermann <72087159+hermannakos@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:12:07 +0200 Subject: [PATCH 14/23] [MBL-17497][Teacher] Load submissions in SpeedGrader (#2411) Test plan: See ticket. SpeedGrader now loads the submissions itself and no longer passed in a bundle. Smoke test thoroughly. refs: MBL-17497 affects: Teacher release note: Fixed a bug where SpeedGrader would not load with large number of submissions. --- .../teacher/ui/SpeedGraderCommentsPageTest.kt | 1 + .../teacher/activities/SpeedGraderActivity.kt | 42 ++- .../teacher/adapters/AssignmentAdapter.kt | 2 +- .../GradeableStudentSubmissionAdapter.kt | 2 +- .../AssignmentDetailPresenterFactory.kt | 2 +- .../factory/AssignmentListPresenterFactory.kt | 2 +- ...ssignmentSubmissionListPresenterFactory.kt | 13 +- .../factory/SpeedGraderPresenterFactory.kt | 12 +- .../details}/AssignmentDetailsFragment.kt | 20 +- .../details}/AssignmentDetailsPresenter.kt | 2 +- .../list}/AssignmentListFragment.kt | 6 +- .../list}/AssignmentListPresenter.kt | 2 +- .../AssignmentSubmissionListFragment.kt | 95 ++++-- .../AssignmentSubmissionListModule.kt | 39 +++ .../AssignmentSubmissionListPresenter.kt | 135 ++++---- .../AssignmentSubmissionRepository.kt | 133 ++++++++ .../submission/SubmissionListFilter.kt | 26 ++ .../discussion/DiscussionsDetailsFragment.kt | 14 +- .../modules/progression/ModuleItemAsset.kt | 2 +- .../progression/ModuleProgressionFragment.kt | 2 +- .../features/syllabus/ui/SyllabusView.kt | 2 +- .../fragments/CourseBrowserFragment.kt | 1 + .../teacher/fragments/QuizDetailsFragment.kt | 3 +- .../presenters/SpeedGraderPresenter.kt | 59 +++- .../teacher/presenters/ToDoPresenter.kt | 2 +- .../teacher/router/RouteMatcher.kt | 3 + .../teacher/router/RouteResolver.kt | 3 + .../unit/AssignmentListPresenterTest.kt | 2 +- .../AssignmentSubmissionRepositoryTest.kt | 311 ++++++++++++++++++ .../canvasapi2/apis/AssignmentAPI.kt | 16 + .../instructure/canvasapi2/apis/CourseAPI.kt | 6 + .../canvasapi2/apis/EnrollmentAPI.kt | 6 + 32 files changed, 806 insertions(+), 160 deletions(-) rename apps/teacher/src/main/java/com/instructure/teacher/{fragments => features/assignment/details}/AssignmentDetailsFragment.kt (96%) rename apps/teacher/src/main/java/com/instructure/teacher/{presenters => features/assignment/details}/AssignmentDetailsPresenter.kt (98%) rename apps/teacher/src/main/java/com/instructure/teacher/{fragments => features/assignment/list}/AssignmentListFragment.kt (97%) rename apps/teacher/src/main/java/com/instructure/teacher/{presenters => features/assignment/list}/AssignmentListPresenter.kt (99%) rename apps/teacher/src/main/java/com/instructure/teacher/{fragments => features/assignment/submission}/AssignmentSubmissionListFragment.kt (80%) create mode 100644 apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListModule.kt rename apps/teacher/src/main/java/com/instructure/teacher/{presenters => features/assignment/submission}/AssignmentSubmissionListPresenter.kt (61%) create mode 100644 apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionRepository.kt create mode 100644 apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/SubmissionListFilter.kt create mode 100644 apps/teacher/src/test/java/com/instructure/teacher/unit/features/assignment/submission/AssignmentSubmissionRepositoryTest.kt diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/SpeedGraderCommentsPageTest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/SpeedGraderCommentsPageTest.kt index cfaefe31a2..57634500cf 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/SpeedGraderCommentsPageTest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/SpeedGraderCommentsPageTest.kt @@ -166,6 +166,7 @@ class SpeedGraderCommentsPageTest : TeacherTest() { // Allows Espresso to distinguish between this and the full name, which is elsewhere on the page authorName = student.shortName, authorPronouns = student.pronouns, + attempt = 1L, comment = "a comment", attachments = if(attachment == null) arrayListOf() else arrayListOf(attachment) ) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/activities/SpeedGraderActivity.kt b/apps/teacher/src/main/java/com/instructure/teacher/activities/SpeedGraderActivity.kt index e637051f83..1a2dae9674 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/activities/SpeedGraderActivity.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/activities/SpeedGraderActivity.kt @@ -57,6 +57,9 @@ import com.instructure.teacher.adapters.SubmissionContentAdapter import com.instructure.teacher.databinding.ActivitySpeedgraderBinding import com.instructure.teacher.events.AssignmentGradedEvent import com.instructure.teacher.factory.SpeedGraderPresenterFactory +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListPresenter +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionRepository +import com.instructure.teacher.features.assignment.submission.SubmissionListFilter import com.instructure.teacher.features.speedgrader.commentlibrary.CommentLibraryAction import com.instructure.teacher.features.speedgrader.commentlibrary.CommentLibraryFragment import com.instructure.teacher.features.speedgrader.commentlibrary.CommentLibraryViewModel @@ -75,12 +78,16 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +import javax.inject.Inject @PageView("courses/{courseId}/gradebook/speed_grader?assignment_id={assignmentId}") @ScreenView(SCREEN_VIEW_SPEED_GRADER) @AndroidEntryPoint class SpeedGraderActivity : BasePresenterActivity(), SpeedGraderView { + @Inject + lateinit var repository: AssignmentSubmissionRepository + private val binding by viewBinding(ActivitySpeedgraderBinding::inflate) /* These should be passed to the presenter factory and should not be directly referenced otherwise */ @@ -89,9 +96,15 @@ class SpeedGraderActivity : BasePresenterActivity by lazy { intent.extras!!.getParcelableArrayList(Const.SUBMISSION) ?: arrayListOf() } private val discussionTopicHeader: DiscussionTopicHeader? by lazy { intent.extras!!.getParcelable(Const.DISCUSSION_HEADER) } private val anonymousGrading: Boolean? by lazy { intent.extras?.getBoolean(Const.ANONYMOUS_GRADING) } + private val filter: SubmissionListFilter by lazy { + intent.extras!!.getSerializable( + FILTER + ) as? SubmissionListFilter + ?: SubmissionListFilter.ALL + } + private val filterValue: Double by lazy { intent.extras!!.getDouble(FILTER_VALUE) } private val initialSelection: Int by lazy { intent.extras!!.getInt(Const.SELECTED_ITEM, 0) } private var currentSelection = 0 @@ -124,9 +137,11 @@ class SpeedGraderActivity : BasePresenterActivity, selectedIdx: Int, anonymousGrading: Boolean? = null): Bundle { return Bundle().apply { putLong(Const.COURSE_ID, courseId) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/adapters/AssignmentAdapter.kt b/apps/teacher/src/main/java/com/instructure/teacher/adapters/AssignmentAdapter.kt index 0d494256f9..173f9534f2 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/adapters/AssignmentAdapter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/adapters/AssignmentAdapter.kt @@ -29,7 +29,7 @@ import com.instructure.teacher.databinding.AdapterAssignmentBinding import com.instructure.teacher.databinding.AdapterAssignmentGroupHeaderBinding import com.instructure.teacher.holders.AssignmentGroupHeaderViewHolder import com.instructure.teacher.holders.AssignmentViewHolder -import com.instructure.teacher.presenters.AssignmentListPresenter +import com.instructure.teacher.features.assignment.list.AssignmentListPresenter import com.instructure.teacher.viewinterface.AssignmentListView import instructure.androidblueprint.SyncExpandableRecyclerAdapter diff --git a/apps/teacher/src/main/java/com/instructure/teacher/adapters/GradeableStudentSubmissionAdapter.kt b/apps/teacher/src/main/java/com/instructure/teacher/adapters/GradeableStudentSubmissionAdapter.kt index cfc13e4e9c..4b769b4225 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/adapters/GradeableStudentSubmissionAdapter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/adapters/GradeableStudentSubmissionAdapter.kt @@ -24,7 +24,7 @@ import com.instructure.canvasapi2.models.Assignment import com.instructure.canvasapi2.models.GradeableStudentSubmission import com.instructure.teacher.databinding.AdapterGradeableStudentSubmissionBinding import com.instructure.teacher.holders.GradeableStudentSubmissionViewHolder -import com.instructure.teacher.presenters.AssignmentSubmissionListPresenter +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListPresenter import com.instructure.teacher.viewinterface.AssignmentSubmissionListView import instructure.androidblueprint.SyncRecyclerAdapter diff --git a/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentDetailPresenterFactory.kt b/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentDetailPresenterFactory.kt index 41e385eadf..704b4e6a97 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentDetailPresenterFactory.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentDetailPresenterFactory.kt @@ -17,7 +17,7 @@ package com.instructure.teacher.factory import com.instructure.canvasapi2.models.Assignment -import com.instructure.teacher.presenters.AssignmentDetailsPresenter +import com.instructure.teacher.features.assignment.details.AssignmentDetailsPresenter import com.instructure.teacher.viewinterface.AssignmentDetailsView import instructure.androidblueprint.PresenterFactory diff --git a/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentListPresenterFactory.kt b/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentListPresenterFactory.kt index 3e14ccb521..314917161c 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentListPresenterFactory.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentListPresenterFactory.kt @@ -17,7 +17,7 @@ package com.instructure.teacher.factory import com.instructure.canvasapi2.models.CanvasContext -import com.instructure.teacher.presenters.AssignmentListPresenter +import com.instructure.teacher.features.assignment.list.AssignmentListPresenter import com.instructure.teacher.viewinterface.AssignmentListView import instructure.androidblueprint.PresenterFactory diff --git a/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentSubmissionListPresenterFactory.kt b/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentSubmissionListPresenterFactory.kt index cd10d97f5e..3f700b71bf 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentSubmissionListPresenterFactory.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/factory/AssignmentSubmissionListPresenterFactory.kt @@ -17,10 +17,17 @@ package com.instructure.teacher.factory import com.instructure.canvasapi2.models.Assignment -import com.instructure.teacher.presenters.AssignmentSubmissionListPresenter +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListPresenter +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionRepository +import com.instructure.teacher.features.assignment.submission.SubmissionListFilter import com.instructure.teacher.viewinterface.AssignmentSubmissionListView import instructure.androidblueprint.PresenterFactory -class AssignmentSubmissionListPresenterFactory(private var mAssignment: Assignment, private var mFilter: AssignmentSubmissionListPresenter.SubmissionListFilter) : PresenterFactory { - override fun create(): AssignmentSubmissionListPresenter = AssignmentSubmissionListPresenter(mAssignment, mFilter) +class AssignmentSubmissionListPresenterFactory( + private var assignment: Assignment, + private var filter: SubmissionListFilter, + private val assignmentSubmissionRepository: AssignmentSubmissionRepository +) : PresenterFactory { + override fun create(): AssignmentSubmissionListPresenter = + AssignmentSubmissionListPresenter(assignment, filter, assignmentSubmissionRepository) } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderPresenterFactory.kt b/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderPresenterFactory.kt index b4405bb7fa..258bb126d5 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderPresenterFactory.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderPresenterFactory.kt @@ -17,18 +17,20 @@ package com.instructure.teacher.factory import com.instructure.canvasapi2.models.DiscussionTopicHeader -import com.instructure.canvasapi2.models.GradeableStudentSubmission +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionRepository +import com.instructure.teacher.features.assignment.submission.SubmissionListFilter import com.instructure.teacher.presenters.SpeedGraderPresenter import com.instructure.teacher.viewinterface.SpeedGraderView import instructure.androidblueprint.PresenterFactory -import java.util.* class SpeedGraderPresenterFactory( private val courseId: Long, private val assignmentId: Long, - private val submissions: ArrayList, private val submissionId: Long, // Id used when we are coming from a push notification - private val discussionEntries: DiscussionTopicHeader? + private val discussionEntries: DiscussionTopicHeader?, + private val repository: AssignmentSubmissionRepository, + private val filter: SubmissionListFilter, + private val filterValue: Double ) : PresenterFactory { - override fun create() = SpeedGraderPresenter(courseId, assignmentId, submissions, submissionId, discussionEntries) + override fun create() = SpeedGraderPresenter(courseId, assignmentId, submissionId, discussionEntries, repository, filter, filterValue) } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/details/AssignmentDetailsFragment.kt similarity index 96% rename from apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt rename to apps/teacher/src/main/java/com/instructure/teacher/features/assignment/details/AssignmentDetailsFragment.kt index 3960dd018a..d712f63cea 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/details/AssignmentDetailsFragment.kt @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.instructure.teacher.fragments +package com.instructure.teacher.features.assignment.details import android.os.Bundle import android.view.LayoutInflater @@ -62,8 +62,12 @@ import com.instructure.teacher.events.AssignmentGradedEvent import com.instructure.teacher.events.AssignmentUpdatedEvent import com.instructure.teacher.events.post import com.instructure.teacher.factory.AssignmentDetailPresenterFactory -import com.instructure.teacher.presenters.AssignmentDetailsPresenter -import com.instructure.teacher.presenters.AssignmentSubmissionListPresenter +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListPresenter +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListFragment +import com.instructure.teacher.features.assignment.submission.SubmissionListFilter +import com.instructure.teacher.fragments.DueDatesFragment +import com.instructure.teacher.fragments.EditAssignmentDetailsFragment +import com.instructure.teacher.fragments.LtiLaunchFragment import com.instructure.teacher.router.RouteMatcher import com.instructure.teacher.utils.getColorCompat import com.instructure.teacher.utils.setupBackButtonWithExpandCollapseAndBack @@ -390,17 +394,17 @@ class AssignmentDetailsFragment : BasePresenterFragment< } submissionsLayout.setOnClickListener { - navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) + navigateToSubmissions(course, assignment, SubmissionListFilter.ALL) } donutGroup.viewAllSubmissions.onClick { submissionsLayout.performClick() } // Separate click listener for a11y donutGroup.gradedWrapper.setOnClickListener { - navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) + navigateToSubmissions(course, assignment, SubmissionListFilter.GRADED) } donutGroup.ungradedWrapper.setOnClickListener { - navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) + navigateToSubmissions(course, assignment, SubmissionListFilter.NOT_GRADED) } donutGroup.notSubmittedWrapper.setOnClickListener { - navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) + navigateToSubmissions(course, assignment, SubmissionListFilter.MISSING) } noDescriptionTextView.setOnClickListener { openEditPage(assignment) } @@ -425,7 +429,7 @@ class AssignmentDetailsFragment : BasePresenterFragment< } } - private fun navigateToSubmissions(course: Course, assignment: Assignment, filter: AssignmentSubmissionListPresenter.SubmissionListFilter) { + private fun navigateToSubmissions(course: Course, assignment: Assignment, filter: SubmissionListFilter) { val args = AssignmentSubmissionListFragment.makeBundle(assignment, filter) RouteMatcher.route(requireActivity(), Route(null, AssignmentSubmissionListFragment::class.java, course, args)) } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentDetailsPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/details/AssignmentDetailsPresenter.kt similarity index 98% rename from apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentDetailsPresenter.kt rename to apps/teacher/src/main/java/com/instructure/teacher/features/assignment/details/AssignmentDetailsPresenter.kt index 2d06d7e18e..19529ba06b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentDetailsPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/details/AssignmentDetailsPresenter.kt @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.instructure.teacher.presenters +package com.instructure.teacher.features.assignment.details import com.instructure.canvasapi2.managers.AssignmentManager import com.instructure.canvasapi2.managers.SubmissionManager diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/list/AssignmentListFragment.kt similarity index 97% rename from apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt rename to apps/teacher/src/main/java/com/instructure/teacher/features/assignment/list/AssignmentListFragment.kt index bf438e8fd3..aeef3a42dc 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/list/AssignmentListFragment.kt @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.instructure.teacher.fragments +package com.instructure.teacher.features.assignment.list import android.os.Bundle import android.view.Gravity @@ -40,7 +40,9 @@ import com.instructure.teacher.adapters.AssignmentAdapter import com.instructure.teacher.databinding.FragmentAssignmentListBinding import com.instructure.teacher.events.AssignmentUpdatedEvent import com.instructure.teacher.factory.AssignmentListPresenterFactory -import com.instructure.teacher.presenters.AssignmentListPresenter +import com.instructure.teacher.features.assignment.details.AssignmentDetailsFragment +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListFragment +import com.instructure.teacher.fragments.QuizDetailsFragment import com.instructure.teacher.router.RouteMatcher import com.instructure.teacher.utils.RecyclerViewUtils import com.instructure.teacher.utils.setHeaderVisibilityListener diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentListPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/list/AssignmentListPresenter.kt similarity index 99% rename from apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentListPresenter.kt rename to apps/teacher/src/main/java/com/instructure/teacher/features/assignment/list/AssignmentListPresenter.kt index ab80e5851d..ae5c7adb17 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentListPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/list/AssignmentListPresenter.kt @@ -15,7 +15,7 @@ * */ -package com.instructure.teacher.presenters +package com.instructure.teacher.features.assignment.list import com.instructure.canvasapi2.StatusCallback import com.instructure.canvasapi2.managers.AssignmentManager diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentSubmissionListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListFragment.kt similarity index 80% rename from apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentSubmissionListFragment.kt rename to apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListFragment.kt index b75c46a017..675c01d47c 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentSubmissionListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListFragment.kt @@ -14,7 +14,7 @@ * along with this program. If not, see . * */ -package com.instructure.teacher.fragments +package com.instructure.teacher.features.assignment.submission import android.os.Bundle import android.view.MenuItem @@ -31,7 +31,15 @@ import com.instructure.pandautils.analytics.SCREEN_VIEW_ASSIGNMENT_SUBMISSION_LI import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding import com.instructure.pandautils.fragments.BaseSyncFragment -import com.instructure.pandautils.utils.* +import com.instructure.pandautils.utils.ParcelableArg +import com.instructure.pandautils.utils.SerializableArg +import com.instructure.pandautils.utils.ThemePrefs +import com.instructure.pandautils.utils.ViewStyler +import com.instructure.pandautils.utils.backgroundColor +import com.instructure.pandautils.utils.isTablet +import com.instructure.pandautils.utils.setGone +import com.instructure.pandautils.utils.setVisible +import com.instructure.pandautils.utils.withArgs import com.instructure.teacher.R import com.instructure.teacher.activities.SpeedGraderActivity import com.instructure.teacher.adapters.GradeableStudentSubmissionAdapter @@ -44,18 +52,24 @@ import com.instructure.teacher.events.SubmissionCommentsUpdated import com.instructure.teacher.events.SubmissionFilterChangedEvent import com.instructure.teacher.factory.AssignmentSubmissionListPresenterFactory import com.instructure.teacher.features.postpolicies.ui.PostPolicyFragment +import com.instructure.teacher.fragments.AddMessageFragment import com.instructure.teacher.holders.GradeableStudentSubmissionViewHolder -import com.instructure.teacher.presenters.AssignmentSubmissionListPresenter -import com.instructure.teacher.presenters.AssignmentSubmissionListPresenter.SubmissionListFilter import com.instructure.teacher.router.RouteMatcher -import com.instructure.teacher.utils.* +import com.instructure.teacher.utils.RecyclerViewUtils +import com.instructure.teacher.utils.setHeaderVisibilityListener +import com.instructure.teacher.utils.setupBackButtonAsBackPressedOnly +import com.instructure.teacher.utils.setupMenu +import com.instructure.teacher.utils.withRequireNetwork import com.instructure.teacher.view.QuizSubmissionGradedEvent import com.instructure.teacher.viewinterface.AssignmentSubmissionListView +import dagger.hilt.android.AndroidEntryPoint import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +import javax.inject.Inject @ScreenView(SCREEN_VIEW_ASSIGNMENT_SUBMISSION_LIST) +@AndroidEntryPoint class AssignmentSubmissionListFragment : BaseSyncFragment< GradeableStudentSubmission, AssignmentSubmissionListPresenter, @@ -63,17 +77,20 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< GradeableStudentSubmissionViewHolder, GradeableStudentSubmissionAdapter>(), AssignmentSubmissionListView { + @Inject + lateinit var assignmentSubmissionRepository: AssignmentSubmissionRepository + private val binding by viewBinding(FragmentAssignmentSubmissionListBinding::bind) - private var mAssignment: Assignment by ParcelableArg(Assignment(), ASSIGNMENT) - private var mCourse: Course by ParcelableArg(Course()) + private var assignment: Assignment by ParcelableArg(Assignment(), ASSIGNMENT) + private var course: Course by ParcelableArg(Course()) private lateinit var mRecyclerView: RecyclerView - private var mFilter by SerializableArg(SubmissionListFilter.ALL, FILTER_TYPE) - private var mCanvasContextsSelected = ArrayList() + private var filter by SerializableArg(SubmissionListFilter.ALL, FILTER_TYPE) + private var canvasContextsSelected = ArrayList() - private var mNeedToForceNetwork = false + private var needToForceNetwork = false - private val mSubmissionFilters: Map by lazy { + private val submissionFilters: Map by lazy { sortedMapOf( Pair(SubmissionListFilter.ALL.ordinal, getString(R.string.all_submissions)), Pair(SubmissionListFilter.LATE.ordinal, getString(R.string.submitted_late)), @@ -87,7 +104,7 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< override fun layoutResId(): Int = R.layout.fragment_assignment_submission_list override val recyclerView: RecyclerView get() = binding.submissionsRecyclerView - override fun getPresenterFactory() = AssignmentSubmissionListPresenterFactory(mAssignment, mFilter) + override fun getPresenterFactory() = AssignmentSubmissionListPresenterFactory(assignment, filter, assignmentSubmissionRepository) override fun onCreateView(view: View) = Unit override fun onPresenterPrepared(presenter: AssignmentSubmissionListPresenter) = with(binding) { mRecyclerView = RecyclerViewUtils.buildRecyclerView(rootView, requireContext(), adapter, presenter, R.id.swipeRefreshLayout, @@ -111,8 +128,8 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< mRecyclerView.adapter = adapter } - presenter.refresh(mNeedToForceNetwork) - mNeedToForceNetwork = false + presenter.refresh(needToForceNetwork) + needToForceNetwork = false updateFilterTitle() binding.clearFilterTextView.setTextColor(ThemePrefs.textButtonColor) @@ -129,11 +146,18 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< } override fun createAdapter(): GradeableStudentSubmissionAdapter { - return GradeableStudentSubmissionAdapter(mAssignment, mCourse.id, requireContext(), presenter) { gradeableStudentSubmission -> + return GradeableStudentSubmissionAdapter(assignment, course.id, requireContext(), presenter) { gradeableStudentSubmission -> withRequireNetwork { val filteredSubmissions = (0 until presenter.data.size()).map { presenter.data[it] } val selectedIdx = filteredSubmissions.indexOf(gradeableStudentSubmission) - val bundle = SpeedGraderActivity.makeBundle(mCourse.id, mAssignment.id, filteredSubmissions, selectedIdx, mAssignment.anonymousGrading) + val bundle = SpeedGraderActivity.makeBundle( + course.id, + assignment.id, + selectedIdx, + assignment.anonymousGrading, + presenter.getFilter(), + presenter.getFilterPoints() + ) RouteMatcher.route(requireActivity(), Route(bundle, RouteContext.SPEED_GRADER)) } } @@ -151,7 +175,7 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< swipeRefreshLayout.isRefreshing = false // Theme the toolbar again since visibilities may have changed - ViewStyler.themeToolbarColored(requireActivity(), assignmentSubmissionListToolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), assignmentSubmissionListToolbar, course.backgroundColor, requireContext().getColor(R.color.white)) updateStatuses() // Muted is now also set by not being in the new gradebook } @@ -170,13 +194,13 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< assignmentSubmissionListToolbar.setupBackButtonAsBackPressedOnly(this@AssignmentSubmissionListFragment) if(isTablet) { - assignmentSubmissionListToolbar.title = mAssignment.name + assignmentSubmissionListToolbar.title = assignment.name } else { assignmentSubmissionListToolbar.setNavigationIcon(R.drawable.ic_back_arrow) assignmentSubmissionListToolbar.title = getString(R.string.submissions) - assignmentSubmissionListToolbar.subtitle = mCourse.name + assignmentSubmissionListToolbar.subtitle = course.name } - ViewStyler.themeToolbarColored(requireActivity(), assignmentSubmissionListToolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), assignmentSubmissionListToolbar, course.backgroundColor, requireContext().getColor(R.color.white)) ViewStyler.themeFAB(addMessage) } @@ -189,7 +213,12 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< } addMessage.setOnClickListener { - val args = AddMessageFragment.createBundle(presenter.getRecipients(), filterTitle.text.toString() + " " + getString(R.string.on) + " " + mAssignment.name, mCourse.contextId, false) + val args = AddMessageFragment.createBundle( + presenter.getRecipients(), + filterTitle.text.toString() + " " + getString(R.string.on) + " " + assignment.name, + course.contextId, + false + ) RouteMatcher.route(requireActivity(), Route(AddMessageFragment::class.java, null, args)) } } @@ -232,8 +261,8 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< private fun setFilter(filterIndex: Int = -1, canvasContexts: ArrayList? = null) = with(binding) { canvasContexts?.let { - mCanvasContextsSelected = ArrayList() - mCanvasContextsSelected.addAll(canvasContexts) + canvasContextsSelected = ArrayList() + canvasContextsSelected.addAll(canvasContexts) presenter.setSections(canvasContexts) @@ -259,13 +288,13 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< updateFilterTitle() } SubmissionListFilter.BELOW_VALUE.ordinal -> { - FilterSubmissionByPointsDialog.getInstance(requireFragmentManager(), getString(R.string.scored_less_than), mAssignment.pointsPossible) { points -> + FilterSubmissionByPointsDialog.getInstance(requireFragmentManager(), getString(R.string.scored_less_than), assignment.pointsPossible) { points -> presenter.setFilter(SubmissionListFilter.BELOW_VALUE, points) updateFilterTitle() }.show(requireActivity().supportFragmentManager, FilterSubmissionByPointsDialog::class.java.simpleName) } SubmissionListFilter.ABOVE_VALUE.ordinal -> { - FilterSubmissionByPointsDialog.getInstance(requireFragmentManager(), getString(R.string.scored_more_than), mAssignment.pointsPossible) { points -> + FilterSubmissionByPointsDialog.getInstance(requireFragmentManager(), getString(R.string.scored_more_than), assignment.pointsPossible) { points -> presenter.setFilter(SubmissionListFilter.ABOVE_VALUE, points) updateFilterTitle() }.show(requireActivity().supportFragmentManager, FilterSubmissionByPointsDialog::class.java.simpleName) @@ -276,7 +305,7 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< val menuItemCallback: (MenuItem) -> Unit = { item -> when (item.itemId) { R.id.filterSubmissions -> { - val (keys, values) = mSubmissionFilters.toList().unzip() + val (keys, values) = submissionFilters.toList().unzip() val dialog = RadioButtonDialog.getInstance(requireActivity().supportFragmentManager, getString(R.string.filter_submissions), values as ArrayList, keys.indexOf(presenter.getFilter().ordinal)) { idx -> EventBus.getDefault().post(SubmissionFilterChangedEvent(keys[idx])) } @@ -285,18 +314,18 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< } R.id.filterBySection -> { //let the user select the course/group they want to see - PeopleListFilterDialog.getInstance(requireActivity().supportFragmentManager, presenter.getSectionListIds(), mCourse, false) { canvasContexts -> + PeopleListFilterDialog.getInstance(requireActivity().supportFragmentManager, presenter.getSectionListIds(), course, false) { canvasContexts -> EventBus.getDefault().post(SubmissionFilterChangedEvent(canvasContext = canvasContexts)) }.show(requireActivity().supportFragmentManager, PeopleListFilterDialog::class.java.simpleName) } R.id.menuPostPolicies -> { - RouteMatcher.route(requireActivity(), PostPolicyFragment.makeRoute(mCourse, mAssignment)) + RouteMatcher.route(requireActivity(), PostPolicyFragment.makeRoute(course, assignment)) } } } private fun updateStatuses() { - if (presenter.mAssignment.anonymousGrading) + if (presenter.assignment.anonymousGrading) binding.anonGradingStatusView.setVisible().text = getString(R.string.anonymousGradingLabel) } @@ -305,7 +334,7 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< fun onAssignmentGraded(event: AssignmentGradedEvent) { event.once(javaClass.simpleName) { //force network call on resume - if(presenter.mAssignment.id == it) mNeedToForceNetwork = true + if(presenter.assignment.id == it) needToForceNetwork = true } } @@ -314,7 +343,7 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< fun onQuizGraded(event: QuizSubmissionGradedEvent) { event.once(javaClass.simpleName) { // Force network call on resume - if (presenter.mAssignment.id == it.assignmentId) mNeedToForceNetwork = true + if (presenter.assignment.id == it.assignmentId) needToForceNetwork = true } } @@ -322,7 +351,7 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onSubmissionCommentUpdated(event: SubmissionCommentsUpdated) { event.once(AssignmentSubmissionListFragment::class.java.simpleName) { - mNeedToForceNetwork = true + needToForceNetwork = true } } @@ -337,7 +366,7 @@ class AssignmentSubmissionListFragment : BaseSyncFragment< @JvmStatic val FILTER_TYPE = "filter_type" fun newInstance(course: Course, args: Bundle) = AssignmentSubmissionListFragment().withArgs(args).apply { - mCourse = course + this.course = course } fun makeBundle(assignment: Assignment): Bundle { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListModule.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListModule.kt new file mode 100644 index 0000000000..2fa5886f72 --- /dev/null +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListModule.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */package com.instructure.teacher.features.assignment.submission + +import com.instructure.canvasapi2.apis.AssignmentAPI +import com.instructure.canvasapi2.apis.CourseAPI +import com.instructure.canvasapi2.apis.EnrollmentAPI +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(ActivityComponent::class) +class AssignmentSubmissionListModule { + + @Provides + fun provideAssignmentSubmissionListRepository( + assignmentApi: AssignmentAPI.AssignmentInterface, + enrollmentApi: EnrollmentAPI.EnrollmentInterface, + courseApi: CourseAPI.CoursesInterface + ): AssignmentSubmissionRepository { + return AssignmentSubmissionRepository(assignmentApi, enrollmentApi, courseApi) + } +} \ No newline at end of file diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentSubmissionListPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListPresenter.kt similarity index 61% rename from apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentSubmissionListPresenter.kt rename to apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListPresenter.kt index 3669714b44..02c7eb8879 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentSubmissionListPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionListPresenter.kt @@ -14,43 +14,41 @@ * along with this program. If not, see . * */ -package com.instructure.teacher.presenters - -import com.instructure.canvasapi2.managers.AssignmentManager -import com.instructure.canvasapi2.managers.CourseManager -import com.instructure.canvasapi2.managers.EnrollmentManager -import com.instructure.canvasapi2.models.* +package com.instructure.teacher.features.assignment.submission + +import com.instructure.canvasapi2.models.Assignment +import com.instructure.canvasapi2.models.CanvasContext +import com.instructure.canvasapi2.models.GradeableStudentSubmission +import com.instructure.canvasapi2.models.Group +import com.instructure.canvasapi2.models.GroupAssignee +import com.instructure.canvasapi2.models.Recipient +import com.instructure.canvasapi2.models.StudentAssignee +import com.instructure.canvasapi2.models.Submission +import com.instructure.canvasapi2.models.User import com.instructure.canvasapi2.utils.intersectBy -import com.instructure.canvasapi2.utils.weave.awaitApi -import com.instructure.canvasapi2.utils.weave.awaitApis import com.instructure.canvasapi2.utils.weave.weave import com.instructure.pandautils.utils.AssignmentUtils2 import com.instructure.teacher.utils.getState import com.instructure.teacher.viewinterface.AssignmentSubmissionListView import instructure.androidblueprint.SyncPresenter import kotlinx.coroutines.Job -import java.util.* - -class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var mFilter: SubmissionListFilter) : SyncPresenter(GradeableStudentSubmission::class.java) { - - enum class SubmissionListFilter { - ALL, - LATE, - MISSING, - NOT_GRADED, - GRADED, - BELOW_VALUE, - ABOVE_VALUE - } +import java.util.Locale +import java.util.Random + +class AssignmentSubmissionListPresenter( + val assignment: Assignment, + private var filter: SubmissionListFilter, + private val assignmentSubmissionRepository: AssignmentSubmissionRepository +) : SyncPresenter(GradeableStudentSubmission::class.java) { private var apiCalls: Job? = null - private var mUnfilteredSubmissions: List = emptyList() - private var mFilteredSubmissions: List = emptyList() + private var unfilteredSubmissions: List = emptyList() + private var filteredSubmissions: List = emptyList() - private var mFilterValue: Double = 0.0 + private var filterValue: Double = 0.0 - private var mSectionsSelected = ArrayList() + private var sectionsSelected = ArrayList() @Suppress("EXPERIMENTAL_FEATURE_WARNING") override fun loadData(forceNetwork: Boolean) { @@ -58,7 +56,7 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var if (apiCalls?.isActive == true) return // Use existing data if we already have it. Unfiltered submissions should be cleared on refresh - if (!forceNetwork && mUnfilteredSubmissions.isNotEmpty()) { + if (!forceNetwork && unfilteredSubmissions.isNotEmpty()) { setFilteredData() return } @@ -67,44 +65,21 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var apiCalls = weave { try { viewCallback?.onRefreshStarted() - val (gradeableStudents, enrollments, submissions) = awaitApis, List, List>( - { AssignmentManager.getAllGradeableStudentsForAssignment(mAssignment.courseId, mAssignment.id, forceNetwork, it) }, - { EnrollmentManager.getAllEnrollmentsForCourse(mAssignment.courseId, null, forceNetwork, it) }, - { AssignmentManager.getAllSubmissionsForAssignment(mAssignment.courseId, mAssignment.id, forceNetwork, it) } - ) - - val enrollmentMap = enrollments.associateBy { it.user?.id } - val students = gradeableStudents.distinctBy { it.id }.map { - // Students need the enrollment info - var user = enrollmentMap[it.id]?.user - // Users can be enrolled in multiple sections, so we need to get all of them - user = user?.copy( - enrollments = user.enrollments + enrollments.filter { enrollment -> enrollment.userId == user?.id }, - isFakeStudent = it.isFakeStudent + unfilteredSubmissions = + assignmentSubmissionRepository.getGradeableStudentSubmissions( + assignment, + assignment.courseId, + forceNetwork ) - // Need to null out the user object to prevent infinite parcels - user?.enrollments?.forEach { it.user = null } - user - }.filterNotNull() - mUnfilteredSubmissions = if (mAssignment.groupCategoryId > 0 && !mAssignment.isGradeGroupsIndividually) { - val groups = awaitApi> { CourseManager.getGroupsForCourse(mAssignment.courseId, it, false) } - .filter { it.groupCategoryId == mAssignment.groupCategoryId } - makeGroupSubmissions(students, groups, submissions) - } else { - val submissionMap = submissions.associateBy { it.userId } - students.map { - GradeableStudentSubmission(StudentAssignee(it), submissionMap[it.id]) - } - } - setFilteredData() } catch (ignore: Throwable) { + ignore.printStackTrace() } } } fun setSections(sections: ArrayList) { - mSectionsSelected = sections + sectionsSelected = sections setFilteredData() } @@ -117,22 +92,22 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var */ fun getSectionListIds(): ArrayList { val contextIds = ArrayList() - mSectionsSelected.forEach { + sectionsSelected.forEach { contextIds.add(it.id) } return contextIds } fun getSectionFilterText() : String { - when (mSectionsSelected.isEmpty()) { + when (sectionsSelected.isEmpty()) { true -> return "" false -> { // get the title based on Section selected val title = StringBuilder() title.append(", ") - mSectionsSelected.forEachIndexed { index, canvasContext -> + sectionsSelected.forEachIndexed { index, canvasContext -> title.append(canvasContext.name) - if ((index + 1) < mSectionsSelected.size) { + if ((index + 1) < sectionsSelected.size) { title.append(", ") } } @@ -142,18 +117,18 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var } fun clearFilterList() { - mSectionsSelected.clear() + sectionsSelected.clear() } private fun setFilteredData() { - mFilteredSubmissions = mUnfilteredSubmissions.filter { - when (mFilter) { + filteredSubmissions = unfilteredSubmissions.filter { + when (filter) { SubmissionListFilter.ALL -> true - SubmissionListFilter.LATE -> it.submission?.let { mAssignment.getState(it, true) in listOf(AssignmentUtils2.ASSIGNMENT_STATE_SUBMITTED_LATE, AssignmentUtils2.ASSIGNMENT_STATE_GRADED_LATE) } ?: false - SubmissionListFilter.NOT_GRADED -> it.submission?.let { mAssignment.getState(it, true) in listOf(AssignmentUtils2.ASSIGNMENT_STATE_SUBMITTED, AssignmentUtils2.ASSIGNMENT_STATE_SUBMITTED_LATE) || !it.isGradeMatchesCurrentSubmission } ?: false - SubmissionListFilter.GRADED -> it.submission?.let { mAssignment.getState(it, true) in listOf(AssignmentUtils2.ASSIGNMENT_STATE_GRADED, AssignmentUtils2.ASSIGNMENT_STATE_GRADED_LATE, AssignmentUtils2.ASSIGNMENT_STATE_GRADED_MISSING) && it.isGradeMatchesCurrentSubmission} ?: false - SubmissionListFilter.ABOVE_VALUE -> it.submission?.let { it.isGraded && it.score >= mFilterValue } ?: false - SubmissionListFilter.BELOW_VALUE -> it.submission?.let { it.isGraded && it.score < mFilterValue } ?: false + SubmissionListFilter.LATE -> it.submission?.let { assignment.getState(it, true) in listOf(AssignmentUtils2.ASSIGNMENT_STATE_SUBMITTED_LATE, AssignmentUtils2.ASSIGNMENT_STATE_GRADED_LATE) } ?: false + SubmissionListFilter.NOT_GRADED -> it.submission?.let { assignment.getState(it, true) in listOf(AssignmentUtils2.ASSIGNMENT_STATE_SUBMITTED, AssignmentUtils2.ASSIGNMENT_STATE_SUBMITTED_LATE) || !it.isGradeMatchesCurrentSubmission } ?: false + SubmissionListFilter.GRADED -> it.submission?.let { assignment.getState(it, true) in listOf(AssignmentUtils2.ASSIGNMENT_STATE_GRADED, AssignmentUtils2.ASSIGNMENT_STATE_GRADED_LATE, AssignmentUtils2.ASSIGNMENT_STATE_GRADED_MISSING) && it.isGradeMatchesCurrentSubmission} ?: false + SubmissionListFilter.ABOVE_VALUE -> it.submission?.let { it.isGraded && it.score >= filterValue } ?: false + SubmissionListFilter.BELOW_VALUE -> it.submission?.let { it.isGraded && it.score < filterValue } ?: false // Filtering by ASSIGNMENT_STATE_MISSING here doesn't work because it assumes that the due date has already passed, which isn't necessarily the case when the teacher wants to see // which students haven't submitted yet SubmissionListFilter.MISSING -> it.submission?.workflowState == "unsubmitted" || it.submission == null @@ -161,17 +136,17 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var } // Shuffle if grading anonymously - if (mAssignment.anonymousGrading) mFilteredSubmissions = mFilteredSubmissions.shuffled(Random(1234)) + if (assignment.anonymousGrading) filteredSubmissions = filteredSubmissions.shuffled(Random(1234)) data.clear() // Filter by section if there is a section filter set - if (mSectionsSelected.isNotEmpty()) { + if (sectionsSelected.isNotEmpty()) { // get list of ids - val sectionIds = mSectionsSelected.map { it.id } + val sectionIds = sectionsSelected.map { it.id } - mFilteredSubmissions.forEach { submission -> + filteredSubmissions.forEach { submission -> sectionIds.forEach { section -> if (submission.assignee is StudentAssignee) { (submission.assignee as StudentAssignee).student.enrollments.forEach { @@ -184,7 +159,7 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var } } else { // No section filter, add all the submission filtered users - data.addOrUpdate(mFilteredSubmissions) + data.addOrUpdate(filteredSubmissions) } viewCallback?.onRefreshFinished() @@ -199,7 +174,7 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var override fun refresh(forceNetwork: Boolean) { clearData() - mUnfilteredSubmissions = emptyList() + unfilteredSubmissions = emptyList() loadData(forceNetwork) } @@ -207,17 +182,17 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var //In case this filter hasn't been shown yet, we'll need to show the loader so the user //doesn't see "no items" view first viewCallback?.onRefreshStarted() - mFilter = filter - mFilterValue = value + this.filter = filter + filterValue = value setFilteredData() } - fun getFilter() : SubmissionListFilter = mFilter + fun getFilter() : SubmissionListFilter = filter - fun getFilterPoints() : Double = mFilterValue + fun getFilterPoints() : Double = filterValue fun getRecipients() : List { - return mFilteredSubmissions.map { submission -> + return filteredSubmissions.map { submission -> when(val assignee = submission.assignee) { is StudentAssignee -> Recipient.from(assignee.student) is GroupAssignee -> Recipient.from(assignee.group) @@ -227,7 +202,7 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var override fun compare(item1: GradeableStudentSubmission, item2: GradeableStudentSubmission): Int { // Turns out we do need to sort them by sortable name, but not when anonymous grading is on - if (item1.assignee is StudentAssignee && item2.assignee is StudentAssignee && !mAssignment.anonymousGrading) { + if (item1.assignee is StudentAssignee && item2.assignee is StudentAssignee && !assignment.anonymousGrading) { return (item1.assignee as StudentAssignee).student.sortableName?.lowercase(Locale.getDefault()) ?.compareTo((item2.assignee as StudentAssignee).student.sortableName?.lowercase( Locale.getDefault() diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionRepository.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionRepository.kt new file mode 100644 index 0000000000..656bb7aecc --- /dev/null +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/AssignmentSubmissionRepository.kt @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */package com.instructure.teacher.features.assignment.submission + +import com.instructure.canvasapi2.apis.AssignmentAPI +import com.instructure.canvasapi2.apis.CourseAPI +import com.instructure.canvasapi2.apis.EnrollmentAPI +import com.instructure.canvasapi2.builders.RestParams +import com.instructure.canvasapi2.models.Assignment +import com.instructure.canvasapi2.models.GradeableStudentSubmission +import com.instructure.canvasapi2.models.Group +import com.instructure.canvasapi2.models.GroupAssignee +import com.instructure.canvasapi2.models.StudentAssignee +import com.instructure.canvasapi2.models.Submission +import com.instructure.canvasapi2.models.User +import com.instructure.canvasapi2.utils.depaginate +import com.instructure.canvasapi2.utils.intersectBy + +class AssignmentSubmissionRepository( + private val assignmentApi: AssignmentAPI.AssignmentInterface, + private val enrollmentApi: EnrollmentAPI.EnrollmentInterface, + private val courseApi: CourseAPI.CoursesInterface +) { + + suspend fun getGradeableStudentSubmissions( + assignment: Assignment, + courseId: Long, + forceNetwork: Boolean + ): List { + val params = RestParams(isForceReadFromNetwork = forceNetwork, usePerPageQueryParam = true) + val gradeableStudents = assignmentApi.getFirstPageGradeableStudentsForAssignment( + courseId, + assignment.id, + params + ).depaginate { + assignmentApi.getNextPageGradeableStudents(it, params) + }.dataOrThrow + + val enrollments = + enrollmentApi.getFirstPageEnrollmentsForCourse(courseId, null, params).depaginate { + enrollmentApi.getNextPage(it, params) + }.dataOrThrow + + val submissions = + assignmentApi.getFirstPageSubmissionsForAssignment(courseId, assignment.id, params) + .depaginate { + assignmentApi.getNextPageSubmissions(it, params) + }.dataOrThrow + + val enrollmentMap = enrollments.associateBy { it.user?.id } + val students = gradeableStudents.distinctBy { it.id }.map { + var user = enrollmentMap[it.id]?.user + user = user?.copy( + enrollments = user.enrollments + enrollments.filter { enrollment -> enrollment.userId == user?.id }, + isFakeStudent = it.isFakeStudent + ) + user?.enrollments?.forEach { it.user = null } + user + }.filterNotNull() + + val allSubmissions = + if (assignment.groupCategoryId > 0 && !assignment.isGradeGroupsIndividually) { + val groups = courseApi.getFirstPageGroups(assignment.courseId, params) + .depaginate { courseApi.getNextPageGroups(it, params) }.dataOrThrow + .filter { it.groupCategoryId == assignment.groupCategoryId } + makeGroupSubmissions(students, groups, submissions) + } else { + val submissionMap = submissions.associateBy { it.userId } + students.map { + GradeableStudentSubmission(StudentAssignee(it), submissionMap[it.id]) + } + } + + return allSubmissions + } + + private fun makeGroupSubmissions( + students: List, + groups: List, + submissions: List + ): List { + val userMap = students.associateBy { it.id } + val groupAssignees = groups.map { GroupAssignee(it, it.users.map { userMap[it.id] ?: it }) } + + val individualIds = + students.map { it.id } - groupAssignees.flatMap { it.students.map { it.id } } + val individualAssignees = individualIds + .map { userMap[it] } + .filterNotNull() + .map { StudentAssignee(it) } + + val (groupedSubmissions, individualSubmissions) = submissions.partition { + (it.group?.id ?: 0L) != 0L + } + val studentSubmissionMap = individualSubmissions.associateBy { it.userId } + val groupSubmissionMap = groupedSubmissions + .groupBy { it.group!!.id } + .mapValues { + it.value.reduce { acc, submission -> + acc.submissionComments = + acc.submissionComments.intersectBy(submission.submissionComments) { + "${it.authorId}|${it.comment}|${it.createdAt?.time}" + } + acc + } + } + .toMap() + + val groupSubs = + groupAssignees.map { GradeableStudentSubmission(it, groupSubmissionMap[it.group.id]) } + val individualSubs = individualAssignees.map { + GradeableStudentSubmission( + it, + studentSubmissionMap[it.student.id] + ) + } + + return groupSubs + individualSubs + } +} \ No newline at end of file diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/SubmissionListFilter.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/SubmissionListFilter.kt new file mode 100644 index 0000000000..54c34722c3 --- /dev/null +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/assignment/submission/SubmissionListFilter.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */package com.instructure.teacher.features.assignment.submission + +enum class SubmissionListFilter { + ALL, + LATE, + MISSING, + NOT_GRADED, + GRADED, + BELOW_VALUE, + ABOVE_VALUE +} \ No newline at end of file diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/discussion/DiscussionsDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/discussion/DiscussionsDetailsFragment.kt index d78031c9c0..017718db54 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/discussion/DiscussionsDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/discussion/DiscussionsDetailsFragment.kt @@ -57,8 +57,10 @@ import com.instructure.teacher.dialog.NoInternetConnectionDialog import com.instructure.teacher.events.* import com.instructure.teacher.events.DiscussionEntryEvent import com.instructure.teacher.factory.DiscussionsDetailsPresenterFactory +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListFragment import com.instructure.teacher.fragments.* -import com.instructure.teacher.presenters.AssignmentSubmissionListPresenter +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListPresenter +import com.instructure.teacher.features.assignment.submission.SubmissionListFilter import com.instructure.teacher.presenters.DiscussionsDetailsPresenter import com.instructure.teacher.router.RouteMatcher import com.instructure.teacher.utils.* @@ -377,21 +379,21 @@ class DiscussionsDetailsFragment : BasePresenterFragment< RouteMatcher.route(requireActivity(), Route(null, DueDatesFragment::class.java, canvasContext, args)) } submissionsLayout.setOnClickListener { - navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, SubmissionListFilter.ALL) } binding.donutGroup.viewAllSubmissions.onClick { submissionsLayout.performClick() } // Separate click listener for a11y binding.donutGroup.gradedWrapper.setOnClickListener { - navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, SubmissionListFilter.GRADED) } binding.donutGroup.ungradedWrapper.setOnClickListener { - navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, SubmissionListFilter.NOT_GRADED) } binding.donutGroup.notSubmittedWrapper.setOnClickListener { - navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, SubmissionListFilter.MISSING) } } - private fun navigateToSubmissions(context: CanvasContext, assignment: Assignment, filter: AssignmentSubmissionListPresenter.SubmissionListFilter) { + private fun navigateToSubmissions(context: CanvasContext, assignment: Assignment, filter: SubmissionListFilter) { val args = AssignmentSubmissionListFragment.makeBundle(assignment, filter) RouteMatcher.route(requireActivity(), Route(null, AssignmentSubmissionListFragment::class.java, context, args)) } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleItemAsset.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleItemAsset.kt index 8e165edd0e..acbb2019ee 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleItemAsset.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleItemAsset.kt @@ -20,7 +20,7 @@ package com.instructure.teacher.features.modules.progression import androidx.fragment.app.Fragment import com.instructure.interactions.router.RouterParams import com.instructure.pandautils.features.discussion.router.DiscussionRouterFragment -import com.instructure.teacher.fragments.AssignmentDetailsFragment +import com.instructure.teacher.features.assignment.details.AssignmentDetailsFragment import com.instructure.teacher.fragments.PageDetailsFragment import com.instructure.teacher.fragments.QuizDetailsFragment diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionFragment.kt index b23f39ccab..2a1dbca054 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/progression/ModuleProgressionFragment.kt @@ -40,7 +40,7 @@ import com.instructure.teacher.R import com.instructure.teacher.databinding.FragmentModuleProgressionBinding import com.instructure.teacher.features.discussion.DiscussionsDetailsFragment import com.instructure.teacher.features.files.details.FileDetailsFragment -import com.instructure.teacher.fragments.AssignmentDetailsFragment +import com.instructure.teacher.features.assignment.details.AssignmentDetailsFragment import com.instructure.teacher.fragments.InternalWebViewFragment import com.instructure.teacher.fragments.PageDetailsFragment import com.instructure.teacher.fragments.QuizDetailsFragment diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusView.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusView.kt index d087ac93a9..245cb36b8b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusView.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusView.kt @@ -40,7 +40,7 @@ import com.instructure.teacher.events.SyllabusUpdatedEvent import com.instructure.teacher.features.calendar.event.CalendarEventFragment import com.instructure.teacher.features.syllabus.SyllabusEvent import com.instructure.teacher.features.syllabus.edit.EditSyllabusFragment -import com.instructure.teacher.fragments.AssignmentDetailsFragment +import com.instructure.teacher.features.assignment.details.AssignmentDetailsFragment import com.instructure.teacher.mobius.common.ui.MobiusView import com.instructure.teacher.router.RouteMatcher import com.instructure.teacher.utils.setupBackButton diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt index 9cc80b09c9..7383ac92c1 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt @@ -46,6 +46,7 @@ import com.instructure.teacher.adapters.CourseBrowserAdapter import com.instructure.teacher.databinding.FragmentCourseBrowserBinding import com.instructure.teacher.events.CourseUpdatedEvent import com.instructure.teacher.factory.CourseBrowserPresenterFactory +import com.instructure.teacher.features.assignment.list.AssignmentListFragment import com.instructure.teacher.features.modules.list.ui.ModuleListFragment import com.instructure.teacher.features.syllabus.ui.SyllabusFragment import com.instructure.teacher.holders.CourseBrowserViewHolder diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt index e1a0ee1cb3..1229b4e251 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt @@ -60,7 +60,8 @@ import com.instructure.teacher.events.AssignmentUpdatedEvent import com.instructure.teacher.events.QuizUpdatedEvent import com.instructure.teacher.events.post import com.instructure.teacher.factory.QuizDetailsPresenterFactory -import com.instructure.teacher.presenters.AssignmentSubmissionListPresenter.SubmissionListFilter +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListFragment +import com.instructure.teacher.features.assignment.submission.SubmissionListFilter import com.instructure.teacher.presenters.QuizDetailsPresenter import com.instructure.teacher.router.RouteMatcher import com.instructure.teacher.utils.anonymousSubmissionsDisplayable diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderPresenter.kt index d36779b0d0..99d0dfba3a 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderPresenter.kt @@ -17,10 +17,30 @@ package com.instructure.teacher.presenters import com.instructure.canvasapi2.apis.EnrollmentAPI -import com.instructure.canvasapi2.managers.* -import com.instructure.canvasapi2.models.* -import com.instructure.canvasapi2.utils.weave.* +import com.instructure.canvasapi2.managers.AssignmentManager +import com.instructure.canvasapi2.managers.CourseManager +import com.instructure.canvasapi2.managers.EnrollmentManager +import com.instructure.canvasapi2.managers.FeaturesManager +import com.instructure.canvasapi2.managers.SubmissionManager +import com.instructure.canvasapi2.managers.UserManager +import com.instructure.canvasapi2.models.Assignment +import com.instructure.canvasapi2.models.Course +import com.instructure.canvasapi2.models.DiscussionTopicHeader +import com.instructure.canvasapi2.models.Enrollment +import com.instructure.canvasapi2.models.GradeableStudentSubmission +import com.instructure.canvasapi2.models.StudentAssignee +import com.instructure.canvasapi2.models.Submission +import com.instructure.canvasapi2.models.User +import com.instructure.canvasapi2.utils.weave.awaitApi +import com.instructure.canvasapi2.utils.weave.awaitApis +import com.instructure.canvasapi2.utils.weave.catch +import com.instructure.canvasapi2.utils.weave.tryWeave +import com.instructure.canvasapi2.utils.weave.weave +import com.instructure.pandautils.utils.AssignmentUtils2 import com.instructure.teacher.events.SubmissionUpdatedEvent +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionRepository +import com.instructure.teacher.features.assignment.submission.SubmissionListFilter +import com.instructure.teacher.utils.getState import com.instructure.teacher.utils.transformForQuizGrading import com.instructure.teacher.viewinterface.SpeedGraderView import instructure.androidblueprint.Presenter @@ -28,15 +48,20 @@ import kotlinx.coroutines.Job import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +import java.util.Locale class SpeedGraderPresenter( - private var courseId: Long, - private var assignmentId: Long, - private var submissions: List, - private var submissionId: Long, - private var discussion: DiscussionTopicHeader? + private var courseId: Long, + private var assignmentId: Long, + private var submissionId: Long, + private var discussion: DiscussionTopicHeader?, + private val repository: AssignmentSubmissionRepository, + private val filter: SubmissionListFilter, + private val filterValue: Double ) : Presenter { + private var submissions: List = emptyList() + private var mView: SpeedGraderView? = null private var mApiJob: Job? = null @@ -97,6 +122,24 @@ class SpeedGraderPresenter( ) course = data.first assignment = data.second + val allSubmissions = repository.getGradeableStudentSubmissions(assignment, courseId, false).sortedBy { + (it.assignee as? StudentAssignee)?.student?.sortableName?.lowercase( + Locale.getDefault()) + } + submissions = allSubmissions.filter { + when (filter) { + SubmissionListFilter.ALL -> true + SubmissionListFilter.LATE -> it.submission?.let { assignment.getState(it, true) in listOf( + AssignmentUtils2.ASSIGNMENT_STATE_SUBMITTED_LATE, AssignmentUtils2.ASSIGNMENT_STATE_GRADED_LATE) } ?: false + SubmissionListFilter.NOT_GRADED -> it.submission?.let { assignment.getState(it, true) in listOf( + AssignmentUtils2.ASSIGNMENT_STATE_SUBMITTED, AssignmentUtils2.ASSIGNMENT_STATE_SUBMITTED_LATE) || !it.isGradeMatchesCurrentSubmission } ?: false + SubmissionListFilter.GRADED -> it.submission?.let { assignment.getState(it, true) in listOf( + AssignmentUtils2.ASSIGNMENT_STATE_GRADED, AssignmentUtils2.ASSIGNMENT_STATE_GRADED_LATE, AssignmentUtils2.ASSIGNMENT_STATE_GRADED_MISSING) && it.isGradeMatchesCurrentSubmission} ?: false + SubmissionListFilter.ABOVE_VALUE -> it.submission?.let { it.isGraded && it.score >= filterValue } ?: false + SubmissionListFilter.BELOW_VALUE -> it.submission?.let { it.isGraded && it.score < filterValue } ?: false + SubmissionListFilter.MISSING -> it.submission?.workflowState == "unsubmitted" || it.submission == null + } + } if (submissionId > 0 && submissions.isEmpty()) { // We don't have all the data we need (we came from a push notification), get all the stuffs first diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/ToDoPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/presenters/ToDoPresenter.kt index 60c62fb729..dbdba4f217 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/ToDoPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/presenters/ToDoPresenter.kt @@ -22,7 +22,7 @@ import com.instructure.canvasapi2.models.* import com.instructure.canvasapi2.utils.weave.* import com.instructure.pandautils.utils.AssignmentUtils2 import com.instructure.teacher.events.ToDoListUpdatedEvent -import com.instructure.teacher.presenters.AssignmentSubmissionListPresenter.Companion.makeGroupSubmissions +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListPresenter.Companion.makeGroupSubmissions import com.instructure.teacher.utils.getState import com.instructure.teacher.viewinterface.ToDoView import instructure.androidblueprint.SyncPresenter diff --git a/apps/teacher/src/main/java/com/instructure/teacher/router/RouteMatcher.kt b/apps/teacher/src/main/java/com/instructure/teacher/router/RouteMatcher.kt index de4ce204a4..9072e223d5 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/router/RouteMatcher.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/router/RouteMatcher.kt @@ -50,6 +50,9 @@ import com.instructure.teacher.PSPDFKit.AnnotationComments.AnnotationCommentList import com.instructure.teacher.R import com.instructure.teacher.activities.* import com.instructure.teacher.adapters.StudentContextFragment +import com.instructure.teacher.features.assignment.details.AssignmentDetailsFragment +import com.instructure.teacher.features.assignment.list.AssignmentListFragment +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListFragment import com.instructure.teacher.features.discussion.DiscussionsDetailsFragment import com.instructure.teacher.features.modules.list.ui.ModuleListFragment import com.instructure.teacher.features.modules.progression.ModuleProgressionFragment diff --git a/apps/teacher/src/main/java/com/instructure/teacher/router/RouteResolver.kt b/apps/teacher/src/main/java/com/instructure/teacher/router/RouteResolver.kt index 6ce4b082db..b1b2dc9e9a 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/router/RouteResolver.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/router/RouteResolver.kt @@ -17,6 +17,9 @@ import com.instructure.pandautils.utils.Const import com.instructure.pandautils.utils.argsWithContext import com.instructure.teacher.PSPDFKit.AnnotationComments.AnnotationCommentListFragment import com.instructure.teacher.adapters.StudentContextFragment +import com.instructure.teacher.features.assignment.details.AssignmentDetailsFragment +import com.instructure.teacher.features.assignment.list.AssignmentListFragment +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionListFragment import com.instructure.teacher.features.calendar.event.CalendarEventFragment import com.instructure.teacher.features.discussion.DiscussionsDetailsFragment import com.instructure.teacher.features.files.search.FileSearchFragment diff --git a/apps/teacher/src/test/java/com/instructure/teacher/unit/AssignmentListPresenterTest.kt b/apps/teacher/src/test/java/com/instructure/teacher/unit/AssignmentListPresenterTest.kt index 469c8c4853..2627b22ad0 100644 --- a/apps/teacher/src/test/java/com/instructure/teacher/unit/AssignmentListPresenterTest.kt +++ b/apps/teacher/src/test/java/com/instructure/teacher/unit/AssignmentListPresenterTest.kt @@ -18,7 +18,7 @@ package com.instructure.teacher.unit import com.instructure.canvasapi2.models.Course import com.instructure.teacher.factory.AssignmentListPresenterFactory -import com.instructure.teacher.presenters.AssignmentListPresenter +import com.instructure.teacher.features.assignment.list.AssignmentListPresenter import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before diff --git a/apps/teacher/src/test/java/com/instructure/teacher/unit/features/assignment/submission/AssignmentSubmissionRepositoryTest.kt b/apps/teacher/src/test/java/com/instructure/teacher/unit/features/assignment/submission/AssignmentSubmissionRepositoryTest.kt new file mode 100644 index 0000000000..75fb9ee390 --- /dev/null +++ b/apps/teacher/src/test/java/com/instructure/teacher/unit/features/assignment/submission/AssignmentSubmissionRepositoryTest.kt @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */package com.instructure.teacher.unit.features.assignment.submission + +import com.instructure.canvasapi2.apis.AssignmentAPI +import com.instructure.canvasapi2.apis.CourseAPI +import com.instructure.canvasapi2.apis.EnrollmentAPI +import com.instructure.canvasapi2.models.Assignment +import com.instructure.canvasapi2.models.Enrollment +import com.instructure.canvasapi2.models.GradeableStudent +import com.instructure.canvasapi2.models.GradeableStudentSubmission +import com.instructure.canvasapi2.models.Group +import com.instructure.canvasapi2.models.GroupAssignee +import com.instructure.canvasapi2.models.StudentAssignee +import com.instructure.canvasapi2.models.Submission +import com.instructure.canvasapi2.models.User +import com.instructure.canvasapi2.utils.DataResult +import com.instructure.canvasapi2.utils.LinkHeaders +import com.instructure.teacher.features.assignment.submission.AssignmentSubmissionRepository +import io.mockk.coEvery +import io.mockk.mockk +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import java.lang.IllegalStateException + +class AssignmentSubmissionRepositoryTest { + + private val courseApi: CourseAPI.CoursesInterface = mockk(relaxed = true) + private val enrollmentApi: EnrollmentAPI.EnrollmentInterface = mockk(relaxed = true) + private val assignmentApi: AssignmentAPI.AssignmentInterface = mockk(relaxed = true) + + private lateinit var repository: AssignmentSubmissionRepository + + @Before + fun setup() { + repository = AssignmentSubmissionRepository(assignmentApi, enrollmentApi, courseApi) + } + + @Test + fun `Create student submissions`() = runTest { + val users = listOf( + User(id = 1L), + User(id = 2L), + User(id = 3L), + User(id = 4L) + ) + val students = listOf( + GradeableStudent(id = 1L), + GradeableStudent(id = 2L), + GradeableStudent(id = 3L), + GradeableStudent(id = 4L) + ) + val enrollments = listOf( + Enrollment(id = 1L, userId = 1L, user = users[0]), + Enrollment(id = 2L, userId = 2L, user = users[1]), + Enrollment(id = 3L, userId = 3L, user = users[2]), + Enrollment(id = 4L, userId = 4L, user = users[3]) + ) + val submissions = listOf( + Submission(id = 1L, userId = 1L), + Submission(id = 2L, userId = 2L), + Submission(id = 3L, userId = 3L), + Submission(id = 4L, userId = 4L) + ) + coEvery { + assignmentApi.getFirstPageGradeableStudentsForAssignment(any(), any(), any()) + } returns DataResult.Success( + students.subList(0, 2), linkHeaders = LinkHeaders(nextUrl = "nextUrl") + ) + + coEvery { + assignmentApi.getNextPageGradeableStudents("nextUrl", any()) + } returns DataResult.Success( + students.subList(2, 4) + ) + + coEvery { + enrollmentApi.getFirstPageEnrollmentsForCourse( + any(), + any(), + any() + ) + } returns DataResult.Success( + enrollments.subList(0, 2), linkHeaders = LinkHeaders(nextUrl = "nextUrl") + ) + coEvery { enrollmentApi.getNextPage("nextUrl", any()) } returns DataResult.Success( + enrollments.subList(2, 4) + ) + coEvery { + assignmentApi.getFirstPageSubmissionsForAssignment( + any(), + any(), + any() + ) + } returns DataResult.Success( + submissions.subList(0, 2), linkHeaders = LinkHeaders(nextUrl = "nextUrl") + ) + coEvery { + assignmentApi.getNextPageSubmissions( + "nextUrl", + any() + ) + } returns DataResult.Success( + submissions.subList(2, 4) + ) + + val expected = listOf( + GradeableStudentSubmission( + assignee = StudentAssignee(users[0]), + submission = submissions[0] + ), + GradeableStudentSubmission( + assignee = StudentAssignee(users[1]), + submission = submissions[1] + ), + GradeableStudentSubmission( + assignee = StudentAssignee(users[2]), + submission = submissions[2] + ), + GradeableStudentSubmission( + assignee = StudentAssignee(users[3]), + submission = submissions[3] + ) + ) + val result = repository.getGradeableStudentSubmissions(Assignment(1L), 1L, false) + + assertEquals(expected, result) + } + + @Test + fun `Create group submissions`() = runTest { + val users = listOf( + User(id = 1L), + User(id = 2L), + User(id = 3L), + User(id = 4L) + ) + val students = listOf( + GradeableStudent(id = 1L), + GradeableStudent(id = 2L), + GradeableStudent(id = 3L), + GradeableStudent(id = 4L) + ) + val enrollments = listOf( + Enrollment(id = 1L, userId = 1L, user = users[0]), + Enrollment(id = 2L, userId = 2L, user = users[1]), + Enrollment(id = 3L, userId = 3L, user = users[2]), + Enrollment(id = 4L, userId = 4L, user = users[3]) + ) + val groups = listOf( + Group(id = 1L, groupCategoryId = 1L, users = users.subList(0, 2)), + Group(id = 2L, groupCategoryId = 1L, users = users.subList(2, 3)), + ) + val submissions = listOf( + Submission(id = 1L, group = groups[0]), + Submission(id = 2L, group = groups[1]), + Submission(id = 3L, userId = 4L) + ) + + coEvery { + assignmentApi.getFirstPageGradeableStudentsForAssignment(any(), any(), any()) + } returns DataResult.Success(students) + coEvery { + enrollmentApi.getFirstPageEnrollmentsForCourse(any(), any(), any()) + } returns DataResult.Success(enrollments) + coEvery { + assignmentApi.getFirstPageSubmissionsForAssignment(any(), any(), any()) + } returns DataResult.Success(submissions) + coEvery { + courseApi.getFirstPageGroups(any(), any()) + } returns DataResult.Success(groups) + + val expectedGroupSubmissions = listOf( + GradeableStudentSubmission( + assignee = GroupAssignee(groups[0], groups[0].users), + submission = submissions[0] + ), + GradeableStudentSubmission( + assignee = GroupAssignee(groups[1], groups[1].users), + submission = submissions[1] + ), + ) + val expectedIndividualSubmission = listOf( + GradeableStudentSubmission( + assignee = StudentAssignee(users[3]), + submission = submissions[2] + ) + ) + + val result = repository.getGradeableStudentSubmissions( + Assignment(1L, groupCategoryId = 1L), + 1L, + false + ) + + assertEquals(expectedGroupSubmissions + expectedIndividualSubmission, result) + } + + @Test(expected = IllegalStateException::class) + fun `Throw exception when getting students fails`() = runTest { + coEvery { + assignmentApi.getFirstPageGradeableStudentsForAssignment( + any(), + any(), + any() + ) + } returns DataResult.Fail() + + repository.getGradeableStudentSubmissions(Assignment(1L), 1L, false) + } + + @Test(expected = IllegalStateException::class) + fun `Throw exception when getting enrollments fails`() = runTest { + coEvery { + assignmentApi.getFirstPageGradeableStudentsForAssignment( + any(), + any(), + any() + ) + } returns DataResult.Success(emptyList()) + + coEvery { + enrollmentApi.getFirstPageEnrollmentsForCourse( + any(), + any(), + any() + ) + } returns DataResult.Fail() + + repository.getGradeableStudentSubmissions(Assignment(1L), 1L, false) + } + + @Test(expected = IllegalStateException::class) + fun `Throw exception when getting submissions fails`() = runTest { + coEvery { + assignmentApi.getFirstPageGradeableStudentsForAssignment( + any(), + any(), + any() + ) + } returns DataResult.Success(emptyList()) + + coEvery { + enrollmentApi.getFirstPageEnrollmentsForCourse( + any(), + any(), + any() + ) + } returns DataResult.Success(emptyList()) + + coEvery { + assignmentApi.getFirstPageSubmissionsForAssignment( + any(), + any(), + any() + ) + } returns DataResult.Fail() + + repository.getGradeableStudentSubmissions(Assignment(1L), 1L, false) + } + + @Test(expected = IllegalStateException::class) + fun `Throw exception when getting groups fails`() = runTest { + coEvery { + assignmentApi.getFirstPageGradeableStudentsForAssignment( + any(), + any(), + any() + ) + } returns DataResult.Success(emptyList()) + + coEvery { + enrollmentApi.getFirstPageEnrollmentsForCourse( + any(), + any(), + any() + ) + } returns DataResult.Success(emptyList()) + + coEvery { + assignmentApi.getFirstPageSubmissionsForAssignment( + any(), + any(), + any() + ) + } returns DataResult.Success(emptyList()) + + coEvery { + courseApi.getFirstPageGroups( + any(), + any() + ) + } returns DataResult.Fail() + + repository.getGradeableStudentSubmissions(Assignment(1L, groupCategoryId = 1L), 1L, false) + } +} \ No newline at end of file diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/AssignmentAPI.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/AssignmentAPI.kt index 5bc48d8b7e..b7a446261b 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/AssignmentAPI.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/AssignmentAPI.kt @@ -122,15 +122,31 @@ object AssignmentAPI { @GET("courses/{courseId}/assignments/{assignmentId}/gradeable_students") fun getFirstPageGradeableStudentsForAssignment(@Path("courseId") courseId: Long, @Path("assignmentId") assignmentId: Long): Call> + @GET("courses/{courseId}/assignments/{assignmentId}/gradeable_students") + suspend fun getFirstPageGradeableStudentsForAssignment( + @Path("courseId") courseId: Long, + @Path("assignmentId") assignmentId: Long, + @Tag restParams: RestParams + ): DataResult> + @GET fun getNextPageGradeableStudents(@Url nextUrl: String): Call> + @GET + suspend fun getNextPageGradeableStudents(@Url nextUrl: String, @Tag restParams: RestParams): DataResult> + @GET("courses/{courseId}/assignments/{assignmentId}/submissions?include[]=rubric_assessment&include[]=submission_history&include[]=submission_comments&include[]=group") fun getFirstPageSubmissionsForAssignment(@Path("courseId") courseId: Long, @Path("assignmentId") assignmentId: Long): Call> + @GET("courses/{courseId}/assignments/{assignmentId}/submissions?include[]=rubric_assessment&include[]=submission_history&include[]=submission_comments&include[]=group") + suspend fun getFirstPageSubmissionsForAssignment(@Path("courseId") courseId: Long, @Path("assignmentId") assignmentId: Long, @Tag params: RestParams): DataResult> + @GET fun getNextPageSubmissions(@Url nextUrl: String): Call> + @GET + suspend fun getNextPageSubmissions(@Url nextUrl: String, @Tag restParams: RestParams): DataResult> + @GET("courses/{courseId}/assignments?include[]=submission&include[]=rubric_assessment&needs_grading_count_by_section=true&override_assignment_dates=true&include[]=all_dates&include[]=overrides") fun getAssignments(@Path("courseId") courseId: Long): Call> diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/CourseAPI.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/CourseAPI.kt index 712e9d1d4d..59d5350152 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/CourseAPI.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/CourseAPI.kt @@ -136,9 +136,15 @@ object CourseAPI { @GET("courses/{courseId}/groups?include[]=users") fun getFirstPageGroups(@Path("courseId") courseId: Long): Call> + @GET("courses/{courseId}/groups?include[]=users") + suspend fun getFirstPageGroups(@Path("courseId") courseId: Long, @Tag restParams: RestParams): DataResult> + @GET fun getNextPageGroups(@Url nextUrl: String): Call> + @GET + suspend fun getNextPageGroups(@Url nextUrl: String, @Tag restParams: RestParams): DataResult> + @GET("courses/{courseId}/permissions") fun getCoursePermissions(@Path("courseId") courseId: Long, @Query("permissions[]") requestedPermissions: List): Call diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/EnrollmentAPI.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/EnrollmentAPI.kt index b69242d366..14ceb9cc15 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/EnrollmentAPI.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/EnrollmentAPI.kt @@ -50,6 +50,12 @@ object EnrollmentAPI { @Path("courseId") courseId: Long, @Query("type[]") enrollmentType: String?): Call> + @GET("courses/{courseId}/enrollments?include[]=avatar_url&state[]=active") + suspend fun getFirstPageEnrollmentsForCourse( + @Path("courseId") courseId: Long, + @Query("type[]") enrollmentType: String?, + @Tag restParams: RestParams): DataResult> + @GET("courses/{courseId}/enrollments?userId") fun getFirstPageEnrollmentsForUserInCourse( @Path("courseId") courseId: Long, From 9cbef3bb9243e2224d8e6bc96018bc77c3060a8e Mon Sep 17 00:00:00 2001 From: Akos Hermann <72087159+hermannakos@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:12:40 +0200 Subject: [PATCH 15/23] [MBL-17226][Student] Changed Sync error subtitle (#2413) Test plan: See ticket. refs: MBL-17226 affects: Student release note: none --- libs/pandares/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/pandares/src/main/res/values/strings.xml b/libs/pandares/src/main/res/values/strings.xml index c665eb497f..8bbe1eddf6 100644 --- a/libs/pandares/src/main/res/values/strings.xml +++ b/libs/pandares/src/main/res/values/strings.xml @@ -1499,7 +1499,7 @@ Offline Content Sync Failed Cancel Sync? It will stop offline content sync. You can do it again later. - One or more files failed to sync. Check your internet connection and retry to submit. + One or more items failed to sync. Please check your internet connection and retry syncing. Download starting Courses cannot be added to favorites offline. All Courses From 05cd237017d7ae63ae0c2aeb8e0a54fbf7382ff8 Mon Sep 17 00:00:00 2001 From: Akos Hermann <72087159+hermannakos@users.noreply.github.com> Date: Wed, 17 Apr 2024 10:46:40 +0200 Subject: [PATCH 16/23] [MBL-17198][Student] Fix flicker on FileDetails (#2414) Test plan: See ticket. refs: MBL-17198 affects: Student release note: none --- .../student/features/files/details/FileDetailsFragment.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/features/files/details/FileDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/features/files/details/FileDetailsFragment.kt index 4c4bfbbfe4..2a36489673 100644 --- a/apps/student/src/main/java/com/instructure/student/features/files/details/FileDetailsFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/features/files/details/FileDetailsFragment.kt @@ -96,6 +96,11 @@ class FileDetailsFragment : ParentFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_file_details, container, false) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.downloadButton.setVisible(repository.isOnline()) + } + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) getFileFolder() @@ -131,8 +136,6 @@ class FileDetailsFragment : ParentFragment() { requestPermissions(PermissionUtils.makeArray(PermissionUtils.WRITE_EXTERNAL_STORAGE), PermissionUtils.WRITE_FILE_PERMISSION_REQUEST_CODE) } } - - binding.downloadButton.setVisible(repository.isOnline()) } override fun onMediaLoadingStarted() { From b2bbbd0c9a137fc1193e429b9526323adc5b6f29 Mon Sep 17 00:00:00 2001 From: Akos Hermann <72087159+hermannakos@users.noreply.github.com> Date: Wed, 17 Apr 2024 10:47:13 +0200 Subject: [PATCH 17/23] [MBL-17228][Student] Enable manual sync when the battery is low (#2415) Test plan: See ticket. refs: MBL-17228 affects: Student release note: none --- .../pandautils/features/offline/sync/OfflineSyncHelper.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelper.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelper.kt index eb80412b8b..920c6f0367 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelper.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/OfflineSyncHelper.kt @@ -79,7 +79,6 @@ class OfflineSyncHelper( val syncSettings = syncSettingsFacade.getSyncSettings() val constraints = Constraints.Builder() .setRequiredNetworkType(if (syncSettings.wifiOnly) NetworkType.UNMETERED else NetworkType.CONNECTED) - .setRequiresBatteryNotLow(true) .build() val inputData = Data.Builder() .putLongArray(COURSE_IDS, courseIds.toLongArray()) From 84c526f53bad5646afba20798a8aac103d4ed63a Mon Sep 17 00:00:00 2001 From: Akos Hermann <72087159+hermannakos@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:35:29 +0200 Subject: [PATCH 18/23] [MBL-17186][Student] Show initials on discussions if offline (#2417) Test plan: See ticket. If offline initials should load instead of profile picture. refs: MBL-17186 affects: Student release note: none --- .../discussion/details/DiscussionDetailsFragment.kt | 3 ++- .../pandautils/discussions/DiscussionUtils.kt | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/features/discussion/details/DiscussionDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/features/discussion/details/DiscussionDetailsFragment.kt index e76d4dbabe..f53fe5f4f5 100644 --- a/apps/student/src/main/java/com/instructure/student/features/discussion/details/DiscussionDetailsFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/features/discussion/details/DiscussionDetailsFragment.kt @@ -612,7 +612,8 @@ class DiscussionDetailsFragment : ParentFragment(), Bookmarkable { canvasContext, discussionTopicHeader, discussionTopic!!.views, - discussionEntryId + discussionEntryId, + repository.isOnline() ) loadDiscussionTopicViews(html) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/discussions/DiscussionUtils.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/discussions/DiscussionUtils.kt index fe22c90bf1..9f7f974584 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/discussions/DiscussionUtils.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/discussions/DiscussionUtils.kt @@ -248,7 +248,8 @@ object DiscussionUtils { canvasContext: CanvasContext, discussionTopicHeader: DiscussionTopicHeader, discussionEntries: List, - startEntryId: Long): String { + startEntryId: Long, + isOnline: Boolean = true): String { val builder = StringBuilder() val brandColor = ThemePrefs.brandColor @@ -276,7 +277,7 @@ object DiscussionUtils { fun buildEntry(discussionEntry: DiscussionEntry, depth: Int) { val isViewableEnd = (depth == maxDepth && discussionEntry.totalChildren > 0) builder.append(build(context, isTablet, canvasContext, discussionTopicHeader, discussionEntry, converter, - template, makeAvatarForWebView(context, discussionEntry), depth, isViewableEnd, brandColor, likeColor, + template, makeAvatarForWebView(context, discussionEntry, isOnline), depth, isViewableEnd, brandColor, likeColor, likeImage, replyButtonWidth)) if (depth < maxDepth) discussionEntry.replies?.forEach { buildEntry(it, depth + 1) } } @@ -475,8 +476,8 @@ object DiscussionUtils { * If the avatar is valid then returns an empty string. Otherwise... * Returns an avatar bitmap converted into a base64 string for webviews. */ - private fun makeAvatarForWebView(context: Context, discussionEntry: DiscussionEntry): String { - if (discussionEntry.author != null && ProfileUtils.shouldLoadAltAvatarImage(discussionEntry.author!!.avatarImageUrl)) { + private fun makeAvatarForWebView(context: Context, discussionEntry: DiscussionEntry, isOnline: Boolean): String { + if (!isOnline || discussionEntry.author != null && ProfileUtils.shouldLoadAltAvatarImage(discussionEntry.author!!.avatarImageUrl)) { val avatarBitmap = ProfileUtils.getInitialsAvatarBitMap( context, discussionEntry.author!!.displayName!!, Color.TRANSPARENT, From e90e4a05a2486ac9e6d268c7cfeaa49f61f8ed3a Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:13:21 +0200 Subject: [PATCH 19/23] [MBL-17512][Student] Invalid 'Delete' and 'Rename' options on course submission files #2421 refs: MBL-17512 affects: Student release note: none --- .../student/features/files/list/FileListFragment.kt | 9 +++++---- .../features/files/list/FileListRecyclerAdapter.kt | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/features/files/list/FileListFragment.kt b/apps/student/src/main/java/com/instructure/student/features/files/list/FileListFragment.kt index 553bb47c90..9aa6614bed 100644 --- a/apps/student/src/main/java/com/instructure/student/features/files/list/FileListFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/features/files/list/FileListFragment.kt @@ -318,7 +318,7 @@ class FileListFragment : ParentFragment(), Bookmarkable, FileUploadDialogParent val isUserFiles = canvasContext.type == CanvasContext.Type.USER if (recyclerAdapter == null) { - recyclerAdapter = FileListRecyclerAdapter(requireContext(), canvasContext, getFileMenuOptions(folder, canvasContext, fileListRepository.isOnline()), folder, adapterCallback, fileListRepository) + recyclerAdapter = FileListRecyclerAdapter(requireContext(), canvasContext, getFileMenuOptions(folder, canvasContext, fileListRepository.isOnline(), folder), folder, adapterCallback, fileListRepository) } configureRecyclerView(requireView(), requireContext(), recyclerAdapter!!, R.id.swipeRefreshLayout, R.id.emptyView, R.id.listView) @@ -362,7 +362,7 @@ class FileListFragment : ParentFragment(), Bookmarkable, FileUploadDialogParent val popup = PopupMenu(requireContext(), anchorView) popup.inflate(R.menu.file_folder_options) with(popup.menu) { - val options = getFileMenuOptions(item, canvasContext, fileListRepository.isOnline()) + val options = getFileMenuOptions(item, canvasContext, fileListRepository.isOnline(), folder) // Only show alternate-open option for PDF files findItem(R.id.openAlternate).isVisible = options.contains(FileMenuType.OPEN_IN_ALTERNATE) findItem(R.id.download).isVisible = options.contains(FileMenuType.DOWNLOAD) @@ -609,7 +609,7 @@ class FileListFragment : ParentFragment(), Bookmarkable, FileUploadDialogParent /** * @return A list of possible actions the user is able to perform on the file/folder */ - fun getFileMenuOptions(fileFolder: FileFolder?, canvasContext: CanvasContext, isOnline: Boolean): List { + fun getFileMenuOptions(fileFolder: FileFolder?, canvasContext: CanvasContext, isOnline: Boolean, folder: FileFolder?): List { if (fileFolder == null) return emptyList() val options: MutableList = mutableListOf() @@ -617,7 +617,8 @@ class FileListFragment : ParentFragment(), Bookmarkable, FileUploadDialogParent // We're in the user's files, they should have options in the options menu if (!fileFolder.isLockedForUser) { // File is not locked for this user - if (!fileFolder.forSubmissions) { + val forSubmission = if (!fileFolder.isFile) fileFolder.forSubmissions else folder?.forSubmissions ?: false + if (!forSubmission) { // File/folder is not for a submission, so we can rename/delete with(options) { add(FileMenuType.RENAME) diff --git a/apps/student/src/main/java/com/instructure/student/features/files/list/FileListRecyclerAdapter.kt b/apps/student/src/main/java/com/instructure/student/features/files/list/FileListRecyclerAdapter.kt index 35f5dae3fd..29e867d69d 100644 --- a/apps/student/src/main/java/com/instructure/student/features/files/list/FileListRecyclerAdapter.kt +++ b/apps/student/src/main/java/com/instructure/student/features/files/list/FileListRecyclerAdapter.kt @@ -68,7 +68,7 @@ open class FileListRecyclerAdapter( } override fun bindHolder(item: FileFolder, holder: FileViewHolder, position: Int) { - holder.bind(item, contextColor, context, FileListFragment.getFileMenuOptions(item, canvasContext, fileListRepository.isOnline()), fileFolderCallback) + holder.bind(item, contextColor, context, FileListFragment.getFileMenuOptions(item, canvasContext, fileListRepository.isOnline(), folder), fileFolderCallback) } override fun createViewHolder(v: View, viewType: Int) = FileViewHolder(v) From df14720aac51396dd4883457c8c59f12e07db987 Mon Sep 17 00:00:00 2001 From: Akos Hermann <72087159+hermannakos@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:25:56 +0200 Subject: [PATCH 20/23] [MBL-17173][Student] Make Announcement/Discussion attachments available offline (#2420) Test plan: Check if the attachments are available offline. Only works for the topic header, not for the replies. Supposedly no db scheme change, but check a migration to be sure. refs: MBL-17173 affects: Student release note: Announcement and Discussion attachments are now available offline. --- .../details/DiscussionDetailsFragment.kt | 46 +++---- .../SubmissionDetailsUpdateTest.kt | 10 ++ .../SubmissionCommentsUpdateTest.kt | 13 ++ .../canvasapi2/models/Attachment.kt | 6 + .../daos/DiscussionTopicRemoteFileDaoTest.kt | 129 ++++++++++++++++++ .../room/offline/daos/RemoteFileDaoTest.kt | 83 +++++++++++ .../pandautils/di/OfflineModule.kt | 25 +++- .../features/offline/sync/CourseSync.kt | 13 +- .../room/offline/OfflineDatabase.kt | 6 + .../daos/DiscussionTopicRemoteFileDao.kt | 33 +++++ .../room/offline/daos/RemoteFileDao.kt | 38 ++++++ .../entities/DiscussionTopicHeaderEntity.kt | 7 +- .../room/offline/entities/RemoteFileEntity.kt | 47 ++++++- .../facade/DiscussionTopicHeaderFacade.kt | 56 ++++++-- .../facade/DiscussionTopicHeaderFacadeTest.kt | 43 +++++- 15 files changed, 505 insertions(+), 50 deletions(-) create mode 100644 libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/DiscussionTopicRemoteFileDaoTest.kt create mode 100644 libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/RemoteFileDaoTest.kt create mode 100644 libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/daos/DiscussionTopicRemoteFileDao.kt create mode 100644 libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/daos/RemoteFileDao.kt diff --git a/apps/student/src/main/java/com/instructure/student/features/discussion/details/DiscussionDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/features/discussion/details/DiscussionDetailsFragment.kt index f53fe5f4f5..bcc9257472 100644 --- a/apps/student/src/main/java/com/instructure/student/features/discussion/details/DiscussionDetailsFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/features/discussion/details/DiscussionDetailsFragment.kt @@ -194,33 +194,29 @@ class DiscussionDetailsFragment : ParentFragment(), Bookmarkable { //region Discussion Actions private fun viewAttachments(remoteFiles: List) { - if (repository.isOnline()) { - // Only one file can be attached to a discussion - val remoteFile = remoteFiles.firstOrNull() ?: return - - // Show lock message if file is locked - if (remoteFile.lockedForUser) { - if (remoteFile.lockExplanation.isValid()) { - Snackbar.make( - requireView(), - remoteFile.lockExplanation!!, - Snackbar.LENGTH_SHORT - ).show() - } else { - Snackbar.make( - requireView(), - R.string.fileCurrentlyLocked, - Snackbar.LENGTH_SHORT - ).show() - } + // Only one file can be attached to a discussion + val remoteFile = remoteFiles.firstOrNull() ?: return + + // Show lock message if file is locked + if (remoteFile.lockedForUser) { + if (remoteFile.lockExplanation.isValid()) { + Snackbar.make( + requireView(), + remoteFile.lockExplanation!!, + Snackbar.LENGTH_SHORT + ).show() + } else { + Snackbar.make( + requireView(), + R.string.fileCurrentlyLocked, + Snackbar.LENGTH_SHORT + ).show() } - - // Show attachment - val attachment = remoteFile.mapToAttachment() - openMedia(attachment.contentType, attachment.url, attachment.filename, canvasContext) - } else { - NoInternetConnectionDialog.show(requireFragmentManager()) } + + // Show attachment + val attachment = remoteFile.mapToAttachment() + openMedia(attachment.contentType, attachment.url, attachment.filename, canvasContext, localFile = attachment.isLocalFile) } private fun showReplyView(discussionEntryId: Long) { diff --git a/apps/student/src/test/java/com/instructure/student/test/assignment/details/submissionDetails/SubmissionDetailsUpdateTest.kt b/apps/student/src/test/java/com/instructure/student/test/assignment/details/submissionDetails/SubmissionDetailsUpdateTest.kt index dba488f7e2..0686b245e1 100644 --- a/apps/student/src/test/java/com/instructure/student/test/assignment/details/submissionDetails/SubmissionDetailsUpdateTest.kt +++ b/apps/student/src/test/java/com/instructure/student/test/assignment/details/submissionDetails/SubmissionDetailsUpdateTest.kt @@ -17,6 +17,7 @@ package com.instructure.student.test.assignment.details.submissionDetails import android.net.Uri import android.webkit.MimeTypeMap +import android.webkit.URLUtil import com.instructure.canvasapi2.models.* import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.DataResult @@ -34,6 +35,7 @@ import com.spotify.mobius.test.NextMatchers.hasNothing import com.spotify.mobius.test.UpdateSpec import com.spotify.mobius.test.UpdateSpec.assertThatNext import io.mockk.* +import org.junit.After import org.junit.Assert import org.junit.Before import org.junit.Test @@ -59,6 +61,14 @@ class SubmissionDetailsUpdateTest : Assert() { submission = Submission(id = 30L, attempt = 1L, assignmentId = assignment.id) initModel = SubmissionDetailsModel(assignmentId = assignment.id, canvasContext = course, isStudioEnabled = isStudioEnabled, assignmentEnhancementsEnabled = true) ltiTool = LTITool(url = "https://www.instructure.com") + + mockkStatic(URLUtil::class) + every { URLUtil.isNetworkUrl(any()) } returns true + } + + @After + fun tearDown() { + unmockkAll() } @Test diff --git a/apps/student/src/test/java/com/instructure/student/test/assignment/details/submissionDetails/commentTab/SubmissionCommentsUpdateTest.kt b/apps/student/src/test/java/com/instructure/student/test/assignment/details/submissionDetails/commentTab/SubmissionCommentsUpdateTest.kt index f153bc0b2b..22ed89ffa7 100644 --- a/apps/student/src/test/java/com/instructure/student/test/assignment/details/submissionDetails/commentTab/SubmissionCommentsUpdateTest.kt +++ b/apps/student/src/test/java/com/instructure/student/test/assignment/details/submissionDetails/commentTab/SubmissionCommentsUpdateTest.kt @@ -16,6 +16,7 @@ */ package com.instructure.student.test.assignment.details.submissionDetails.commentTab +import android.webkit.URLUtil import com.instructure.canvasapi2.models.* import com.instructure.student.mobius.assignmentDetails.submissionDetails.drawer.comments.SubmissionCommentsEffect import com.instructure.student.mobius.assignmentDetails.submissionDetails.drawer.comments.SubmissionCommentsEvent @@ -30,6 +31,10 @@ import com.spotify.mobius.test.NextMatchers.hasModel import com.spotify.mobius.test.NextMatchers.hasNoModel import com.spotify.mobius.test.UpdateSpec import com.spotify.mobius.test.UpdateSpec.assertThatNext +import io.mockk.every +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import org.junit.After import org.junit.Assert import org.junit.Before import org.junit.Test @@ -51,6 +56,14 @@ class SubmissionCommentsUpdateTest : Assert() { attemptId = 1, assignmentEnhancementsEnabled = true ) + + mockkStatic(URLUtil::class) + every { URLUtil.isNetworkUrl(any()) } returns false + } + + @After + fun teardown() { + unmockkAll() } @Test diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/models/Attachment.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/models/Attachment.kt index 14890338b7..065283bee4 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/models/Attachment.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/models/Attachment.kt @@ -17,7 +17,10 @@ package com.instructure.canvasapi2.models +import android.webkit.URLUtil import com.google.gson.annotations.SerializedName +import com.instructure.canvasapi2.utils.isValid +import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import java.util.* @@ -40,4 +43,7 @@ data class Attachment( ) : CanvasModel() { override val comparisonDate get() = createdAt override val comparisonString get() = displayName + + @IgnoredOnParcel + val isLocalFile = url.isValid() && !URLUtil.isNetworkUrl(url) } diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/DiscussionTopicRemoteFileDaoTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/DiscussionTopicRemoteFileDaoTest.kt new file mode 100644 index 0000000000..8692631aa9 --- /dev/null +++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/DiscussionTopicRemoteFileDaoTest.kt @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */package com.instructure.pandautils.room.offline.daos + +import android.content.Context +import android.database.sqlite.SQLiteConstraintException +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.instructure.canvasapi2.models.Course +import com.instructure.canvasapi2.models.DiscussionParticipant +import com.instructure.canvasapi2.models.DiscussionTopicHeader +import com.instructure.canvasapi2.models.RemoteFile +import com.instructure.pandautils.room.offline.OfflineDatabase +import com.instructure.pandautils.room.offline.entities.CourseEntity +import com.instructure.pandautils.room.offline.entities.DiscussionParticipantEntity +import com.instructure.pandautils.room.offline.entities.DiscussionTopicHeaderEntity +import com.instructure.pandautils.room.offline.entities.DiscussionTopicPermissionEntity +import com.instructure.pandautils.room.offline.entities.DiscussionTopicRemoteFileEntity +import com.instructure.pandautils.room.offline.entities.RemoteFileEntity +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class DiscussionTopicRemoteFileDaoTest { + + private lateinit var db: OfflineDatabase + private lateinit var remoteFileDao: RemoteFileDao + private lateinit var discussionTopicHeaderDao: DiscussionTopicHeaderDao + private lateinit var discussionTopicRemoteFileDao: DiscussionTopicRemoteFileDao + + @Before + fun setUp() = runTest { + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder(context, OfflineDatabase::class.java).build() + remoteFileDao = db.remoteFileDao() + discussionTopicRemoteFileDao = db.discussionTopicRemoteFileDao() + discussionTopicHeaderDao = db.discussionTopicHeaderDao() + + db.courseDao().insert(CourseEntity(Course(1L))) + } + + @After + fun tearDown() { + db.close() + } + + @Test + fun testFindByDiscussionId() = runTest { + val remoteFileEntity = RemoteFileEntity(RemoteFile(1L)) + val discussionTopicEntity = DiscussionTopicHeaderEntity(DiscussionTopicHeader(2L), 1L) + discussionTopicHeaderDao.insert(discussionTopicEntity) + remoteFileDao.insert(remoteFileEntity) + + val discussionTopicRemoteFileEntity = DiscussionTopicRemoteFileEntity(discussionTopicEntity.id, remoteFileEntity.id) + discussionTopicRemoteFileDao.insert(discussionTopicRemoteFileEntity) + val result = discussionTopicRemoteFileDao.findByDiscussionId(discussionTopicEntity.id) + assertEquals(listOf(discussionTopicRemoteFileEntity), result) + } + + @Test(expected = SQLiteConstraintException::class) + fun testInsertDiscussionForeignKey() = runTest { + val remoteFileEntity = RemoteFileEntity(RemoteFile(1L)) + remoteFileDao.insert(remoteFileEntity) + + val discussionTopicRemoteFileEntity = DiscussionTopicRemoteFileEntity(1L, remoteFileEntity.id) + discussionTopicRemoteFileDao.insert(discussionTopicRemoteFileEntity) + } + + @Test(expected = SQLiteConstraintException::class) + fun testInsertRemoteFileForeignKey() = runTest { + val discussionTopicEntity = DiscussionTopicHeaderEntity(DiscussionTopicHeader(1L), 1L) + discussionTopicHeaderDao.insert(discussionTopicEntity) + + val discussionTopicRemoteFileEntity = DiscussionTopicRemoteFileEntity(discussionTopicEntity.id, 1L) + discussionTopicRemoteFileDao.insert(discussionTopicRemoteFileEntity) + } + + @Test + fun testRemoteFileCascade() = runTest { + val remoteFileEntity = RemoteFileEntity(RemoteFile(1L)) + remoteFileDao.insert(remoteFileEntity) + + val discussionTopicEntity = DiscussionTopicHeaderEntity(DiscussionTopicHeader(2L), 1L) + discussionTopicHeaderDao.insert(discussionTopicEntity) + + val discussionTopicRemoteFileEntity = DiscussionTopicRemoteFileEntity(discussionTopicEntity.id, remoteFileEntity.id) + discussionTopicRemoteFileDao.insert(discussionTopicRemoteFileEntity) + + remoteFileDao.delete(remoteFileEntity) + val result = discussionTopicRemoteFileDao.findByDiscussionId(discussionTopicEntity.id) + assertEquals(emptyList(), result) + } + + @Test + fun testDiscussionTopicCascade() = runTest { + val remoteFileEntity = RemoteFileEntity(RemoteFile(1L)) + remoteFileDao.insert(remoteFileEntity) + + val discussionTopicEntity = DiscussionTopicHeaderEntity(DiscussionTopicHeader(2L), 1L) + discussionTopicHeaderDao.insert(discussionTopicEntity) + + val discussionTopicRemoteFileEntity = DiscussionTopicRemoteFileEntity(discussionTopicEntity.id, remoteFileEntity.id) + discussionTopicRemoteFileDao.insert(discussionTopicRemoteFileEntity) + + discussionTopicHeaderDao.delete(discussionTopicEntity) + val result = discussionTopicRemoteFileDao.findByDiscussionId(discussionTopicEntity.id) + assertEquals(emptyList(), result) + } + +} \ No newline at end of file diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/RemoteFileDaoTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/RemoteFileDaoTest.kt new file mode 100644 index 0000000000..c27b9a24da --- /dev/null +++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/offline/daos/RemoteFileDaoTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */package com.instructure.pandautils.room.offline.daos + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.instructure.canvasapi2.models.RemoteFile +import com.instructure.pandautils.room.offline.OfflineDatabase +import com.instructure.pandautils.room.offline.entities.RemoteFileEntity +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class RemoteFileDaoTest { + + private lateinit var db: OfflineDatabase + private lateinit var remoteFileDao: RemoteFileDao + + @Before + fun setUp() { + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder(context, OfflineDatabase::class.java).build() + remoteFileDao = db.remoteFileDao() + } + + @After + fun tearDown() { + db.close() + } + + @Test + fun testInsertReplace() = runTest { + val remoteFileEntity = RemoteFileEntity(RemoteFile(1L)) + val updated = remoteFileEntity.copy(displayName = "new name") + remoteFileDao.insert(remoteFileEntity) + remoteFileDao.insert(updated) + val result = remoteFileDao.findById(1) + assertEquals(updated, result) + } + + @Test + fun testInsertAllReplace() = runTest { + val remoteFileEntity1 = RemoteFileEntity(RemoteFile(1L)) + val remoteFileEntity2 = RemoteFileEntity(RemoteFile(2L)) + val updated = remoteFileEntity1.copy(displayName = "new name") + remoteFileDao.insertAll(listOf(remoteFileEntity1, remoteFileEntity2)) + remoteFileDao.insertAll(listOf(updated)) + val result1 = remoteFileDao.findById(1) + val result2 = remoteFileDao.findById(2) + assertEquals(updated, result1) + assertEquals(remoteFileEntity2, result2) + } + + @Test + fun testFindById() = runTest { + val remoteFileEntity = RemoteFileEntity(RemoteFile(1L)) + val remoteFileEntity2 = RemoteFileEntity(RemoteFile(2L)) + remoteFileDao.insertAll(listOf(remoteFileEntity, remoteFileEntity2)) + val result = remoteFileDao.findById(1) + assertEquals(remoteFileEntity, result) + } + +} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/di/OfflineModule.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/di/OfflineModule.kt index 6cbed10131..2f4b27eb49 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/di/OfflineModule.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/di/OfflineModule.kt @@ -282,9 +282,20 @@ class OfflineModule { discussionTopicHeaderDao: DiscussionTopicHeaderDao, discussionParticipantDao: DiscussionParticipantDao, discussionTopicPermissionDao: DiscussionTopicPermissionDao, + remoteFileDao: RemoteFileDao, + localFileDao: LocalFileDao, + discussionTopicRemoteFileDao: DiscussionTopicRemoteFileDao, offlineDatabase: OfflineDatabase, ): DiscussionTopicHeaderFacade { - return DiscussionTopicHeaderFacade(discussionTopicHeaderDao, discussionParticipantDao, discussionTopicPermissionDao, offlineDatabase) + return DiscussionTopicHeaderFacade( + discussionTopicHeaderDao, + discussionParticipantDao, + discussionTopicPermissionDao, + remoteFileDao, + localFileDao, + discussionTopicRemoteFileDao, + offlineDatabase + ) } @Provides @@ -532,4 +543,16 @@ class OfflineModule { ): OfflineSyncHelper { return OfflineSyncHelper(workManager, syncSettingsFacade, apiPrefs) } + + @Provides + fun provideRemoteFileDao( + appDatabase: OfflineDatabase + ): RemoteFileDao { + return appDatabase.remoteFileDao() + } + + @Provides + fun provideDiscussionTopicRemoteFileDao(appDatabase: OfflineDatabase): DiscussionTopicRemoteFileDao { + return appDatabase.discussionTopicRemoteFileDao() + } } \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/CourseSync.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/CourseSync.kt index aa2b4fea03..cd9bed2f7d 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/CourseSync.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/offline/sync/CourseSync.kt @@ -59,6 +59,7 @@ import com.instructure.pandautils.room.offline.entities.CourseSyncProgressEntity import com.instructure.pandautils.room.offline.entities.CourseSyncSettingsEntity import com.instructure.pandautils.room.offline.entities.FileFolderEntity import com.instructure.pandautils.room.offline.entities.QuizEntity +import com.instructure.pandautils.room.offline.entities.RemoteFileEntity import com.instructure.pandautils.room.offline.facade.AssignmentFacade import com.instructure.pandautils.room.offline.facade.ConferenceFacade import com.instructure.pandautils.room.offline.facade.CourseFacade @@ -150,7 +151,11 @@ class CourseSync( listOf(contentDeferred, filesDeferred).awaitAll() } - fileSync.syncAdditionalFiles(courseSettings, additionalFileIdsToSync[courseId].orEmpty(), externalFilesToSync[courseId].orEmpty()) + fileSync.syncAdditionalFiles( + courseSettings, + additionalFileIdsToSync[courseId].orEmpty(), + externalFilesToSync[courseId].orEmpty(), + ) val progress = courseSyncProgressDao.findByCourseId(courseId) progress @@ -411,6 +416,9 @@ class CourseSync( discussions.forEach { it.message = parseHtmlContent(it.message, courseId) + it.attachments.forEach { + additionalFileIdsToSync[courseId] = additionalFileIdsToSync[courseId].orEmpty() + it.id + } } discussionTopicHeaderFacade.insertDiscussions(discussions, courseId, false) @@ -433,6 +441,9 @@ class CourseSync( announcements.forEach { it.message = parseHtmlContent(it.message, courseId) + it.attachments.forEach { + additionalFileIdsToSync[courseId] = additionalFileIdsToSync[courseId].orEmpty() + it.id + } } discussionTopicHeaderFacade.insertDiscussions(announcements, courseId, true) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/OfflineDatabase.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/OfflineDatabase.kt index b455e6f507..3b2d610ee9 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/OfflineDatabase.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/OfflineDatabase.kt @@ -43,6 +43,7 @@ import com.instructure.pandautils.room.offline.daos.DiscussionParticipantDao import com.instructure.pandautils.room.offline.daos.DiscussionTopicDao import com.instructure.pandautils.room.offline.daos.DiscussionTopicHeaderDao import com.instructure.pandautils.room.offline.daos.DiscussionTopicPermissionDao +import com.instructure.pandautils.room.offline.daos.DiscussionTopicRemoteFileDao import com.instructure.pandautils.room.offline.daos.EditDashboardItemDao import com.instructure.pandautils.room.offline.daos.EnrollmentDao import com.instructure.pandautils.room.offline.daos.FileFolderDao @@ -66,6 +67,7 @@ import com.instructure.pandautils.room.offline.daos.ModuleObjectDao import com.instructure.pandautils.room.offline.daos.PageDao import com.instructure.pandautils.room.offline.daos.PlannerOverrideDao import com.instructure.pandautils.room.offline.daos.QuizDao +import com.instructure.pandautils.room.offline.daos.RemoteFileDao import com.instructure.pandautils.room.offline.daos.RubricCriterionAssessmentDao import com.instructure.pandautils.room.offline.daos.RubricCriterionDao import com.instructure.pandautils.room.offline.daos.RubricCriterionRatingDao @@ -341,4 +343,8 @@ abstract class OfflineDatabase : RoomDatabase() { abstract fun discussionEntryDao(): DiscussionEntryDao abstract fun discussionTopicPermissionDao(): DiscussionTopicPermissionDao + + abstract fun remoteFileDao(): RemoteFileDao + + abstract fun discussionTopicRemoteFileDao(): DiscussionTopicRemoteFileDao } \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/daos/DiscussionTopicRemoteFileDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/daos/DiscussionTopicRemoteFileDao.kt new file mode 100644 index 0000000000..3f911a20fb --- /dev/null +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/daos/DiscussionTopicRemoteFileDao.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */package com.instructure.pandautils.room.offline.daos + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import com.instructure.pandautils.room.offline.entities.DiscussionTopicRemoteFileEntity + +@Dao +interface DiscussionTopicRemoteFileDao { + + @Insert + suspend fun insert(entity: DiscussionTopicRemoteFileEntity) + + @Insert + suspend fun insertAll(entities: List) + + @Query("SELECT * FROM DiscussionTopicRemoteFileEntity WHERE discussionId = :discussionId") + suspend fun findByDiscussionId(discussionId: Long): List +} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/daos/RemoteFileDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/daos/RemoteFileDao.kt new file mode 100644 index 0000000000..0d6afbd1f2 --- /dev/null +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/daos/RemoteFileDao.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 - present Instructure, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */package com.instructure.pandautils.room.offline.daos + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.instructure.pandautils.room.offline.entities.RemoteFileEntity + +@Dao +interface RemoteFileDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(entity: RemoteFileEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(entities: List) + + @Delete + suspend fun delete(entity: RemoteFileEntity) + + @Query("SELECT * FROM RemoteFileEntity WHERE id = :id") + suspend fun findById(id: Long): RemoteFileEntity? +} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/DiscussionTopicHeaderEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/DiscussionTopicHeaderEntity.kt index 5eea233e1d..4b26468d14 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/DiscussionTopicHeaderEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/DiscussionTopicHeaderEntity.kt @@ -24,6 +24,7 @@ import com.instructure.canvasapi2.models.Assignment import com.instructure.canvasapi2.models.DiscussionParticipant import com.instructure.canvasapi2.models.DiscussionTopicHeader import com.instructure.canvasapi2.models.DiscussionTopicPermission +import com.instructure.canvasapi2.models.RemoteFile import com.instructure.pandautils.utils.orDefault import java.util.Date @@ -126,7 +127,8 @@ data class DiscussionTopicHeaderEntity( fun toApiModel( author: DiscussionParticipant? = null, assignment: Assignment? = null, - permissions: DiscussionTopicPermission? = null + permissions: DiscussionTopicPermission? = null, + attachments: List ) = DiscussionTopicHeader( id = id, discussionType = discussionType, @@ -151,8 +153,7 @@ data class DiscussionTopicHeaderEntity( groupCategoryId = groupCategoryId, announcement = announcement, groupTopicChildren = emptyList(), - //TODO - attachments = mutableListOf(), + attachments = attachments.toMutableList(), //TODO permissions = permissions, assignment = assignment, diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/RemoteFileEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/RemoteFileEntity.kt index 87d0980a4e..cca851c4a9 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/RemoteFileEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/entities/RemoteFileEntity.kt @@ -19,6 +19,7 @@ package com.instructure.pandautils.room.offline.entities import androidx.room.Entity import androidx.room.PrimaryKey +import com.instructure.canvasapi2.models.RemoteFile @Entity data class RemoteFileEntity( @@ -42,4 +43,48 @@ data class RemoteFileEntity( val lockedForUser: Boolean, val previewUrl: String?, val lockExplanation: String? -) \ No newline at end of file +) { + constructor(remoteFile: RemoteFile) : this( + id = remoteFile.id, + folderId = remoteFile.folderId, + displayName = remoteFile.displayName, + fileName = remoteFile.fileName, + contentType = remoteFile.contentType, + url = remoteFile.url, + size = remoteFile.size, + createdAt = remoteFile.createdAt, + updatedAt = remoteFile.updatedAt, + unlockAt = remoteFile.unlockAt, + locked = remoteFile.locked, + hidden = remoteFile.hidden, + lockAt = remoteFile.lockAt, + hiddenForUser = remoteFile.hiddenForUser, + thumbnailUrl = remoteFile.thumbnailUrl, + modifiedAt = remoteFile.modifiedAt, + lockedForUser = remoteFile.lockedForUser, + previewUrl = remoteFile.previewUrl, + lockExplanation = remoteFile.lockExplanation + ) + + fun toApiModel() = RemoteFile( + id = id, + folderId = folderId, + displayName = displayName, + fileName = fileName, + contentType = contentType, + url = url, + size = size, + createdAt = createdAt, + updatedAt = updatedAt, + unlockAt = unlockAt, + locked = locked, + hidden = hidden, + lockAt = lockAt, + hiddenForUser = hiddenForUser, + thumbnailUrl = thumbnailUrl, + modifiedAt = modifiedAt, + lockedForUser = lockedForUser, + previewUrl = previewUrl, + lockExplanation = lockExplanation + ) +} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/facade/DiscussionTopicHeaderFacade.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/facade/DiscussionTopicHeaderFacade.kt index ffba0e901f..d2c6fcb53a 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/facade/DiscussionTopicHeaderFacade.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/offline/facade/DiscussionTopicHeaderFacade.kt @@ -23,20 +23,32 @@ import com.instructure.pandautils.room.offline.OfflineDatabase import com.instructure.pandautils.room.offline.daos.DiscussionParticipantDao import com.instructure.pandautils.room.offline.daos.DiscussionTopicHeaderDao import com.instructure.pandautils.room.offline.daos.DiscussionTopicPermissionDao +import com.instructure.pandautils.room.offline.daos.DiscussionTopicRemoteFileDao +import com.instructure.pandautils.room.offline.daos.LocalFileDao +import com.instructure.pandautils.room.offline.daos.RemoteFileDao import com.instructure.pandautils.room.offline.entities.DiscussionParticipantEntity import com.instructure.pandautils.room.offline.entities.DiscussionTopicHeaderEntity import com.instructure.pandautils.room.offline.entities.DiscussionTopicPermissionEntity +import com.instructure.pandautils.room.offline.entities.DiscussionTopicRemoteFileEntity +import com.instructure.pandautils.room.offline.entities.RemoteFileEntity class DiscussionTopicHeaderFacade( private val discussionTopicHeaderDao: DiscussionTopicHeaderDao, private val discussionParticipantDao: DiscussionParticipantDao, private val discussionTopicPermissionDao: DiscussionTopicPermissionDao, + private val remoteFileDao: RemoteFileDao, + private val localFileDao: LocalFileDao, + private val discussionTopicRemoteFileDao: DiscussionTopicRemoteFileDao, private val offlineDatabase: OfflineDatabase ) { suspend fun insertDiscussion(discussionTopicHeader: DiscussionTopicHeader, courseId: Long): Long { discussionTopicHeader.author?.let { discussionParticipantDao.insert(DiscussionParticipantEntity(it)) } val discussionTopicHeaderId = discussionTopicHeaderDao.insert(DiscussionTopicHeaderEntity(discussionTopicHeader, courseId, null)) val permissionId = discussionTopicHeader.permissions?.let { discussionTopicPermissionDao.insert(DiscussionTopicPermissionEntity(it, discussionTopicHeaderId)) } + val attachments = discussionTopicHeader.attachments.map { RemoteFileEntity(it) } + val connectionEntities = attachments.map { DiscussionTopicRemoteFileEntity(discussionTopicHeaderId, it.id) } + remoteFileDao.insertAll(attachments) + discussionTopicRemoteFileDao.insertAll(connectionEntities) discussionTopicHeaderDao.update(DiscussionTopicHeaderEntity(discussionTopicHeader.copy(id = discussionTopicHeaderId), courseId, permissionId)) return discussionTopicHeaderId } @@ -51,15 +63,23 @@ class DiscussionTopicHeaderFacade( discussionParticipantDao.upsertAll(authors) - val discussionEntities = - discussionTopicHeaders.mapIndexed { index, discussionTopicHeader -> - DiscussionTopicHeaderEntity( - discussionTopicHeader, - courseId, - null - ) - } + val discussionEntities = mutableListOf() + val attachmentEntities = mutableListOf() + val discussionRemoteFileEntities = mutableListOf() + + discussionTopicHeaders.forEach { discussion -> + val entity = DiscussionTopicHeaderEntity(discussion, courseId, null) + val attachments = discussion.attachments.map { RemoteFileEntity(it) } + val connectionEntites = attachments.map { DiscussionTopicRemoteFileEntity(entity.id, it.id) } + + discussionEntities.add(entity) + attachmentEntities.addAll(attachments) + discussionRemoteFileEntities.addAll(connectionEntites) + } + discussionTopicHeaderDao.insertAll(discussionEntities) + remoteFileDao.insertAll(attachmentEntities) + discussionTopicRemoteFileDao.insertAll(discussionRemoteFileEntities) val permissionIds = discussionTopicHeaders.mapIndexed { index, discussionTopicHeader -> discussionTopicHeader.permissions?.let { @@ -76,9 +96,7 @@ class DiscussionTopicHeaderFacade( suspend fun getDiscussionsForCourse(courseId: Long): List { return discussionTopicHeaderDao.findAllDiscussionsForCourse(courseId) .map { discussionTopic -> - val authorEntity = discussionTopic.authorId?.let { discussionParticipantDao.findById(it) } - val permission = discussionTopicPermissionDao.findByDiscussionTopicHeaderId(discussionTopic.id) - discussionTopic.toApiModel(author = authorEntity?.toApiModel(), permissions = permission?.toApiModel()) + createDiscussionApiModel(discussionTopic) } } @@ -97,8 +115,18 @@ class DiscussionTopicHeaderFacade( } private suspend fun createDiscussionApiModel(discussionTopicHeaderEntity: DiscussionTopicHeaderEntity): DiscussionTopicHeader { - val authorEntity = discussionTopicHeaderEntity.authorId?.let { discussionParticipantDao.findById(it) } - val permission = discussionTopicPermissionDao.findByDiscussionTopicHeaderId(discussionTopicHeaderEntity.id) - return discussionTopicHeaderEntity.toApiModel(authorEntity?.toApiModel(), permissions = permission?.toApiModel()) + val authorEntity = + discussionTopicHeaderEntity.authorId?.let { discussionParticipantDao.findById(it) } + val permission = + discussionTopicPermissionDao.findByDiscussionTopicHeaderId(discussionTopicHeaderEntity.id) + val attachments = discussionTopicRemoteFileDao.findByDiscussionId( + discussionTopicHeaderEntity.id + ).mapNotNull { remoteFileDao.findById(it.remoteFileId) } + .map { + val path = localFileDao.findById(it.id)?.path + it.copy(url = path) + } + .map { it.toApiModel() } + return discussionTopicHeaderEntity.toApiModel(authorEntity?.toApiModel(), permissions = permission?.toApiModel(), attachments = attachments) } } \ No newline at end of file diff --git a/libs/pandautils/src/test/java/com/instructure/pandautils/room/offline/facade/DiscussionTopicHeaderFacadeTest.kt b/libs/pandautils/src/test/java/com/instructure/pandautils/room/offline/facade/DiscussionTopicHeaderFacadeTest.kt index 7b6206f1b9..61cba7faf7 100644 --- a/libs/pandautils/src/test/java/com/instructure/pandautils/room/offline/facade/DiscussionTopicHeaderFacadeTest.kt +++ b/libs/pandautils/src/test/java/com/instructure/pandautils/room/offline/facade/DiscussionTopicHeaderFacadeTest.kt @@ -21,13 +21,20 @@ import androidx.room.withTransaction import com.instructure.canvasapi2.models.DiscussionParticipant import com.instructure.canvasapi2.models.DiscussionTopicHeader import com.instructure.canvasapi2.models.DiscussionTopicPermission +import com.instructure.canvasapi2.models.RemoteFile import com.instructure.pandautils.room.offline.OfflineDatabase import com.instructure.pandautils.room.offline.daos.DiscussionParticipantDao import com.instructure.pandautils.room.offline.daos.DiscussionTopicHeaderDao import com.instructure.pandautils.room.offline.daos.DiscussionTopicPermissionDao +import com.instructure.pandautils.room.offline.daos.DiscussionTopicRemoteFileDao +import com.instructure.pandautils.room.offline.daos.LocalFileDao +import com.instructure.pandautils.room.offline.daos.RemoteFileDao import com.instructure.pandautils.room.offline.entities.DiscussionParticipantEntity import com.instructure.pandautils.room.offline.entities.DiscussionTopicHeaderEntity import com.instructure.pandautils.room.offline.entities.DiscussionTopicPermissionEntity +import com.instructure.pandautils.room.offline.entities.DiscussionTopicRemoteFileEntity +import com.instructure.pandautils.room.offline.entities.LocalFileEntity +import com.instructure.pandautils.room.offline.entities.RemoteFileEntity import io.mockk.MockKAnnotations import io.mockk.coEvery import io.mockk.coVerify @@ -41,6 +48,7 @@ import org.junit.After import org.junit.Assert import org.junit.Before import org.junit.Test +import java.util.Date @ExperimentalCoroutinesApi class DiscussionTopicHeaderFacadeTest { @@ -48,9 +56,12 @@ class DiscussionTopicHeaderFacadeTest { private val discussionTopicHeaderDao: DiscussionTopicHeaderDao = mockk(relaxed = true) private val discussionParticipantDao: DiscussionParticipantDao = mockk(relaxed = true) private val discussionTopicPermissionDao: DiscussionTopicPermissionDao = mockk(relaxed = true) + private val remoteFileDao: RemoteFileDao = mockk(relaxed = true) + private val localFileDao: LocalFileDao = mockk(relaxed = true) + private val discussionTopicRemoteFileDao: DiscussionTopicRemoteFileDao = mockk(relaxed = true) private val offlineDatabase: OfflineDatabase = mockk(relaxed = true) - private val facade = DiscussionTopicHeaderFacade(discussionTopicHeaderDao, discussionParticipantDao, discussionTopicPermissionDao, offlineDatabase) + private val facade = DiscussionTopicHeaderFacade(discussionTopicHeaderDao, discussionParticipantDao, discussionTopicPermissionDao, remoteFileDao, localFileDao, discussionTopicRemoteFileDao, offlineDatabase) @Before fun setup() { @@ -75,7 +86,7 @@ class DiscussionTopicHeaderFacadeTest { fun `Calling insertDiscussion should insert discussion topic header and related entities`() = runTest { val discussionParticipant = DiscussionParticipant(id = 1L) val discussionTopicPermission = DiscussionTopicPermission() - val discussionTopicHeader = DiscussionTopicHeader(author = discussionParticipant, permissions = discussionTopicPermission) + val discussionTopicHeader = DiscussionTopicHeader(author = discussionParticipant, permissions = discussionTopicPermission, attachments = mutableListOf(RemoteFile(1L))) coEvery { discussionParticipantDao.insert(any()) } returns 1L coEvery { discussionTopicHeaderDao.insert(any()) } returns 1L @@ -87,14 +98,16 @@ class DiscussionTopicHeaderFacadeTest { coVerify { discussionTopicHeaderDao.insert(DiscussionTopicHeaderEntity(discussionTopicHeader, 1L, null)) } coVerify { discussionTopicPermissionDao.insert(DiscussionTopicPermissionEntity(discussionTopicPermission.copy(), 1L)) } coVerify { discussionTopicHeaderDao.update(DiscussionTopicHeaderEntity(discussionTopicHeader.copy(id = 1), 1L, 1L)) } + coVerify { remoteFileDao.insertAll(listOf(RemoteFileEntity(discussionTopicHeader.attachments[0]))) } + coVerify { discussionTopicRemoteFileDao.insertAll(listOf(DiscussionTopicRemoteFileEntity(1, 1)))} } @Test fun `insertDiscussions should insert discussion topic headers and related entities`() = runTest { val discussionParticipant = DiscussionParticipant(id = 1L) val discussionParticipant2 = DiscussionParticipant(id = 2L) - val discussionTopicHeader = DiscussionTopicHeader(author = discussionParticipant) - val discussionTopicHeader2 = DiscussionTopicHeader(author = discussionParticipant2) + val discussionTopicHeader = DiscussionTopicHeader(id = 1, author = discussionParticipant, attachments = mutableListOf(RemoteFile(1L))) + val discussionTopicHeader2 = DiscussionTopicHeader(id = 2, author = discussionParticipant2, attachments = mutableListOf(RemoteFile(2L))) facade.insertDiscussions(listOf(discussionTopicHeader, discussionTopicHeader2), 1, false) @@ -114,6 +127,23 @@ class DiscussionTopicHeaderFacadeTest { ) ) } + + coVerify { + remoteFileDao.insertAll( + listOf( + RemoteFileEntity(discussionTopicHeader.attachments[0]), + RemoteFileEntity(discussionTopicHeader2.attachments[0]) + ) + ) + } + coVerify { + discussionTopicRemoteFileDao.insertAll( + listOf( + DiscussionTopicRemoteFileEntity(1, 1), + DiscussionTopicRemoteFileEntity(2, 2) + ) + ) + } } @Test @@ -121,11 +151,14 @@ class DiscussionTopicHeaderFacadeTest { val discussionTopicHeaderId = 1L val discussionParticipant = DiscussionParticipant(id = 1L, displayName = "displayName") val discussionPermission = DiscussionTopicPermission() - val discussionTopicHeader = DiscussionTopicHeader(id = discussionTopicHeaderId, author = discussionParticipant, permissions = discussionPermission, title = "Title") + val discussionTopicHeader = DiscussionTopicHeader(id = discussionTopicHeaderId, author = discussionParticipant, permissions = discussionPermission, title = "Title", attachments = mutableListOf(RemoteFile(1L, url = "path"))) coEvery { discussionParticipantDao.findById(any()) } returns DiscussionParticipantEntity(discussionParticipant) coEvery { discussionTopicHeaderDao.findById(any()) } returns DiscussionTopicHeaderEntity(discussionTopicHeader, 1) coEvery { discussionTopicPermissionDao.findByDiscussionTopicHeaderId(any()) } returns DiscussionTopicPermissionEntity(discussionPermission, discussionTopicHeaderId) + coEvery { remoteFileDao.findById(any()) } returns RemoteFileEntity(discussionTopicHeader.attachments[0]) + coEvery { discussionTopicRemoteFileDao.findByDiscussionId(any()) } returns listOf(DiscussionTopicRemoteFileEntity(discussionTopicHeaderId, 1)) + coEvery { localFileDao.findById(any()) } returns LocalFileEntity(id = 1, path = "path", courseId = 1L, createdDate = Date()) val result = facade.getDiscussionTopicHeaderById(discussionTopicHeaderId)!! From f844bd4f203a767fb98a3ffca6fb2d2afb21995c Mon Sep 17 00:00:00 2001 From: inst-danger Date: Thu, 25 Apr 2024 11:01:06 +0200 Subject: [PATCH 21/23] Update translations (#2424) --- apps/flutter_parent/lib/l10n/res/intl_ja.arb | 2 +- .../src/main/res/values-ar/strings.xml | 3 +- .../res/values-b+da+DK+instk12/strings.xml | 3 +- .../res/values-b+en+AU+unimelb/strings.xml | 3 +- .../res/values-b+en+GB+instukhe/strings.xml | 3 +- .../res/values-b+nb+NO+instk12/strings.xml | 4 +- .../res/values-b+sv+SE+instk12/strings.xml | 3 +- .../src/main/res/values-b+zh+HK/strings.xml | 3 +- .../src/main/res/values-b+zh+Hans/strings.xml | 3 +- .../src/main/res/values-b+zh+Hant/strings.xml | 3 +- .../src/main/res/values-ca/strings.xml | 4 +- .../src/main/res/values-cy/strings.xml | 3 +- .../src/main/res/values-da/strings.xml | 3 +- .../src/main/res/values-de/strings.xml | 3 +- .../src/main/res/values-en-rAU/strings.xml | 3 +- .../src/main/res/values-en-rCA/strings.xml | 3 +- .../src/main/res/values-en-rCY/strings.xml | 3 +- .../src/main/res/values-en-rGB/strings.xml | 3 +- .../src/main/res/values-es-rES/strings.xml | 6 +- .../src/main/res/values-es/strings.xml | 3 +- .../src/main/res/values-fi/strings.xml | 3 +- .../src/main/res/values-fr-rCA/strings.xml | 3 +- .../src/main/res/values-fr/strings.xml | 3 +- .../src/main/res/values-ht/strings.xml | 3 +- .../src/main/res/values-id/strings.xml | 1 + .../src/main/res/values-is/strings.xml | 3 +- .../src/main/res/values-it/strings.xml | 3 +- .../src/main/res/values-ja/strings.xml | 7 +- .../src/main/res/values-mi/strings.xml | 3 +- .../src/main/res/values-ms/strings.xml | 2 +- .../src/main/res/values-nb/strings.xml | 4 +- .../src/main/res/values-nl/strings.xml | 3 +- .../src/main/res/values-pl/strings.xml | 3 +- .../src/main/res/values-pt-rBR/strings.xml | 3 +- .../src/main/res/values-pt-rPT/strings.xml | 3 +- .../src/main/res/values-ru/strings.xml | 3 +- .../src/main/res/values-sl/strings.xml | 3 +- .../src/main/res/values-sv/strings.xml | 3 +- .../src/main/res/values-th/strings.xml | 3 +- .../src/main/res/values-vi/strings.xml | 6 +- .../src/main/res/values-zh/strings.xml | 3 +- .../lib/l10n/res/intl_ja.arb | 2 +- .../src/main/res/values-ar/strings.xml | 76 +++++++++++++++++++ .../res/values-b+da+DK+instk12/strings.xml | 72 ++++++++++++++++++ .../res/values-b+en+AU+unimelb/strings.xml | 72 ++++++++++++++++++ .../res/values-b+en+GB+instukhe/strings.xml | 72 ++++++++++++++++++ .../res/values-b+nb+NO+instk12/strings.xml | 72 ++++++++++++++++++ .../res/values-b+sv+SE+instk12/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-b+zh+HK/strings.xml | 71 +++++++++++++++++ .../src/main/res/values-b+zh+Hans/strings.xml | 71 +++++++++++++++++ .../src/main/res/values-b+zh+Hant/strings.xml | 71 +++++++++++++++++ .../src/main/res/values-ca/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-cy/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-da/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-de/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-en-rAU/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-en-rCY/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-en-rGB/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-es-rES/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-es/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-fi/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-fr-rCA/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-fr/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-ht/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-id/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-is/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-it/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-ja/strings.xml | 75 +++++++++++++++++- .../src/main/res/values-mi/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-ms/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-nb/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-nl/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-pl/strings.xml | 74 ++++++++++++++++++ .../src/main/res/values-pt-rBR/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-pt-rPT/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-ru/strings.xml | 74 ++++++++++++++++++ .../src/main/res/values-sl/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-sv/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-th/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-vi/strings.xml | 72 ++++++++++++++++++ .../src/main/res/values-zh/strings.xml | 71 +++++++++++++++++ 81 files changed, 2859 insertions(+), 90 deletions(-) diff --git a/apps/flutter_parent/lib/l10n/res/intl_ja.arb b/apps/flutter_parent/lib/l10n/res/intl_ja.arb index 4a6e294d49..cf1ca754a3 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ja.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ja.arb @@ -171,7 +171,7 @@ "placeholders_order": [], "placeholders": {} }, - "pointsPossible": "{points}の可能なポイント", + "pointsPossible": "配点 {points}", "@pointsPossible": { "description": "Screen reader label used for the points possible for an assignment, quiz, etc.", "type": "text", diff --git a/apps/teacher/src/main/res/values-ar/strings.xml b/apps/teacher/src/main/res/values-ar/strings.xml index b8db1987d2..aba9693fe1 100644 --- a/apps/teacher/src/main/res/values-ar/strings.xml +++ b/apps/teacher/src/main/res/values-ar/strings.xml @@ -763,7 +763,6 @@ تغيير المستخدم عام إرسال تعليقات - التقييم على App Store سياسة الخصوصية اتفاقية ترخيص المستخدم شروط الاستخدام @@ -928,7 +927,6 @@ -%s نقطة -%s نقاط - قائمة مهام %d غير مقروءة إعدادات ملف التعريف تنزيل الملف… تم تنزيل الملف بنجاح. @@ -953,4 +951,5 @@ لا يوجد تقدير إعفاء تقدير مبالغ فيه بواسطة %s + لا يمكن إلغاء نشر %s في حالة وجود عمليات إرسال لطالب diff --git a/apps/teacher/src/main/res/values-b+da+DK+instk12/strings.xml b/apps/teacher/src/main/res/values-b+da+DK+instk12/strings.xml index abd699dd1f..c8bd24c125 100644 --- a/apps/teacher/src/main/res/values-b+da+DK+instk12/strings.xml +++ b/apps/teacher/src/main/res/values-b+da+DK+instk12/strings.xml @@ -717,7 +717,6 @@ Skift bruger Generelt Send feedback - Vurder i App Store Datapolitik EULA Betingelser for brug @@ -874,7 +873,6 @@ -%s point -%s point - Opgaveliste %d ulæst Profilindstillinger Downloader fil… Fil blev downloaded! @@ -899,4 +897,5 @@ Ingen vurdering Undskyld Givet for høj vurdering af %s + Kan ikke fjerne offentliggørelse af %s, hvis der er afleveringer fra elever diff --git a/apps/teacher/src/main/res/values-b+en+AU+unimelb/strings.xml b/apps/teacher/src/main/res/values-b+en+AU+unimelb/strings.xml index e7180fe4be..04f123136e 100644 --- a/apps/teacher/src/main/res/values-b+en+AU+unimelb/strings.xml +++ b/apps/teacher/src/main/res/values-b+en+AU+unimelb/strings.xml @@ -715,7 +715,6 @@ Change User General Send Feedback - Rate on the App Store Privacy Policy EULA Terms of Use @@ -872,7 +871,6 @@ -%s point -%s points - To do %d unread Profile Settings Downloading file… File downloaded successfully. @@ -897,4 +895,5 @@ No grade Excuse Overgraded by %s + Cannot unpublish %s if there are student submissions diff --git a/apps/teacher/src/main/res/values-b+en+GB+instukhe/strings.xml b/apps/teacher/src/main/res/values-b+en+GB+instukhe/strings.xml index 3d43fe160d..3de4b5e46a 100644 --- a/apps/teacher/src/main/res/values-b+en+GB+instukhe/strings.xml +++ b/apps/teacher/src/main/res/values-b+en+GB+instukhe/strings.xml @@ -715,7 +715,6 @@ Change user General Send feedback - Rate on the App Store Privacy Policy EULA Terms of Use @@ -872,7 +871,6 @@ -%s point -%s points - To do %d unread Profile settings Downloading file… File downloaded successfully. @@ -897,4 +895,5 @@ No grade Excuse Over graded by %s + Cannot unpublish %s if there are student submissions diff --git a/apps/teacher/src/main/res/values-b+nb+NO+instk12/strings.xml b/apps/teacher/src/main/res/values-b+nb+NO+instk12/strings.xml index 0eba6238dc..b7605c5556 100644 --- a/apps/teacher/src/main/res/values-b+nb+NO+instk12/strings.xml +++ b/apps/teacher/src/main/res/values-b+nb+NO+instk12/strings.xml @@ -313,7 +313,6 @@ Oppgaveliste Fagoversikt - Flere forfallsdatoer Alle @@ -718,7 +717,6 @@ Endre bruker Generelt Send tilbakemelding - Vurder på App Store Retningslinjer for personvern EULA brukervilkår @@ -875,7 +873,6 @@ -%s poeng -%s poeng - Gjøremål %d ulest Profilinnstillinger Laster ned fil… Filnedlasting utført. @@ -901,4 +898,5 @@ Unnskyldning Overgradert av %s + Kan ikke avpublisere %s hvis det er elev innleveringer diff --git a/apps/teacher/src/main/res/values-b+sv+SE+instk12/strings.xml b/apps/teacher/src/main/res/values-b+sv+SE+instk12/strings.xml index eb7a5025ae..a7a2c3a26e 100644 --- a/apps/teacher/src/main/res/values-b+sv+SE+instk12/strings.xml +++ b/apps/teacher/src/main/res/values-b+sv+SE+instk12/strings.xml @@ -717,7 +717,6 @@ Ändra användare Allmänt Skicka feedback - Bedöm på App Store Integritetspolicy EULA Användningsvillkor @@ -874,7 +873,6 @@ -%s poäng -%s poäng - Att göra %d olästa Profilinställningar Hämtar fil… Filen hämtades. @@ -899,4 +897,5 @@ Ingen bedömning Ursäkta Överbedömd med %s + Det går inte att avpublicera %s om elevinlämningar finns diff --git a/apps/teacher/src/main/res/values-b+zh+HK/strings.xml b/apps/teacher/src/main/res/values-b+zh+HK/strings.xml index a00c82be81..8da217713f 100644 --- a/apps/teacher/src/main/res/values-b+zh+HK/strings.xml +++ b/apps/teacher/src/main/res/values-b+zh+HK/strings.xml @@ -703,7 +703,6 @@ 變更使用者 一般 發送回饋 - 前往 App Store 評分 《隱私政策》 EULA 《使用條款》 @@ -858,7 +857,6 @@ -%s 分 - 待辦 %d 未讀 個人檔案設定 下載檔案… 檔案已成功下載。 @@ -883,4 +881,5 @@ 無評分 豁免 已由%s重寫評分 + 如果有學生提交,則無法取消發佈 %s diff --git a/apps/teacher/src/main/res/values-b+zh+Hans/strings.xml b/apps/teacher/src/main/res/values-b+zh+Hans/strings.xml index 1e1ac223c3..f0b0323f9f 100644 --- a/apps/teacher/src/main/res/values-b+zh+Hans/strings.xml +++ b/apps/teacher/src/main/res/values-b+zh+Hans/strings.xml @@ -703,7 +703,6 @@ 更改用户 一般 发送反馈 - 给应用商店评分 隐私政策 最终用户许可协议 使用条款 @@ -858,7 +857,6 @@ -%s 分 - 待办%d未读 个人资料设置 正在下载文件… 文件下载成功 @@ -883,4 +881,5 @@ 无评分 原因 %s 评分过高 + 如果有学生提交文件,将无法取消发布%s diff --git a/apps/teacher/src/main/res/values-b+zh+Hant/strings.xml b/apps/teacher/src/main/res/values-b+zh+Hant/strings.xml index a00c82be81..8da217713f 100644 --- a/apps/teacher/src/main/res/values-b+zh+Hant/strings.xml +++ b/apps/teacher/src/main/res/values-b+zh+Hant/strings.xml @@ -703,7 +703,6 @@ 變更使用者 一般 發送回饋 - 前往 App Store 評分 《隱私政策》 EULA 《使用條款》 @@ -858,7 +857,6 @@ -%s 分 - 待辦 %d 未讀 個人檔案設定 下載檔案… 檔案已成功下載。 @@ -883,4 +881,5 @@ 無評分 豁免 已由%s重寫評分 + 如果有學生提交,則無法取消發佈 %s diff --git a/apps/teacher/src/main/res/values-ca/strings.xml b/apps/teacher/src/main/res/values-ca/strings.xml index c73fe877fa..95d8a1e62e 100644 --- a/apps/teacher/src/main/res/values-ca/strings.xml +++ b/apps/teacher/src/main/res/values-ca/strings.xml @@ -313,7 +313,6 @@ Llista d\'activitats Temari - Dates de lliurament múltiples Tothom @@ -718,7 +717,6 @@ Canvia l\'usuari General Envia els comentaris - Puntuació a App Store Política de privacitat EULA Condicions d\'ús @@ -875,7 +873,6 @@ -%s punt -%s punts - La tasca pendent %d no s\'ha llegit Configuració del perfil S\'està baixant el fitxer… S\'ha baixat correctament el fitxer. @@ -901,4 +898,5 @@ Excusa Sobrequalificat per %s + No es pot anul·lar la publicació de %s si hi ha entregues d\'estudiants diff --git a/apps/teacher/src/main/res/values-cy/strings.xml b/apps/teacher/src/main/res/values-cy/strings.xml index a7f86304c6..677cf77b34 100644 --- a/apps/teacher/src/main/res/values-cy/strings.xml +++ b/apps/teacher/src/main/res/values-cy/strings.xml @@ -715,7 +715,6 @@ Newid Defnyddiwr Cyffredinol Anfon Adborth - Sgorio’r App Store Polisi Preifatrwydd EULA Telerau Defnyddio @@ -872,7 +871,6 @@ %s pwynt %s pwynt - I\'w gwneud %d heb eu darllen Gosodiadau Proffil Llwytho ffeil i lawr… Wedi llwyddo i lwytho ffeil i lawr. @@ -897,4 +895,5 @@ Dim gradd Esgusodi Wedi’i or-raddio gan %s + Does dim modd datgyhoeddi %s os oes gwaith wedi’i gyflwyno gan fyfyrwyr diff --git a/apps/teacher/src/main/res/values-da/strings.xml b/apps/teacher/src/main/res/values-da/strings.xml index 8045a7237c..12a59d8b40 100644 --- a/apps/teacher/src/main/res/values-da/strings.xml +++ b/apps/teacher/src/main/res/values-da/strings.xml @@ -715,7 +715,6 @@ Skift bruger Generelt Send feedback - Vurder i App Store Datapolitik EULA Betingelser for brug @@ -872,7 +871,6 @@ -%s point -%s point - Opgaveliste %d ulæst Profilindstillinger Downloader fil… Fil blev downloaded! @@ -897,4 +895,5 @@ Ingen karakter Undskyld Givet for høj karakter af %s + Kan ikke fjerne offentliggørelse af %s, hvis der er afleveringer fra studerende diff --git a/apps/teacher/src/main/res/values-de/strings.xml b/apps/teacher/src/main/res/values-de/strings.xml index c32d43e9da..6433a86838 100644 --- a/apps/teacher/src/main/res/values-de/strings.xml +++ b/apps/teacher/src/main/res/values-de/strings.xml @@ -715,7 +715,6 @@ Benutzer*in wechseln Allgemein Feedback senden - Im App Store bewerten Datenschutzrichtlinien EULA Nutzungsbedingungen @@ -872,7 +871,6 @@ %s Punkt %s Punkte - %d Ungelesene erledigen Profileinstellungen Datei herunterladen… Datei erfolgreich heruntergeladen. @@ -897,4 +895,5 @@ Unbenotet Entschuldigund Überbenotet von %s + %s kann nicht unveröffentlicht werden, wenn Abgaben von Studierenden vorhanden sind diff --git a/apps/teacher/src/main/res/values-en-rAU/strings.xml b/apps/teacher/src/main/res/values-en-rAU/strings.xml index f76891b261..06b8105d60 100644 --- a/apps/teacher/src/main/res/values-en-rAU/strings.xml +++ b/apps/teacher/src/main/res/values-en-rAU/strings.xml @@ -715,7 +715,6 @@ Change User General Send Feedback - Rate on the App Store Privacy Policy EULA Terms of Use @@ -872,7 +871,6 @@ -%s point -%s points - To do %d unread Profile Settings Downloading file… File downloaded successfully. @@ -897,4 +895,5 @@ No mark Excuse Overmarked by %s + Cannot unpublish %s if there are student submissions diff --git a/apps/teacher/src/main/res/values-en-rCA/strings.xml b/apps/teacher/src/main/res/values-en-rCA/strings.xml index e663c539c4..fbc2ea08f7 100644 --- a/apps/teacher/src/main/res/values-en-rCA/strings.xml +++ b/apps/teacher/src/main/res/values-en-rCA/strings.xml @@ -718,7 +718,6 @@ Change User General Send Feedback - Rate on the App Store Privacy Policy EULA Terms of Use @@ -875,7 +874,6 @@ -%s point -%s points - To do %d unread Profile Settings Downloading file… File downloaded successfully. @@ -900,5 +898,6 @@ No grade Excuse Overgraded by %s + Cannot unpublish %s if there are student submissions diff --git a/apps/teacher/src/main/res/values-en-rCY/strings.xml b/apps/teacher/src/main/res/values-en-rCY/strings.xml index 3d43fe160d..3de4b5e46a 100644 --- a/apps/teacher/src/main/res/values-en-rCY/strings.xml +++ b/apps/teacher/src/main/res/values-en-rCY/strings.xml @@ -715,7 +715,6 @@ Change user General Send feedback - Rate on the App Store Privacy Policy EULA Terms of Use @@ -872,7 +871,6 @@ -%s point -%s points - To do %d unread Profile settings Downloading file… File downloaded successfully. @@ -897,4 +895,5 @@ No grade Excuse Over graded by %s + Cannot unpublish %s if there are student submissions diff --git a/apps/teacher/src/main/res/values-en-rGB/strings.xml b/apps/teacher/src/main/res/values-en-rGB/strings.xml index edcfd0f24c..11e86aaa5c 100644 --- a/apps/teacher/src/main/res/values-en-rGB/strings.xml +++ b/apps/teacher/src/main/res/values-en-rGB/strings.xml @@ -715,7 +715,6 @@ Change user General Send feedback - Rate on the App Store Privacy Policy EULA Terms of Use @@ -872,7 +871,6 @@ -%s point -%s points - To do %d unread Profile settings Downloading file… File downloaded successfully. @@ -897,4 +895,5 @@ No grade Excuse Over graded by %s + Cannot unpublish %s if there are student submissions diff --git a/apps/teacher/src/main/res/values-es-rES/strings.xml b/apps/teacher/src/main/res/values-es-rES/strings.xml index 50bfff9dbc..6c3403cd11 100644 --- a/apps/teacher/src/main/res/values-es-rES/strings.xml +++ b/apps/teacher/src/main/res/values-es-rES/strings.xml @@ -165,7 +165,7 @@ No hay mensajes No hay mensajes destacados - Toca \\"+\\" para crear una nueva conversación. + Toca \"+\" para crear una nueva conversación. Destaca mensajes tocando la estrella en el mensaje. Respuesta @@ -313,7 +313,6 @@ Lista de actividades Programa de la asignatura - Varias fechas de entrega Todos @@ -718,7 +717,6 @@ Cambiar usuario General Enviar comentarios - Califica en la App Store Política de privacidad EULA Términos de uso @@ -875,7 +873,6 @@ -%s punto -%s puntos - %d Tarea pendiente sin leer Configuración del perfil Descargando el archivo… Se ha descargado el archivo. @@ -901,4 +898,5 @@ Justificación Sobreevaluado por %s + No se puede cancelar la publicación de %s si hay entregas de estudiantes diff --git a/apps/teacher/src/main/res/values-es/strings.xml b/apps/teacher/src/main/res/values-es/strings.xml index 4c2f9a3fc3..79e3aad87c 100644 --- a/apps/teacher/src/main/res/values-es/strings.xml +++ b/apps/teacher/src/main/res/values-es/strings.xml @@ -716,7 +716,6 @@ Cambiar usuario General Enviar los comentarios - Califique en App Store Política de privacidad EULA Términos de uso @@ -873,7 +872,6 @@ -%s punto -%s puntos - Por hacer %d sin leer Configuración del perfil Descargando el archivo… Archivo descargado exitosamente. @@ -898,4 +896,5 @@ Sin calificación Justificación Sobrecalificado por %s + No se puede cancelar la publicación %s si hay entregas de los estudiantes diff --git a/apps/teacher/src/main/res/values-fi/strings.xml b/apps/teacher/src/main/res/values-fi/strings.xml index 496f5a72c2..e6852587ce 100644 --- a/apps/teacher/src/main/res/values-fi/strings.xml +++ b/apps/teacher/src/main/res/values-fi/strings.xml @@ -715,7 +715,6 @@ Change User (Vaihda käyttäjää) Yleinen Lähetä palautetta - Arvostele App Storessa Tietosuojakäytäntö EULA Käyttöehdot @@ -872,7 +871,6 @@ -%s piste -%s pistettä - Tehtävä %d lukematon Profiiliasetukset Tiedostoa ladataan… Tiedoston lataus onnistui. @@ -897,4 +895,5 @@ Ei arvosanaa Vapauta tehtävästä Yliarvosteltu, arvostelija %s + Julkaisua %s ei voida peruuttaa, jos opiskelijoiden tehtäväpalautuksia on olemassa diff --git a/apps/teacher/src/main/res/values-fr-rCA/strings.xml b/apps/teacher/src/main/res/values-fr-rCA/strings.xml index 8e4a5feca0..28b3d09aec 100644 --- a/apps/teacher/src/main/res/values-fr-rCA/strings.xml +++ b/apps/teacher/src/main/res/values-fr-rCA/strings.xml @@ -715,7 +715,6 @@ Changer d\'utilisateur Général Envoyer un avis - Noter sur l\'App Store Politique de confidentialité CLUF Conditions d\'utilisation @@ -872,7 +871,6 @@ -%s point -%s points - À faire %d non lue Paramètres de profil Téléchargement du fichier … Fichiers téléchargé avec succès. @@ -897,4 +895,5 @@ Aucune note Exempté Surclassement par %s + Impossible d’annuler la publication %s s’il y a des envois d’étudiants diff --git a/apps/teacher/src/main/res/values-fr/strings.xml b/apps/teacher/src/main/res/values-fr/strings.xml index 488d38ab0c..c2959160c5 100644 --- a/apps/teacher/src/main/res/values-fr/strings.xml +++ b/apps/teacher/src/main/res/values-fr/strings.xml @@ -715,7 +715,6 @@ Changer d\'utilisateur Général Envoyer un avis - Évaluez sur l’App Store Politique de confidentialité CGU Conditions d\'utilisation @@ -872,7 +871,6 @@ -%s point -%s points - À faire %d non lu Paramètres de profil Téléchargement du fichier… Fichier téléchargé avec succès. @@ -897,4 +895,5 @@ Aucune note Dispense Surnoté par %s + Impossible de publier %s s\'il existe des soumissions effectuées par des élèves. diff --git a/apps/teacher/src/main/res/values-ht/strings.xml b/apps/teacher/src/main/res/values-ht/strings.xml index e0a6854a12..f3c0ba5e73 100644 --- a/apps/teacher/src/main/res/values-ht/strings.xml +++ b/apps/teacher/src/main/res/values-ht/strings.xml @@ -715,7 +715,6 @@ Chanje Itilizatè Jeneral Voye Kòmantè - Evalye nan App Store Politik Konfidansyalite EULA Kondisyon Itilizasyon @@ -872,7 +871,6 @@ -%s pwen -%s pwen yo - Pou fè %d poko li Paramèt Pwofi Telechajman fichye… Fichye telechaje kòrèkteman. @@ -897,4 +895,5 @@ Okenn nòt Eskiz Evalyasyon depase pa %s + Yo paka depibliye %s si elèv yo gentan fè soumisyon diff --git a/apps/teacher/src/main/res/values-id/strings.xml b/apps/teacher/src/main/res/values-id/strings.xml index 15ca1db9c2..8c4d99dfab 100644 --- a/apps/teacher/src/main/res/values-id/strings.xml +++ b/apps/teacher/src/main/res/values-id/strings.xml @@ -898,4 +898,5 @@ Dibolehkan Nilai lebih oleh %s + Tidak dapat menghapus terbitan %s jika ada penyerahan siswa diff --git a/apps/teacher/src/main/res/values-is/strings.xml b/apps/teacher/src/main/res/values-is/strings.xml index 274a6471af..b1413ea568 100644 --- a/apps/teacher/src/main/res/values-is/strings.xml +++ b/apps/teacher/src/main/res/values-is/strings.xml @@ -716,7 +716,6 @@ Breyta notanda Almennt Senda endurgjöf - Gefa einkunn á App Store Persónuverndarstefna EULA Notandaskilmálar @@ -873,7 +872,6 @@ -%s punktur -%s punktar - Verkefnalisti %d ólesin Uppsetningarstillingar Sæki skrá… Skrá var sótt. @@ -898,4 +896,5 @@ Engin einkunn Gefa undanþágu Of há einkunn gefin um %s + Ekki hægt að fela %s ef skil nemenda eru til staðar diff --git a/apps/teacher/src/main/res/values-it/strings.xml b/apps/teacher/src/main/res/values-it/strings.xml index 95f8fdad58..e352181d0a 100644 --- a/apps/teacher/src/main/res/values-it/strings.xml +++ b/apps/teacher/src/main/res/values-it/strings.xml @@ -716,7 +716,6 @@ Cambia utente Generale Invia feedback - Valuta su App Store Informativa sulla privacy EULA Termini di utilizzo @@ -873,7 +872,6 @@ -%s punto -%s punti - Elenco attività: %d da leggere Impostazioni profilo Download del file… Download del file completato. @@ -898,4 +896,5 @@ Nessun voto Giustifica Assegnato voto più alto da %s + Impossibile annullare la pubblicazione di %s se ci sono consegne degli studenti diff --git a/apps/teacher/src/main/res/values-ja/strings.xml b/apps/teacher/src/main/res/values-ja/strings.xml index 65996d9873..e15a2f8b82 100644 --- a/apps/teacher/src/main/res/values-ja/strings.xml +++ b/apps/teacher/src/main/res/values-ja/strings.xml @@ -173,7 +173,7 @@ アーカイブ 削除 メッセージ - アーカイブ解凍 + アーカイブの解除 メッセージオプション 未読としてマークする このメッセージのコピーを本当に削除しますか?この操作は元に戻すことはできません。 @@ -556,7 +556,7 @@ 保留中のレビュー 課題グループ: - 回答のシャッフル: + 解答をシャッフルする: 複数の試行: 制限時間: 保存するスコア: @@ -703,7 +703,6 @@ ユーザーを変更する 一般 フィードバックを送る - App Storeでレート プライバシーポリシー EULA 利用規約 @@ -858,7 +857,6 @@ -%s 点 - 未読の「するべきこと」%d プロファイル設定 ファイルをダウンロード中… ファイルのダウンロードに成功しました。 @@ -883,4 +881,5 @@ 評定なし 免除 採点が %s 過剰 + 受講生の提出がある場合は%sの公開を取り消すことはできません diff --git a/apps/teacher/src/main/res/values-mi/strings.xml b/apps/teacher/src/main/res/values-mi/strings.xml index 0891fbac4d..376f2bee5f 100644 --- a/apps/teacher/src/main/res/values-mi/strings.xml +++ b/apps/teacher/src/main/res/values-mi/strings.xml @@ -715,7 +715,6 @@ Huri Kaiwhakamahi Whānui Tuku Urupare - Auau i runga i te Taupānga Toa Kaupapahere Tūmataiti EULA Ngā ritenga whakamahi @@ -872,7 +871,6 @@ -%s koinga -%s ngā koinga - Hei mahi %d kaore i pānuitia Ngā Tautuhinga Kōtaha E tikiake ake ana kōnae… I pai te tikiake i te kōnae. @@ -897,4 +895,5 @@ Kaore he kōeke Whakawātea Kōeketia nui ma te %s + Kaore e taea te whakaputa %s mehemea he tukunga a nga tauira diff --git a/apps/teacher/src/main/res/values-ms/strings.xml b/apps/teacher/src/main/res/values-ms/strings.xml index fcec3a49c9..140e5026e7 100644 --- a/apps/teacher/src/main/res/values-ms/strings.xml +++ b/apps/teacher/src/main/res/values-ms/strings.xml @@ -717,7 +717,6 @@ Tukar Pengguna Umum Hantar Maklum Balas - Nilaikan di App Store Dasar Privasi EULA Terma Penggunaan @@ -899,4 +898,5 @@ Maafkan Digredkan Terlebih oleh %s + Tidak dapat menyahterbit %s jika terdapat serahan pelajar diff --git a/apps/teacher/src/main/res/values-nb/strings.xml b/apps/teacher/src/main/res/values-nb/strings.xml index 1fa082cfe1..7da1bbf506 100644 --- a/apps/teacher/src/main/res/values-nb/strings.xml +++ b/apps/teacher/src/main/res/values-nb/strings.xml @@ -313,7 +313,6 @@ Oppgaveliste Emneoversikt - Flere forfallsdatoer Alle @@ -718,7 +717,6 @@ Endre bruker Generelt Send tilbakemelding - Vurder på App Store Retningslinjer for personvern EULA brukervilkår @@ -875,7 +873,6 @@ -%s poeng -%s poeng - Gjøremål %d ulest Profilinnstillinger Laster ned fil… Filnedlasting utført. @@ -901,4 +898,5 @@ Unnskyldning Overgradert av %s + Kan ikke avpublisere %s hvis det er innleveringer fra student diff --git a/apps/teacher/src/main/res/values-nl/strings.xml b/apps/teacher/src/main/res/values-nl/strings.xml index 0f7734ef68..2cbfe98971 100644 --- a/apps/teacher/src/main/res/values-nl/strings.xml +++ b/apps/teacher/src/main/res/values-nl/strings.xml @@ -715,7 +715,6 @@ Gebruiker wijzigen Algemeen Feedback versturen - Beoordelen in de App Store Privacy-beleid EULA Gebruiksvoorwaarden @@ -872,7 +871,6 @@ -%s punt -%s punten - Taken %d ongelezen Profielinstellingen Bestand downloaden… Bestand is gedownload. @@ -897,4 +895,5 @@ Geen cijfer Vrijstellen Te hoog beoordeeld door %s + Kan publicatie van %s niet ongedaan maken als er inleveringen van studenten zijn diff --git a/apps/teacher/src/main/res/values-pl/strings.xml b/apps/teacher/src/main/res/values-pl/strings.xml index 79921c3d6e..cd2281d04d 100644 --- a/apps/teacher/src/main/res/values-pl/strings.xml +++ b/apps/teacher/src/main/res/values-pl/strings.xml @@ -739,7 +739,6 @@ Zmień użytkownika Ogólne Wyślij informację zwrotną - Pobierz w sklepie App Store Polityka prywatności EULA Warunki korzystania @@ -900,7 +899,6 @@ -%s pkt -%s pkt - Lista zadań – nieprzeczytane %d Ustawienia profilu Pobieranie pliku… Pobrano plik. @@ -925,4 +923,5 @@ Brak oceny Usprawiedliwienie Zawyżona ocena od %s + Nie można cofnąć publikowania %s, jeśli występują przesłane materiały uczestnika diff --git a/apps/teacher/src/main/res/values-pt-rBR/strings.xml b/apps/teacher/src/main/res/values-pt-rBR/strings.xml index 49bd9f5111..7e43cf492b 100644 --- a/apps/teacher/src/main/res/values-pt-rBR/strings.xml +++ b/apps/teacher/src/main/res/values-pt-rBR/strings.xml @@ -716,7 +716,6 @@ Trocar Usuário Geral Enviar Comentários - Avaliar na App Store Política de privacidade EULA Termos de Uso @@ -873,7 +872,6 @@ -%s ponto -%s pontos - %d da lista de tarefas não lido(s) Configurações do perfil Baixando arquivo… Arquivo baixado com sucesso. @@ -898,4 +896,5 @@ Sem nota Com licença Superado por %s + Não é possível remover a publicação de %s se houver trabalhos submetidos pelos alunos diff --git a/apps/teacher/src/main/res/values-pt-rPT/strings.xml b/apps/teacher/src/main/res/values-pt-rPT/strings.xml index e8255c1bf1..105d004522 100644 --- a/apps/teacher/src/main/res/values-pt-rPT/strings.xml +++ b/apps/teacher/src/main/res/values-pt-rPT/strings.xml @@ -715,7 +715,6 @@ Mudar utilizador Geral Enviar Comentários - Taxa na App Store Política de Privacidade EULA Termos de uso @@ -872,7 +871,6 @@ -%s ponto -%s pontos - Para fazer %d não ler Configurações de perfil Download do ficheiro… Ficheiro baixado com sucesso. @@ -897,4 +895,5 @@ Sem classificação Desculpe Sobreclassificado por %s + Não é possível publicar %s se existirem envios de alunos diff --git a/apps/teacher/src/main/res/values-ru/strings.xml b/apps/teacher/src/main/res/values-ru/strings.xml index 3ed833e356..f9eefc587b 100644 --- a/apps/teacher/src/main/res/values-ru/strings.xml +++ b/apps/teacher/src/main/res/values-ru/strings.xml @@ -739,7 +739,6 @@ Сменить пользователя Общее Отправить отзыв - Оценить в App Store Политика конфиденциальности Лицензионный договор Условия использования @@ -900,7 +899,6 @@ -%s балла/баллов -%s балла/баллов - Выполнить непрочитанные %d Настройки профиля Загрузка файла… Файл успешно загружен. @@ -925,4 +923,5 @@ Без оценки Освободить Переоценил %s + Невозможно отменить публикацию %s, если есть отправленные студентами задания diff --git a/apps/teacher/src/main/res/values-sl/strings.xml b/apps/teacher/src/main/res/values-sl/strings.xml index 42d3901e5c..53ebe55cfe 100644 --- a/apps/teacher/src/main/res/values-sl/strings.xml +++ b/apps/teacher/src/main/res/values-sl/strings.xml @@ -715,7 +715,6 @@ Spremeni uporabnika Splošno Pošlji povratne informacije - Oceni v trgovini App Store Pravilnik o zasebnosti Licenčni pogoji za končnega uporabnika Pogoji uporabe @@ -872,7 +871,6 @@ – %s točka – %s točk - Seznam opravil, %d neprebranih Nastavitve profila Prenos datoteke… Datoteka je uspešno prenesena. @@ -897,4 +895,5 @@ Ni ocene Opravičilo Ponovno ocenil(a) %s + Preklic objave %s ni mogoč, če obstajajo oddaje študentov diff --git a/apps/teacher/src/main/res/values-sv/strings.xml b/apps/teacher/src/main/res/values-sv/strings.xml index bd1a7dab53..dbdb7be57a 100644 --- a/apps/teacher/src/main/res/values-sv/strings.xml +++ b/apps/teacher/src/main/res/values-sv/strings.xml @@ -716,7 +716,6 @@ Ändra användare Allmänt Skicka feedback - Bedöm på App Store Integritetspolicy EULA Användningsvillkor @@ -873,7 +872,6 @@ -%s poäng -%s poäng - Att göra %d olästa Profilinställningar Hämtar fil… Filen hämtades. @@ -898,4 +896,5 @@ Inget omdöme Ursäkta Överbedömd med %s + Det går inte att avpublicera %s om studentinlämningar finns diff --git a/apps/teacher/src/main/res/values-th/strings.xml b/apps/teacher/src/main/res/values-th/strings.xml index 98a1168099..000cab0e4d 100644 --- a/apps/teacher/src/main/res/values-th/strings.xml +++ b/apps/teacher/src/main/res/values-th/strings.xml @@ -717,7 +717,6 @@ เปลี่ยนผู้ใช้ ทั่วไป ส่งความเห็น - ให้คะแนนใน App Store นโยบายความเป็นส่วนตัว EULA เงื่อนไขการใช้งาน @@ -874,7 +873,6 @@ -%s คะแนน -%s คะแนน - สิ่งที่ต้องทำ %d ที่ไม่ได้อ่าน ค่าปรับตั้งโพรไฟล์ กำลังดาวน์โหลดไฟล์… ดาวน์โหลดไฟล์เสร็จสิ้น @@ -900,4 +898,5 @@ ยกเว้น ให้เกรดเกินโดย %s + ไม่สามารถเลิกเผยแพร่ %s หากมีผลงานจัดส่งจากผู้เรียน diff --git a/apps/teacher/src/main/res/values-vi/strings.xml b/apps/teacher/src/main/res/values-vi/strings.xml index 9c37b925be..a45e8633f8 100644 --- a/apps/teacher/src/main/res/values-vi/strings.xml +++ b/apps/teacher/src/main/res/values-vi/strings.xml @@ -165,7 +165,7 @@ Không có tin nhắn Không Có Tin Nhắn Được Gắn Dấu Sao - Nhấn vào dấu \\"+\\" để tạo cuộc trò chuyện mới. + Nhấn vào dấu \"+\" để tạo cuộc trò chuyện mới. Gắn dấu sao bằng cách nhấn vào hình ngôi sao trong tin nhắn. Trả Lời @@ -313,7 +313,6 @@ Danh Sách Bài Tập Chương Trình Học - Nhiều Ngày Đến Hạn Mọi Người @@ -718,7 +717,6 @@ Thay Đổi Người Dùng Chung Gửi Ý Kiến Phản Hồi - Đánh giá trên App Store Chính Sách Quyền Riêng Tư EULA Điều Khoản Sử Dụng @@ -875,7 +873,6 @@ -%s điểm thành phần -%s điểm thành phần - Cần làm %d chưa đọc Cài Đặt Hồ Sơ Đang tải xuống tập tin… Đã tải xuống tập tin thành công. @@ -901,4 +898,5 @@ Xin Phép Được chấm điểm nhiều lần bởi %s + Không thể hủy công bố %s nếu có các bài nộp của sinh viên diff --git a/apps/teacher/src/main/res/values-zh/strings.xml b/apps/teacher/src/main/res/values-zh/strings.xml index 1e1ac223c3..f0b0323f9f 100644 --- a/apps/teacher/src/main/res/values-zh/strings.xml +++ b/apps/teacher/src/main/res/values-zh/strings.xml @@ -703,7 +703,6 @@ 更改用户 一般 发送反馈 - 给应用商店评分 隐私政策 最终用户许可协议 使用条款 @@ -858,7 +857,6 @@ -%s 分 - 待办%d未读 个人资料设置 正在下载文件… 文件下载成功 @@ -883,4 +881,5 @@ 无评分 原因 %s 评分过高 + 如果有学生提交文件,将无法取消发布%s diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb index 9a29c1dad8..00e120e7c3 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb @@ -90,7 +90,7 @@ "placeholders_order": [], "placeholders": {} }, - "pointsPossible": "{points}の可能なポイント", + "pointsPossible": "配点 {points}", "@pointsPossible": { "description": "Screen reader label used for the points possible for an assignment, quiz, etc.", "type": "text", diff --git a/libs/pandares/src/main/res/values-ar/strings.xml b/libs/pandares/src/main/res/values-ar/strings.xml index 400a75f0e8..16dcf73223 100644 --- a/libs/pandares/src/main/res/values-ar/strings.xml +++ b/libs/pandares/src/main/res/values-ar/strings.xml @@ -1614,4 +1614,80 @@ محتوى مساق إضافي فشل تحديث ترتيب بطاقات لوحة المعلومات + نشر كل الوحدات النمطية والعناصر + نشر الوحدات النمطية فقط + إلغاء نشر كل الوحدات النمطية والعناصر + خيارات الوحدة النمطية + نشر الوحدة النمطية وكل العناصر + نشر الوحدة النمطية فقط + إلغاء نشر الوحدة النمطية وكل العناصر + خيارات الوحدة النمطية لـ %s + خيارات عنصر الوحدة النمطية لـ %s + نشر عنصر الوحدة النمطية + إلغاء نشر عنصر الوحدة النمطية + نشر؟ + سيؤدي هذا إلى إظهار الوحدة النمطية فقط أمام الطلاب. + سيؤدي هذا إلى إظهار الوحدة النمطية وكل العناصر أمام الطلاب. + إلغاء النشر؟ + سيؤدي هذا إلى إخفاء الوحدة النمطية وكل العناصر أمام الطلاب. + سيؤدي هذا إلى إخفاء هذا العنصر فقط أمام الطلاب. + سيؤدي هذا إلى إخفاء هذا العنصر فقط أمام الطلاب. + سيؤدي هذا إلى إظهار كل الوحدات النمطية والعناصر أمام الطلاب. + سيؤدي هذا إلى إظهار الوحدات النمطية فقط أمام الطلاب. + سيؤدي هذا إلى إخفاء كل الوحدات النمطية والعناصر أمام الطلاب. + متاح فقط لمن لديهم رابط + مدى توافر الجدول + تم نشر العنصر + تم إلغاء نشر العنصر + تم نشر الوحدة النمطية فقط + تم نشر الوحدة النمطية وكل العناصر + تم إلغاء نشر الوحدة النمطية وكل العناصر + تم نشر الوحدات النمطية فقط + تم نشر كل الوحدات النمطية وكل العناصر + تم إلغاء نشر كل الوحدات النمطية وكل العناصر + الاستيراد من المساق + أعضاء المساق + أعضاء المؤسسة التعليمية + عام + تحرير أذونات + تحديث + الإتاحة + الرؤية + متاح من + متاح حتى + من + حتى + التاريخ + الوقت + مسح تاريخ البدء + مسح تاريخ الانتهاء + قد تستغرق هذه العملية بضع دقائق. يمكنك إغلاق مربع الحوار أو الانتقال بعيدًا عن الصفحة أثناء هذه العملية. + الملاحظة + جميع الوحدات النمطية + كل الوحدات النمطية والعناصر + الوحدات النمطية والعناصر المحددة + الوحدات النمطية المحددة + جارٍ النشر + جارٍ إلغاء النشر + لن يتم عكس الوحدات والعناصر التي تمت معالجتها بالفعل إلى حالتها السابقة عند إيقاف العملية. + نجحت العملية! + تعذر التحديث + تم إلغاء التحديث + مخفي + مجدول + منشور + غير منشور + + %.0f من النقاط + %.0f نقطة + %.0f من النقاط + %.0f من النقاط + %.0f من النقاط + %.0f من النقاط + + نشر + إلغاء النشر + نشر + إلغاء النشر + تحديث diff --git a/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml b/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml index 2c8e219f1e..59dfd6ff1c 100644 --- a/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml +++ b/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml @@ -1519,4 +1519,76 @@ Yderligere fagindhold Kunne ikke opdatere oversigtens kortordren + Offentliggør alle forløb og elementer + Offentliggør kun forløb + Fjern offentliggørelse af alle forløb og elementer + Forløb Indstillinger + Offentliggør forløb og alle elementer + Offentliggør kun forløb + Fjern offentliggørelse af forløb og alle elementer + Forløb indstillinger for %s + Forløbelementindstillinger for %s + Offentliggør forløbelement + Fjern offentliggørelse af forløbelement + Offentliggør? + Dette vil gøre kun forløbet synligt for eleverne. + Dette vil gøre forløbet og alle elementer synlige for eleverne. + Fjern offentliggørelse? + Dette vil gøre forløbet og alle elementer usynlige for eleverne. + Dette vil gøre kun dette element synligt for eleverne. + Dette vil gøre kun dette element usynligt for eleverne. + Dette vil gøre alle forløb og elementer synlige for eleverne. + Dette vil gøre kun forløb synlige for eleverne. + Dette vil gøre alle forløb og elementer usynlige for eleverne. + Kun tilgængelig med link + Planlæg tilgængelighed + Element offentliggjort + Elementet ikke offentliggjort + Kun forløb offentliggjort + Forløb og alle elementer offentliggjort + Forløb og alle elementer er ikke offentliggjort + Kun forløb offentliggjort + Alle forløb og alle elementer offentliggjort + Alle forløb og alle elementer ikke offentliggjort + Arv fra fag + Fagdeltagere + Institutionsmedlemmer + Offentligt + Rediger tilladelser + Opdatering + Tilgængelighed + Synlighed + Tilgængelig fra + Tilgængelig indtil + Fra + Indtil + Dato + Tidspunkt + Ryd fra-dato + Ryd indtil-dato + Denne process kan tage nogle minutter. Du kan lukke modalen eller navigere væk fra siden under denne proces. + Bemærk + Alle forløb + Alle forløb og elementer + Udvalgte forløb og elementer + Udvalgte forløb + Offentliggør + Fjerner offentliggørelse + Forløb og elementer, der allerede er blevet behandlet, vil ikke blive ført tilbage til deres tidligere tilstand, når processen afbrydes. + Succes! + Opdatering mislykkedes + Opdatering annulleret + Skjult + Planlagt + Offentliggjort + Ikke offentliggjort + + %.0f point + %.0f point + + Offentliggør + Annuller offentliggørelse + Offentliggør + Annuller offentliggørelse + Opdater diff --git a/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml b/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml index 2db17bcd15..8d08f7e873 100644 --- a/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml +++ b/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml @@ -1519,4 +1519,76 @@ Additional subject content Failed to update Dashboard cards order + Publish all Modules and Items + Publish Modules only + Unpublish all Modules and Items + Module Options + Publish Module and all Items + Publish Module only + Unpublish Module and all Items + Module options for %s + Module item options for %s + Publish Module Item + Unpublish Module Item + Publish? + This will make only the module visible to students. + This will make the module and all items visible to students. + Unpublish? + This will make the module and all items invisible to students. + This will make only this item visible to students. + This will make only this item invisible to students. + This will make all modules and items visible to students. + This will make only the modules visible to students. + This will make all modules and items invisible to students. + Only available with link + Schedule availability + Item published + Item unpublished + Only Module published + Module and all Items published + Module and all Items unpublished + Only Modules published + All Modules and all Items published + All Modules and all Items unpublished + Inherit from Subject + Subject Members + Institution Members + Public + Edit Permissions + Update + Availability + Visibility + Available From + Available Until + From + Until + Date + Time + Clear From Date + Clear Until Date + This process could take a few minutes. You may close the modal or navigate away from the page during this process. + Note + All Modules + All Modules and Items + Selected Modules and Items + Selected Modules + Publishing + Unpublishing + Modules and items that have already been processed will not be reverted to their previous state when the process is discontinued. + Success! + Update failed + Update cancelled + Hidden + Scheduled + Published + Unpublished + + %.0f pt + %.0f pts + + Publish + Unpublish + Publish + Unpublish + Refresh diff --git a/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml b/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml index 6eb60cea44..37e41c53e3 100644 --- a/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml +++ b/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml @@ -1519,4 +1519,76 @@ Additional module content Failed to update Dashboard cards order + Publish all Units and Items + Publish Units only + Unpublish all Units and Items + Unit Options + Publish Unit and all Items + Publish Unit only + Unpublish Unit and all Items + Unit options for %s + Unit item options for %s + Publish Unit Item + Unpublish Unit Item + Publish? + This will make only the unit visible to students. + This will make the unit and all items visible to students. + Unpublish? + This will make the unit and all items invisible to students. + This will make only this item visible to students. + This will make only this item invisible to students. + This will make all units and items visible to students. + This will make only the units visible to students. + This will make all units and items invisible to students. + Only available with link + Schedule availability + Item published + Item unpublished + Only Unit published + Unit and all Items published + Unit and all Items unpublished + Only Units published + All Units and all Items published + All Units and all Items unpublished + Inherit from Module + Module Members + Institution Members + Public + Edit Permissions + Update + Availability + Visibility + Available from + Available until + From + Until + Date + Time + Clear From Date + Clear Until Date + This process could take a few minutes. You may close the modal or navigate away from the page during this process. + Note + All Units + All Units and Items + Selected Units and Items + Selected Units + Publishing + Unpublishing + Units and items that have already been processed will not be reverted to their previous state when the process is discontinued. + Success! + Update failed + Update cancelled + Hidden + Scheduled + Published + Unpublished + + %.0f pt + %.0f pts + + Publish + Unpublish + Publish + Unpublish + Refresh diff --git a/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml b/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml index dd33e1e55a..a4f217d03b 100644 --- a/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml +++ b/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml @@ -1520,4 +1520,76 @@ Ytterligere faginnhold Kunne ikke oppdatere rekkefølge på oversiktskort + Publiser alle moduler og elementer + Publiser kun moduler + Avpubliser alle moduler og elementer + Modulalternativer + Publiser modul og alle elementer + Publiser kun moduler + Avpubliser modul og alle elementer + Modulalternativer for %s + Modulelementalternativer for %s + Publiser modulelement + Avpubliser modulelement + Publisere? + Dette vil gjøre kun modulen synlig for elever. + Dette vil gjøre modulen og alle elementer synlige for elever. + Avpublisere? + Dette vil gjøre modulen og alle elementer usynlige for elever. + Dette vil gjøre kun dette elementet synlig for elever. + Dette vil gjøre kun dette elementet usynlig for elever. + Dette vil gjøre alle moduler og elementer synlige for elever. + Dette vil gjøre kun modulene synlig for elever. + Dette vil gjøre alle moduler og elementer usynlige for elever. + Kun tilgjengelig med lenke + Planlegg tilgjengelighet + Element publisert + Element avpublisert + Kun modul publisert + Modul og alle elementer publisert + Modul og alle elementer avpublisert + Alle moduler publisert + Alle moduler og alle elementer publisert + Alle moduler og alle elementer avpublisert + Arve fra fag + Fagmedlemmer + Institusjonsmedlemmer + Offentlig + Redigeringstillatelser + Oppdater + Tilgjengelighet + Synlighet + Tilgjengelig fra + Tilgjengelig frem til + Fra + Inntil + Dato + Tid + Slett fra dato + Slett til dato + Denne prosessen kan ta noen minutter. Du kan lukke modalen eller navigere bor fra siden under denne prosessen. + Merknad + Alle moduler + Alle moduler og elementer + Valgte moduler og elementer + Valgte moduler + Publiserer + Avpubliserer + Moduler og elementer som allerede er behandlet, vil ikke bli tilbakestilt til tidligere tilstand når prosessen avbrytes. + Vellykket! + Oppdatering mislyktes + Oppdatering avbrutt + Skjult + Planlagt + Publisert + Upublisert + + %.0f poeng + %.0f poeng + + Publiser + Avpubliser + Publiser + Avpubliser + Oppdater diff --git a/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml b/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml index ac8b92c870..65c115138e 100644 --- a/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml +++ b/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml @@ -1519,4 +1519,76 @@ Extra kursinnehåll Det gick inte att uppdatera översiktens kortordning + Publicera alla moduler och objekt + Publicera endast moduler + Avpublicera alla moduler och objekt + Modulalternativ + Publicera modulen och alla objekt + Publicera endast modulen + Avpublicera modulen och alla objekt + Modulalternativ för %s + Modulobjektalternativ för %s + Publicera modulobjekt + Avpublicera modulobjekt + Publicera? + Detta gör modulen synlig endast för elever. + Detta gör modulen och alla objekt synliga för elever. + Avpublicera? + Detta döljer modulen och alla objekt för elever. + Detta gör endast detta objekt synligt för elever. + Detta döljer endast detta objekt för elever. + Detta gör alla moduler och objekt synliga för elever. + Detta gör endast modulerna synliga för elever. + Detta döljer alla moduler och objekt för elever. + Endast tillgängligt med länk + Schematillgänglighet + Objekt publicerat + Objekt avpublicerat + Endast modulen publicerades + Modulen och alla objekten publicerades + Modulen och alla objekten avpublicerades + Endast moduler publicerades + Alla moduler och objekt publicerades + Alla moduler och objekt avpublicerades + Ärv från kurs + Kursmedlem + Institutionens medlemmar + Offentlig + Redigera behörigheter + Uppdatera + Tillgänglighet + Synlighet + Tillgänglig från + Tillgänglig till + Från + Tills + Datum + Tid + Rensa från-datum + Rensa fram till-datum + Detta kan ta några minuter. Du kan stänga modalen eller navigera bort från sidan under det här förloppet. + Anteckning + Alla moduler + Alla moduler och objekt + Valda moduler och objekt + Valda moduler + Publicerar + Avpublicerar + Moduler som redan har bearbetats återställs inte till tidigare tillstånd är processen avslutas. + Framgång! + Uppdateringen misslyckades + Uppdateringen avbruten + Dold + Schemalagd + Publicerad + Ej offentliggjord + + %.0f poäng + %.0f poäng + + Publicera + Avpublicera + Publicera + Avpublicera + Uppdatera diff --git a/libs/pandares/src/main/res/values-b+zh+HK/strings.xml b/libs/pandares/src/main/res/values-b+zh+HK/strings.xml index 23ca780ba0..1ca7e8612d 100644 --- a/libs/pandares/src/main/res/values-b+zh+HK/strings.xml +++ b/libs/pandares/src/main/res/values-b+zh+HK/strings.xml @@ -1495,4 +1495,75 @@ 額外課程內容 無法更新控制面板卡片順序 + 發佈所有單元和項目 + 僅發佈單元 + 取消發佈所有單元和項目 + 單元選項 + 發佈單元和所有項目 + 僅發佈單元 + 取消發佈單元和所有項目 + %s 的單元選項 + %s 的單元項目選項 + 發佈單元項目 + 取消發佈單元項目 + 發佈嗎? + 這將使學生僅看到單元。 + 這將使學生看到單元和所有項目。 + 取消發佈嗎? + 這將使學生看不到單元和所有項目。 + 這將使學生僅看到此項目。 + 這將使學生僅看不到此項目。 + 這將使學生看到所有單元和項目。 + 這將使學生僅看到單元。 + 這將使學生看不到所有單元和項目。 + 只可以搭配連結使用 + 排程可用性 + 已發佈項目 + 已取消發佈項目 + 僅已發佈單元 + 已發佈單元和所有項目 + 已取消發佈單元和所有項目 + 僅已發佈單元 + 已發佈所有單元和所有項目 + 已取消發佈所有單元和所有項目 + 自課程繼承 + 學生 + 機構成員 + 公開 + 編輯權限 + 更新 + 可用期 + 可見度 + 可用起始日期 + 可用截止時間 + 開始時間 + 結束 + 日期 + 時間 + 清除開始日期 + 清除截止日期 + 此程序可能需要幾分鐘的時間。您可以在此處理期間關閉模態,或導覽遠離頁面。 + 注釋 + 所有單元 + 所有單元和項目 + 選取的單元和項目 + 選取的單元 + 正在發佈 + 取消發佈 + 中止處理時,已處理的單元和項目將不會回復到之前的狀態。 + 成功! + 更新失敗 + 已取消更新 + 已隱藏 + 已排程 + 已發佈 + 已取消發佈 + + %.0f 分 + + 發佈 + 取消發佈 + 發佈 + 取消發佈 + 重新整理 diff --git a/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml b/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml index 0617c4530c..fd42ac2ee9 100644 --- a/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml +++ b/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml @@ -1495,4 +1495,75 @@ 更多课程内容 无法更新控制面板卡片顺序 + 发布所有模块和项目 + 仅发布模块 + 取消发布所有模块和项目 + 模块选项 + 发布模块和所有项目 + 仅发布模块 + 取消发布模块和所有项目 + %s 的模块选项 + %s 的模块项目选项 + 发布模块项目 + 取消发布模块项目 + 是否发布? + 这将仅使模块对学生可见。 + 这将使模块和所有项目对学生可见。 + 是否取消发布? + 这将使模块和所有项目对学生不可见。 + 这将仅使该项目对学生可见。 + 这将仅使该项目对学生不可见。 + 这将使所有模块和项目对学生可见。 + 这将仅使模块对学生可见。 + 这将使所有模块和项目对学生不可见。 + 仅提供链接 + 计划表可用性 + 已发布项目 + 已取消发布项目 + 已仅发布模块 + 已发布模块和所有项目 + 已取消发布模块和所有项目 + 已仅发布模块 + 已发布所有模块和所有项目 + 已取消发布所有模块和所有项目 + 从课程继承 + 课程成员 + 机构成员 + 公开 + 编辑权限 + 更新 + 可用性 + 可见性 + 开始日期 + 截止时间 + 开始日期 + 直至 + 日期 + 时间 + 清除开始日期 + 清除截止日期 + 该过程可能需要几分钟。在此期间,您可以关闭该模式或离开页面。 + + 所有模块 + 所有模块和项目 + 已选的模块和项目 + 已选的模块 + 正在发布 + 正在取消发布 + 停止处理后,已经处理的模块和项目不会退回到之前的状态。 + 成功! + 更新失败 + 更新已取消 + 隐藏 + 已计划 + 已发布 + 未发布 + + %.0f 分 + + 发布 + 取消发布 + 发布 + 取消发布 + 刷新 diff --git a/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml b/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml index 23ca780ba0..1ca7e8612d 100644 --- a/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml +++ b/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml @@ -1495,4 +1495,75 @@ 額外課程內容 無法更新控制面板卡片順序 + 發佈所有單元和項目 + 僅發佈單元 + 取消發佈所有單元和項目 + 單元選項 + 發佈單元和所有項目 + 僅發佈單元 + 取消發佈單元和所有項目 + %s 的單元選項 + %s 的單元項目選項 + 發佈單元項目 + 取消發佈單元項目 + 發佈嗎? + 這將使學生僅看到單元。 + 這將使學生看到單元和所有項目。 + 取消發佈嗎? + 這將使學生看不到單元和所有項目。 + 這將使學生僅看到此項目。 + 這將使學生僅看不到此項目。 + 這將使學生看到所有單元和項目。 + 這將使學生僅看到單元。 + 這將使學生看不到所有單元和項目。 + 只可以搭配連結使用 + 排程可用性 + 已發佈項目 + 已取消發佈項目 + 僅已發佈單元 + 已發佈單元和所有項目 + 已取消發佈單元和所有項目 + 僅已發佈單元 + 已發佈所有單元和所有項目 + 已取消發佈所有單元和所有項目 + 自課程繼承 + 學生 + 機構成員 + 公開 + 編輯權限 + 更新 + 可用期 + 可見度 + 可用起始日期 + 可用截止時間 + 開始時間 + 結束 + 日期 + 時間 + 清除開始日期 + 清除截止日期 + 此程序可能需要幾分鐘的時間。您可以在此處理期間關閉模態,或導覽遠離頁面。 + 注釋 + 所有單元 + 所有單元和項目 + 選取的單元和項目 + 選取的單元 + 正在發佈 + 取消發佈 + 中止處理時,已處理的單元和項目將不會回復到之前的狀態。 + 成功! + 更新失敗 + 已取消更新 + 已隱藏 + 已排程 + 已發佈 + 已取消發佈 + + %.0f 分 + + 發佈 + 取消發佈 + 發佈 + 取消發佈 + 重新整理 diff --git a/libs/pandares/src/main/res/values-ca/strings.xml b/libs/pandares/src/main/res/values-ca/strings.xml index 4281c11c5d..97887e8e33 100644 --- a/libs/pandares/src/main/res/values-ca/strings.xml +++ b/libs/pandares/src/main/res/values-ca/strings.xml @@ -1520,4 +1520,76 @@ Contingut de l’assignatura addicional No s’ha pogut actualitzar la comanda de targetes del panell de control + Publica tots els continguts i elements + Publica només els continguts + Anul·la tots els continguts i elements + Opcions de contingut + Publica el contingut i tots els elements + Publica només el contingut + Anul·la la publicació del contingut i de tots els elements + Opcions de contingut per a %s + Opcions d’elements de contingut per a %s + Publica l’element de contingut + Anul·la la publicació de l\'element de contingut + Voleu publicar-ho? + D’aquesta manera, els estudiants només podran veure el contingut. + D’aquesta manera, els estudiants podran veure el contingut i tots els elements. + Voleu anul·lar-ne la publicació? + D’aquesta manera, el contingut i tots els elements seran invisibles per als estudiants. + D’aquesta manera, els estudiants només podran veure aquest element. + D’aquesta manera, només aquest element serà invisible per als estudiants. + D’aquesta manera, els estudiants podran veure tots els continguts i elements. + D’aquesta manera, els estudiants només podran veure els continguts. + D’aquesta manera, tots els continguts i elements seran invisibles per als estudiants. + Només està disponible a través d\'un enllaç + Planifica la disponibilitat + S’ha publicat l\'element + S’ha anul·lat la publicació de l\'element + Només s’ha publicat el contingut + S’han publicat el contingut i tots els elements + S’ha anul·lat la publicació del contingut i de tots els elements + Només s’han publicat els continguts + S’han publicat tots els continguts i elements + S’ha anul·lat la publicació de tots els continguts i elements + Hereta de l\'assignatura + Membres del curs + Membres de la institució + Públic + Edita els permisos + Actualitza + Disponibilitat + Visibilitat + Disponible des del + Disponible fins al + Des del + Fins al + Data + Hora + Esborra la data Des del + Esborra la data Fins al + Pot ser que aquest procés trigui uns minuts. Podeu tancar el modal o sortir de la pàgina durant aquest procés. + Nota + Tots els continguts + Tots els continguts i elements + Continguts i elements seleccionats + Continguts seleccionats + S\'està publicant + Se n\'està anul·lant la publicació + Els continguts i els elements que ja s’hagin processat no es revertiran al seu estat anterior un cop interromput el procés. + Operació correcta! + No s’ha pogut realitzar l’actualització + S’ha cancel·lat l’actualització + Ocult + S’ha planificat + S’ha publicat + Se n’ha anul·lat la publicació + + %.0f punt + %.0f punts + + Publica-ho + Anul·la’n la publicació + Publica-ho + Anul·la’n la publicació + Actualitza-ho diff --git a/libs/pandares/src/main/res/values-cy/strings.xml b/libs/pandares/src/main/res/values-cy/strings.xml index abcde4e9b9..d4936f0543 100644 --- a/libs/pandares/src/main/res/values-cy/strings.xml +++ b/libs/pandares/src/main/res/values-cy/strings.xml @@ -1519,4 +1519,76 @@ Cynnwys cwrs ychwanegol Wedi methu diweddaru trefn cardiau’r Dangosfwrdd + Cyhoeddi Pob Modiwl ac Eitem + Cyhoeddi Modiwlau’n Unig + Datgyhoeddi Pob Modiwl ac Eitem + Opsiynau Modiwl + Cyhoeddi Modiwl a phob eitem + Cyhoeddi Modiwl yn unig + Datgyhoeddi Modiwl a phob eitem + Opsiynau modiwl ar gyfer %s + Opsiynau eitemau modiwlau ar gyfer %s + Cyhoeddi Eitem Modiwl + Datgyhoeddi Eitem Modiwl + Cyhoeddi? + Bydd hyn yn gwneud dim ond y modiwl yn weladwy i fyfyrwyr. + Bydd hyn yn gwneud y modiwl a’r holl eitemau’n weladwy i fyfyrwyr. + Datgyhoeddi? + Bydd hyn yn gwneud y modiwl a’r holl eitemau’n anweladwy i fyfyrwyr. + Bydd hyn yn gwneud dim ond yr eitem hwn yn weladwy i fyfyrwyr. + Bydd hyn yn gwneud dim ond yr eitem hwn yn anweladwy i fyfyrwyr. + Bydd hyn yn gwneud pob modiwl ac eitem yn weladwy i fyfyrwyr. + Bydd hyn yn gwneud dim ond y modiwlau’n weladwy i fyfyrwyr. + Bydd hyn yn gwneud pob modiwl ac eitem yn anweladwy i fyfyrwyr. + Dim ond ar gael gyda dolen + Trefnu argaeledd + Eitem wedi’i gyhoeddi + Eitem wedi’i ddatgyhoeddi + Dim Ond Modiwl wedi’i gyhoeddi + Modiwl a phob eitem wedi’u cyhoeddi + Modiwl a phob eitem wedi’u datgyhoeddi + Dim Ond Modiwlau wedi’u cyhoeddi + Pob Modiwl a phob eitem wedi’u cyhoeddi + Pob Modiwl a phob eitem wedi’u datgyhoeddi + Etifeddu o Gwrs + Aelodau o’r Cwrs + Aelodau Sefydliad + Cyhoeddus + Golygu Hawliau + Diweddaru + Argaeledd + Gwelededd + Ar gael o + Ar gael tan + O + Tan + Dyddiad + Amser + Clirio O Ddyddiad + Clirio Tan Ddyddiad + Gall y broses gymryd munud neu ddau. Rhaid i chi gau’r is-ffenestr neu adael y dudalen yn ystod y broses hon. + Nodyn + Pob Modiwl + Pob Modiwl ac Eitem + Modiwlau ac Eitemau wedi’u Dewis + Modiwlau wedi’u Dewis + Wrthi’n cyhoeddi + Wrthi’n datgyhoeddi + Ni fydd modiwlau ac eitemau sydd eisoes wedi’u prosesu yn dychwelyd i’w cyflwr blaenorol pan fyddwch chi’n rhoi’r gorau i’r broses. + Wedi llwyddo! + Wedi methu diweddaru + Diweddariad wedi’i ganslo + Cudd + Wedi’i Drefnu + Wedi Cyhoeddi + Wedi dad-gyhoeddi + + %.0f pwynt + %.0f pwynt + + Cyhoeddi + Dad-gyhoeddi + Cyhoeddi + Dad-gyhoeddi + Adnewyddu diff --git a/libs/pandares/src/main/res/values-da/strings.xml b/libs/pandares/src/main/res/values-da/strings.xml index 1f2c7986a5..11eea0a456 100644 --- a/libs/pandares/src/main/res/values-da/strings.xml +++ b/libs/pandares/src/main/res/values-da/strings.xml @@ -1519,4 +1519,76 @@ Yderligere fagindhold Kunne ikke opdatere oversigtens kortordren + Offentliggør alle moduler og elementer + Offentliggør kun moduler + Fjern offentliggørelse af alle moduler og elementer + Modulindstillinger + Offentliggør modul og alle elementer + Offentliggør kun modul + Fjern offentliggørelse af modul og alle elementer + Modulindstillinger for %s + Modulelementindstillinger for %s + Offentliggør modulelement + Fjern offentliggørelse af modulelement + Offentliggør? + Dette vil gøre kun modulet synligt for studerende. + Dette vil gøre modulet og alle elementer synlige for studerende. + Fjern offentliggørelse? + Dette vil gøre modulet og alle elementer usynlige for studerende. + Dette vil gøre kun dette element synligt for studerende. + Dette vil gøre kun dette element usynligt for studerende. + Dette vil gøre alle moduler og elementer synlige for studerende. + Dette vil gøre kun modulerne synlige for studerende. + Dette vil gøre alle moduler og elementer usynlige for studerende. + Kun tilgængelig med link + Planlæg tilgængelighed + Element offentliggjort + Elementet ikke offentliggjort + Kun modul offentliggjort + Modul og alle elementer offentliggjort + Modul og alle elementer er ikke offentliggjort + Kun moduler offentliggjort + Alle moduler og alle elementer offentliggjort + Alle moduler og alle elementer ikke offentliggjort + Arv fra fag + Fagdeltagere + Institutionsmedlemmer + Offentligt + Rediger tilladelser + Opdatering + Tilgængelighed + Synlighed + Tilgængelig fra + Tilgængelig indtil + Fra + Indtil + Dato + Tidspunkt + Ryd fra-dato + Ryd indtil-dato + Denne process kan tage nogle minutter. Du kan lukke modalen eller navigere væk fra siden under denne proces. + Bemærk + Alle moduler + Alle moduler og elementer + Udvalgte moduler og elementer + Udvalgte moduler + Offentliggør + Fjerner offentliggørelse + Moduler og elementer, der allerede er blevet behandlet, vil ikke blive ført tilbage til deres tidligere tilstand, når processen afbrydes. + Succes! + Opdatering mislykkedes + Opdatering annulleret + Skjult + Planlagt + Offentliggjort + Ikke offentliggjort + + %.0f point + %.0f point + + Offentliggør + Annuller offentliggørelse + Offentliggør + Annuller offentliggørelse + Opdater diff --git a/libs/pandares/src/main/res/values-de/strings.xml b/libs/pandares/src/main/res/values-de/strings.xml index a0b12c826d..ca039d8e36 100644 --- a/libs/pandares/src/main/res/values-de/strings.xml +++ b/libs/pandares/src/main/res/values-de/strings.xml @@ -1519,4 +1519,76 @@ Zusätzliche Kursinhalte Reihenfolge der Dashboard-Karten konnte nicht aktualisiert werden + Alle Module und Elemente veröffentlichen + Nur Module veröffentlichen + Alle Module und Elemente unveröffentlichen + Modul-Optionen + Modul und alle Elemente veröffentlichen + Nur Modul veröffentlichen + Modul und alle Elemente unveröffentlichen + Modul-Optionen für %s + Modulelement-Optionen für %s + Modulelement veröffentlichen + Modulelement unveröffentlichen + Veröffentlichen? + Dadurch wird nur das Modul für Studierende sichtbar. + Dadurch werden das Modul und alle Elemente für Studierende sichtbar. + Unveröffentlichen? + Dadurch werden das Modul und alle Elemente für Studierende unsichtbar. + Dadurch nur dieses Element für Studierende sichtbar. + Dadurch nur dieses Element für Studierende unsichtbar. + Dadurch werden alle Module und Elemente für Studierende sichtbar. + Dadurch werden nur die Module für Studierende sichtbar. + Dadurch werden alle Module und Elemente für Studierende unsichtbar. + Nur verfügbar mit Link + Verfügbarkeit planen + Element veröffentlicht + Element unveröffentlicht + Nur Modul veröffentlicht + Modul und alle Elemente veröffentlicht + Modul und alle Elemente unveröffentlicht + Nur Module veröffentlicht + Alle Module und alle Elemente veröffentlicht + Alle Module und alle Elemente unveröffentlicht + Von Kurs vererben + Kursteilnehmer*innen + Mitglieder der Institution + Öffentlich + Berechtigungen bearbeiten + Aktualisieren + Verfügbarkeit + Sichtbarkeit + Verfügbar von + Verfügbar bis + Von + Bis + Datum + Zeit + Von-Datum löschen + Bis-Datum löschen + Dieser Prozess kann ein paar Minuten dauern. Sie können während dieses Vorgangs das Modalfenster schließen oder von der Seite weg navigieren. + Anmerkung + Alle Module + Alle Module und Elemente + Ausgewählte Module und Elemente + Ausgewählte Module + Wird veröffentlicht + Unveröffentlicht + Module und Elemente, die bereits verarbeitet wurden, werden nicht in den vorherigen Zustand zurückversetzt, wenn der Vorgang abgebrochen wird. + Erfolg! + Aktualisierung fehlgeschlagen + Update abgebrochen + Ausgeblendet + Geplant + Veröffentlicht + Nicht veröffentlicht + + %.0f Pkt. + %.0f Pkte. + + Veröffentlichen + Veröffentlichung rückgängig machen + Veröffentlichen + Veröffentlichung rückgängig machen + Aktualisieren diff --git a/libs/pandares/src/main/res/values-en-rAU/strings.xml b/libs/pandares/src/main/res/values-en-rAU/strings.xml index 3afd34175a..c50400db4e 100644 --- a/libs/pandares/src/main/res/values-en-rAU/strings.xml +++ b/libs/pandares/src/main/res/values-en-rAU/strings.xml @@ -1519,4 +1519,76 @@ Additional course content Failed to update Dashboard cards order + Publish all Modules and Items + Publish Modules only + Unpublish all Modules and Items + Module Options + Publish Module and all Items + Publish Module only + Unpublish Module and all Items + Module options for %s + Module item options for %s + Publish Module Item + Unpublish Module Item + Publish? + This will make only the module visible to students. + This will make the module and all items visible to students. + Unpublish? + This will make the module and all items invisible to students. + This will make only this item visible to students. + This will make only this item invisible to students. + This will make all modules and items visible to students. + This will make only the modules visible to students. + This will make all modules and items invisible to students. + Only available with link + Schedule availability + Item published + Item unpublished + Only Module published + Module and all Items published + Module and all Items unpublished + Only Modules published + All Modules and all Items published + All Modules and all Items unpublished + Inherit from Course + Course Members + Institution Members + Public + Edit Permissions + Update + Availability + Visibility + Available From + Available Until + From + Until + Date + Time + Clear From Date + Clear Until Date + This process could take a few minutes. You may close the modal or navigate away from the page during this process. + Note + All Modules + All Modules and Items + Selected Modules and Items + Selected Modules + Publishing + Unpublishing + Modules and items that have already been processed will not be reverted to their previous state when the process is discontinued. + Success! + Update failed + Update cancelled + Hidden + Scheduled + Published + Unpublished + + %.0f pt + %.0f pts + + Publish + Unpublish + Publish + Unpublish + Refresh diff --git a/libs/pandares/src/main/res/values-en-rCY/strings.xml b/libs/pandares/src/main/res/values-en-rCY/strings.xml index 6eb60cea44..37e41c53e3 100644 --- a/libs/pandares/src/main/res/values-en-rCY/strings.xml +++ b/libs/pandares/src/main/res/values-en-rCY/strings.xml @@ -1519,4 +1519,76 @@ Additional module content Failed to update Dashboard cards order + Publish all Units and Items + Publish Units only + Unpublish all Units and Items + Unit Options + Publish Unit and all Items + Publish Unit only + Unpublish Unit and all Items + Unit options for %s + Unit item options for %s + Publish Unit Item + Unpublish Unit Item + Publish? + This will make only the unit visible to students. + This will make the unit and all items visible to students. + Unpublish? + This will make the unit and all items invisible to students. + This will make only this item visible to students. + This will make only this item invisible to students. + This will make all units and items visible to students. + This will make only the units visible to students. + This will make all units and items invisible to students. + Only available with link + Schedule availability + Item published + Item unpublished + Only Unit published + Unit and all Items published + Unit and all Items unpublished + Only Units published + All Units and all Items published + All Units and all Items unpublished + Inherit from Module + Module Members + Institution Members + Public + Edit Permissions + Update + Availability + Visibility + Available from + Available until + From + Until + Date + Time + Clear From Date + Clear Until Date + This process could take a few minutes. You may close the modal or navigate away from the page during this process. + Note + All Units + All Units and Items + Selected Units and Items + Selected Units + Publishing + Unpublishing + Units and items that have already been processed will not be reverted to their previous state when the process is discontinued. + Success! + Update failed + Update cancelled + Hidden + Scheduled + Published + Unpublished + + %.0f pt + %.0f pts + + Publish + Unpublish + Publish + Unpublish + Refresh diff --git a/libs/pandares/src/main/res/values-en-rGB/strings.xml b/libs/pandares/src/main/res/values-en-rGB/strings.xml index 670d7f2794..19d4d34729 100644 --- a/libs/pandares/src/main/res/values-en-rGB/strings.xml +++ b/libs/pandares/src/main/res/values-en-rGB/strings.xml @@ -1519,4 +1519,76 @@ Additional course content Failed to update Dashboard cards order + Publish all Modules and Items + Publish Modules only + Unpublish all Modules and Items + Module Options + Publish Module and all Items + Publish Module only + Unpublish Module and all Items + Module options for %s + Module item options for %s + Publish Module Item + Unpublish Module Item + Publish? + This will make only the module visible to students. + This will make the module and all items visible to students. + Unpublish? + This will make the module and all items invisible to students. + This will make only this item visible to students. + This will make only this item invisible to students. + This will make all modules and items visible to students. + This will make only the modules visible to students. + This will make all modules and items invisible to students. + Only available with link + Schedule availability + Item published + Item unpublished + Only Module published + Module and all Items published + Module and all Items unpublished + Only Modules published + All Modules and all Items published + All Modules and all Items unpublished + Inherit from Course + Course Members + Institution Members + Public + Edit Permissions + Update + Availability + Visibility + Available from + Available until + From + Until + Date + Time + Clear From Date + Clear Until Date + This process could take a few minutes. You may close the modal or navigate away from the page during this process. + Note + All Modules + All Modules and Items + Selected Modules and Items + Selected Modules + Publishing + Unpublishing + Modules and items that have already been processed will not be reverted to their previous state when the process is discontinued. + Success! + Update failed + Update cancelled + Hidden + Scheduled + Published + Unpublished + + %.0f pt + %.0f pts + + Publish + Unpublish + Publish + Unpublish + Refresh diff --git a/libs/pandares/src/main/res/values-es-rES/strings.xml b/libs/pandares/src/main/res/values-es-rES/strings.xml index f9d488801c..fa8a3fbdfe 100644 --- a/libs/pandares/src/main/res/values-es-rES/strings.xml +++ b/libs/pandares/src/main/res/values-es-rES/strings.xml @@ -1521,4 +1521,76 @@ Contenido del curso adicional Error al actualizar el orden de las tarjetas en el cuadro de mando + Publicar todos los módulos e ítems + Publicar solo módulos + No publicar ni todos los módulos ni ítems + Opciones del módulo + Publicar módulo y todos los ítems + Publicar solo el módulo + No publicar ni el módulo ni todos los ítems + Opciones del módulo para %s + Opciones de los ítems del módulo para %s + Publicar el ítem del módulo + No publicar el ítem del módulo + ¿Publicar? + Esto hará que solo el módulo sea visible para los estudiantes. + Esto hará que el módulo y todos los ítems sean visibles para los estudiantes. + ¿No publicar? + Esto hará que el módulo y todos los ítems sean invisibles para los estudiantes. + Esto hará que solo este ítem sea visible para los estudiantes. + Esto hará que solo este ítem sea invisible para los estudiantes. + Esto hará que todos los módulos e ítems sean visibles para los estudiantes. + Esto hará que solo el módulo sea invisible para los estudiantes. + Esto hará que todos los módulos e ítems sean invisibles para los estudiantes. + Disponible solo con enlace + Programar la disponibilidad + Ítem publicado + Ítem no publicado + Solo módulo publicado + Módulo y todos los ítems publicados + Módulo y todos los ítems no publicados + Solo módulos publicados + Todos los módulos y todos los ítems publicados + Todos los módulos y todos los ítems no publicados + Heredar del curso + Miembros del curso + Miembros de la institución + Público + Editar permisos + Actualizar + Disponibilidad + Visibilidad + Disponible desde + Disponible hasta + Desde + Hasta + Fecha + Hora + Borrar fecha desde + Borrar fecha hasta + Esto podría tardar unos minutos. Puede cerrar el modal o salir de la página durante este proceso. + Nota + Todos los contenidos + Todos los módulos e ítems + Módulos e ítems seleccionados + Módulos seleccionados + Publicando + Cancelando publicación + Los módulos e ítems que ya se hayan procesado no volverán a su estado anterior cuando el proceso esté interrumpido. + Hecho + No se ha podido actualizar + Actualización cancelada + Oculto + Programado + Publicado + No publicado + + %.0f punto + %.0f puntos + + Publicar + Cancelar publicación + Publicar + Cancelar publicación + Actualizar diff --git a/libs/pandares/src/main/res/values-es/strings.xml b/libs/pandares/src/main/res/values-es/strings.xml index a3d2c4eecc..a54666b3b4 100644 --- a/libs/pandares/src/main/res/values-es/strings.xml +++ b/libs/pandares/src/main/res/values-es/strings.xml @@ -1519,4 +1519,76 @@ Contenido adicional del curso No se pudo actualizar el orden de las tarjetas del tablero + Publicar todos los módulos e ítems + Publicar solo los módulos + Cancelar publicación de todos los módulos e ítems + Opciones de módulo + Publicar el módulo y todos los ítems + Publicar solo el módulo + Cancelar publicación del módulo y todos los ítems + Opciones de módulo para %s + Opciones de ítems del módulo para %s + Publicar ítem del módulo + Cancelar la publicación del ítem del módulo + ¿Quieres publicarlo? + Los estudiantes podrán ver solo el módulo. + Los estudiantes no podrán ver el módulo ni ningún ítem. + ¿Quieres cancelar la publicación? + Los estudiantes no podrán ver ningún módulo ni ítem. + Los estudiantes podrán ver solo este ítem. + Los estudiantes no podrán ver solo este ítem. + Los estudiantes podrán ver los módulos e ítems. + Los estudiantes podrán ver solo los módulos. + Los estudiantes no podrán ver ningún módulo ni ítem. + Solo disponible con enlace + Disponibilidad en el planificador + Ítem publicado + Ítem no publicado + Solo se publicó el módulo + Se publicaron todos los módulos e ítems + No se publicó el módulo ni ningún ítem + Solo se publicaron los módulos + Se publicaron todos los módulos e ítems + No se publicaron los módulos ni los ítems + Heredar del curso + Miembros del curso + Miembros de la Institución + Público + Editar permisos + Actualizar + Disponibilidad + Visibilidad + Disponible desde + Disponible hasta + Desde + Hasta + Fecha + Hora + Reestablecer fecha de inicio + Reestablecer fecha de finalización + Este proceso puede tardar unos minutos. Puede cerrar el modal o navegar fuera de la página durante el proceso. + Nota + Todos los módulos + Todos los módulos e ítems + Seleccionar módulos e ítems + Seleccionar módulos + Industria editorial + Cancelando publicación + Si se interrumpe el proceso, los módulos y los ítems que ya hayan sido procesados no volverán a su estado previo. + ¡Éxito! + Se produjo un error al actualizar + Actualización cancelada + Oculto + Programado + Publicado + No publicado + + %.0f punto + %.0f puntos + + Publicar + Cancelar publicación + Publicar + Cancelar publicación + Actualizar diff --git a/libs/pandares/src/main/res/values-fi/strings.xml b/libs/pandares/src/main/res/values-fi/strings.xml index fcaf19fae0..cf05853adb 100644 --- a/libs/pandares/src/main/res/values-fi/strings.xml +++ b/libs/pandares/src/main/res/values-fi/strings.xml @@ -1519,4 +1519,76 @@ LIsäkurssin sisältö. Hallintapaneelin koontinäytön korttien päivittäminen epäonnistui + Julkaise kaikki moduulit ja kohteet + Julkaise vain moduulit + Peruuta kaikkien moduulien ja kohteiden julkaisu + Moduulin asetukset + Julkaise moduuli ja kaikki kohteet + Julkaise vain moduuli + Peruuta kaikkien moduulien ja kohteiden julkaisu + Moduulin asetukset kohteelle %s + Moduulikohteen asetukset kohteelle %s + Julkaise moduulin kohde + Peruuta moduulin kohteen julkaisu + Julkaistaanko? + Tämä tekee ainoastaan moduulin näkyväksi opiskelijoille. + Tämä tekee moduulin ja kaikki kohteet näkyviksi opiskelijoille.. + Peruutetaanko julkaisu? + Tämä tekee moduulin ja kaikki kohteet näkymättömiksi opiskelijoille. + Tämä tekee ainoastaan tämän kohteen näkyväksi opiskelijoille. + Tämä tekee ainoastaan tämän kohteen näkymättömäksi opiskelijoille. + Tämä tekee kaikki moduulit ja kohteet näkyviksi opiskelijoille. + Tämä tekee ainoastaan mooduulit näkyviksi opiskelijoille. + Tämä tekee kaikki moduulit ja kohteet näkymättömiksi opiskelijoille. + Saatavissa linkin kanssa + Aikataulun saatavissa olo + Kohde julkaistu + Kohteen julkaisu peruutettu + Vain moduuli julkaistu + Moduuli ja kohteet julkaistu + Moduulin ja kohteiden julkistetaminen kumottu + Kaikki moduuliy julkaistu + Kaikki moduulit ja kohteet julkaistu + Kaikkien moduulien ja kohteiden julkaisu peruttu + Peri kurssilta + Kurssin jäsenet + Laitoksen jäsenet + Julkinen + Muokkaa oikeuksia + Päivitä + Saatavuus + Näkyvyys + Saatavissa alkaen + Saatavissa tähän päivämäärään saakka + Lähettäjä + Asti + Päivämäärä + Aika + Tyhjennä tästä päivämäärästä lähtien + Tyhjennä tähän päivämäärään asti + Tämä prosessi voi kestää muutamia minuutteja. Voit sulkea modaalin tai siirtyä pois sivulta tämän prosessin aikana. + Huomautus + Kaikki moduulit + Kaikki moduulit ja kohteet + Valitut moduulit ja kohteet + Valitut moduulit + Julkaistaan + Julkaisua kumotaan + Moduulit ja kohteet, jotka on jo käsitelty ei palauteta aikaisempaan tilaan, kun käsittely keskeytetään. + Onnistui! + Päivitys epäonnistui + Päivitys peruutettu + Piilotettu + Aikataulutettu + Julkaistu + Julkaisematon + + %.0f piste + %.0f pistettä + + Julkaise + Peruuta julkaisu + Julkaise + Peruuta julkaisu + Päivitä diff --git a/libs/pandares/src/main/res/values-fr-rCA/strings.xml b/libs/pandares/src/main/res/values-fr-rCA/strings.xml index ebabfdafd4..b0cc5e2491 100644 --- a/libs/pandares/src/main/res/values-fr-rCA/strings.xml +++ b/libs/pandares/src/main/res/values-fr-rCA/strings.xml @@ -1519,4 +1519,76 @@ Contenu supplémentaire de cours Échec de la mise à jour de la commande des cartes du tableau de bord + Publier tous les modules et éléments + Publier les modules uniquement + Annuler la publication de tous les modules et éléments + Options du module + Publier le module et tous les éléments + Publier le module uniquement + Annuler la publication du module et tous les éléments + Options de module pour %s + Options de l’élément de module pour %s + Publier un élément du module + Annuler la publication d’un élément du module + Publier? + Ainsi, seul le module sera visible par les étudiants. + Le module et tous ses éléments seront alors visibles par les étudiants. + Annuler la publication? + Cela rendra le module et tous les éléments invisibles pour les étudiants. + Seul cet élément sera visible par les étudiants. + Seul cet élément sera invisible pour les étudiants. + Tous les modules et éléments seront ainsi visibles par les étudiants. + Seuls les modules seront visibles par les étudiants. + Cela rendra tous les modules et éléments invisibles pour les étudiants. + Seulement disponible avec un lien + Programmer les disponibilités + Article publié + Article non publié + Seul le module est publié + Module et tous les éléments publiés + Module et tous les éléments non publiés + Uniquement les modules publiés + Tous les modules et tous les articles publiés + Tous les modules et tous les éléments ne sont pas publiés + Hériter depuis le cours + Membres du cours + Membres de l’institution + Public + Modifier les autorisations + Mettre à jour + Disponibilité + Visibilité + Disponible depuis + Disponible jusqu’à + De + Jusqu’au + Date + Heure + Effacer la date de début + Effacer la date d’échéance + Ce processus peut prendre quelques minutes. Vous pouvez fermer le modal ou quitter la page pendant ce processus. + Remarque + Tous les modules + Tous les modules et éléments + Modules et éléments sélectionnés + Modules sélectionnés + Publication + Annulation de la publication + Les modules et les éléments qui ont déjà été traités ne retrouveront pas leur état précédent lorsque le processus sera interrompu. + Succès! + Échec de la mise à jour + Mise à jour annulée + Masqué + Planifié + Publié + Non publié + + %.0f pt + %.0f pts + + Publier + Annuler la publication + Publier + Annuler la publication + Actualiser diff --git a/libs/pandares/src/main/res/values-fr/strings.xml b/libs/pandares/src/main/res/values-fr/strings.xml index bfecb2f221..e8781ce24a 100644 --- a/libs/pandares/src/main/res/values-fr/strings.xml +++ b/libs/pandares/src/main/res/values-fr/strings.xml @@ -1519,4 +1519,76 @@ Contenu de cours supplémentaire Impossible de mettre à jour l’ordre des cartes du tableau de bord + Publier tous les modules et les éléments + Publier les modules uniquement + Annuler la publication de tous les modules et des éléments + Options de module + Publier le module et tous les éléments + Publier le module uniquement + Annuler la publication du module et de tous les éléments + Options de module pour %s + Options d\'élément de module pour %s + Publier l\'élément de module + Annuler la publication de l\'élément de module + Publier ? + Seul le module sera visible pour les élèves. + Le module et tous les éléments seront visibles pour les élèves. + Annuler la publication ? + Le module et tous les éléments seront invisibles pour les élèves. + Seul cet élément sera visible pour les élèves. + Seul cet élément sera invisible pour les élèves. + Tous les modules et les éléments seront visibles pour les élèves. + Seuls les modules seront visibles pour les élèves. + Tous les modules et les éléments seront invisibles pour les élèves. + Accessible uniquement via un lien + Calendrier de disponibilités + Élément publié + Élément non publié + Module publié uniquement + Module et tous les éléments publiés + Module et tous les éléments non publiés + Modules publiés uniquement + Tous les modules et les éléments publiés + Tous les modules et les éléments non publiés + Hériter du cours + Membres du cours + Membres de l\'établissement + Public + Modifier les permissions + Mettre à jour + Disponibilité + Visibilité + Disponible depuis le + Disponible jusqu’au + Du + Au + Date + Temps + Effacer la date de début + Effacer la date de fin + Ce processus peut prendre quelques minutes. Vous pouvez fermer la fenêtre modale ou quitter la page pendant ce processus. + Remarque + Tous les modules + Tous les modules et les éléments + Les modules et les éléments sélectionnés + Les modules sélectionnés + Publication + Annulation de la publication + Les modules et éléments déjà traités ne seront pas ramenés à leur état initial si le processus est interrompu. + Réussite ! + Échec de la mise à jour + Mise à jour annulée + Masqué + Prévus + Publié + Non publié + + %.0f pt + %.0f pts + + Publier + Ne pas publier + Publier + Ne pas publier + Actualiser diff --git a/libs/pandares/src/main/res/values-ht/strings.xml b/libs/pandares/src/main/res/values-ht/strings.xml index 2a8d1c352c..f596c35e02 100644 --- a/libs/pandares/src/main/res/values-ht/strings.xml +++ b/libs/pandares/src/main/res/values-ht/strings.xml @@ -1519,4 +1519,76 @@ Plis kontni kou Echèk mizajou kòmann kat tablodbò a + Pibliye Tout Modil yo ak Atik yo + Pibliye Modil yo sèlman + Depibliye tout Modil yo ak Atik yo + Opsyon Modil yo + Pibliye Modil la ak tout Atik yo + Pibliye Modil sèlman + Depibliye Modil la ak tout Atik yo + Opsyon modil pou %s + Opsyon modil atik pou %s + Pibliye Modil Atik + Depibliye Modil Atik + Pibliye? + Sa ap rann sèlman modil la vizib pou etidyan yo. + Sa ap rann modil la ak tout atik yo vizib pou etidyan yo. + Depibliye + Sa ap rann modil la ak tout atik yo envizib pou etidyan yo. + Sa ap rann sèlman atik sa a vizib pou etidyan yo. + Sa ap rann sèlman atik sa a envizib pou etidyan yo. + Sa ap rann tout modil yo ak atik yo vizib pou etidyan yo. + Sa ap rann sèlman modil yo vizib pou etidyan yo. + Sa ap rann tout modil yo ak atik yo envizib pou etidyan yo. + Disponib sèlman ak lyen + Orè Disponib + Atik pibliye + Atik Depibliye + Modil ki pibliye sèlman + Modil ak tout Atik ki pibliye + Modil ak Tout Atik ki depibliye yo + Modil ki pibliye yo sèlman + Tout Modil ak tout Atik ki pibliye + Tout Modil ak tout Atik ki depibliye + Eritaj Kou + Manm Kou + Manm Enstitisyon + Piblik + Modifye Pèmisyon yo + Aktyalize + Disponibilite + Vizibilite + Disponib Depi + Disponib Jiska + De + Jiska + Dat + Tan + Efase Depi Dat + Efase Jiska Dat + Pwosesis sa a ka pran kèk minit. Ou ka fèmen fenèt modal la oswa kite paj la pandan pwosesis sa a. + Nòt + Tout Eleman + Tout Modil yo + Modil ak Atik Seleksyone yo + Modil Seleksyone yo + Pibliye + Pa Pibliye + Modil ak atik ki te deja trete yo pa p retounen nan eta yo te ye anvan an lè pwosesis la sipann. + Reyisi! + Mizajou echwe + Mizajou anile + Kache + Orè + Pibliye + Pa Pibliye + + %.0f pwen + %.0f pwen + + Pibliye + Pa Pibliye + Pibliye + Pa Pibliye + Aktyalize diff --git a/libs/pandares/src/main/res/values-id/strings.xml b/libs/pandares/src/main/res/values-id/strings.xml index 37180e7e64..24cf00da35 100644 --- a/libs/pandares/src/main/res/values-id/strings.xml +++ b/libs/pandares/src/main/res/values-id/strings.xml @@ -1522,4 +1522,76 @@ Konten kursus tambahan Gagal memperbarui urutan kartu Dashboard + Terbitkan semua Modul dan Item + Terbitkan Modul saja + Batalkan Penerbitan semua Modul dan Item + Opsi Modul + Terbitkan Modul dan semua Item + Terbitkan Modul saja + Batalkan Penerbitan Modul dan semua Item + Opsi modul untuk %s + Opsi item modul untuk %s + Terbitkan Item Modul + Batalkan Penerbitan Item Modul + Terbitkan? + Ini akan membuat hanya modul tampak oleh siswa. + Ini akan membuat modul dan semua item tampak oleh siswa. + Batalkan Penerbitan? + Ini akan membuat modul dan semua item tidak tampak oleh siswa. + Ini akan membuat hanya item tampak oleh siswa. + Ini akan membuat hanya item tidak tampak oleh siswa. + Ini akan membuat semua modul dan item tampak oleh siswa. + Ini akan membuat hanya modul tampak oleh siswa. + Ini akan membuat semua modul dan item tidak tampak oleh siswa. + Hanya tersedia dengan tautan + Ketersediaan jadwal + Item diterbitkan + Item batal diterbitkan + Hanya Modul diterbitkan + Modul dan semua item diterbitkan + Modul dan semua item batal diterbitkan + Hanya Modul diterbitkan + Semua Modul dan semua Item diterbitkan + Semua Modul dan semua Item batal diterbitkan + Warisi dari Kursus + Anggota Kursus + Anggota Institusi + Publik + Edit Izin + Pembaruan + Ketersediaan + Visibilitas + Tersedia Mulai + Tersedia Hingga + Dari + Hingga + Tanggal + Waktu + Kosongkan Tanggal Mulai + Kosongkan Tanggal Hingga + Proses ini dapat membutuhkan waktu beberapa menit. Anda dapat menutup modal atau menjauh dari halaman ini selama proses ini. + Catatan + Semua Modul + Semua Modul dan Item + Modul dan Item Pilihan + Modul Pilihan + Menerbitkan + Batal Diterbitkan + Modul dan item yang sudah diproses tidak akan dikembalikan ke kondisi sebelumnya ketika proses dihentikan. + Sukses! + Gagal memperbarui + Pembaruan dibatalkan + Tersembunyi + Dijadwalkan + Diterbitkan + Belum Diterbitkan + + %.0f poin + %.0f poin + + Terbitkan + Batalkan Terbit + Terbitkan + Batalkan Terbit + Muat Ulang diff --git a/libs/pandares/src/main/res/values-is/strings.xml b/libs/pandares/src/main/res/values-is/strings.xml index e72b1018dc..4c37c01555 100644 --- a/libs/pandares/src/main/res/values-is/strings.xml +++ b/libs/pandares/src/main/res/values-is/strings.xml @@ -1519,4 +1519,76 @@ Viðbótar námskeiðsefni Ekki tókst að uppfæra röð Skjáborðsspjalda + Birta allt námsefni og atriði + Birta eingöngu námsefni + Fela allt námsefni og atriði + Valkostir námsefnis + Birta námsefni og öll atriði + Birta eingöngu námsefni + Fela námsefni og öll atriði + Valkostir námsefnis fyrir %s + Valkostir námsefnisatriðis fyrir %s + Birta námsefnisatriði + Fela námsefnisatriði + Birta? + Þetta mun gera aðeins námsefnið sýnilegt nemendum. + Þetta mun gera námsefnið og öll atriði sýnileg nemendum. + Fela? + Þetta mun gera námsefnið og öll atriði ósýnileg nemendum. + Þetta mun aðeins gera þetta atriði sýnilegt nemendum. + Þetta mun aðeins gera þetta atriði ósýnilegt nemendum. + Þetta mun gera allt námsefni og atriði sýnileg nemendum. + Þetta mun aðeins gera námsefnið sýnilegar nemendum. + Þetta mun gera allt námsefni og atriði ósýnileg nemendum. + Eingöngu tiltækt með tengli + Áætla tiltækileika + Atriði birt + Atriði falið + Aðeins námsefni birt + Námsefni og öll atriði birt + Námsefni og öll atriði óbirt + Aðeins námsefni birt + Allt námsefni og öll atriði birt + Allt námsefni og öll atriði falin + Erft frá námskeiði + Meðlimir námskeiðs + Meðlimir stofnunar + Opinbert + Breyta aðgangsheimildum + Uppfæra + Tiltækileiki + Sýnileiki + Tiltækt frá + Tiltækt til + Frá + Þar til + Dagsetning + Tími + Hreinsa frá dagsetningu + Hreinsa til dagsetningu + Þetta ferli getur tekið nokkrar mínútur. Þú getur lokað staðfestingarglugganum eða farið af síðunni meðan á þessu ferli stendur. + Athugasemd + Allt námsefni + Allt námsefni og atriði + Valið námsefni og atriði + Valið námsefni + Birtir + Felur + Námsefni og atriði sem þegar hafa verið unnin verða ekki færð í fyrra ástand þegar ferlinu er hætt. + Tókst! + Uppfærsla mistókst + Hætt við uppfærslu + Falið + Tímasett + Birt + Falið + + %.0f punktur + %.0f punktar + + Birta + Fela + Birta + Fela + Glæða diff --git a/libs/pandares/src/main/res/values-it/strings.xml b/libs/pandares/src/main/res/values-it/strings.xml index d2718c0d31..4b3ad2d5fb 100644 --- a/libs/pandares/src/main/res/values-it/strings.xml +++ b/libs/pandares/src/main/res/values-it/strings.xml @@ -1519,4 +1519,76 @@ Altro contenuto del corso Impossibile aggiornare l’ordine delle schede dashboard + Pubblica tutti i moduli e gli elementi + Pubblica solo i moduli + Non pubblicare tutti i moduli e gli elementi + Opzioni modulo + Pubblica il modulo e tutti gli elementi + Pubblica solo il modulo + Non pubblicare il modulo e tutti gli elementi + Opzioni modulo per %s + Opzioni elemento modulo per %s + Pubblica elemento modulo + Non pubblicare elemento modulo + Pubblicare? + Tale operazione renderà visibile solo il modulo agli studenti. + Tale operazione renderà visibile il modulo e tutti gli elementi agli studenti. + Annullare pubblicazione? + Tale operazione renderà invisibile il modulo e tutti gli elementi agli studenti. + Tale operazione renderà visibile solo questo elemento agli studenti. + Tale operazione renderà invisibile solo questo elemento agli studenti. + Tale operazione renderà visibili tutti i moduli e gli elementi agli studenti. + Tale operazione renderà visibili solo i moduli agli studenti. + Tale operazione renderà invisibile tutti i moduli e gli elementi agli studenti. + Disponibile solo con link + Pianifica disponibilità + Elemento pubblicato + Elemento non pubblicato + Pubblicato solo modulo + Modulo e tutti gli elementi pubblicati + Modulo e tutti gli elementi non pubblicati + Pubblicati solo i moduli + Tutti i moduli e tutti gli elementi pubblicati + Tutti i moduli e tutti gli elementi non pubblicati + Eredita da corso + Membri del corso + Membri dell’istituto + Pubblico + Modifica Permessi + Aggiorna + Disponibilità + Visibilità + Disponibile da + Disponibile fino a + Da + Fino a + Data + Ora + Data di inizio cancellazione + Data di fine cancellazione + Questo processo potrebbe richiedere alcuni minuti. Durante questo processo è possibile chiudere il modale o uscire dalla pagina. + Nota + Tutti i moduli + Tutti i moduli e gli elementi + Moduli ed elementi selezionati + Moduli selezionati + Pubblicazione + Annullamento pubblicazione + Quando il processo viene interrotto, i moduli e gli elementi già elaborati non saranno riportati al loro stato precedente. + Operazione riuscita. + Aggiornamento non riuscito + Aggiornamento annullato + Nascosto + Programmato + Pubblicato + Non pubblicato + + %.0f pt + %.0f pt. + + Pubblica + Annulla pubblicazione + Pubblica + Annulla pubblicazione + Aggiorna diff --git a/libs/pandares/src/main/res/values-ja/strings.xml b/libs/pandares/src/main/res/values-ja/strings.xml index 5de5425ac7..428f0982fd 100644 --- a/libs/pandares/src/main/res/values-ja/strings.xml +++ b/libs/pandares/src/main/res/values-ja/strings.xml @@ -388,7 +388,7 @@ メッセージオプション 転送 すべてに返信 - アーカイブ解凍 + アーカイブの解除 このメッセージのコピーを本当に削除しますか?この操作は元に戻すことはできません。 この会話のコピーを本当に削除しますか?この操作は元に戻すことはできません。 この操作を実行できません。接続を確認して、もう一度お試しください。 @@ -1370,7 +1370,7 @@ 削除 アーカイブ - アーカイブ解凍 + アーカイブの解除 既読にする 未読にする スター @@ -1495,4 +1495,75 @@ 追加のコースコンテンツ ダッシュボードのカードの順番を更新できませんでした + すべてのモジュールと項目を公開する + モジュールのみ公開する + すべてのモジュールと項目の公開を解除する + モジュールオプション + モジュールとすべての項目を公開する + モジュールのみ公開する + モジュールとすべての項目の公開を解除する + %sモジュールオプション + %sモジュール項目オプション + モジュール項目を公開する + モジュール項目を非公開にする + 公開しますか? + これはモジュールのみを受講者に公開します。 + これはモジュールおよびすべての項目を受講者に公開します。 + 非公開にしますか? + モジュールとすべての項目を受講者に非公開にします。 + この項目のみ受講者に見えるようにします。 + これはこの項目のみを受講者に公開します。 + これはすべてのモジュールおよび項目を受講者に公開します。 + これはモジュールのみを受講者に公開します。 + これはすべてのモジュールと項目を受講者に非公開にします。 + リンクを知っている受講者のみ利用可能 + 期間を設定する + 項目が公開 + 項目が非公開 + モジュールのみ公開 + モジュールと全項目が公開 + モジュールと全項目が非公開 + モジュールのみ公開 + すべてのモジュールとすべての項目が公開 + すべてのモジュールとすべての項目が非公開 + コースから継承 + コースメンバー + 教育機関メンバー + 公開 + 承認を編集 + 更新 + 利用可能性 + 可視性 + 開始日時 + 終了日時 + From(~から) + 利用終了日時 + 日付 + 制限時間 + クリア開始日 + クリア終了日 + この処理には数分かかることがあります。この処理の間、モーダルを閉じたり、ページから移動したりできます。 + + すべてのモジュール + すべてのモジュールと項目 + モジュールおよび項目を選択 + モジュールを選択 + 公開 + 公開取り消し中 + すでに処理されているモジュールや項目は、処理を中止しても以前の状態には戻りません。 + 成功! + 更新失敗 + 更新がキャンセルされました。 + 非表示 + スケジュール済み + 公開済み + 非公開 + + %.0f点 + + 公開 + 非公開 + 公開 + 非公開 + 更新 diff --git a/libs/pandares/src/main/res/values-mi/strings.xml b/libs/pandares/src/main/res/values-mi/strings.xml index ac24ae565b..dbc81d51fe 100644 --- a/libs/pandares/src/main/res/values-mi/strings.xml +++ b/libs/pandares/src/main/res/values-mi/strings.xml @@ -1519,4 +1519,76 @@ Ihirangi akoranga taapiri I rahua te whakahōu i te raupapa kāri Papatohu + Whakaputa i ngā Kōwae me ngā Tūemi katoa + Whakaputa Kōwae anake + Wete whakaputa i ngā Kōwae me ngā Tūemi katoa + Ngā Kōwhiringa Kōwae + Whakaputa Kōwae me ngā Tūemi Katoa + Whakaputa Kōwae anake + Wete whakaputa i te Kōwae me ngā Tūemi katoa + Ngā kōwhiringa kōwae mō %s + Ngā kōwhiringa tūemi kōwae mō %s + Whakaputa Tūemi Kōwae + Wete whakaputa i te Tūemi Kōwae + Whakaputa? + Mā tēnei anake e kitea ai te kōwae ki ngā ākonga. + Mā tēnei ka kitea te kōwae me ngā tūemi katoa e kitea ana e ngā ākonga. + Weteputa? + Mā tēnei ka noho te kōwae me ngā tūemi katoa kāore e kitea ana e ngā ākonga. + Mā tēnei anake e kitea ai tēnei tūemi ki ngā ākonga. + Mā tēnei anake e mahi i tēnei tūemi kāore e kitea ana e ngā ākonga. + Mā tēnei ka kitea ngā kōwae me ngā tūemi katoa ki ngā ākonga. + Mā tēnei anake e kitea ai ngā kōwae ki ngā ākonga. + Mā tēnei ka kitea ngā kōwae me ngā tūemi katoa ki ngā ākonga. + E waatea ana me te hono + Whakaritea te waate + Tūemi i whakaputaina + Kāore i whakaputaina te tūemi + Kōwae anake i whakaputaina + Kōwae me ngā Tūemi katoa i whakaputaina + Kōwae me ngā Tūemi katoa kāore anō kia whakaputaina + Ngā Kōwae anake i whakaputaina + Ngā Kōwae Katoa me ngā Tūemi katoa i whakaputaina + Ngā Kōwae Katoa me ngā Tūemi katoa kāore i whakaputaina + Tukua mai i te Akoranga + Nga Mema Akoranga + Nga Mema Whare + tūmatanui + Whakatika Whakaaetanga + Whakahou + Wātea + Aritanga + Wātea Mai + Wātea Kia + + Tae noa ki + + + Ūkui Mai i te Rā + Ūkui tae noa ki te Rā + He meneti torutoru pea tenei tukanga. Ka taea e koe te kati i te matapihi aratau ka waiho ranei i te wharangi i tenei mahi. + Tuhipoka + Ngā kōwae katoa + Ngā Kōwae me ngā Tūemi Katoa + Ngā Kōwae me ngā Tūemi kua tīpakohia + Ngā Kōwae kua Tīpakohia + Whakaputa ana + Wete whakaputa ana + Ko nga waahanga me nga mea kua oti te tukatuka e kore e hoki ki to ratau ahuatanga o mua ka haukotia te tukanga. + Angitu! + I rahua te whakahou + Kua whakakorea te whakahōu + Huna + Kua Raupapatia + I whakaputaina + Kaore ii whakaputaina + + %.0f koinga + %.0f ngā koinga + + Whakaputa + Kaore i tāngia + Whakaputa + Kaore i tāngia + Whakahouhia diff --git a/libs/pandares/src/main/res/values-ms/strings.xml b/libs/pandares/src/main/res/values-ms/strings.xml index b832724d45..0e53a37475 100644 --- a/libs/pandares/src/main/res/values-ms/strings.xml +++ b/libs/pandares/src/main/res/values-ms/strings.xml @@ -1525,4 +1525,76 @@ Kandungan kursus tambahan Gagal untuk mengemas kini susunan kad Papan Pemuka + Terbitkan semua Modul dan Item + Terbitkan Modul sahaja + Nyahterbit semua Modul dan Item + Pilihan Modul + Terbitkan Modul dan semua Item + Terbitkan Modul sahaja + Nyahterbit Modul dan semua Item + Pilihan Modul untuk %s + Pilihan item Modul untuk %s + Terbitkan Item Modul + Nyahterbit Item Modul + Terbitkan? + Ini akan menjadikan modul ini sahaja kelihatan kepada pelajar. + Ini akan menjadikan modul ini dan semua item kelihatan kepada pelajar. + Nyahterbit? + Ini akan menjadikan modul ini dan semua item tidak kelihatan kepada pelajar. + Ini akan menjadikan item ini sahaja kelihatan kepada pelajar. + Ini akan menjadikan item ini sahaja tidak kelihatan kepada pelajar. + Ini akan menjadikan semua modul dan item kelihatan kepada pelajar. + Ini akan menjadikan modul-modul ini sahaja kelihatan kepada pelajar. + Ini akan menjadikan semua modul dan item ini tidak kelihatan kepada pelajar. + Hanya tersedia dengan pautan + Jadualkan ketersediaan + Item diterbitkan + Item dinyahterbit + Hanya Modul diterbitkan + Modul dan semua Item diterbitkan + Modul dan semua Item dinyahterbit + Hanya Modul diterbitkan + Semua Modul dan semua Item diterbitkan + Semua Modul dan semua Item dinyahterbit + Warisi daripada Kursus + Ahli Kursus + Ahli Institusi + Umum + Edit Keizinan + Kemas kini + Ketersediaan + Kebolehlihatan + Tersedia Dari + Tersedia Hingga + Dari + Sehingga + Tarikh + Masa + Kosongkan dari Tarikh + Kosong Sehingga Tarikh + Proses ini mungkin mengambil masa beberapa minit. Anda boleh menutup modal atau menavigasi keluar daripada halaman semasa proses ini. + Nota + Semua Modul + Semua Modul dan Item + Modul dan Item terpilih + Modul terpilih + Menerbitkan + Menyahterbit + Modul dan item yang telah diproses tidak akan ditukar kembali kepada keadaan sebelumnya apabila proses dihentikan. + Berjaya! + Kemas Kini gagal + Kemas kini dibatalkan + Tersembunyi. + Dijadualkan + Telah diterbitkan + Dinyahterbit + + %.0f mata + %.0f mata + + Terbitkan + Nyahterbit + Terbitkan + Nyahterbit + Segar semula diff --git a/libs/pandares/src/main/res/values-nb/strings.xml b/libs/pandares/src/main/res/values-nb/strings.xml index 2103ae6842..49f959e434 100644 --- a/libs/pandares/src/main/res/values-nb/strings.xml +++ b/libs/pandares/src/main/res/values-nb/strings.xml @@ -1520,4 +1520,76 @@ Ytterligere emneinnhold Kunne ikke oppdatere rekkefølge på Dashbord-kort + Publiser alle moduler og elementer + Publiser kun moduler + Avpubliser alle moduler og elementer + Modulalternativer + Publiser modul og alle elementer + Publiser kun moduler + Avpubliser modul og alle elementer + Modulalternativer for %s + Modulelementalternativer for %s + Publiser modulelement + Avpubliser modulelement + Publisere? + Dette vil gjøre kun modulen synlig for studenter. + Dette vil gjøre modulen og alle elementer synlige for studenter. + Avpublisere? + Dette vil gjøre modulen og alle elementer usynlige for studenter. + Dette vil gjøre kun dette elementet synlig for studenter. + Dette vil gjøre kun dette elementet usynlig for studenter. + Dette vil gjøre alle moduler og elementer synlige for studenter. + Dette vil gjøre kun modulene synlig for studenter. + Dette vil gjøre alle moduler og elementer usynlige for studenter. + Kun tilgjengelig med lenke + Planlegg tilgjengelighet + Element publisert + Element avpublisert + Kun modul publisert + Modul og alle elementer publisert + Modul og alle elementer avpublisert + Alle moduler publisert + Alle moduler og alle elementer publisert + Alle moduler og alle elementer avpublisert + Arve fra emne + Emnemedlemmer + Institusjonsmedlemmer + Offentlig + Redigeringstillatelser + Oppdater + Tilgjengelighet + Synlighet + Tilgjengelig fra + Tilgjengelig frem til + Fra + Inntil + Dato + Tid + Slett fra dato + Slett til dato + Denne prosessen kan ta noen minutter. Du kan lukke modalen eller navigere bor fra siden under denne prosessen. + Merknad + Alle moduler + Alle moduler og elementer + Valgte moduler og elementer + Valgte moduler + Publiserer + Avpubliserer + Moduler og elementer som allerede er behandlet, vil ikke bli tilbakestilt til tidligere tilstand når prosessen avbrytes. + Vellykket! + Oppdatering mislyktes + Oppdatering avbrutt + Skjult + Planlagt + Publisert + Upublisert + + %.0f poeng + %.0f poeng + + Publiser + Avpubliser + Publiser + Avpubliser + Oppdater diff --git a/libs/pandares/src/main/res/values-nl/strings.xml b/libs/pandares/src/main/res/values-nl/strings.xml index a71a87122c..8a798ac782 100644 --- a/libs/pandares/src/main/res/values-nl/strings.xml +++ b/libs/pandares/src/main/res/values-nl/strings.xml @@ -1519,4 +1519,76 @@ Aanvullende cursusinhoud Kan de volgorde van Dashboard-kaarten niet wijzigen + Alle modules en items publiceren + Alleen modules publiceren + Publicatie van alle modules en items ongedaan maken + Moduleopties + Module en alle items publiceren + Alleen module publiceren + Publicatie van module en alle items ongedaan maken + Moduleopties voor %s + Module-itemopties voor %s + Module-item publiceren + Publicatie van module-item ongedaan maken + Publiceren? + Hiermee wordt alleen de module zichtbaar voor studenten. + Hiermee worden de module en alle items zichtbaar voor studenten. + Publicatie ongedaan maken? + Hiermee worden de module en alle items onzichtbaar voor studenten. + Hiermee wordt alleen dit item zichtbaar voor studenten. + Hiermee wordt alleen dit item onzichtbaar voor studenten. + Hiermee worden alle modules en items zichtbaar voor studenten. + Hiermee worden alleen de modules zichtbaar voor studenten. + Hiermee worden alle modules en items onzichtbaar voor studenten. + Alleen beschikbaar met koppeling + Beschikbaarheid van schema + Item gepubliceerd + Publicatie van item ongedaan gemaakt + Alleen module gepubliceerd + Module en alle items gepubliceerd + Publicatie van module en alle items ongedaan gemaakt + Alleen modules gepubliceerd + Alle modules en items gepubliceerd + Publicatie van alle modules en items ongedaan gemaakt + Overnemen uit cursus + Cursusdeelnemers + Leden van de organisatie + Openbaar + Machtigingen bewerken + Bijwerken + Beschikbaarheid + Zichtbaarheid + Beschikbaar vanaf + Beschikbaar tot + Van + Tot + Datum + Tijd + Datum Van wissen + Datum Tot wissen + Dit proces kan een paar minuten duren. Je kunt tijdens dit proces de modal sluiten of de pagina verlaten. + Opmerking + Alle modules + Alle modules en items + Geselecteerde modules en items + Geselecteerde modules + Bezig met publiceren + Publicatie ongedaan maken + Modules en items die al zijn verwerkt, worden niet naar hun eerdere status teruggezet wanneer het proces wordt stopgezet. + Gelukt! + Bijwerken mislukt + Bijwerken geannuleerd + Verborgen + Gepland + Gepubliceerd + Niet-gepubliceerd + + %.0f pt + %.0f punten + + Publiceren + Publicatie ongedaan maken + Publiceren + Publicatie ongedaan maken + Vernieuwen diff --git a/libs/pandares/src/main/res/values-pl/strings.xml b/libs/pandares/src/main/res/values-pl/strings.xml index 3506aef4af..8d30608d7d 100644 --- a/libs/pandares/src/main/res/values-pl/strings.xml +++ b/libs/pandares/src/main/res/values-pl/strings.xml @@ -1567,4 +1567,78 @@ Dodatkowa zawartość kursu Nie udało się zaktualizować kolejności kart pulpitu nawigacyjnego + Publikuj wszystkie moduły i elementy + Publikuj tylko moduły + Cofnij publikację wszystkich modułów i elementów + Opcje modułu + Publikuj moduł i wszystkie elementy + Publikuj tylko moduł + Cofnij publikację modułu i wszystkich elementów + Opcje modułu dla %s + Opcje elementów modułu dla %s + Publikuj element modułu + Cofnij publikację elementu modułu + Publikować? + Dzięki temu tylko moduły będą widoczne dla uczestników. + Dzięki temu ten moduł i wszystkie elementy będą widoczne dla uczestników. + Cofnąć publikację? + Dzięki temu ten moduł i wszystkie elementy będą niewidoczne dla uczestników. + Dzięki temu tylko ten element będzie widoczny dla uczestników. + Dzięki temu tylko ten element będzie niewidoczny dla uczestników. + Dzięki temu wszystkie moduły i elementy będą widoczne dla uczestników. + Dzięki temu tylko moduły będą widoczne dla uczestników. + Dzięki temu wszystkie moduły i elementy będą niewidoczne dla uczestników. + Dostępne tylko z łączem + Zaplanuj dostępność + Opublikowano element + Cofnięto publikację elementu + Opublikowano tylko moduł + Opublikowano moduł i wszystkie elementy + Cofnięto publikację modułu i wszystkich elementów + Opublikowano tylko moduły + Opublikowano wszystkie moduły i elementy + Cofnięto publikację wszystkich modułów i elementów + Odziedzicz z kursu + Uczestnicy kursu + Członkowie instytucji + Publiczny + Edytuj uprawnienia + Zaktualizuj + Dostępność + Widoczność + Dostępne od + Dostępne do + Z + Do + Data + Czas + Wyczyść od dnia + Wyczyść do dnia + Proces ten może zająć kilka minut. W trakcie tego procesu można zamknąć okno dialogowe lub przejść poza stronę. + Uwaga + Wszystkie moduły + Wszystkie moduły i elementy + Wybrano wszystkie moduły i elementy + Wybrano moduły + Publikowanie + Cofanie publikacji + Przetworzone moduły i elementy nie zostaną przywrócone do poprzedniego stanu, jeśli proces zostanie przerwany. + Zakończono powodzeniem! + Aktualizacja nie powiodła się + Anulowano aktualizacje + Ukryte + Zaplanowane + Opublikowane + Nieopublikowane + + %.0f pkt + %.0f pkt + %.0f pkt + %.0f pkt + + Publikuj + Cofnij publikowanie + Publikuj + Cofnij publikowanie + Odśwież diff --git a/libs/pandares/src/main/res/values-pt-rBR/strings.xml b/libs/pandares/src/main/res/values-pt-rBR/strings.xml index fc76fee792..602ae0fcc2 100644 --- a/libs/pandares/src/main/res/values-pt-rBR/strings.xml +++ b/libs/pandares/src/main/res/values-pt-rBR/strings.xml @@ -1519,4 +1519,76 @@ Conteúdo adicional do curso Falha ao atualizar a ordem dos cartões do Painel + Publicar todos os módulos e itens + Publicar somente módulos + Cancelar a publicação de todos os módulos e itens + Opções de módulo + Publicar o módulo e todos os itens + Somente publicar módulo + Cancelar a publicação do módulo e de todos os itens + Opções de módulo para %s + Opções de itens do módulo para %s + Publicar item do módulo + Cancelar a publicação do item do módulo + Publicar? + Isso fará com que apenas o módulo fique visível para os alunos. + Isso tornará o módulo e todos os itens visíveis para os alunos. + Cancelar a publicação? + Isso fará com que o módulo e todos os itens fiquem invisíveis para os alunos. + Isso fará com que apenas esse item fique visível para os alunos. + Isso fará com que apenas esse item fique invisível para os alunos. + Isso fará com que todos os módulos e itens fiquem visíveis para os alunos. + Isso fará com que apenas os módulos fiquem visíveis para os alunos. + Isso fará com que todos os módulos e itens fiquem invisíveis para os alunos. + Disponível apenas com link + Disponibilidade de agendamento + Item publicado + Item não publicado + Somente o módulo publicado + Módulo e todos os itens publicados + Módulo e todos os itens não publicados + Somente módulos publicados + Todos os módulos e todos os itens publicados + Todos os módulos e todos os itens não publicados + Herdar do curso + Membros do curso + Membros da instituição + Público + Editar permissões + Atualizar + Disponibilidade + Visibilidade + Disponível de + Disponível até + De + Até + Data + Tempo + Limpar da data + Limpar até a data + Esse processo pode levar alguns minutos. Você pode fechar o modal ou sair da página durante esse processo. + Observação + Todos os módulos + Todos os módulos e itens + Módulos e itens selecionados + Módulos selecionados + Publicando + Cancelando publicação + Os módulos e itens que já foram processados não serão revertidos ao estado anterior quando o processo for interrompido. + Sucesso! + Atualização falhou + Atualização cancelada + Oculto + Agendado + Publicado(as) + Não publicado + + %.0f pt + %.0f pts + + Publicar + Despublicar + Publicar + Despublicar + Atualizar diff --git a/libs/pandares/src/main/res/values-pt-rPT/strings.xml b/libs/pandares/src/main/res/values-pt-rPT/strings.xml index 7bad8df237..9237cd7e84 100644 --- a/libs/pandares/src/main/res/values-pt-rPT/strings.xml +++ b/libs/pandares/src/main/res/values-pt-rPT/strings.xml @@ -1519,4 +1519,76 @@ Conteúdo adicional da disciplina Falha ao atualizar a ordem dos cartões do Painel + Publicar todos os Módulos e Itens + Publicar apenas módulos + Cancelar a publicação de todos os módulos e itens + Opções de módulo + Publicar módulo e todos os itens + Publicar apenas o módulo + Cancelar a publicação do módulo e de todos os itens + Opções de módulo para %s + Opções de item do módulo para %s + Publicar item do módulo + Cancelar a publicação do item do módulo + Publicar? + Isto fará com que apenas o módulo seja visível para os alunos. + Isto tornará o módulo e todos os itens visíveis para os alunos. + Cancelar publicação? + Isto tornará o módulo e todos os itens invisíveis para os alunos. + Isto tornará apenas este item visível para os alunos. + Isto tornará apenas este item invisível para os alunos. + Isto tornará todos os módulos e itens visíveis para os alunos. + Isto fará com que apenas os módulos sejam visíveis para os alunos. + Isto tornará todos os módulos e itens invisíveis para os alunos. + Apenas disponível com ligação + Disponibilidade de horários + Item publicado + Item não publicado + Apenas o módulo publicado + Módulo e todos os itens publicados + Módulo e todos os itens não publicados + Apenas módulos publicados + Todos os módulos e todos os itens publicados + Todos os módulos e todos os itens não publicados + Herdar da Disciplina + Membros da Disciplina + Membros da instituição + Público + Editar permissões + Atualizar + Disponibilidade + Visibilidade + Disponível de + Disponível até + De + Até + Data + Hora + Limpar da data + Limpar até à data + Este processo pode demorar alguns minutos. Podes fechar o modal ou navegar para fora da página durante este processo. + Observação + Todos os módulos + Todos os módulos e itens + Módulos e itens seleccionados + Módulos seleccionados + Publicando + Não publicar + Os módulos e itens que já tenham sido processados não serão revertidos para o seu estado anterior quando o processo for interrompido. + Bem sucedido! + Atualização falhada + Atualização cancelada + Ocultar + Agendado + Publicado + Não publicado + + %.0f pt + %.0f pts + + Publicar + Anular publicação + Publicar + Anular publicação + Atualizar diff --git a/libs/pandares/src/main/res/values-ru/strings.xml b/libs/pandares/src/main/res/values-ru/strings.xml index 0dd4d9f812..132f92eebf 100644 --- a/libs/pandares/src/main/res/values-ru/strings.xml +++ b/libs/pandares/src/main/res/values-ru/strings.xml @@ -1567,4 +1567,78 @@ Дополнительный контент курса Не удалось обновить порядок карт на информационной панели + Опубликовать все модули и элементы + Опубликовать только модули + Отменить публикацию всех модулей и элементов + Параметры модуля + Опубликовать модуль и все элементы + Опубликовать только модуль + Отменить публикацию модуля и всех элементов + Параметры модуля для %s + Параметры элемента модуля для %s + Опубликовать элемент модуля + Отменить публикацию элемента модуля + Опубликовать? + После этого учащимся будет виден только модуль. + После этого учащимся будут видны модуль и все элементы. + Отменить публикацию? + После этого учащимся не будут видны модуль и все элементы. + После этого учащимся будет виден только этот элемент. + После этого учащимся не будет виден только этот элемент. + После этого учащимся будут видны все модули и все элементы. + После этого учащимся будут видны только модули. + После этого учащимся не будут видны все модули и все элементы. + Доступно только по ссылке + Доступность расписания + Элемент опубликован + Отменена публикация элемента + Опубликован только модуль + Опубликованы модуль и все элементы + Отменена публикация модуля и всех элементов + Опубликованы только модули + Опубликованы все модули и все элементы + Публикация всех модулей и всех элементов отменена + Наследование из курса + Участники курса + Члены организации + Общедоступно + Редактировать разрешения + Обновить + Доступность + Доступность + Доступно с + Доступно до + С + До + Дата + Время + Очистить с даты + Очистить до даты + Этот процесс может занять несколько минут. Во время этой процедуры вы можете закрыть модальное окно или перейти на другую страницу. + Примечание + Все модули + Все модули и элементы + Выбранные модули и элементы + Выбранные модули + Выполняется публикация + Выполняется отмена публикации + Модули и элементы, которые уже были обработаны, не будут возвращены в прежнее состояние при остановке процедуры. + Успешно! + Сбой обновления + Обновление отменено + Скрыто + Запланировано + Опубликовано + Не опубликовано + + %.0f баллов + %.0f баллов + %.0f баллов + %.0f баллов + + Опубликовать + Отменить публикацию + Опубликовать + Отменить публикацию + Обновить diff --git a/libs/pandares/src/main/res/values-sl/strings.xml b/libs/pandares/src/main/res/values-sl/strings.xml index d59c7cf222..181be9c3d9 100644 --- a/libs/pandares/src/main/res/values-sl/strings.xml +++ b/libs/pandares/src/main/res/values-sl/strings.xml @@ -1519,4 +1519,76 @@ Dodatna vsebina predmeta Vrstnega reda kartic nadzorne plošče ni bilo mogoče posodobiti + Objavi vse module in elemente + Objavi samo module + Prekliči objavo vseh modulov in elementov + Možnosti modula + Objavi modul in vse elemente + Objavi samo modul + Prekliči objavo modula in vseh elementov + Možnosti modula za %s + Možnosti elementa modula za %s + Objavi element modula + Prekliči objavo elementa modula + Želite izvesti objavo? + S tem bo študentom postal viden le modul. + S tem bo modul in vsi elementi študentom postali vidni. + Želite preklicati objavo? + S tem bo modul in vsi elementi študentom postali nevidni. + S tem bo študentom postal viden le ta element. + S tem bo študentom postal neviden le ta element. + S tem bodo vsi moduli in predmeti študentom postali vidni. + S tem bodo študentom postali vidni le moduli. + S tem bodo vsi moduli in predmeti študentom postali nevidni. + Na voljo samo s povezavo + Razporedi razpoložljivost + Element je objavljen + Objava elementa je preklicana + Objavljen je samo modul + Objavljen je modul in vsi elementi + Objava modula in vseh elementov je preklicana + Objavljeni so samo moduli + Objavljeni so vsi moduli in vsi elementi + Objava vseh modulov in vseh elementov je preklicana + Podeduj od predmeta + Člani predmeta + Člani ustanove + Javno dostopno + Urejanje dovoljenj + Posodobi + Razpoložljivost + Vidnost + Na voljo od + Na voljo do + Pošiljatelj + Do + Datum + Čas + Počisti od datuma + Počisti do datuma + Ta proces lahko traja nekaj minut. Med tem postopkom lahko zaprete modalno okno ali zapustite stran. + Opomba + Vsi moduli + Vsi moduli in elementi + Izbrani moduli in elementi + Izbrani moduli + Poteka objavljanje + Poteka preklic objave + Moduli in elementi, ki so bili že obdelani, po prekinitvi procesa ne bodo povrnjeni v predhodno stanje. + Uspešno! + Posodobitev ni uspela + Posodobitev je preklicana + Skrito + Načrtovano + Objavljeno + Neobjavljeno + + %.0f točka + %.0f točk + + Objavi + Ne objavi + Objavi + Ne objavi + Osveži diff --git a/libs/pandares/src/main/res/values-sv/strings.xml b/libs/pandares/src/main/res/values-sv/strings.xml index 19363df17a..0697d7a83b 100644 --- a/libs/pandares/src/main/res/values-sv/strings.xml +++ b/libs/pandares/src/main/res/values-sv/strings.xml @@ -1519,4 +1519,76 @@ Extra kursinnehåll Det gick inte att uppdatera översiktens kortordning + Publicera alla moduler och objekt + Publicera endast moduler + Avpublicera alla moduler och objekt + Modulalternativ + Publicera modulen och alla objekt + Publicera endast modulen + Avpublicera modulen och alla objekt + Modulalternativ för %s + Modulobjektalternativ för %s + Publicera modulobjekt + Avpublicera modulobjekt + Publicera? + Detta gör modulen synlig endast för studenter. + Detta gör modulen och alla objekt synliga för studenter. + Avpublicera? + Detta döljer modulen och alla objekt för studenter. + Detta gör endast detta objekt synligt för studenter. + Detta döljer endast detta objekt för studenter. + Detta gör alla moduler och objekt synliga för studenter. + Detta gör endast modulerna synliga för studenter. + Detta döljer alla moduler och objekt för studenter. + Endast tillgängligt med länk + Schematillgänglighet + Objekt publicerat + Objekt avpublicerat + Endast modulen publicerades + Modulen och alla objekten publicerades + Modulen och alla objekten avpublicerades + Endast moduler publicerades + Alla moduler och objekt publicerades + Alla moduler och objekt avpublicerades + Ärv från kurs + Kursmedlem + Lärosätets medlemmar + Offentlig + Redigera behörigheter + Uppdatera + Tillgänglighet + Synlighet + Tillgänglig från + Tillgänglig till + Från + Tills + Datum + Tid + Rensa från-datum + Rensa fram till-datum + Detta kan ta några minuter. Du kan stänga modalen eller navigera bort från sidan under det här förloppet. + Anteckning + Alla moduler + Alla moduler och objekt + Valda moduler och objekt + Valda moduler + Publicerar + Avpublicerar + Moduler som redan har bearbetats återställs inte till tidigare tillstånd är processen avslutas. + Framgång! + Uppdateringen misslyckades + Uppdateringen avbruten + Dold + Schemalagd + Publicerad + Ej offentliggjord + + %.0f poäng + %.0f poäng + + Publicera + Avpublicera + Publicera + Avpublicera + Uppdatera diff --git a/libs/pandares/src/main/res/values-th/strings.xml b/libs/pandares/src/main/res/values-th/strings.xml index a3282f2149..fb92e26dfd 100644 --- a/libs/pandares/src/main/res/values-th/strings.xml +++ b/libs/pandares/src/main/res/values-th/strings.xml @@ -1519,4 +1519,76 @@ เนื้อหาบทเรียนเพิ่มเติม ไม่สามารถอัพเดตลำดับการ์ดแผงข้อมูล + เผยแพร่หน่วยการเรียนทั้งหมดและรายการที่เกี่ยวข้อง + เผยแพร่เฉพาะหน่วยการเรียน + เลิกเผยแพร่หน่วยการเรียนทั้งหมดและรายการที่เกี่ยวข้อง + ตัวเลือกหน่วยการเรียน + เผยแพร่หน่วยการเรียนที่เกี่ยวข้องและรายการทั้งหมด + เผยแพร่เฉพาะหน่วยการเรียน + เลิกเผยแพร่หน่วยการเรียนที่เกี่ยวข้องและรายการทั้งหมด + ตัวเลือกหน่วยการเรียนสำหรับ %s + ตัวเลือกรายการในหน่วยการเรียนสำหรับ %s + เผยแพร่รายการในหน่วยการเรียน + เลิกเผยแพร่รายการในหน่วยการเรียน + เผยแพร่หรือไม่ + ขั้นตอนนี้จะทำให้หน่วยการเรียนเปิดแสดงเฉพาะกับผู้เรียนเท่านั้น + ขั้นตอนนี้จะทำให้หน่วยการเรียนที่เกี่ยวข้องและรายการทั้งหมดถูกแสดงผลให้แก่ผู้เรียน + เลิกเผยแพร่หรือไม่ + ขั้นตอนนี้จะทำให้หน่วยการเรียนที่เกี่ยวข้องและรายการทั้งหมดถูกปิดการแสดงผลสำหรับผู้เรียน + ขั้นตอนนี้จะทำให้รายการนี้ถูกแสดงผลให้แก่ผู้เรียนเท่านั้น + ขั้นตอนนี้จะทำให้รายการนี้ถูกปิดการแสดงผลสำหรับผู้เรียนเท่านั้น + ขั้นตอนนี้จะทำให้หน่วยการเรียนและรายการทั้งหมดถูกแสดงผลให้แก่ผู้เรียน + ขั้นตอนนี้จะทำให้หน่วยการเรียนเปิดแสดงเฉพาะกับผู้เรียนเท่านั้น + ขั้นตอนนี้จะทำให้หน่วยการเรียนและรายการทั้งหมดถูกปิดการแสดงผลสำหรับผู้เรียน + จัดไว้ให้พร้อมลิงค์เท่านั้น + นัดหมายเวลาที่พร้อม + เผยแพร่รายการแล้ว + เลิกเผยแพร่รายการแล้ว + เฉพาะหน่วยการเรียนที่มีการเผยแพร่ + เผยแพร่หน่วยการเรียนที่เกี่ยวข้องและรายการทั้งหมดแล้ว + เลิกเผยแพร่หน่วยการเรียนที่เกี่ยวข้องและรายการทั้งหมดแล้ว + เฉพาะหน่วยการเรียนที่มีการเผยแพร่ + เผยแพร่หน่วยการเรียนทั้งหมดและรายการทั้งหมดแล้ว + เลิกเผยแพร่หน่วยการเรียนทั้งหมดและรายการทั้งหมดแล้ว + รับช่วงจากบทเรียน (Course) + สมาชิกบทเรียน + สมาชิกของสถาบัน + สาธารณะ + แก้ไขสิทธิ์อนุญาต + อัพเดต + ความพร้อม + การแสดงผล + ใช้ได้จาก + ใช้ได้จนถึง + จาก + จนถึง + วันที่ + เวลา + ล้างวันที่ จาก + ล้างวันที่ ถึง + กระบวนการนี้อาจต้องใช้เวลาสักครู่ คุณสามารถปิดโมดัลหรือออกจากเพจได้ระหว่างกระบวนการนี้ + หมายเหตุ + หน่วยการเรียนทั้งหมด + หน่วยการเรียนทั้งหมดและรายการที่เกี่ยวข้อง + หน่วยการเรียนและรายการที่เลือก + หน่วยการเรียนที่เลือก + กำลังเผยแพร่ + กำลังเลิกเผยแพร่ + หน่วยการเรียนและรายการที่ดำเนินการไปแล้วจะไม่กลับคืนเป็นสถานะก่อนหน้าเมื่อมีการยุติการดำเนินการ + เสร็จสิ้น! + อัพเดตล้มเหลว + การอัพเดตถูกยกเลิก + ซ่อนไว้ + กำหนดเวลาแล้ว + เผยแพร่แล้ว + ไม่ได้เผยแพร่ + + %.0f คะแนน + %.0f คะแนน + + เผยแพร่ + เลิกเผยแพร่ + เผยแพร่ + เลิกเผยแพร่ + รีเฟรช diff --git a/libs/pandares/src/main/res/values-vi/strings.xml b/libs/pandares/src/main/res/values-vi/strings.xml index e73a2afbb2..bbf0bf4f13 100644 --- a/libs/pandares/src/main/res/values-vi/strings.xml +++ b/libs/pandares/src/main/res/values-vi/strings.xml @@ -1520,4 +1520,76 @@ Nội dung khóa học bổ sung Không thể cập nhật thứ tự thẻ Bảng Điều Khiển + Phát hành tất cả các Học Phần và Mục + Chỉ phát hành Các Học Phần + Hủy phát hành tất cả các Học Phần và Mục + Tùy Chọn Học Phần + Phát Hành Học Phần và tất cả Các Mục + Chỉ phát hành Học Phần + Hủy phát hành Học Phần và tất cả Các Mục + Tùy chọn học phần cho %s + Tùy chọn mục học phần cho %s + Phát Hành Mục Học Phần + Hủy Phát Hành Mục Học Phần + Phát hành? + Chức năng này sẽ chỉ hiển thị học phần cho sinh viên. + Chức năng này sẽ hiển thị học phần và tất cả các mục cho sinh viên. + Hủy phát hành? + Chức năng này sẽ ẩn học phần và tất cả các mục đối với sinh viên. + Chức năng này sẽ chỉ hiển thị mục này cho sinh viên. + Chức năng này sẽ chỉ ẩn mục này đối với sinh viên. + Chức năng này sẽ hiển thị tất cả các học phần và mục cho sinh viên. + Chức năng này sẽ chỉ hiển thị các học phần cho sinh viên. + Chức năng này sẽ ẩn tất cả các học phần và mục đối với sinh viên. + Chỉ khả dụng với liên kết + Đặt lịch độ khả dụng + Mục đã phát hành + Mục đã hủy phát hành + Riêng Học Phần đã phát hành + Học Phần và tất cả Các Mục đã phát hành + Học Phần và tất cả Các Mục đã hủy phát hành + Riêng Các Học Phần đã phát hành + Tất cả Các Học Phần và tất cả Các Mục đã phát hành + Tất cả Các Học Phần và tất cả Các Mục đã hủy phát hành + Kế thừa từ Khóa Học + Các Thành Viên Khóa Học + Các Thành Viên Của Tổ Chức + Công Khai + Quyền Chỉnh Sửa + Cập Nhật + Độ Khả Dụng + Hiển Thị + Khả Dụng Từ + Khả Dụng Đến + Từ + Đến + Ngày + Thời Gian + Xóa Từ Ngày + Xóa Đến Ngày + Quá trình này có thể mất vài phút. Bạn có thể đóng phương thức hoặc điều hướng khỏi trang trong quá trình này. + Ghi chú + Tất Cả Các Học Phần + Tất Cả Các Học Phần và Mục + Các Học Phần và Mục Đã Chọn + Các Học Phần Đã Chọn + Đang phát hành + Đang hủy phát hành + Các học phần và mục đã được xử lý sẽ không được hoàn nguyên về trạng thái trước đó khi ngừng quá trình. + Thành công! + Cập nhật không thành công + Đã hủy bỏ cập nhật + Ẩn + Đã Đặt Lịch + Đã phát hành + Chưa Phát Hành + + %.0f điểm + %.0f điểm + + Phát Hành + Hủy Phát Hành + Phát Hành + Hủy Phát Hành + Làm Mới diff --git a/libs/pandares/src/main/res/values-zh/strings.xml b/libs/pandares/src/main/res/values-zh/strings.xml index 0617c4530c..fd42ac2ee9 100644 --- a/libs/pandares/src/main/res/values-zh/strings.xml +++ b/libs/pandares/src/main/res/values-zh/strings.xml @@ -1495,4 +1495,75 @@ 更多课程内容 无法更新控制面板卡片顺序 + 发布所有模块和项目 + 仅发布模块 + 取消发布所有模块和项目 + 模块选项 + 发布模块和所有项目 + 仅发布模块 + 取消发布模块和所有项目 + %s 的模块选项 + %s 的模块项目选项 + 发布模块项目 + 取消发布模块项目 + 是否发布? + 这将仅使模块对学生可见。 + 这将使模块和所有项目对学生可见。 + 是否取消发布? + 这将使模块和所有项目对学生不可见。 + 这将仅使该项目对学生可见。 + 这将仅使该项目对学生不可见。 + 这将使所有模块和项目对学生可见。 + 这将仅使模块对学生可见。 + 这将使所有模块和项目对学生不可见。 + 仅提供链接 + 计划表可用性 + 已发布项目 + 已取消发布项目 + 已仅发布模块 + 已发布模块和所有项目 + 已取消发布模块和所有项目 + 已仅发布模块 + 已发布所有模块和所有项目 + 已取消发布所有模块和所有项目 + 从课程继承 + 课程成员 + 机构成员 + 公开 + 编辑权限 + 更新 + 可用性 + 可见性 + 开始日期 + 截止时间 + 开始日期 + 直至 + 日期 + 时间 + 清除开始日期 + 清除截止日期 + 该过程可能需要几分钟。在此期间,您可以关闭该模式或离开页面。 + + 所有模块 + 所有模块和项目 + 已选的模块和项目 + 已选的模块 + 正在发布 + 正在取消发布 + 停止处理后,已经处理的模块和项目不会退回到之前的状态。 + 成功! + 更新失败 + 更新已取消 + 隐藏 + 已计划 + 已发布 + 未发布 + + %.0f 分 + + 发布 + 取消发布 + 发布 + 取消发布 + 刷新 From cded309915d9220c56f33e8de64412b0f7f14b3b Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Thu, 25 Apr 2024 13:01:10 +0200 Subject: [PATCH 22/23] Version update. --- apps/teacher/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/teacher/build.gradle b/apps/teacher/build.gradle index d58c3fc40c..98611fae43 100644 --- a/apps/teacher/build.gradle +++ b/apps/teacher/build.gradle @@ -39,8 +39,8 @@ android { defaultConfig { minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 64 - versionName = '1.29.0' + versionCode = 65 + versionName = '1.30.0' vectorDrawables.useSupportLibrary = true multiDexEnabled true testInstrumentationRunner 'com.instructure.teacher.ui.espresso.TeacherHiltTestRunner' From b4cf2297a3c4d2497153ecdb2afd16218aeec99f Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:36:08 +0200 Subject: [PATCH 23/23] [MBL-17526][Teacher] EditAssignmentDetails crash #2428 refs: MBL-17526 affects: Teacher release note: none --- .../fragments/EditAssignmentDetailsFragment.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/EditAssignmentDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/EditAssignmentDetailsFragment.kt index 89e9c31a94..0ffade26c5 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/EditAssignmentDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/EditAssignmentDetailsFragment.kt @@ -362,8 +362,18 @@ class EditAssignmentDetailsFragment : BaseFragment() { if (dueDateGroup.isEveryone) { assignees += getString(if (editDateGroups.any { it.hasOverrideAssignees }) R.string.everyone_else else R.string.everyone) } - dueDateGroup.groupIds.forEach { assignees.add(groupsMapped[it]?.name!!) } - dueDateGroup.sectionIds.forEach { assignees.add(sectionsMapped[it]?.name!!) } + dueDateGroup.groupIds.forEach { + val group = groupsMapped[it] + if (group?.name != null) { + assignees.add(group.name!!) + } + } + dueDateGroup.sectionIds.forEach { + val section = sectionsMapped[it] + if (section != null) { + assignees.add(section.name) + } + } dueDateGroup.studentIds.forEach { val student = studentsMapped[it] if (student != null) {