From edfb3e230bdad6a6816da2523f7a8fabb8a3f93a Mon Sep 17 00:00:00 2001 From: Muhammad Haseeb Date: Wed, 20 Jan 2021 17:42:28 +0500 Subject: [PATCH] EDLY-2438 Upgrade to junipper (#7) * Update translations * Update translations * Update edx-credentials-themes to the bootstrap version * Update to DOT instead of DOP for OAuth2 Authentication * After this commit, credentials no longer depends on OpenID Connect. * Split OAuth2 credentials into two different sets: 1. "SSO" for user auth (authorization grant). 2. "backend-service" for server-to-server auth (client credentials grant). * Bump auth-backends to 1.2.2 for DOT support. * Deprecate credentials.apps.api.authentication.BearerAuthentication For hackathon J-N-T (Julia-Nimisha-Troy). * Remove usages of OAUTH2_PROVIDER_URL, add BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL Replace with either of these alternatives, depending on how its used: * BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL * SOCIAL_AUTH_EDX_OAUTH2_URL_ROOT * Updated Credentials-theme package version Updated credentials-themes version in base.txt * LEARNER-7043: Encode filename to download record * Remove PUBLIC_URL default from base - should only be set in devstack. * Update translations * fix(i18n): update translations * Add username replacement API New API replaces all copies of username in this service with a new username. Requires user be in a username_replacement_admin group. Should only be ran as a larger job to update usernames across all services, otherwise the system will be left in a broken state for those users. * fix(i18n): update translations * send emails for program if records_enabled is true send_updated_emails_for_program() should be called only if the site configurations has the value for records_enabled set to true. PROD-189 * Revert "send emails for program if records_enabled is true" This reverts commit 3d26cc05e9560caff5fa90a3d7ee6f8b24823622. * Use the site configuration associated with the ProgramCredentials instead * Delete AUTHORS * fix(i18n): update translations * fix(i18n): update translations * Disable share and send record button on onClick Due to a known issue in get_or_create() method which is prone to a race-condition which can result in multiple rows with the same parameters being inserted simultaneously, added a unique_together constraint and disabled button after onClick event to avoid concurrent requests. PROD-197 * Update openedx.yaml * Add masters-neem as a supporting team * Add a tag. * Upgrade DRF to 3.9.4 * unpin django * Move credentials defaults from configration repo (#656) * Pin django to 1.11.22 * fix(i18n): update translations * fix(i18n): update translations * Updating credentials-themes to 0.1.29 (#661) * fix(i18n): update translations * Fix program certificate print view. Program certificate print preview is loading in vertical instead of horizontal like course certificate. PROD-492 * Upgrade Django to 1.11.23 Fixes four security vulnerabilities. EDUCATOR-4551 * Fix program record url in credit request email. Building the program record URL incorrectly by concatenating site domain and record path. Fixed by using request.build_absolute_uri(). PROD-564 * Add make update to credentials (#668) * add make upgrade for oep-18 compliance, unpin dependencies in .in files * Update openedx.yaml for Masters squad reorg * Updating Python Requirements (#671) * Field names updated for the search field attributes. * Changed from_address while sending email to partner previously we were using learner-records@edx.org in from address. Now we will be using learner eamil address so that partner can contact with learner. Prod-624 * fix(i18n): update translations * Enabled admin search for uuid Prod-706 * Updating Python Requirements * fix(i18n): update translations * Invalid UUID error in credentials api Learner-5999 Problem: Giving invalid uuid in credentials api raises a validation exception. Solution: Added an except clause to return empty list in case of invalid uuid instead of raising an exception. Test checks if invalid uuid returns an empty list instead of raising validation error or not. * Updating Python Requirements * Revert "Changed from_address while sending email to partner" This reverts commit 19e4252d71471328465e741e4c73ae89115917f6. * Add the admin section for the Catalog Cache * Updating Python Requirements * Add better admin table data for search and viewing * Restriction on the version of MySql that we use for testing (#689) [PROD-830] This is being added to deal with a failure in tests running with the current version. * learner email added in reply_field PROD-624 * Updating Python Requirements * Rename start and end fields (1/4) This is the first stage of renaming the start and end fields of CourseRun to start_date and end_date. This release ONLY adds the new column, no code changes. DE-1708 * Rename start and end fields (2/4) This is the second stage of renaming the start and end fields of CourseRun to start_date and end_date. This release updates the code to make writes additionally go into the new column (preserving writes to the old column, still). DE-1708 * Rename start and end fields (2.5/4) This is the second-and-a-halfth stage of renaming the start and end fields of CourseRun to start_date and end_date. This release includes a data migration which copies all values from old into new field. DE-1708 * Rename start and end fields (3/4) (#695) This is the third stage of renaming the start and end fields of CourseRun to start_date and end_date. This release updates the code to make writes ONLY go into the new column, and removes references to the old column. DE-1708 * Rename start and end fields (4/4) (#696) This is the fourth stage of renaming the start and end fields of CourseRun to start_date and end_date. This release ONLY removes the old columns which, by now, should not be referenced in any code. DE-1708 * Revert "Rename start and end fields (4/4) (#696)" This reverts commit da001b165b5e75ea83de37005164d461b61f9d70. * Rename start and end fields (4.1/4) This is the 4.1th stage of renaming the start and end fields of CourseRun to start_date and end_date. This release ONLY removes the django model fields corresponding to the old columns. Note that this does not include the migration to remove the columns. DE-1708 * upgrade edx-drf-extensions to 2.4.5 - Upgrade edx-drf-extensions to 2.4.5 - Remove unused JWT_AUTH_REFRESH_COOKIE setting. ARCH-418, ARCH-1269, ARCH-1044 * Rename start and end fields (4.2/4) This is the 4.2th stage of renaming the start and end fields of CourseRun to start_date and end_date. This release ONLY removes the old columns via migration. Note that this does not include removing the django model fields corresponding to the old columns. DE-1708 * fix(i18n): update translations * Added devstack config yml file to app repo (#700) * Added devstack config yml file to app repo * Added config yml file to app repo * Updating Python Requirements * fix(i18n): update translations * fix(i18n): update translations * Updating Python Requirements * test path problem * fix(i18n): update translations * fix(i18n): update translations * make upgrade Removing these constraints appears to get us moving forward again without conflicts, which nominally was the only reason they were constrained. * Grades on learner record are not consistent with progress page. Credentials service is not rounding the user grade in a particular course while displaying it on the learner record.This makes an inconsistent behaviour compared to support tool as well as progress page.To fix it, learner grade are rounded properly so that a consistent grade would appear on all views. PROD-1051 * Updating Python Requirements * Address an issue that was preventing make upgrade It seems like there was a pip/piptools version incompatibility which was preventing make upgrade from even getting started * Updating Python Requirements * fix(i18n): update translations * Django 2.2 Upgrade via codemods (#723) * Move devstack credentials.yml t configuration repo (#724) * Move default variables from config repo (#725) * Updating Python Requirements * fix(i18n): update translations * Remove oidc settings. * Update credentials themes to include MB certs Upgrading requirements to bump credentials theme to install the new MicroBachelors programs cert templates. * replacing django-ratelimit to the version that supports django22 (#732) * BOM-1260 -Upgrade social-auth-app-django to support django22 * upgrade edx-lint and fixed quality (#733) * removed the constraint for edx-ace (#735) * BOM-1257 (#731) Remove the django-filter constraint. * BOM-1281 (#739) Fixing the tox. * BOM-1283 (#740) Fixing m2m warnings. * Removing use of BearerAuthentication (#741) BearerAuthentication is deprecated and is not currently used by system. * BOM-1281 : Implement Tox in credentials (#742) * use tox for running tests * changes in tox.ini * revert package-lock * correct indentation * reset some changes * test * removing jasmine from tox * all requirements * Removed js from tox.ini * removed setup.py Co-authored-by: Ayub * Bump codecov from 2.3.1 to 3.6.5 Bumps [codecov](https://github.com/codecov/codecov-node) from 2.3.1 to 3.6.5. - [Release notes](https://github.com/codecov/codecov-node/releases) - [Commits](https://github.com/codecov/codecov-node/commits) Signed-off-by: dependabot[bot] * fix(i18n): update translations * added django 2.x workers to travis (#748) * BOM-1346 (#746) Fixing permissions and tests issues. * Updating Python Requirements (#752) * BOM-1045 - edx-drf-extensions - fixed tests - preserves admin group behavior while using edx-drf-extensions instead. * Pin edx-ace because it causes test issues. * Allow edx-ace to be upgraded by unpinning attrs (#756) * BOM-1407 (#757) upgrade django2.2 * Updating Python Requirements (#758) * run keyword linter in CI (#759) * run linter on all systems (#760) * fix(i18n): update translations * Downgrade social-auth-core to work around permission loss JIRA:ARCHBOM-1078 * fix(i18n): update translations * Removed Django version < 2.2 * Updating Python Requirements * Replace deprecated static loading (#766) * Bump eslint from 3.19.0 to 4.18.2 (#750) Bumps [eslint](https://github.com/eslint/eslint) from 3.19.0 to 4.18.2. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v3.19.0...v4.18.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump axios from 0.18.0 to 0.18.1 (#749) Bumps [axios](https://github.com/axios/axios) from 0.18.0 to 0.18.1. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v0.18.1/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v0.18.0...v0.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Address EDUCATOR-4716: Backend (#769) * Updating Python Requirements * Import private.py in devstack.py * Added Python 3.8 for testing (#772) * Added data migration PROD-1443 * Updating Python Requirements * Switch from pyinotify to pywatchman for filemonitoring. pyinotify was causing performance issues with django 2.2 in development environments. * fix(i18n): update translations * Updating Python Requirements (#778) * Updating Python Requirements (#779) Added Nose for missing testing dependency * Updating Python Requirements (#780) * Updating Python Requirements * Upgrade Django to 2.2.13 * Resolved conflicts and fixed deprecations EDLY-2438 * Removed unnessary migrations EDLY-2438 * Added migrations with proper naming EDLY-2438 Co-authored-by: edX Transifex Bot Co-authored-by: Michael Terry Co-authored-by: Nimisha Asthagiri Co-authored-by: Troy Sankey Co-authored-by: Usama Co-authored-by: Zainab Amir Co-authored-by: Julia Eskew Co-authored-by: Matt Tuchfarber Co-authored-by: Zainab Amir Co-authored-by: Simon Chen Co-authored-by: Brandon Baker Co-authored-by: Kyle McCormick Co-authored-by: Feanil Patel Co-authored-by: Alex Dusenbery Co-authored-by: jansenk Co-authored-by: syedimranhassan <45480841+syedimranhassan@users.noreply.github.com> Co-authored-by: Albert (AJ) St. Aubin Co-authored-by: Waheed Ahmed Co-authored-by: edX requirements bot <49161187+edx-requirements-bot@users.noreply.github.com> Co-authored-by: usama sadiq Co-authored-by: adeelehsan Co-authored-by: edX requirements bot Co-authored-by: Troy Sankey Co-authored-by: Robert Raposa Co-authored-by: Matt Hughes Co-authored-by: uzairr Co-authored-by: M Zulqarnain Co-authored-by: Diana Huang Co-authored-by: Aarif Co-authored-by: Ayub-khan Co-authored-by: Muhammad Soban Javed <58461728+iamsobanjaved@users.noreply.github.com> Co-authored-by: Awais Qureshi Co-authored-by: Manjinder Singh <49171515+jinder1s@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Stu Young Co-authored-by: usamasadiq Co-authored-by: Hassan Tariq Co-authored-by: Ned Batchelder --- .gitignore | 1 + .travis.yml | 52 +- AUTHORS | 17 - Makefile | 72 +- README.rst | 17 + acceptance_tests/mixins.py | 2 +- credentials/apps/api/accreditors.py | 13 +- credentials/apps/api/authentication.py | 38 +- credentials/apps/api/exceptions.py | 2 - credentials/apps/api/permissions.py | 7 +- credentials/apps/api/tests/mixins.py | 8 +- .../apps/api/tests/test_accreditors.py | 9 +- .../apps/api/tests/test_authentication.py | 33 +- credentials/apps/api/urls.py | 2 +- credentials/apps/api/v2/filters.py | 19 +- credentials/apps/api/v2/permissions.py | 9 + credentials/apps/api/v2/serializers.py | 11 +- .../apps/api/v2/tests/test_serializers.py | 6 +- credentials/apps/api/v2/tests/test_views.py | 96 +- credentials/apps/api/v2/urls.py | 12 +- credentials/apps/api/v2/views.py | 151 ++- credentials/apps/catalog/admin.py | 35 + ...0011_add_new_start_date_end_date_fields.py | 25 + .../0012_courserun_copy_column_values.py | 41 + .../0013_drop_old_start_end_fields.py | 23 + credentials/apps/catalog/models.py | 6 +- credentials/apps/catalog/tests/factories.py | 14 +- .../apps/catalog/tests/test_commands.py | 13 +- credentials/apps/catalog/tests/test_utils.py | 4 +- credentials/apps/catalog/utils.py | 10 +- credentials/apps/core/apps.py | 2 +- credentials/apps/core/constants.py | 4 +- .../apps/core/migrations/0001_initial.py | 2 +- .../migrations/0003_auto_20160331_0218.py | 17 +- .../apps/core/migrations/0022_last_name.py | 18 + credentials/apps/core/models.py | 16 +- credentials/apps/core/tests/factories.py | 6 +- credentials/apps/core/tests/mixins.py | 6 +- credentials/apps/core/tests/test_commands.py | 7 +- credentials/apps/core/tests/test_views.py | 3 +- credentials/apps/core/utils.py | 2 +- credentials/apps/core/views.py | 2 +- credentials/apps/credentials/admin.py | 9 +- credentials/apps/credentials/constants.py | 4 +- credentials/apps/credentials/exceptions.py | 1 - credentials/apps/credentials/issuers.py | 25 +- .../credentials/migrations/0001_initial.py | 12 +- .../0016_credential_content_type.py | 19 + credentials/apps/credentials/models.py | 23 +- .../apps/credentials/tests/factories.py | 10 +- .../apps/credentials/tests/test_forms.py | 2 +- .../apps/credentials/tests/test_issuer.py | 28 +- .../apps/credentials/tests/test_views.py | 1 - .../static/sass/_print-rendering.scss | 149 ++- .../professional-certificate/certificate.html | 2 +- .../edly_credentials_app/api/v1/urls.py | 2 +- .../edly_credentials_app/middleware.py | 5 +- .../edx_django_extensions/tests/test_views.py | 12 +- credentials/apps/records/admin.py | 2 +- credentials/apps/records/constants.py | 2 +- .../management/commands/seed-records.py | 7 +- credentials/apps/records/messages.py | 11 +- ...017_ProgramCertRecord_unique_constraint.py | 22 + .../0018_change_learners_course_run.py | 66 + credentials/apps/records/models.py | 20 +- credentials/apps/records/tests/factories.py | 6 +- credentials/apps/records/tests/test_utils.py | 13 +- credentials/apps/records/tests/test_views.py | 45 +- credentials/apps/records/utils.py | 6 +- credentials/apps/records/views.py | 42 +- .../conf/locale/ar/LC_MESSAGES/django.mo | Bin 5163 -> 5122 bytes .../conf/locale/ar/LC_MESSAGES/django.po | 2 +- .../conf/locale/ar/LC_MESSAGES/djangojs.mo | Bin 559 -> 542 bytes .../conf/locale/ar/LC_MESSAGES/djangojs.po | 5 +- .../conf/locale/bg_BG/LC_MESSAGES/django.mo | Bin 528 -> 487 bytes .../conf/locale/bg_BG/LC_MESSAGES/djangojs.mo | Bin 486 -> 445 bytes .../conf/locale/bn_BD/LC_MESSAGES/django.mo | Bin 531 -> 476 bytes .../conf/locale/bn_BD/LC_MESSAGES/django.po | 3 +- .../conf/locale/bn_BD/LC_MESSAGES/djangojs.mo | Bin 541 -> 476 bytes .../conf/locale/bn_BD/LC_MESSAGES/djangojs.po | 16 +- .../conf/locale/bn_IN/LC_MESSAGES/django.mo | Bin 481 -> 440 bytes .../conf/locale/bn_IN/LC_MESSAGES/djangojs.mo | Bin 481 -> 440 bytes .../conf/locale/bs/LC_MESSAGES/django.mo | Bin 580 -> 539 bytes .../conf/locale/bs/LC_MESSAGES/djangojs.mo | Bin 541 -> 500 bytes .../conf/locale/ca/LC_MESSAGES/django.mo | Bin 9589 -> 9548 bytes .../conf/locale/ca/LC_MESSAGES/djangojs.mo | Bin 7027 -> 6986 bytes .../conf/locale/cs/LC_MESSAGES/django.mo | Bin 580 -> 539 bytes .../conf/locale/cs/LC_MESSAGES/djangojs.mo | Bin 583 -> 542 bytes .../conf/locale/cy/LC_MESSAGES/django.mo | Bin 510 -> 469 bytes .../conf/locale/cy/LC_MESSAGES/djangojs.mo | Bin 510 -> 469 bytes .../conf/locale/da/LC_MESSAGES/django.mo | Bin 504 -> 463 bytes .../conf/locale/da/LC_MESSAGES/djangojs.mo | Bin 508 -> 467 bytes .../conf/locale/de_DE/LC_MESSAGES/django.mo | Bin 9206 -> 11803 bytes .../conf/locale/de_DE/LC_MESSAGES/django.po | 41 +- .../conf/locale/de_DE/LC_MESSAGES/djangojs.mo | Bin 4144 -> 7057 bytes .../conf/locale/de_DE/LC_MESSAGES/djangojs.po | 44 +- .../conf/locale/el/LC_MESSAGES/django.mo | Bin 642 -> 601 bytes .../conf/locale/el/LC_MESSAGES/djangojs.mo | Bin 511 -> 470 bytes .../conf/locale/en/LC_MESSAGES/django.mo | Bin 466 -> 425 bytes .../conf/locale/en/LC_MESSAGES/djangojs.mo | Bin 466 -> 425 bytes .../conf/locale/eo/LC_MESSAGES/django.mo | Bin 19307 -> 19266 bytes .../conf/locale/eo/LC_MESSAGES/djangojs.mo | Bin 11815 -> 11774 bytes .../conf/locale/es_419/LC_MESSAGES/django.mo | Bin 11612 -> 11559 bytes .../conf/locale/es_419/LC_MESSAGES/django.po | 8 +- .../locale/es_419/LC_MESSAGES/djangojs.mo | Bin 6958 -> 6991 bytes .../locale/es_419/LC_MESSAGES/djangojs.po | 9 +- .../conf/locale/es_AR/LC_MESSAGES/django.mo | Bin 528 -> 487 bytes .../conf/locale/es_AR/LC_MESSAGES/djangojs.mo | Bin 485 -> 444 bytes .../conf/locale/es_EC/LC_MESSAGES/django.mo | Bin 527 -> 486 bytes .../conf/locale/es_EC/LC_MESSAGES/djangojs.mo | Bin 527 -> 486 bytes .../conf/locale/es_ES/LC_MESSAGES/django.mo | Bin 530 -> 489 bytes .../conf/locale/es_ES/LC_MESSAGES/django.po | 2 +- .../conf/locale/es_ES/LC_MESSAGES/djangojs.mo | Bin 530 -> 473 bytes .../conf/locale/es_ES/LC_MESSAGES/djangojs.po | 6 +- .../conf/locale/es_MX/LC_MESSAGES/django.mo | Bin 529 -> 488 bytes .../conf/locale/es_MX/LC_MESSAGES/djangojs.mo | Bin 512 -> 471 bytes .../conf/locale/es_PE/LC_MESSAGES/django.mo | Bin 540 -> 499 bytes .../conf/locale/es_PE/LC_MESSAGES/djangojs.mo | Bin 480 -> 439 bytes .../conf/locale/et_EE/LC_MESSAGES/django.mo | Bin 513 -> 472 bytes .../conf/locale/et_EE/LC_MESSAGES/djangojs.mo | Bin 484 -> 443 bytes .../conf/locale/eu_ES/LC_MESSAGES/django.mo | Bin 2033 -> 4344 bytes .../conf/locale/eu_ES/LC_MESSAGES/django.po | 72 +- .../conf/locale/eu_ES/LC_MESSAGES/djangojs.mo | Bin 514 -> 2593 bytes .../conf/locale/eu_ES/LC_MESSAGES/djangojs.po | 74 +- .../conf/locale/fa_IR/LC_MESSAGES/django.mo | Bin 487 -> 446 bytes .../conf/locale/fa_IR/LC_MESSAGES/djangojs.mo | Bin 520 -> 479 bytes .../conf/locale/fi_FI/LC_MESSAGES/django.mo | Bin 497 -> 480 bytes .../conf/locale/fi_FI/LC_MESSAGES/django.po | 4 +- .../conf/locale/fi_FI/LC_MESSAGES/djangojs.mo | Bin 525 -> 484 bytes .../conf/locale/fr/LC_MESSAGES/django.mo | Bin 2815 -> 8281 bytes .../conf/locale/fr/LC_MESSAGES/django.po | 119 +- .../conf/locale/fr/LC_MESSAGES/djangojs.mo | Bin 781 -> 813 bytes .../conf/locale/fr/LC_MESSAGES/djangojs.po | 5 +- .../conf/locale/fr_CA/LC_MESSAGES/django.mo | Bin 11429 -> 11555 bytes .../conf/locale/fr_CA/LC_MESSAGES/django.po | 2 + .../conf/locale/fr_CA/LC_MESSAGES/djangojs.mo | Bin 7100 -> 7059 bytes .../conf/locale/gl/LC_MESSAGES/django.mo | Bin 525 -> 484 bytes .../conf/locale/gl/LC_MESSAGES/djangojs.mo | Bin 495 -> 454 bytes .../conf/locale/gu/LC_MESSAGES/django.mo | Bin 479 -> 438 bytes .../conf/locale/gu/LC_MESSAGES/djangojs.mo | Bin 468 -> 427 bytes .../conf/locale/he/LC_MESSAGES/django.mo | Bin 7367 -> 7326 bytes .../conf/locale/he/LC_MESSAGES/django.po | 2 +- .../conf/locale/he/LC_MESSAGES/djangojs.mo | Bin 594 -> 553 bytes .../conf/locale/hi/LC_MESSAGES/django.mo | Bin 486 -> 472 bytes .../conf/locale/hi/LC_MESSAGES/django.po | 153 ++- .../conf/locale/hi/LC_MESSAGES/djangojs.mo | Bin 506 -> 472 bytes .../conf/locale/hi/LC_MESSAGES/djangojs.po | 280 +++- .../conf/locale/hr/LC_MESSAGES/django.mo | Bin 559 -> 518 bytes .../conf/locale/hr/LC_MESSAGES/djangojs.mo | Bin 573 -> 532 bytes .../conf/locale/hu/LC_MESSAGES/django.mo | Bin 505 -> 464 bytes .../conf/locale/hu/LC_MESSAGES/djangojs.mo | Bin 505 -> 464 bytes .../conf/locale/hy_AM/LC_MESSAGES/django.mo | Bin 517 -> 476 bytes .../conf/locale/hy_AM/LC_MESSAGES/djangojs.mo | Bin 529 -> 488 bytes .../conf/locale/id/LC_MESSAGES/django.mo | Bin 507 -> 10825 bytes .../conf/locale/id/LC_MESSAGES/django.po | 213 +-- .../conf/locale/id/LC_MESSAGES/djangojs.mo | Bin 518 -> 6671 bytes .../conf/locale/id/LC_MESSAGES/djangojs.po | 149 ++- .../conf/locale/it_IT/LC_MESSAGES/django.mo | Bin 530 -> 467 bytes .../conf/locale/it_IT/LC_MESSAGES/django.po | 150 ++- .../conf/locale/it_IT/LC_MESSAGES/djangojs.mo | Bin 494 -> 453 bytes .../conf/locale/ja_JP/LC_MESSAGES/django.mo | Bin 7369 -> 7328 bytes .../conf/locale/ja_JP/LC_MESSAGES/djangojs.mo | Bin 515 -> 474 bytes .../conf/locale/ka/LC_MESSAGES/django.mo | Bin 4513 -> 4472 bytes .../conf/locale/ka/LC_MESSAGES/djangojs.mo | Bin 485 -> 444 bytes .../conf/locale/kk_KZ/LC_MESSAGES/django.mo | Bin 523 -> 482 bytes .../conf/locale/kk_KZ/LC_MESSAGES/djangojs.mo | Bin 512 -> 471 bytes .../conf/locale/km_KH/LC_MESSAGES/django.mo | Bin 475 -> 434 bytes .../conf/locale/km_KH/LC_MESSAGES/djangojs.mo | Bin 475 -> 434 bytes .../conf/locale/ko_KR/LC_MESSAGES/django.mo | Bin 501 -> 460 bytes .../conf/locale/ko_KR/LC_MESSAGES/djangojs.mo | Bin 501 -> 460 bytes .../conf/locale/lt_LT/LC_MESSAGES/django.mo | Bin 648 -> 607 bytes .../conf/locale/lt_LT/LC_MESSAGES/djangojs.mo | Bin 653 -> 612 bytes .../conf/locale/ml/LC_MESSAGES/django.mo | Bin 484 -> 443 bytes .../conf/locale/ml/LC_MESSAGES/djangojs.mo | Bin 469 -> 428 bytes .../conf/locale/mn/LC_MESSAGES/django.mo | Bin 634 -> 991 bytes .../conf/locale/mn/LC_MESSAGES/django.po | 6 +- .../conf/locale/mn/LC_MESSAGES/djangojs.mo | Bin 593 -> 552 bytes .../conf/locale/ms/LC_MESSAGES/django.mo | Bin 494 -> 453 bytes .../conf/locale/ms/LC_MESSAGES/djangojs.mo | Bin 494 -> 453 bytes .../conf/locale/nb/LC_MESSAGES/django.mo | Bin 4770 -> 4726 bytes .../conf/locale/nb/LC_MESSAGES/django.po | 3 +- .../conf/locale/nb/LC_MESSAGES/djangojs.mo | Bin 514 -> 473 bytes .../conf/locale/nl_NL/LC_MESSAGES/django.mo | Bin 532 -> 491 bytes .../conf/locale/nl_NL/LC_MESSAGES/djangojs.mo | Bin 517 -> 476 bytes .../conf/locale/pl/LC_MESSAGES/django.mo | Bin 8367 -> 8326 bytes .../conf/locale/pl/LC_MESSAGES/djangojs.mo | Bin 655 -> 614 bytes .../conf/locale/pt_BR/LC_MESSAGES/django.mo | Bin 719 -> 678 bytes .../conf/locale/pt_BR/LC_MESSAGES/djangojs.mo | Bin 514 -> 473 bytes .../conf/locale/pt_PT/LC_MESSAGES/django.mo | Bin 527 -> 555 bytes .../conf/locale/pt_PT/LC_MESSAGES/django.po | 10 +- .../conf/locale/pt_PT/LC_MESSAGES/djangojs.mo | Bin 808 -> 776 bytes .../conf/locale/pt_PT/LC_MESSAGES/djangojs.po | 8 +- .../conf/locale/ro/LC_MESSAGES/django.mo | Bin 525 -> 483 bytes .../conf/locale/ro/LC_MESSAGES/django.po | 4 +- .../conf/locale/ro/LC_MESSAGES/djangojs.mo | Bin 549 -> 508 bytes .../conf/locale/rtl/LC_MESSAGES/django.mo | Bin 12998 -> 12957 bytes .../conf/locale/rtl/LC_MESSAGES/djangojs.mo | Bin 7744 -> 7703 bytes .../conf/locale/ru/LC_MESSAGES/django.mo | Bin 3473 -> 14823 bytes .../conf/locale/ru/LC_MESSAGES/django.po | 147 +- .../conf/locale/ru/LC_MESSAGES/djangojs.mo | Bin 1013 -> 9080 bytes .../conf/locale/ru/LC_MESSAGES/djangojs.po | 124 +- .../conf/locale/sk/LC_MESSAGES/django.mo | Bin 590 -> 549 bytes .../conf/locale/sk/LC_MESSAGES/djangojs.mo | Bin 541 -> 500 bytes .../conf/locale/sl/LC_MESSAGES/django.mo | Bin 555 -> 514 bytes .../conf/locale/sl/LC_MESSAGES/djangojs.mo | Bin 562 -> 521 bytes .../conf/locale/sq/LC_MESSAGES/django.mo | Bin 503 -> 462 bytes .../conf/locale/sq/LC_MESSAGES/djangojs.mo | Bin 503 -> 462 bytes .../conf/locale/sr/LC_MESSAGES/django.mo | Bin 574 -> 533 bytes .../conf/locale/sr/LC_MESSAGES/djangojs.mo | Bin 586 -> 545 bytes .../conf/locale/sw_KE/LC_MESSAGES/django.mo | Bin 4915 -> 5195 bytes .../conf/locale/sw_KE/LC_MESSAGES/django.po | 14 +- .../conf/locale/sw_KE/LC_MESSAGES/djangojs.mo | Bin 523 -> 2143 bytes .../conf/locale/sw_KE/LC_MESSAGES/djangojs.po | 49 +- .../conf/locale/ta/LC_MESSAGES/django.mo | Bin 501 -> 460 bytes .../conf/locale/ta/LC_MESSAGES/djangojs.mo | Bin 465 -> 424 bytes .../conf/locale/te/LC_MESSAGES/django.mo | Bin 527 -> 486 bytes .../conf/locale/te/LC_MESSAGES/djangojs.mo | Bin 466 -> 425 bytes .../conf/locale/th/LC_MESSAGES/django.mo | Bin 498 -> 450 bytes .../conf/locale/th/LC_MESSAGES/django.po | 4 +- .../conf/locale/th/LC_MESSAGES/djangojs.mo | Bin 505 -> 464 bytes .../conf/locale/tr_TR/LC_MESSAGES/django.mo | Bin 8652 -> 8738 bytes .../conf/locale/tr_TR/LC_MESSAGES/django.po | 6 +- .../conf/locale/tr_TR/LC_MESSAGES/djangojs.mo | Bin 3669 -> 3816 bytes .../conf/locale/tr_TR/LC_MESSAGES/djangojs.po | 8 +- .../conf/locale/uk/LC_MESSAGES/django.mo | Bin 1590 -> 1549 bytes .../conf/locale/uk/LC_MESSAGES/djangojs.mo | Bin 725 -> 684 bytes .../conf/locale/ur/LC_MESSAGES/django.mo | Bin 518 -> 477 bytes .../conf/locale/ur/LC_MESSAGES/djangojs.mo | Bin 464 -> 423 bytes .../conf/locale/vi/LC_MESSAGES/django.mo | Bin 497 -> 455 bytes .../conf/locale/vi/LC_MESSAGES/django.po | 3 +- .../conf/locale/vi/LC_MESSAGES/djangojs.mo | Bin 502 -> 461 bytes .../conf/locale/zh_CN/LC_MESSAGES/django.mo | Bin 10350 -> 10303 bytes .../conf/locale/zh_CN/LC_MESSAGES/django.po | 5 +- .../conf/locale/zh_CN/LC_MESSAGES/djangojs.mo | Bin 6388 -> 6335 bytes .../conf/locale/zh_CN/LC_MESSAGES/djangojs.po | 9 +- .../conf/locale/zh_HK/LC_MESSAGES/django.mo | Bin 492 -> 451 bytes .../conf/locale/zh_HK/LC_MESSAGES/djangojs.mo | Bin 514 -> 473 bytes .../conf/locale/zh_TW/LC_MESSAGES/django.mo | Bin 1108 -> 1067 bytes .../conf/locale/zh_TW/LC_MESSAGES/djangojs.mo | Bin 515 -> 474 bytes credentials/settings/base.py | 72 +- credentials/settings/devstack.py | 46 +- credentials/settings/local.py | 19 +- credentials/settings/private.py.example | 9 +- credentials/settings/production.py | 2 +- credentials/settings/test.py | 16 +- .../static/components/ProgramRecord.jsx | 21 +- credentials/templates/404.html | 2 +- credentials/templates/_footer.html | 2 +- credentials/templates/_header.html | 2 +- credentials/urls.py | 20 +- db_keyword_overrides.yml | 13 + docker-compose.yml | 2 +- docs/credentials_admin.rst | 4 +- docs/getting_started.rst | 47 +- openedx.yaml | 10 +- package-lock.json | 1192 ++++++++++------- package.json | 6 +- pylintrc | 34 +- requirements/all.in | 10 + requirements/all.txt | 153 +++ requirements/base.in | 50 + requirements/base.txt | 113 +- requirements/constraints.txt | 33 + requirements/dev.in | 19 + requirements/dev.txt | 148 ++ requirements/django.txt | 1 + requirements/docs.in | 13 + requirements/docs.txt | 36 +- requirements/local.txt | 15 - requirements/pip_tools.in | 3 + requirements/pip_tools.txt | 12 + requirements/production.in | 19 + requirements/production.txt | 99 +- requirements/test.in | 31 + requirements/test.txt | 138 +- tox.ini | 17 + 276 files changed, 4027 insertions(+), 1540 deletions(-) delete mode 100644 AUTHORS create mode 100644 credentials/apps/catalog/admin.py create mode 100644 credentials/apps/catalog/migrations/0011_add_new_start_date_end_date_fields.py create mode 100644 credentials/apps/catalog/migrations/0012_courserun_copy_column_values.py create mode 100644 credentials/apps/catalog/migrations/0013_drop_old_start_end_fields.py create mode 100644 credentials/apps/core/migrations/0022_last_name.py create mode 100644 credentials/apps/credentials/migrations/0016_credential_content_type.py create mode 100644 credentials/apps/records/migrations/0017_ProgramCertRecord_unique_constraint.py create mode 100644 credentials/apps/records/migrations/0018_change_learners_course_run.py create mode 100644 db_keyword_overrides.yml create mode 100644 requirements/all.in create mode 100644 requirements/all.txt create mode 100644 requirements/base.in create mode 100644 requirements/constraints.txt create mode 100644 requirements/dev.in create mode 100644 requirements/dev.txt create mode 100644 requirements/django.txt create mode 100644 requirements/docs.in delete mode 100644 requirements/local.txt create mode 100644 requirements/pip_tools.in create mode 100644 requirements/pip_tools.txt create mode 100644 requirements/production.in create mode 100644 requirements/test.in create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index d4aef892f..b28ad3428 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ coverage.xml diff_*.html *.report report +reports venv # auth.py holds an autogenerated token acceptance_tests/auth.py diff --git a/.travis.yml b/.travis.yml index cc70407f4..9e36b80e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,5 @@ language: python -python: - - "3.5" - branches: only: - master @@ -25,18 +22,27 @@ before_install: matrix: include: - - env: TESTNAME=quality-and-translations - - # Doing so is a waste of time since they won't be used. + - python: 3.5 + env: TESTNAME=quality-and-translations install: true - script: - make exec-requirements - make exec-check_translations_up_to_date - make exec-validate-translations - make exec-quality + - make exec-check_keywords + - python: 3.8 + env: TESTNAME=quality-and-translations + install: true + script: + - make exec-requirements + - make exec-check_translations_up_to_date + - make exec-validate-translations + - make exec-quality + - make exec-check_keywords - - env: TESTNAME=acceptance-tests + - python: 3.5 + env: TESTNAME=acceptance-tests install: - make exec-requirements script: @@ -44,18 +50,32 @@ matrix: - make exec-accept addons: firefox: latest + - python: 3.8 + env: TESTNAME=acceptance-tests + install: + - make exec-requirements + script: + - make exec-static + - make exec-accept + addons: + firefox: latest - - env: TESTNAME=unit-tests - - # Doing so is a waste of time since they won't be used. + - python: 3.5 + env: TOXENV=django22 + install: true + script: + - make exec-requirements + - make exec-static + - make exec-tests + - python: 3.8 + env: TOXENV=django22 install: true - script: - make exec-requirements - make exec-static - make exec-tests - after_success: - - pip install --upgrade codecov - - make exec-coverage - - codecov +after_success: + - pip install --upgrade codecov + - make exec-coverage + - codecov diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 924e43291..000000000 --- a/AUTHORS +++ /dev/null @@ -1,17 +0,0 @@ -Clinton Blackburn -Zubair Afzal -Renzo Lucioni -Ahsan Ulhaq -Awais Qureshi -Tasawer Nawaz -Waheed Ahmed -Bill DeRusha -Mike Dikan -Michael Terry -Alasdair Swan -Jeff LaJoie -Spencer Hance -Diana Huang -Cole Rogers -Cali Stenson -Ben Holt diff --git a/Makefile b/Makefile index 4f0d6c9f0..31fa54843 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,12 @@ .DEFAULT_GOAL := tests NODE_BIN=./node_modules/.bin +TOX = '' -.PHONY: requirements +.PHONY: requirements upgrade piptools production-requirements all-requirements + +ifdef TOXENV +TOX := tox -- #to isolate each tox environment if TOXENV is defined +endif # Generates a help message. Borrowed from https://github.com/pydanny/cookiecutter-djangopackage. help: ## Display this help message @@ -12,16 +17,24 @@ clean: ## Remove all generated files coverage erase find . -path '*/__pycache__/*' -delete find . -name \*.pyc -o -name \*.pyo -o -name __pycache__ -delete - rm -rf credentials/assets/ credentials/static/bundles/ credentials/static/jsi18n/ coverage htmlcov test_root/uploads + rm -rf credentials/assets/ credentials/static/bundles/ credentials/static/jsi18n/ coverage htmlcov test_root/uploads reports git clean -fd credentials/conf/locale -production-requirements: ## Install requirements for production +production-requirements: piptools ## Install requirements for production npm install --production --no-save - pip install -r requirements.txt + pip-sync requirements.txt + +js-requirements: ## Install frontend requirements + npm install -requirements: ## Install requirements for local development +all-requirements: piptools ## Install local and prod requirements npm install --unsafe-perm ## This flag exists to force node-sass to build correctly on docker. Remove as soon as possible. - pip install -r requirements/local.txt + npm install --production --no-save + pip-sync requirements/all.txt + +requirements: piptools ## Install requirements for local development + npm install --unsafe-perm ## This flag exists to force node-sass to build correctly on docker. Remove as soon as possible. + pip-sync requirements/dev.txt quality: ## Run linters isort --check-only --recursive acceptance_tests/ credentials/ @@ -36,20 +49,24 @@ test-react: ## Run Jest tests for React npm run test-react tests: ## Run tests and generate coverage report - coverage run -m pytest --ds credentials.settings.test --durations=25 - coverage report + $(TOX)coverage run -m pytest --ds credentials.settings.test --durations=25 + $(TOX)coverage report + $(NODE_BIN)/gulp test + make test-react + +js-tests: ## Run tests and generate coverage report $(NODE_BIN)/gulp test make test-react static: ## Gather all static assets for production (minimized) $(NODE_BIN)/webpack --config webpack.config.js --display-error-details --progress --optimize-minimize - python manage.py compilejsi18n - python manage.py collectstatic --noinput -i *.scss + $(TOX)python manage.py compilejsi18n + $(TOX)python manage.py collectstatic --noinput -i *.scss static.dev: ## Gather all static assets for development (not minimized) $(NODE_BIN)/webpack --config webpack.config.js --display-error-details --progress - python manage.py compilejsi18n - python manage.py collectstatic --noinput -i *.scss + $(TOX)python manage.py compilejsi18n + $(TOX)python manage.py collectstatic --noinput -i *.scss static.watch: ## Gather static assets when they change (not minimized) $(NODE_BIN)/webpack --config webpack.config.js --display-error-details --progress --watch @@ -73,25 +90,28 @@ exec-validate-translations: ## Check translations on a container exec-check_translations_up_to_date: ## test translations on a container docker exec -t credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && make check_translations_up_to_date' +exec-check_keywords: ## Scan the Django models in all installed apps in this project for restricted field names + docker exec -t credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && make check_keywords' + exec-clean: ## Remove all generated files from a container docker exec -t credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && make clean' exec-requirements: - docker exec -t credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && make requirements && make production-requirements' + docker exec -t credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && make all-requirements' exec-static: ## Gather static assets on a container - docker exec -t credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && make static' + docker exec -e TOXENV=$(TOXENV) -t credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && make static' exec-quality: ## Run linters on a container docker exec -t credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && make quality' exec-tests: ## Run tests on a container - docker exec -it credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && xvfb-run make tests' + docker exec -e TOXENV=$(TOXENV) -it credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && xvfb-run make tests' exec-accept: ## Run acceptance tests on a container docker exec -it credentials bash -c 'source /edx/app/credentials/credentials_env && cd /edx/app/credentials/credentials/ && make accept' -exec-validate: exec-validate-translations exec-clean exec-static exec-quality exec-tests exec-accept ## Run linters and tests after checking translations and gathering static assets +exec-validate: exec-validate-translations exec-clean exec-static exec-quality exec-tests exec-accept exec-check_keywords ## Run linters and tests after checking translations and gathering static assets exec-coverage: ## Generate XML coverage report on a container docker exec -t credentials bash -c 'coverage xml' @@ -135,3 +155,23 @@ validate_translations: ## Test translations files cd credentials && i18n_tool validate -v --check-all check_translations_up_to_date: fake_translations detect_changed_source_translations ## Install fake translations and check if translation files are up-to-date + +piptools: + pip install -q -r requirements/pip_tools.txt + +export CUSTOM_COMPILE_COMMAND = make upgrade +upgrade: piptools ## update the requirements/*.txt files with the latest packages satisfying requirements/*.in + pip-compile --rebuild --upgrade -o requirements/pip_tools.txt requirements/pip_tools.in + pip-compile --rebuild --upgrade -o requirements/base.txt requirements/base.in + pip-compile --rebuild --upgrade -o requirements/test.txt requirements/test.in + pip-compile --rebuild --upgrade -o requirements/docs.txt requirements/docs.in + pip-compile --rebuild --upgrade -o requirements/dev.txt requirements/dev.in + pip-compile --rebuild --upgrade -o requirements/production.txt requirements/production.in + pip-compile --rebuild --upgrade -o requirements/all.txt requirements/all.in + # Let tox control the Django version for tests + grep -e "^django==" requirements/production.txt > requirements/django.txt + sed '/^[dD]jango==/d' requirements/test.txt > requirements/test.tmp + mv requirements/test.tmp requirements/test.txt + +check_keywords: ## Scan the Django models in all installed apps in this project for restricted field names + python manage.py check_reserved_keywords --override_file db_keyword_overrides.yml diff --git a/README.rst b/README.rst index 3e3fcb2a2..d9aa36e6d 100644 --- a/README.rst +++ b/README.rst @@ -30,6 +30,23 @@ Reporting Security Issues Please do not report security issues in public. Please email security@edx.org. +Testing +------- + +The command below runs all of the Python and JS tests:: + + $ make tests + +The Python tests can be run independently with:: + + $ pytest --ds credentials.settings.test + +If this is the first time you've run tests, you'll have to run:: + + $ make static + +first, otherwise you'll run into ``webpack_loader.exceptions.WebpackBundleLookupErrors``. + Get Help -------- diff --git a/acceptance_tests/mixins.py b/acceptance_tests/mixins.py index e824975ec..9502ff07c 100644 --- a/acceptance_tests/mixins.py +++ b/acceptance_tests/mixins.py @@ -7,7 +7,7 @@ auth = None -class LoginMixin(object): +class LoginMixin: """ Mixin used for log in through a cookie.""" def login(self, superuser=False): diff --git a/credentials/apps/api/accreditors.py b/credentials/apps/api/accreditors.py index 18453286b..ef97715a4 100644 --- a/credentials/apps/api/accreditors.py +++ b/credentials/apps/api/accreditors.py @@ -1,4 +1,3 @@ -# pylint: disable=missing-docstring import logging from credentials.apps.api import exceptions @@ -8,7 +7,7 @@ logger = logging.getLogger(__name__) -class Accreditor(object): +class Accreditor: """ Accreditor class identifies credential type and calls corresponding issuer class for generating credential. """ @@ -30,7 +29,12 @@ def _create_credential_type_issuer_map(self): else: self.credential_type_issuer_map[credential_type] = issuer - def issue_credential(self, credential, username, status=UserCredentialStatus.AWARDED, attributes=None): + def issue_credential( + self, credential, username, + status=UserCredentialStatus.AWARDED, + attributes=None, + request=None + ): """Issues a credential. Arguments: @@ -38,6 +42,7 @@ def issue_credential(self, credential, username, status=UserCredentialStatus.AWA username (str): Username of the recipient. status (str): Status of credential. attributes (List[dict]): attributes list containing dictionaries of attributes + request (HttpRequest): request object to build program record absolute uris Returns: UserCredential @@ -54,4 +59,4 @@ def issue_credential(self, credential, username, status=UserCredentialStatus.AWA ) ) - return credential_issuer.issue_credential(credential, username, status, attributes) + return credential_issuer.issue_credential(credential, username, status, attributes, request) diff --git a/credentials/apps/api/authentication.py b/credentials/apps/api/authentication.py index 93a2d2309..a040907b2 100644 --- a/credentials/apps/api/authentication.py +++ b/credentials/apps/api/authentication.py @@ -4,14 +4,11 @@ import logging -from django.conf import settings +import edx_rest_framework_extensions.auth.jwt.authentication as edx_drf_auth + from django.contrib.auth.models import Group -from edx_rest_framework_extensions.auth.bearer.authentication import BearerAuthentication as BaseBearerAuthentication -from rest_framework.exceptions import AuthenticationFailed -from rest_framework_jwt.authentication import JSONWebTokenAuthentication from credentials.apps.core.constants import Role -from credentials.apps.core.models import User logger = logging.getLogger(__name__) @@ -28,7 +25,7 @@ def _set_user_roles(user, payload): user.groups.remove(admin_group) -def pipeline_set_user_roles(response, user=None, *_, **__): +def pipeline_set_user_roles(response, user=None, *_, **__): # pylint: disable=keyword-arg-before-vararg """ Social auth pipeline function to update group memberships based on claims present in the id token. @@ -39,40 +36,19 @@ def pipeline_set_user_roles(response, user=None, *_, **__): return {} -class JwtAuthentication(JSONWebTokenAuthentication): +class JwtAuthentication(edx_drf_auth.JwtAuthentication): """ - Custom authentication using JWT from the edx oidc provider. + Overrides the default JwtAuthentication class to ensure that admin users are added to the admin group. """ def authenticate_credentials(self, payload): """ - Return a user object to be associated with the present request, based on - the content of an already-decoded / verified JWT payload. - In the process of inflating the user object based on the payload, we also - make sure that the roles associated with this user are up-to-date. + Return the user object with the admin group added or removed if the user is an admin. """ - if 'preferred_username' not in payload: - logger.warning('Invalid JWT payload: preferred_username not present.') - raise AuthenticationFailed() - username = payload['preferred_username'] - user, __ = User.objects.get_or_create(username=username) + user = super(JwtAuthentication, self).authenticate_credentials(payload) admin_group = Group.objects.get(name=Role.ADMINS) if payload.get('administrator'): user.groups.add(admin_group) else: user.groups.remove(admin_group) - return user - - -class BearerAuthentication(BaseBearerAuthentication): - """ - Simple token based authentication. - - This authentication class is useful for authenticating an OAuth2 access token against a remote - authentication provider. Clients should authenticate by passing the token key in the "Authorization" HTTP header, - prepended with the string `"Bearer "`. - """ - def get_user_info_url(self): - """ Returns the URL, hosted by the OAuth2 provider, from which user information can be pulled. """ - return '{base}/user_info/'.format(base=settings.OAUTH2_PROVIDER_URL) diff --git a/credentials/apps/api/exceptions.py b/credentials/apps/api/exceptions.py index 1fd0af3f3..4caadb9dd 100644 --- a/credentials/apps/api/exceptions.py +++ b/credentials/apps/api/exceptions.py @@ -4,11 +4,9 @@ class UnsupportedCredentialTypeError(Exception): """ Raised when the Accreditor is asked to issue a type of credential for which there is no registered issuer. """ - pass class DuplicateAttributeError(Exception): """ Raised when the Accreditor is asked to issue credential with duplicate attributes. """ - pass diff --git a/credentials/apps/api/permissions.py b/credentials/apps/api/permissions.py index 79a73c5e9..d94bd365c 100644 --- a/credentials/apps/api/permissions.py +++ b/credentials/apps/api/permissions.py @@ -38,7 +38,7 @@ def has_permission(self, request, view): implicitly granted, until after DRF has fetched the requested object. """ return super(UserCredentialViewSetPermissions, self).has_permission(request, view) or ( - request.user.is_authenticated() and request.method in permissions.SAFE_METHODS + request.user.is_authenticated and request.method in permissions.SAFE_METHODS ) def has_object_permission(self, request, view, obj): @@ -49,7 +49,6 @@ def has_object_permission(self, request, view, obj): """ if super(UserCredentialViewSetPermissions, self).has_permission(request, view): return True - elif request.user.username.lower() == obj.username.lower(): + if request.user.username.lower() == obj.username.lower(): return True - else: - raise Http404 + raise Http404 diff --git a/credentials/apps/api/tests/mixins.py b/credentials/apps/api/tests/mixins.py index f02f775da..1ec4d8f4a 100644 --- a/credentials/apps/api/tests/mixins.py +++ b/credentials/apps/api/tests/mixins.py @@ -16,7 +16,7 @@ JWT_AUTH = 'JWT_AUTH' -class JwtMixin(object): +class JwtMixin: """ Mixin with JWT-related helper functions. """ JWT_SECRET_KEY = getattr(settings, JWT_AUTH)['JWT_SECRET_KEY'] @@ -27,11 +27,11 @@ def generate_token(self, payload, secret=None): """Generate a JWT token with the provided payload.""" secret = secret or self.JWT_SECRET_KEY token = jwt.encode(payload, secret) - return token + return token.decode('utf-8') def generate_id_token(self, user, admin=False, ttl=1, **overrides): """Generate a JWT id_token that looks like the ones currently - returned by the edx oidc provider.""" + returned by the edx oauth provider.""" payload = self.default_payload(user=user, admin=admin, ttl=ttl) payload.update(overrides) @@ -59,7 +59,7 @@ def default_payload(self, user, admin=False, ttl=1): } -class CredentialViewSetTestsMixin(object): +class CredentialViewSetTestsMixin: """ Base Class for ProgramCredentialViewSetTests and CourseCredentialViewSetTests. """ list_path = None diff --git a/credentials/apps/api/tests/test_accreditors.py b/credentials/apps/api/tests/test_accreditors.py index 2e0981046..aa73fb55b 100644 --- a/credentials/apps/api/tests/test_accreditors.py +++ b/credentials/apps/api/tests/test_accreditors.py @@ -45,16 +45,17 @@ def test_issue_credential(self): accreditor = Accreditor(issuers=[ProgramCertificateIssuer()]) with patch.object(ProgramCertificateIssuer, 'issue_credential') as mock_method: accreditor.issue_credential(self.program_cert, 'tester', attributes=self.attributes) - mock_method.assert_called_with(self.program_cert, 'tester', 'awarded', self.attributes) + mock_method.assert_called_with(self.program_cert, 'tester', 'awarded', self.attributes, None) def test_constructor_with_multiple_issuers_for_same_credential_type(self): """ Verify the Accreditor supports a single Issuer per credential type. Attempts to register additional issuers for a credential type should result in a warning being logged. """ - msg = 'The issuer [{}] is already registered to issue credentials of type [{}]. [{}] will NOT be used.'.format( - ProgramCertificateIssuer, self.program_credential, ProgramCertificateIssuer - ) + msg = 'The issuer [{0}] is already registered to issue credentials of type [{1}]. [{0}] will NOT be used.'\ + .format( + ProgramCertificateIssuer, self.program_credential + ) with LogCapture(LOGGER_NAME) as l: accreditor = Accreditor(issuers=[ProgramCertificateIssuer(), ProgramCertificateIssuer()]) diff --git a/credentials/apps/api/tests/test_authentication.py b/credentials/apps/api/tests/test_authentication.py index 5f867dcf9..549ccd8d9 100644 --- a/credentials/apps/api/tests/test_authentication.py +++ b/credentials/apps/api/tests/test_authentication.py @@ -8,7 +8,7 @@ from rest_framework.exceptions import AuthenticationFailed from rest_framework.test import APIRequestFactory -from credentials.apps.api.authentication import BearerAuthentication, JwtAuthentication, pipeline_set_user_roles +from credentials.apps.api.authentication import JwtAuthentication, pipeline_set_user_roles from credentials.apps.api.tests.mixins import JwtMixin from credentials.apps.core.constants import Role from credentials.apps.core.tests.factories import UserFactory @@ -47,6 +47,7 @@ def test_admin_user(self): } ) self.assertEqual(user.username, self.USERNAME) + self.assertTrue(user.is_staff) self.assertEqual(len(user.groups.all()), 1) self.assertEqual(user.groups.all()[0].name, Role.ADMINS) @@ -119,33 +120,3 @@ def test_no_user(self): """ result = pipeline_set_user_roles({}, None) self.assertEqual(result, {}) - - -class BearerAuthenticationTests(TestCase): - """ Tests for the BearerAuthentication class. """ - - def setUp(self): - super(BearerAuthenticationTests, self).setUp() - self.auth = BearerAuthentication() - self.factory = APIRequestFactory() - - def _create_request(self, token='12345', token_name='Bearer'): - """Create request with authorization header. """ - auth_header = '{} {}'.format(token_name, token) - request = self.factory.get('/', HTTP_AUTHORIZATION=auth_header) - return request - - def test_authenticate_header(self): - """The method should return the string Bearer.""" - self.assertEqual(self.auth.authenticate_header(self._create_request()), 'Bearer') - - def test_authenticate_invalid_token(self): - """If no token is supplied, or if the token contains spaces, the method should raise an exception.""" - - # Missing token - request = self._create_request('') - self.assertRaises(AuthenticationFailed, self.auth.authenticate, request) - - # Token with spaces - request = self._create_request('abc 123 456') - self.assertRaises(AuthenticationFailed, self.auth.authenticate, request) diff --git a/credentials/apps/api/urls.py b/credentials/apps/api/urls.py index c99e30f6b..fe6d90377 100644 --- a/credentials/apps/api/urls.py +++ b/credentials/apps/api/urls.py @@ -7,5 +7,5 @@ from django.conf.urls import include, url urlpatterns = [ - url(r'^v2/', include('credentials.apps.api.v2.urls', namespace='v2')), + url(r'^v2/', include(('credentials.apps.api.v2.urls', 'v2'), namespace='v2')), ] diff --git a/credentials/apps/api/v2/filters.py b/credentials/apps/api/v2/filters.py index 8cc44f648..33649e631 100644 --- a/credentials/apps/api/v2/filters.py +++ b/credentials/apps/api/v2/filters.py @@ -1,4 +1,5 @@ import django_filters +from django.core.exceptions import ValidationError from django.db.models import Q from credentials.apps.catalog.models import Program @@ -8,8 +9,16 @@ class ProgramRelatedFilter(django_filters.Filter): def filter(self, qs, value): - program = Program.objects.filter(uuid=value).first() - course_runs = [run.key for run in program.course_runs.all()] if program else [] + if value is None: + return qs + try: + program = Program.objects.filter(uuid=value).first() + except ValidationError: + return UserCredential.objects.none() + + course_runs = [ + run.key for run in program.course_runs.all() + ] if program else [] return qs.filter(Q(program_credentials__program_uuid=value) | Q(course_credentials__course_id__in=course_runs)) @@ -18,7 +27,7 @@ class CredentialTypeFilter(django_filters.Filter): def filter(self, qs, value): if value == 'program': return qs.filter(program_credentials__isnull=False) - elif value == 'course-run': + if value == 'course-run': return qs.filter(course_credentials__isnull=False) return qs @@ -38,7 +47,9 @@ class UserCredentialFilter(django_filters.FilterSet): choices=UserCredential._meta.get_field('status').choices, label='Status of the credential' ) - username = django_filters.CharFilter(label='Username of the recipient of the credential') + username = django_filters.CharFilter( + label='Username of the recipient of the credential' + ) only_visible = django_filters.BooleanFilter(method=handle_only_visible) class Meta: diff --git a/credentials/apps/api/v2/permissions.py b/credentials/apps/api/v2/permissions.py index 8decde59b..d6a053812 100644 --- a/credentials/apps/api/v2/permissions.py +++ b/credentials/apps/api/v2/permissions.py @@ -1,6 +1,7 @@ """ Custom permissions classes for use with DRF. """ +from django.conf import settings from rest_framework import permissions @@ -40,3 +41,11 @@ def has_permission(self, request, view): return default or (request.user.username.lower() == filtered_username) return default + + +class CanReplaceUsername(permissions.BasePermission): + """ + Grants access to the Username Replacement API for the service user. + """ + def has_permission(self, request, view): + return request.user.username == settings.USERNAME_REPLACEMENT_WORKER diff --git a/credentials/apps/api/v2/serializers.py b/credentials/apps/api/v2/serializers.py index 15f352cad..03b5e47ec 100644 --- a/credentials/apps/api/v2/serializers.py +++ b/credentials/apps/api/v2/serializers.py @@ -112,7 +112,7 @@ def to_representation(self, value): class UserCredentialAttributeSerializer(serializers.ModelSerializer): """ Serializer for CredentialAttribute objects """ - class Meta(object): + class Meta: model = UserCredentialAttribute fields = ('name', 'value') @@ -124,7 +124,7 @@ class UserCredentialSerializer(serializers.ModelSerializer): attributes = UserCredentialAttributeSerializer(many=True, read_only=True) certificate_url = UserCertificateURLField(source='uuid', read_only=True) - class Meta(object): + class Meta: model = UserCredential fields = ( 'username', 'credential', 'status', 'download_url', 'uuid', 'attributes', 'created', 'modified', @@ -165,13 +165,14 @@ def issue_credential(self, validated_data): username = validated_data['username'] status = validated_data.get('status', UserCredentialStatus.AWARDED) attributes = validated_data.pop('attributes', None) + request = self.context.get('request') - return accreditor.issue_credential(credential, username, status=status, attributes=attributes) + return accreditor.issue_credential(credential, username, status=status, attributes=attributes, request=request) def create(self, validated_data): return self.issue_credential(validated_data) - class Meta(object): + class Meta: model = UserCredential fields = UserCredentialSerializer.Meta.fields read_only_fields = ('download_url', 'uuid', 'created', 'modified',) @@ -181,7 +182,7 @@ class UserGradeSerializer(serializers.ModelSerializer): """ Serializer for UserGrade objects. """ course_run = CourseRunField() - class Meta(object): + class Meta: model = UserGrade fields = ( 'id', 'username', 'course_run', 'letter_grade', 'percent_grade', 'verified', 'created', 'modified', diff --git a/credentials/apps/api/v2/tests/test_serializers.py b/credentials/apps/api/v2/tests/test_serializers.py index 6c868b13f..e6bca0e56 100644 --- a/credentials/apps/api/v2/tests/test_serializers.py +++ b/credentials/apps/api/v2/tests/test_serializers.py @@ -28,7 +28,11 @@ def setUp(self): self.program_certificate = ProgramCertificateFactory(site=self.site) self.course_certificate = CourseCertificateFactory(site=self.site, certificate_type='verified') self.field_instance = CredentialField() - self.field_instance.context['request'] = namedtuple('HttpRequest', ['site'])(self.site) + # see: https://github.com/encode/django-rest-framework/blob/3.9.x/rest_framework/fields.py#L610 + # pylint: disable=protected-access + self.field_instance._context = { + 'request': namedtuple('HttpRequest', ['site'])(self.site), + } def assert_program_uuid_validation_error_raised(self, program_uuid): try: diff --git a/credentials/apps/api/v2/tests/test_views.py b/credentials/apps/api/v2/tests/test_views.py index c1aaf666d..fcdf19f2f 100644 --- a/credentials/apps/api/v2/tests/test_views.py +++ b/credentials/apps/api/v2/tests/test_views.py @@ -2,12 +2,14 @@ from decimal import Decimal import ddt +import mock from django.contrib.auth.models import Permission from django.test import TestCase from django.urls import reverse from rest_framework.renderers import JSONRenderer from rest_framework.test import APIRequestFactory, APITestCase +from credentials.apps.api.tests.mixins import JwtMixin from credentials.apps.api.v2.serializers import (UserCredentialAttributeSerializer, UserCredentialSerializer, UserGradeSerializer) from credentials.apps.api.v2.views import CredentialRateThrottle @@ -25,8 +27,6 @@ LOGGER_NAME_SERIALIZER = 'credentials.apps.api.v2.serializers' -# pylint: disable=no-member - @ddt.ddt class CredentialViewSetTests(SiteMixin, APITestCase): list_path = reverse('api:v2:credentials-list') @@ -254,6 +254,15 @@ def test_list_username_filtering(self): self.assert_list_username_filter_request_succeeds(username, expected) + def test_innvalid_program_uuid_filtering(self): + """ Verify that endpoint returns no results for invalid program uuid + instead of raising ValidationError. """ + self.authenticate_user(self.user) + self.add_user_permission(self.user, 'view_usercredential') + + response = self.client.get(self.list_path + '?program_uuid=1234fewef') + self.assertListEqual(response.data['results'], []) + def test_list_program_uuid_filtering(self): """ Verify the endpoint returns data for all UserCredentials in the given program. """ @@ -517,3 +526,86 @@ def test_throttle_configuration(self, scope): """ Verify that throttling is configured for each scope. """ self.throttle.scope = scope self.assertIsNotNone(self.throttle.parse_rate(self.throttle.get_rate())) + + +@ddt.ddt +@mock.patch('django.conf.settings.USERNAME_REPLACEMENT_WORKER', 'test_replace_username_service_worker') +class UsernameReplacementViewTests(JwtMixin, APITestCase): + """ Tests UsernameReplacementView """ + SERVICE_USERNAME = 'test_replace_username_service_worker' + + def setUp(self): + super(UsernameReplacementViewTests, self).setUp() + self.service_user = UserFactory(username=self.SERVICE_USERNAME) + self.url = reverse("api:v2:replace_usernames") + + def build_jwt_headers(self, user): + """ + Helper function for creating headers for the JWT authentication. + """ + jwt_payload = self.default_payload(user) + token = self.generate_token(jwt_payload) + headers = {'HTTP_AUTHORIZATION': 'JWT ' + token} + return headers + + def call_api(self, user, data): + """ Helper function to call API with data """ + data = json.dumps(data) + headers = self.build_jwt_headers(user) + return self.client.post(self.url, data, **headers, content_type=JSON_CONTENT_TYPE) + + def test_auth(self): + """ Verify the endpoint only works with the service worker """ + data = { + "username_mappings": [ + {"test_username_1": "test_new_username_1"}, + {"test_username_2": "test_new_username_2"} + ] + } + + # Test unauthenticated + response = self.client.post(self.url) + self.assertEqual(response.status_code, 401) + + # Test non-service worker + random_user = UserFactory() + response = self.call_api(random_user, data) + self.assertEqual(response.status_code, 403) + + # Test service worker + response = self.call_api(self.service_user, data) + self.assertEqual(response.status_code, 200) + + @ddt.data( + [{}, {}], + {}, + [{"test_key": "test_value", "test_key_2": "test_value_2"}] + ) + def test_bad_schema(self, mapping_data): + """ Verify the endpoint rejects bad data schema """ + data = { + "username_mappings": mapping_data + } + response = self.call_api(self.service_user, data) + self.assertEqual(response.status_code, 400) + + def test_existing_and_non_existing_users(self): + """ + Tests a mix of existing and non existing users. Users that don't exist + in this service are also treated as a success because no work needs to + be done changing their username. + """ + random_users = [UserFactory() for _ in range(5)] + fake_usernames = ["myname_" + str(x) for x in range(5)] + existing_users = [{user.username: user.username + '_new'} for user in random_users] + non_existing_users = [{username: username + '_new'} for username in fake_usernames] + data = { + "username_mappings": existing_users + non_existing_users + } + expected_response = { + 'failed_replacements': [], + 'successful_replacements': existing_users + non_existing_users + } + response = self.call_api(self.service_user, data) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, expected_response) diff --git a/credentials/apps/api/v2/urls.py b/credentials/apps/api/v2/urls.py index 75780aca6..a763179ae 100644 --- a/credentials/apps/api/v2/urls.py +++ b/credentials/apps/api/v2/urls.py @@ -1,11 +1,15 @@ +from django.conf.urls import url from rest_framework.routers import DefaultRouter from credentials.apps.api.v2 import views +urlpatterns = [ + url(r'^replace_usernames/$', views.UsernameReplacementView.as_view(), name="replace_usernames") +] + router = DefaultRouter() # pylint: disable=invalid-name # URLs can not have hyphen as it is not currently supported by slumber # as mentioned https://github.com/samgiles/slumber/issues/44 -router.register(r'credentials', views.CredentialViewSet, base_name='credentials') -router.register(r'grades', views.GradeViewSet, base_name='grades') - -urlpatterns = router.urls +router.register(r'credentials', views.CredentialViewSet, basename='credentials') +router.register(r'grades', views.GradeViewSet, basename='grades') +urlpatterns += router.urls diff --git a/credentials/apps/api/v2/views.py b/credentials/apps/api/v2/views.py index 6724e67bd..c002e6a92 100644 --- a/credentials/apps/api/v2/views.py +++ b/credentials/apps/api/v2/views.py @@ -1,14 +1,17 @@ import logging +from django.apps import apps +from django.db import transaction from django.db.models import Q -from rest_framework import mixins, viewsets +from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication +from rest_framework import mixins, permissions, status, viewsets from rest_framework.exceptions import Throttled from rest_framework.response import Response from rest_framework.throttling import ScopedRateThrottle -from rest_framework.views import exception_handler +from rest_framework.views import APIView, exception_handler from credentials.apps.api.v2.filters import UserCredentialFilter -from credentials.apps.api.v2.permissions import UserCredentialPermissions +from credentials.apps.api.v2.permissions import CanReplaceUsername, UserCredentialPermissions from credentials.apps.api.v2.serializers import (UserCredentialCreationSerializer, UserCredentialSerializer, UserGradeSerializer) from credentials.apps.credentials.models import UserCredential @@ -16,6 +19,8 @@ log = logging.getLogger(__name__) +# pylint: disable= no-member + def credentials_throttle_handler(exc, context): """ Exception handler for logging messages when an endpoint is throttled. """ @@ -45,13 +50,13 @@ class CredentialRateThrottle(ScopedRateThrottle): def allow_request(self, request, view): user = request.user if user.is_authenticated and (user.is_staff or user.is_superuser): - setattr(view, 'throttle_scope', 'staff_override') + view.throttle_scope = 'staff_override' return super(CredentialRateThrottle, self).allow_request(request, view) class CredentialViewSet(viewsets.ModelViewSet): - filter_class = UserCredentialFilter + filterset_class = UserCredentialFilter lookup_field = 'uuid' permission_classes = (UserCredentialPermissions,) serializer_class = UserCredentialSerializer @@ -145,3 +150,139 @@ def partial_update(self, request, *args, **kwargs): # pylint: disable=useless-s def update(self, request, *args, **kwargs): # pylint: disable=useless-super-delegation """ Update a grade. """ return super(GradeViewSet, self).update(request, *args, **kwargs) + + +class UsernameReplacementView(APIView): + """ + WARNING: This API is only meant to be used as part of a larger job that + updates usernames across all services. DO NOT run this alone or users will + not match across the system and things will be broken. This API should be + called from the LMS endpoint which verifies uniqueness of the username + first. + + API will recieve a list of current usernames and their new username. + """ + + authentication_classes = (JwtAuthentication, ) + permission_classes = (permissions.IsAuthenticated, CanReplaceUsername) + + def post(self, request): + """ + **POST Parameters** + + A POST request must include the following parameter. + + * username_mappings: Required. A list of objects that map the current username (key) + to the new username (value) + { + "username_mappings": [ + {"current_username_1": "new_username_1"}, + {"current_username_2": "new_username_2"} + ] + } + + **POST Response Values** + + As long as data validation passes, the request will return a 200 with a new mapping + of old usernames (key) to new username (value) + + { + "successful_replacements": [ + {"old_username_1": "new_username_1"} + ], + "failed_replacements": [ + {"old_username_2": "new_username_2"} + ] + } + """ + # (model_name, column_name) + MODELS_WITH_USERNAME = ( + ('core.user', 'username'), + ('credentials.usercredential', 'username'), + ('records.usergrade', 'username'), + ) + username_mappings = request.data.get("username_mappings") + + replacement_locations = self._load_models(MODELS_WITH_USERNAME) + + if not self._has_valid_schema(username_mappings): + return Response(status=status.HTTP_400_BAD_REQUEST) + + successful_replacements, failed_replacements = [], [] + + for username_pair in username_mappings: + current_username = list(username_pair.keys())[0] + new_username = list(username_pair.values())[0] + successfully_replaced = self._replace_username_for_all_models( + current_username, + new_username, + replacement_locations + ) + if successfully_replaced: + successful_replacements.append({current_username: new_username}) + else: + failed_replacements.append({current_username: new_username}) + return Response( + status=status.HTTP_200_OK, + data={ + "successful_replacements": successful_replacements, + "failed_replacements": failed_replacements + } + ) + + def _load_models(self, models_with_fields): + """ Takes tuples that contain a model path and returns the list with a loaded version of the model """ + try: + replacement_locations = [(apps.get_model(model), column) for (model, column) in models_with_fields] + except LookupError: + log.exception("Unable to load models for username replacement") + raise + return replacement_locations + + def _has_valid_schema(self, post_data): + """ Verifies the data is a list of objects with a single key:value pair """ + if not isinstance(post_data, list): + return False + for obj in post_data: + if not (isinstance(obj, dict) and len(obj) == 1): + return False + return True + + def _replace_username_for_all_models(self, current_username, new_username, replacement_locations): + """ + Replaces current_username with new_username for all (model, column) pairs in replacement locations. + Returns if it was successful or not. Usernames that don't exist in this service will be treated as + a success because no work needs to be done changing their username. + """ + try: + with transaction.atomic(): + num_rows_changed = 0 + for (model, column) in replacement_locations: + num_rows_changed += model.objects.filter( + **{column: current_username} + ).update( + **{column: new_username} + ) + except Exception as exc: # pylint: disable=broad-except + log.exception( + "Unable to change username from %s to %s. Failed on table %s because %s", + current_username, + new_username, + model.__class__.__name__, + exc + ) + return False + if num_rows_changed == 0: + log.info( + "Unable to change username from %s to %s because %s doesn't exist.", + current_username, + new_username, + current_username, + ) + else: + log.info( + "Successfully changed username from %s to %s.", + current_username, + new_username, + ) + return True diff --git a/credentials/apps/catalog/admin.py b/credentials/apps/catalog/admin.py new file mode 100644 index 000000000..e00cc6729 --- /dev/null +++ b/credentials/apps/catalog/admin.py @@ -0,0 +1,35 @@ +from django.contrib import admin + +from credentials.apps.catalog.models import Course, CourseRun, Organization, Pathway, Program + + +@admin.register(Course) +class CourseAdmin(admin.ModelAdmin): + list_display = ('key', 'uuid', 'title') + list_filter = ('site',) + readonly_fields = ('uuid',) + search_fields = ('key', 'title', 'uuid') + + +@admin.register(CourseRun) +class CourseRunAdmin(admin.ModelAdmin): + list_display = ('key', 'uuid', 'title_override', 'start_date', 'end_date') + readonly_fields = ('uuid',) + search_fields = ('key', 'title_override', 'uuid') + + +@admin.register(Program) +class ProgramAdmin(admin.ModelAdmin): + list_display = ('title', 'uuid', 'type') + readonly_fields = ('uuid',) + search_fields = ('title', 'uuid') + + +@admin.register(Pathway) +class PathwayAdmin(admin.ModelAdmin): + list_display = ('name', 'org_name', 'pathway_type', 'email', 'uuid') + readonly_fields = ('uuid',) + search_fields = ('name', 'uuid') + + +admin.site.register(Organization) diff --git a/credentials/apps/catalog/migrations/0011_add_new_start_date_end_date_fields.py b/credentials/apps/catalog/migrations/0011_add_new_start_date_end_date_fields.py new file mode 100644 index 000000000..b82861ebd --- /dev/null +++ b/credentials/apps/catalog/migrations/0011_add_new_start_date_end_date_fields.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.25 on 2019-10-31 16:40 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('catalog', '0010_auto_20180828_1336'), + ] + + operations = [ + migrations.AddField( + model_name='courserun', + name='end_date', + field=models.DateTimeField(blank=True, db_index=True, null=True), + ), + migrations.AddField( + model_name='courserun', + name='start_date', + field=models.DateTimeField(blank=True, db_index=True, null=True), + ), + ] diff --git a/credentials/apps/catalog/migrations/0012_courserun_copy_column_values.py b/credentials/apps/catalog/migrations/0012_courserun_copy_column_values.py new file mode 100644 index 000000000..c711c66e4 --- /dev/null +++ b/credentials/apps/catalog/migrations/0012_courserun_copy_column_values.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.25 on 2019-11-01 17:04 +from __future__ import unicode_literals + +from django.db import migrations + + +def copy_column_values_forwards(apps, schema_editor): + """ + Copy the start and end fields into the start_date and end_date fields respectively. + + This table should have a few thousand rows at most, so I'm not so concerned about long-term + database locking. + """ + CourseRun = apps.get_model('catalog', 'CourseRun') + for course_run in CourseRun.objects.all(): + course_run.start_date = course_run.start + course_run.end_date = course_run.end + course_run.save() + + +def copy_column_values_backwards(apps, schema_editor): + """ + We must specify a backwards migration even if it is empty, or else django will raise an + exception if we try to rollback this migration. + """ + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('catalog', '0011_add_new_start_date_end_date_fields'), + ] + + operations = [ + migrations.RunPython( + copy_column_values_forwards, + copy_column_values_backwards, + ), + ] diff --git a/credentials/apps/catalog/migrations/0013_drop_old_start_end_fields.py b/credentials/apps/catalog/migrations/0013_drop_old_start_end_fields.py new file mode 100644 index 000000000..b0f4c2760 --- /dev/null +++ b/credentials/apps/catalog/migrations/0013_drop_old_start_end_fields.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.25 on 2019-10-31 18:08 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('catalog', '0012_courserun_copy_column_values'), + ] + + operations = [ + migrations.RemoveField( + model_name='courserun', + name='end', + ), + migrations.RemoveField( + model_name='courserun', + name='start', + ), + ] diff --git a/credentials/apps/catalog/models.py b/credentials/apps/catalog/models.py index 1e9fdc44c..03bb2ab7f 100644 --- a/credentials/apps/catalog/models.py +++ b/credentials/apps/catalog/models.py @@ -46,15 +46,15 @@ def __str__(self): class CourseRun(TimeStampedModel): """ CourseRun model. """ - course = models.ForeignKey(Course, related_name='course_runs') + course = models.ForeignKey(Course, related_name='course_runs', on_delete=models.CASCADE) uuid = models.UUIDField(verbose_name='UUID') key = models.CharField(max_length=255) title_override = models.CharField( max_length=255, default=None, null=True, blank=True, help_text="Title specific for this run of a course. " "Leave this value blank to default to the parent course's title.") - start = models.DateTimeField(null=True, blank=True, db_index=True) - end = models.DateTimeField(null=True, blank=True, db_index=True) + start_date = models.DateTimeField(null=True, blank=True, db_index=True) + end_date = models.DateTimeField(null=True, blank=True, db_index=True) # Note that we don't have a status field here -- there are only two statuses for CourseRuns: published and # unpublished. But unpublished is really used as a 'retired' flag. So in both cases, we want the run. diff --git a/credentials/apps/catalog/tests/factories.py b/credentials/apps/catalog/tests/factories.py index dc5c596ef..406fb7458 100644 --- a/credentials/apps/catalog/tests/factories.py +++ b/credentials/apps/catalog/tests/factories.py @@ -19,7 +19,7 @@ def add_m2m_data(m2m_relation, data): class OrganizationFactory(factory.DjangoModelFactory): - class Meta(object): + class Meta: model = Organization site = factory.SubFactory(SiteFactory) @@ -29,7 +29,7 @@ class Meta(object): class CourseFactory(factory.DjangoModelFactory): - class Meta(object): + class Meta: model = Course site = factory.SubFactory(SiteFactory) @@ -44,19 +44,19 @@ def owners(self, create, extracted): class CourseRunFactory(factory.DjangoModelFactory): - class Meta(object): + class Meta: model = CourseRun course = factory.SubFactory(CourseFactory) uuid = factory.LazyFunction(uuid4) key = FuzzyText(prefix='course-run-id/', suffix='/fake') title_override = None - start = FuzzyDateTime(datetime.datetime(2014, 1, 1, tzinfo=UTC)) - end = FuzzyDateTime(datetime.datetime(2014, 1, 1, tzinfo=UTC)).end_dt + start_date = FuzzyDateTime(datetime.datetime(2014, 1, 1, tzinfo=UTC)) + end_date = FuzzyDateTime(datetime.datetime(2014, 1, 1, tzinfo=UTC)).end_dt class ProgramFactory(factory.DjangoModelFactory): - class Meta(object): + class Meta: model = Program site = factory.SubFactory(SiteFactory) @@ -77,7 +77,7 @@ def authoring_organizations(self, create, extracted): class PathwayFactory(factory.DjangoModelFactory): - class Meta(object): + class Meta: model = Pathway uuid = factory.LazyFunction(uuid4) diff --git a/credentials/apps/catalog/tests/test_commands.py b/credentials/apps/catalog/tests/test_commands.py index ca7934e10..115b128a1 100644 --- a/credentials/apps/catalog/tests/test_commands.py +++ b/credentials/apps/catalog/tests/test_commands.py @@ -47,8 +47,8 @@ class CopyCatalogCommandTests(SiteMixin, TestCase): 'uuid': '33f0dded-fee9-4dec-a333-b9d8c2c82bd3', 'key': 'course-v1:FakeX+Course1+Run1', 'title': 'Course 1', - 'start': '2018-01-01T00:00:00Z', - 'end': '2018-06-01T00:00:00Z', + 'start_date': '2018-01-01T00:00:00Z', + 'end_date': '2018-06-01T00:00:00Z', }, ], }, @@ -83,15 +83,15 @@ class CopyCatalogCommandTests(SiteMixin, TestCase): 'uuid': '33f0dded-fee9-4dec-a333-c9d8c2c82bd3', 'key': 'course-v1:CakeX+Course1+Run1', 'title': 'Course 1', - 'start': '2018-01-01T00:00:00Z', - 'end': '2018-06-01T00:00:00Z', + 'start_date': '2018-01-01T00:00:00Z', + 'end_date': '2018-06-01T00:00:00Z', }, { 'uuid': '33f0dded-fee9-4dec-a333-c9d8c2c82bd4', 'key': 'course-v1:CakeX+Course1+Run2', 'title': 'Course 2', - 'start': '2018-02-01T00:00:00Z', - 'end': '2018-07-01T00:00:00Z', + 'start_date': '2018-02-01T00:00:00Z', + 'end_date': '2018-07-01T00:00:00Z', }, ], }, @@ -123,7 +123,6 @@ class CopyCatalogCommandTests(SiteMixin, TestCase): ] def setUp(self): - # pylint: disable=no-member super(CopyCatalogCommandTests, self).setUp() self.site_configuration = SiteConfigurationFactory.build(segment_key=self.faker.word()) diff --git a/credentials/apps/catalog/tests/test_utils.py b/credentials/apps/catalog/tests/test_utils.py index e495987dd..ea7a3fe8c 100644 --- a/credentials/apps/catalog/tests/test_utils.py +++ b/credentials/apps/catalog/tests/test_utils.py @@ -22,10 +22,10 @@ class ParseTests(TestCase): COURSERUN1_DATA = {'uuid': '33f0dded-fee9-4dec-a333-b9d8c2c82bd3', 'key': 'runkey', 'title': 'Course Run Title', - 'start': '2018-01-01T00:00:00Z', 'end': '2018-06-01T00:00:00Z'} + 'start_date': '2018-01-01T00:00:00Z', 'end_date': '2018-06-01T00:00:00Z'} COURSERUN1_VALUES = {'uuid': '33f0dded-fee9-4dec-a333-b9d8c2c82bd3', 'key': 'runkey', 'title_override': 'Course Run Title', - 'start': datetime(2018, 1, 1), 'end': datetime(2018, 6, 1)} + 'start_date': datetime(2018, 1, 1), 'end_date': datetime(2018, 6, 1)} COURSE1_DATA = {'uuid': '33f0dded-fee9-4dec-a333-b9d8c2c82bd4', 'key': 'coursekey', 'title': 'Course Title', 'owners': [ORG1_DATA], 'course_runs': [COURSERUN1_DATA]} diff --git a/credentials/apps/catalog/utils.py b/credentials/apps/catalog/utils.py index 19e74c925..055865c7c 100644 --- a/credentials/apps/catalog/utils.py +++ b/credentials/apps/catalog/utils.py @@ -29,8 +29,14 @@ def parse_course_run(course, data): defaults={ 'key': data['key'], 'title_override': data['title'] if data['title'] != course.title else None, - 'start': data['start'], - 'end': data['end'], + + # We are migrating all 'start' and 'end' model fields to include + # the _date suffix. During this transition, support both variants + # provided by the Discovery service. DE-1708. + # TODO: After updating the Discovery service to send 'start_date' + # and 'end_date', simplify this logic. + 'start_date': data['start_date'] if 'start_date' in data else data['start'], + 'end_date': data['end_date'] if 'end_date' in data else data['end'], }, ) return course_run diff --git a/credentials/apps/core/apps.py b/credentials/apps/core/apps.py index 81e2a6384..86cbe6e1e 100644 --- a/credentials/apps/core/apps.py +++ b/credentials/apps/core/apps.py @@ -9,4 +9,4 @@ def ready(self): super(CoreAppConfig, self).ready() # noinspection PyUnresolvedReferences - import credentials.apps.core.signals # pylint: disable=unused-variable + import credentials.apps.core.signals # pylint: disable=unused-import, import-outside-toplevel diff --git a/credentials/apps/core/constants.py b/credentials/apps/core/constants.py index 7616efa32..4d7119613 100644 --- a/credentials/apps/core/constants.py +++ b/credentials/apps/core/constants.py @@ -1,13 +1,13 @@ """ Constants for the core app. """ -class Status(object): +class Status: """Health statuses.""" OK = 'OK' UNAVAILABLE = 'UNAVAILABLE' -class Role(object): +class Role: """Named roles (django Groups).""" LEARNERS = 'Learners' ADMINS = 'Admins' diff --git a/credentials/apps/core/migrations/0001_initial.py b/credentials/apps/core/migrations/0001_initial.py index 87a505df8..9e2d35e7d 100644 --- a/credentials/apps/core/migrations/0001_initial.py +++ b/credentials/apps/core/migrations/0001_initial.py @@ -44,7 +44,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('lms_url_root', models.URLField(help_text="Root URL of this site's LMS (e.g. https://courses.stage.edx.org)", verbose_name='LMS base url for custom site')), ('theme_scss_path', models.CharField(help_text="Path to site theme's main SCSS file", max_length=255, verbose_name='Path to custom site theme')), - ('site', models.OneToOneField(to='sites.Site')), + ('site', models.OneToOneField(to='sites.Site', on_delete=models.CASCADE)), ], ), ] diff --git a/credentials/apps/core/migrations/0003_auto_20160331_0218.py b/credentials/apps/core/migrations/0003_auto_20160331_0218.py index 96846dc37..46470c775 100644 --- a/credentials/apps/core/migrations/0003_auto_20160331_0218.py +++ b/credentials/apps/core/migrations/0003_auto_20160331_0218.py @@ -13,13 +13,16 @@ def create_view_permission(apps, schema_editor): the ADMIN role. """ content_type = ContentType.objects.get(app_label="credentials", model="usercredential") - permission, created = Permission.objects.get_or_create( - content_type=content_type, - codename='view_usercredential', - name='Can view any user credential', - ) - if created: - Group.objects.get(name=Role.ADMINS).permissions.add(permission) + + # Django2.1 is creating view permission by default now. Adding check otherwise unique constraints triggers. + if not Permission.objects.filter(content_type=content_type, codename='view_usercredential').exists(): + permission, created = Permission.objects.get_or_create( + content_type=content_type, + codename='view_usercredential', + name='Can view any user credential', + ) + if created: + Group.objects.get(name=Role.ADMINS).permissions.add(permission) def destroy_view_permission(apps, schema_editor): diff --git a/credentials/apps/core/migrations/0022_last_name.py b/credentials/apps/core/migrations/0022_last_name.py new file mode 100644 index 000000000..74f2fdde2 --- /dev/null +++ b/credentials/apps/core/migrations/0022_last_name.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.13 on 2021-01-19 07:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0021_siteconfiguration_edx_org_short_name'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='last_name', + field=models.CharField(blank=True, max_length=150, verbose_name='last name'), + ), + ] diff --git a/credentials/apps/core/models.py b/credentials/apps/core/models.py index 368a6e893..593068b07 100644 --- a/credentials/apps/core/models.py +++ b/credentials/apps/core/models.py @@ -12,13 +12,13 @@ from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ from edx_rest_api_client.client import EdxRestApiClient -from jsonfield.fields import JSONField +from jsonfield import JSONField log = logging.getLogger(__name__) class SiteConfiguration(models.Model): - site = models.OneToOneField(Site, null=False, blank=False) + site = models.OneToOneField(Site, null=False, blank=False, on_delete=models.CASCADE) edx_org_short_name = models.CharField( max_length=255, unique=True, @@ -157,15 +157,15 @@ def __str__(self): @property def oauth2_provider_url(self): - return settings.SOCIAL_AUTH_EDX_OIDC_URL_ROOT + return settings.BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL @property def oauth2_client_id(self): - return settings.SOCIAL_AUTH_EDX_OIDC_KEY + return settings.BACKEND_SERVICE_EDX_OAUTH2_KEY @property def oauth2_client_secret(self): - return settings.SOCIAL_AUTH_EDX_OIDC_SECRET + return settings.BACKEND_SERVICE_EDX_OAUTH2_SECRET @property def user_api_url(self): @@ -289,7 +289,7 @@ def get_edly_configuration_value(self, name, default=None): class User(AbstractUser): - """ Custom user model for use with OpenID Connect. """ + """ Custom user model for use with python-social-auth via edx-auth-backends. """ full_name = models.CharField(_('Full Name'), max_length=255, blank=True, null=True) @property @@ -299,11 +299,11 @@ def access_token(self): Assumes user has authenticated at least once with edX Open ID Connect. """ try: - return self.social_auth.first().extra_data['access_token'] + return self.social_auth.first().extra_data['access_token'] # pylint: disable=no-member except Exception: # pylint: disable=broad-except return None - class Meta(object): + class Meta: get_latest_by = 'date_joined' def __str__(self): diff --git a/credentials/apps/core/tests/factories.py b/credentials/apps/core/tests/factories.py index 611a76bff..59aac8e9f 100644 --- a/credentials/apps/core/tests/factories.py +++ b/credentials/apps/core/tests/factories.py @@ -10,7 +10,7 @@ class UserFactory(django.DjangoModelFactory): - class Meta(object): + class Meta: model = User username = Sequence(lambda n: 'user_%d' % n) @@ -25,7 +25,7 @@ class Meta(object): class SiteFactory(django.DjangoModelFactory): - class Meta(object): + class Meta: model = Site domain = Faker('domain_name') @@ -33,7 +33,7 @@ class Meta(object): class SiteConfigurationFactory(django.DjangoModelFactory): - class Meta(object): + class Meta: model = SiteConfiguration site = SubFactory(SiteFactory) diff --git a/credentials/apps/core/tests/mixins.py b/credentials/apps/core/tests/mixins.py index 0e703499c..6714aaf76 100644 --- a/credentials/apps/core/tests/mixins.py +++ b/credentials/apps/core/tests/mixins.py @@ -10,7 +10,7 @@ JSON = 'application/json' -class SiteMixin(object): +class SiteMixin: def setUp(self): super(SiteMixin, self).setUp() cache.clear() @@ -28,7 +28,7 @@ def setUp(self): def mock_access_token_response(self, status=200): """ Mock the response from the OAuth provider's access token endpoint. """ - oauth2_provider_url = self.site.siteconfiguration.oauth2_provider_url # pylint: disable=no-member + oauth2_provider_url = self.site.siteconfiguration.oauth2_provider_url url = '{root}/access_token'.format(root=oauth2_provider_url) token = 'abc123' body = json.dumps({ @@ -41,7 +41,7 @@ def mock_access_token_response(self, status=200): def mock_catalog_api_response(self, endpoint, body, status=200): """ Mock a response from a Catalog API endpoint. """ - root = self.site.siteconfiguration.catalog_api_url.strip('/') # pylint: disable=no-member + root = self.site.siteconfiguration.catalog_api_url.strip('/') url = '{root}/{endpoint}'.format(root=root, endpoint=endpoint) responses.add(responses.GET, url, body=json.dumps(body), content_type=JSON, status=status, match_querystring=True) diff --git a/credentials/apps/core/tests/test_commands.py b/credentials/apps/core/tests/test_commands.py index 2eef41a4a..569ef8628 100644 --- a/credentials/apps/core/tests/test_commands.py +++ b/credentials/apps/core/tests/test_commands.py @@ -14,7 +14,6 @@ class CreateOrUpdateSiteCommandTests(SiteMixin, TestCase): faker = Faker() def setUp(self): - # pylint: disable=no-member super(CreateOrUpdateSiteCommandTests, self).setUp() self.site_configuration = SiteConfigurationFactory.build(segment_key=self.faker.word()) @@ -79,7 +78,7 @@ def _check_site_configuration(self, site_configuration): def test_create_site(self): """ Verify the command creates Site and SiteConfiguration. """ - site_domain = self.faker.domain_name() # pylint: disable=no-member + site_domain = self.faker.domain_name() self._call_command( site_domain=site_domain, @@ -91,7 +90,7 @@ def test_create_site(self): def test_update_site(self): """ Verify the command updates Site and SiteConfiguration. """ - expected_site_domain = self.faker.domain_name() # pylint: disable=no-member + expected_site_domain = self.faker.domain_name() expected_site_name = 'Fake Credentials Server' site = SiteFactory() @@ -149,7 +148,6 @@ def test_missing_arguments(self, command_args): def test_enable_social_sharing(self, flag_name): """ Verify the command supports activating social sharing functionality. """ - # pylint: disable=no-member self._call_command( site_domain=self.site.domain, site_name=self.site.name, @@ -163,7 +161,6 @@ def test_invalid_facebook_configuration(self): """ Verify the Facebook app ID is required to enable Facebook sharing. """ kwargs = {'enable_facebook_sharing': True} - # pylint: disable=no-member with self.assertRaisesMessage(CommandError, 'A Facebook app ID must be supplied to enable Facebook sharing'): self._call_command(site_domain=self.site.domain, site_name=self.site.name, **kwargs) diff --git a/credentials/apps/core/tests/test_views.py b/credentials/apps/core/tests/test_views.py index 38bca939f..03ab9a6c4 100644 --- a/credentials/apps/core/tests/test_views.py +++ b/credentials/apps/core/tests/test_views.py @@ -6,11 +6,10 @@ import mock from django.conf import settings from django.contrib.auth import get_user_model -from django.core.urlresolvers import clear_url_caches from django.db import DatabaseError from django.test import TestCase from django.test.utils import override_settings -from django.urls import reverse +from django.urls import clear_url_caches, reverse from credentials.apps.core.constants import Status from credentials.apps.core.tests.mixins import SiteMixin diff --git a/credentials/apps/core/utils.py b/credentials/apps/core/utils.py index 9bbab1d44..4aa71b353 100644 --- a/credentials/apps/core/utils.py +++ b/credentials/apps/core/utils.py @@ -2,7 +2,7 @@ # This function is used by our oauth2 authorization pipeline. See settings/base.py -def update_full_name(strategy, details, user=None, *_args, **_kwargs): +def update_full_name(strategy, details, user=None, *_args, **_kwargs): # pylint: disable=keyword-arg-before-vararg """Update the user's full name using data from provider.""" if user: full_name = details.get('full_name') diff --git a/credentials/apps/core/views.py b/credentials/apps/core/views.py index 3106e96e3..4d3ce1cd6 100644 --- a/credentials/apps/core/views.py +++ b/credentials/apps/core/views.py @@ -115,7 +115,7 @@ def try_select_theme_template(self, templates): return '' -def render_500(request, template_name='500.html'): # pylint: disable=unused-argument +def render_500(request, template_name='500.html'): """ Custom 500 error handler. Arguments: diff --git a/credentials/apps/credentials/admin.py b/credentials/apps/credentials/admin.py index f329860f6..b35ec15cb 100644 --- a/credentials/apps/credentials/admin.py +++ b/credentials/apps/credentials/admin.py @@ -40,7 +40,14 @@ class ProgramCertificateAdmin(TimeStampedModelAdminMixin, admin.ModelAdmin): form = ProgramCertificateAdminForm list_display = ('program_uuid', 'site', 'is_active') list_filter = ('is_active', 'site',) - search_fields = ('program_uuid',) + search_fields = ('program_uuid', ) + + def get_search_results(self, request, queryset, search_term): + + queryset, use_distinct = super(ProgramCertificateAdmin, self).get_search_results( + request, queryset, search_term.replace('-', '')) + + return queryset, use_distinct @admin.register(Signatory) diff --git a/credentials/apps/credentials/constants.py b/credentials/apps/credentials/constants.py index 64cfd17ab..d88745b46 100644 --- a/credentials/apps/credentials/constants.py +++ b/credentials/apps/credentials/constants.py @@ -7,14 +7,14 @@ UUID_PATTERN = r'(?P{})'.format(UUID_REGEX) -class UserCredentialStatus(object): +class UserCredentialStatus: """Allowed values for UserCredential.status""" AWARDED = 'awarded' REVOKED = 'revoked' -class CertificateType(object): +class CertificateType: """Allowed values for CourseCertificate.certificate_type""" HONOR = 'honor' diff --git a/credentials/apps/credentials/exceptions.py b/credentials/apps/credentials/exceptions.py index 49fc4204a..456bdabc8 100644 --- a/credentials/apps/credentials/exceptions.py +++ b/credentials/apps/credentials/exceptions.py @@ -2,4 +2,3 @@ class MissingCertificateLogoError(Exception): """ Raised when a Program fetched as part of a program certificate configuration has an organization without a defined certificate logo image url """ - pass diff --git a/credentials/apps/credentials/issuers.py b/credentials/apps/credentials/issuers.py index 2b403f791..260a2e5eb 100644 --- a/credentials/apps/credentials/issuers.py +++ b/credentials/apps/credentials/issuers.py @@ -17,7 +17,7 @@ @six.add_metaclass(abc.ABCMeta) -class AbstractCredentialIssuer(object): +class AbstractCredentialIssuer: """ Abstract credential issuer. @@ -36,7 +36,12 @@ def issued_credential_type(self): raise NotImplementedError # pragma: no cover @transaction.atomic - def issue_credential(self, credential, username, status=UserCredentialStatus.AWARDED, attributes=None): + def issue_credential( + self, credential, username, + status=UserCredentialStatus.AWARDED, + attributes=None, + request=None + ): """ Issue a credential to the user. @@ -48,6 +53,7 @@ def issue_credential(self, credential, username, status=UserCredentialStatus.AWA username (str): username of user for which credential required status (str): status of credential attributes (List[dict]): optional list of attributes that should be associated with the issued credential. + request (HttpRequest): request object to build program record absolute uris Returns: UserCredential @@ -93,7 +99,12 @@ class ProgramCertificateIssuer(AbstractCredentialIssuer): issued_credential_type = ProgramCertificate @transaction.atomic - def issue_credential(self, credential, username, status=UserCredentialStatus.AWARDED, attributes=None): + def issue_credential( + self, credential, username, + status=UserCredentialStatus.AWARDED, + attributes=None, + request=None + ): """ Issue a Program Certificate to the user. @@ -109,6 +120,7 @@ def issue_credential(self, credential, username, status=UserCredentialStatus.AWA username (str): username of user for which credential required status (str): status of credential attributes (List[dict]): optional list of attributes that should be associated with the issued credential. + request (HttpRequest): request object to build program record absolute uris Returns: UserCredential @@ -125,8 +137,11 @@ def issue_credential(self, credential, username, status=UserCredentialStatus.AWA # Send an updated email to a pathway org only if the user has previously sent one # This function call should be moved into some type of task queue # once credentials has that functionality - if created: - send_updated_emails_for_program(username, credential) + site_config = getattr(credential.site, 'siteconfiguration', None) + # Add a check to see if records_enabled is True for the site associated with + # the credentials. If records is not enabled, we should not send this email + if created and site_config and site_config.records_enabled: + send_updated_emails_for_program(request, username, credential) self.set_credential_attributes(user_credential, attributes) diff --git a/credentials/apps/credentials/migrations/0001_initial.py b/credentials/apps/credentials/migrations/0001_initial.py index 939f5abc5..73b8bbe83 100644 --- a/credentials/apps/credentials/migrations/0001_initial.py +++ b/credentials/apps/credentials/migrations/0001_initial.py @@ -98,7 +98,7 @@ class Migration(migrations.Migration): ('status', models.CharField(default='awarded', max_length=255, choices=[('awarded', 'awarded'), ('revoked', 'revoked')])), ('download_url', models.CharField(help_text='Download URL for the PDFs.', max_length=255, null=True, blank=True)), ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)), - ('credential_content_type', models.ForeignKey(to='contenttypes.ContentType')), + ('credential_content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)), ], ), migrations.CreateModel( @@ -109,7 +109,7 @@ class Migration(migrations.Migration): ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), ('name', models.CharField(max_length=255)), ('value', models.CharField(max_length=255)), - ('user_credential', models.ForeignKey(related_name='attributes', to='credentials.UserCredential')), + ('user_credential', models.ForeignKey(related_name='attributes', to='credentials.UserCredential', on_delete=models.CASCADE)), ], ), migrations.AddField( @@ -120,12 +120,12 @@ class Migration(migrations.Migration): migrations.AddField( model_name='programcertificate', name='site', - field=models.ForeignKey(to='sites.Site'), + field=models.ForeignKey(to='sites.Site', on_delete=models.CASCADE), ), migrations.AddField( model_name='programcertificate', name='template', - field=models.ForeignKey(blank=True, to='credentials.CertificateTemplate', null=True), + field=models.ForeignKey(blank=True, to='credentials.CertificateTemplate', null=True, on_delete=models.CASCADE), ), migrations.AddField( model_name='coursecertificate', @@ -135,12 +135,12 @@ class Migration(migrations.Migration): migrations.AddField( model_name='coursecertificate', name='site', - field=models.ForeignKey(to='sites.Site'), + field=models.ForeignKey(to='sites.Site', on_delete=models.CASCADE), ), migrations.AddField( model_name='coursecertificate', name='template', - field=models.ForeignKey(blank=True, to='credentials.CertificateTemplate', null=True), + field=models.ForeignKey(blank=True, to='credentials.CertificateTemplate', null=True, on_delete=models.CASCADE), ), migrations.AlterUniqueTogether( name='usercredentialattribute', diff --git a/credentials/apps/credentials/migrations/0016_credential_content_type.py b/credentials/apps/credentials/migrations/0016_credential_content_type.py new file mode 100644 index 000000000..319822038 --- /dev/null +++ b/credentials/apps/credentials/migrations/0016_credential_content_type.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.13 on 2021-01-19 07:39 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('credentials', '0015_auto_20180822_1648'), + ] + + operations = [ + migrations.AlterField( + model_name='usercredential', + name='credential_content_type', + field=models.ForeignKey(limit_choices_to={'model__in': ('coursecertificate', 'programcertificate')}, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'), + ), + ] diff --git a/credentials/apps/credentials/models.py b/credentials/apps/credentials/models.py index 57a89633c..e20da8faa 100644 --- a/credentials/apps/credentials/models.py +++ b/credentials/apps/credentials/models.py @@ -56,10 +56,10 @@ class AbstractCredential(TimeStampedModel): """ Abstract Credentials configuration model. """ - site = models.ForeignKey(Site) + site = models.ForeignKey(Site, on_delete=models.CASCADE) is_active = models.BooleanField(default=False) - class Meta(object): + class Meta: abstract = True @@ -81,7 +81,7 @@ class Signatory(TimeStampedModel): validators=[validate_image] ) - class Meta(object): + class Meta: verbose_name_plural = 'Signatories' def __str__(self): @@ -115,7 +115,7 @@ class AbstractCertificate(AbstractCredential): help_text='Custom certificate title to override default display_name for a course/program.' ) - class Meta(object): + class Meta: abstract = True @@ -133,7 +133,8 @@ class UserCredential(TimeStampedModel): ) credential_content_type = models.ForeignKey( - ContentType, limit_choices_to={'model__in': ('coursecertificate', 'programcertificate')} + ContentType, limit_choices_to={'model__in': ('coursecertificate', 'programcertificate')}, + on_delete=models.CASCADE ) credential_id = models.PositiveIntegerField() credential = GenericForeignKey('credential_content_type', 'credential_id') @@ -153,7 +154,7 @@ class UserCredential(TimeStampedModel): ) uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) - class Meta(object): + class Meta: unique_together = (('username', 'credential_content_type', 'credential_id'),) def get_absolute_url(self): @@ -186,7 +187,7 @@ class CourseCertificate(AbstractCertificate): related_query_name='course_credentials' ) - class Meta(object): + class Meta: unique_together = (('course_id', 'certificate_type', 'site'),) verbose_name = "Course certificate configuration" @@ -234,13 +235,13 @@ class ProgramCertificate(AbstractCertificate): def __str__(self): return 'ProgramCertificate: {uuid}'.format(uuid=self.program_uuid) - class Meta(object): + class Meta: verbose_name = "Program certificate configuration" unique_together = (('site', 'program_uuid'),) def get_program_api_data(self): """ Returns program data from the Catalog API. """ - return self.site.siteconfiguration.get_program(self.program_uuid) + return self.site.siteconfiguration.get_program(self.program_uuid) # pylint: disable=no-member # TODO: drop this query in favor of our local copy of # catalog data (and start copying all data we need) @@ -278,9 +279,9 @@ class UserCredentialAttribute(TimeStampedModel): """ Different attributes of User's Credential such as white list, grade etc. """ - user_credential = models.ForeignKey(UserCredential, related_name='attributes') + user_credential = models.ForeignKey(UserCredential, related_name='attributes', on_delete=models.CASCADE) name = models.CharField(max_length=255) value = models.CharField(max_length=255) - class Meta(object): + class Meta: unique_together = (('user_credential', 'name'),) diff --git a/credentials/apps/credentials/tests/factories.py b/credentials/apps/credentials/tests/factories.py index d537ad842..45d58b6a2 100644 --- a/credentials/apps/credentials/tests/factories.py +++ b/credentials/apps/credentials/tests/factories.py @@ -13,7 +13,7 @@ class AbstractCertificateFactory(factory.django.DjangoModelFactory): class CourseCertificateFactory(AbstractCertificateFactory): - class Meta(object): + class Meta: model = models.CourseCertificate course_id = factory.Sequence(lambda o: 'course-%d' % o) @@ -22,7 +22,7 @@ class Meta(object): class ProgramCertificateFactory(AbstractCertificateFactory): - class Meta(object): + class Meta: model = models.ProgramCertificate is_active = True @@ -30,7 +30,7 @@ class Meta(object): class UserCredentialFactory(factory.django.DjangoModelFactory): - class Meta(object): + class Meta: model = models.UserCredential credential = factory.SubFactory(ProgramCertificateFactory) @@ -41,7 +41,7 @@ class Meta(object): class UserCredentialAttributeFactory(factory.django.DjangoModelFactory): - class Meta(object): + class Meta: model = models.UserCredentialAttribute user_credential = factory.SubFactory(UserCredentialFactory) @@ -50,7 +50,7 @@ class Meta(object): class SignatoryFactory(factory.django.DjangoModelFactory): - class Meta(object): + class Meta: model = models.Signatory name = factory.Faker('name') diff --git a/credentials/apps/credentials/tests/test_forms.py b/credentials/apps/credentials/tests/test_forms.py index b66185126..c14ce50eb 100644 --- a/credentials/apps/credentials/tests/test_forms.py +++ b/credentials/apps/credentials/tests/test_forms.py @@ -30,7 +30,7 @@ def test_program_uuid(self): no certificate images. """ sc = SiteConfigurationFactory() data = factory.build(dict, FACTORY_CLASS=ProgramCertificateFactory) - data['site'] = sc.site.id # pylint: disable=no-member + data['site'] = sc.site.id form = ProgramCertificateAdminForm(data) with mock.patch.object(SiteConfiguration, 'get_program', return_value=self.BAD_MOCK_API_PROGRAM) as mock_method: diff --git a/credentials/apps/credentials/tests/test_issuer.py b/credentials/apps/credentials/tests/test_issuer.py index f5ca44f04..b06ba1406 100644 --- a/credentials/apps/credentials/tests/test_issuer.py +++ b/credentials/apps/credentials/tests/test_issuer.py @@ -1,11 +1,12 @@ """ Tests for Issuer class. """ +import mock from django.test import TestCase from credentials.apps.api.exceptions import DuplicateAttributeError from credentials.apps.catalog.tests.factories import ProgramFactory -from credentials.apps.core.tests.factories import SiteFactory, UserFactory +from credentials.apps.core.tests.factories import SiteConfigurationFactory, SiteFactory, UserFactory from credentials.apps.credentials.issuers import CourseCertificateIssuer, ProgramCertificateIssuer from credentials.apps.credentials.models import CourseCertificate, ProgramCertificate, UserCredentialAttribute from credentials.apps.credentials.tests.factories import CourseCertificateFactory, ProgramCertificateFactory @@ -14,7 +15,7 @@ # pylint: disable=no-member -class CertificateIssuerBase(object): +class CertificateIssuerBase: """ Tests an Issuer class and its methods.""" issuer = None cert_factory = None @@ -134,14 +135,35 @@ class ProgramCertificateIssuerTests(CertificateIssuerBase, TestCase): cert_type = ProgramCertificate def setUp(self): + super().setUp() self.site = SiteFactory() + self.site_config = SiteConfigurationFactory(site=self.site) self.program = ProgramFactory(site=self.site) self.certificate = self.cert_factory.create(program_uuid=self.program.uuid, site=self.site) - self.username = 'tester' + self.username = 'tester2' self.user = UserFactory(username=self.username) self.user_cred = self.issuer.issue_credential(self.certificate, self.username) self.attributes = [{"name": "whitelist_reason", "value": "Reason for whitelisting."}] + def test_records_enabled_is_unchecked(self): + """Verify that if SiteConfiguration.records_enabled is unchecked then don't send + updated email to a pathway org. + """ + self.site_config.records_enabled = False + self.site_config.save() + + with mock.patch('credentials.apps.credentials.issuers.send_updated_emails_for_program') as mock_method: + self.issuer.issue_credential(self.certificate, 'testuser3', attributes=self.attributes) + self.assertEqual(mock_method.call_count, 0) + + def test_records_enabled_is_checked(self): + """Verify that if SiteConfiguration.records_enabled is checked and new record is created + then updated email is sent to a pathway org. + """ + with mock.patch('credentials.apps.credentials.issuers.send_updated_emails_for_program') as mock_method: + self.issuer.issue_credential(self.certificate, 'testuser4', attributes=self.attributes) + self.assertEqual(mock_method.call_count, 1) + class CourseCertificateIssuerTests(CertificateIssuerBase, TestCase): """ Tests for course Issuer class and its methods.""" diff --git a/credentials/apps/credentials/tests/test_views.py b/credentials/apps/credentials/tests/test_views.py index 4e3b5b10c..2bb0ff759 100644 --- a/credentials/apps/credentials/tests/test_views.py +++ b/credentials/apps/credentials/tests/test_views.py @@ -22,7 +22,6 @@ @ddt.ddt -# pylint: disable=no-member class RenderCredentialViewTests(SiteMixin, TestCase): faker = Faker() MOCK_USER_DATA = {'username': 'test-user', 'name': 'Test User', 'email': 'test@example.org', } diff --git a/credentials/apps/credentials_theme_openedx/static/sass/_print-rendering.scss b/credentials/apps/credentials_theme_openedx/static/sass/_print-rendering.scss index b380701a8..416d3ee2d 100644 --- a/credentials/apps/credentials_theme_openedx/static/sass/_print-rendering.scss +++ b/credentials/apps/credentials_theme_openedx/static/sass/_print-rendering.scss @@ -10,77 +10,88 @@ // ------------------------------ // #LAYOUT // ------------------------------ -@media print { - - .accomplishment-rendering { - @include text-align(left); - top: 0 !important; - box-shadow: none; - border: none; - padding: 0.625rem 2.5rem !important; - background-color: $white; - - .wrapper-accomplishment-title { - @include make-row(); - - .accomplishment-title { - @include make-col-ready(); - @include make-col(8); - @extend %mt-2, %mb-0; - display: inline-block; - vertical-align: middle; - } - } - - .wrapper-accomplishment-orgs { - @include make-col-ready(); - @include make-col(4); - display: inline-block; - vertical-align: middle; - margin-bottom: 30px !important; - - img { - max-width: 100%; - max-height: 100px; - } - } - - .wrapper-accomplishment-statement { - @include make-row(); - } - - .accomplishment-statement { - @include make-col-ready(); - @include make-col(8); - @include text-align(left); - display: inline-block; - vertical-align: top; - border-bottom: 0; - margin: 0 !important; - padding-bottom: 1rem !important; - } - - .accomplishment-signatories { - @include make-col-ready(); - @include make-col(4); - @include text-align(right); - display: inline-block; - vertical-align: top; - border-bottom: 0; - padding-bottom: 0 !important; - } - - .list-signatories { - display: flex; - flex-flow: row wrap; - } +@page { + size: landscape; +} - .signatory { - @extend %m-0, %mb-2; - width: 100%; +@media print { - &:last-child { - @extend %mb-0; + .certificate { + background: white; + + .accomplishment { + max-width: 100%; + + .accomplishment-rendering { + @include text-align(left); + top: 0 !important; + box-shadow: none; + padding: 0.625rem 2.5rem !important; + background-color: $white; + + .wrapper-accomplishment-title { + @include make-row(); + + .accomplishment-title { + @include make-col-ready(); + @include make-col(8); + @extend %mt-2, %mb-0; + display: inline-block; + vertical-align: middle; + } + } + + .wrapper-accomplishment-orgs { + @include make-col-ready(); + @include make-col(4); + display: inline-block; + vertical-align: middle; + margin-bottom: 30px !important; + + img { + max-width: 100%; + max-height: 100px; + } + } + + .wrapper-accomplishment-statement { + @include make-row(); + } + + .accomplishment-statement { + @include make-col-ready(); + @include make-col(8); + @include text-align(left); + display: inline-block; + vertical-align: top; + border-bottom: 0; + margin: 0 !important; + padding-bottom: 1rem !important; + } + + .accomplishment-signatories { + @include make-col-ready(); + @include make-col(4); + @include text-align(right); + display: inline-block; + vertical-align: top; + border-bottom: 0; + padding-bottom: 0 !important; + } + + .list-signatories { + display: flex; + flex-flow: row wrap; + } + + .signatory { + @extend %m-0, %mb-2; + width: 100%; + + &:last-child { + @extend %mb-0; + } + } } } } diff --git a/credentials/apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html b/credentials/apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html index bf5eb7fef..731856cdb 100644 --- a/credentials/apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html +++ b/credentials/apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html @@ -1,7 +1,7 @@ {% extends 'credentials/programs/base.html' %} {% load i18n_assets %} {% load i18n %} -{% load staticfiles %} +{% load static %} {% block accomplishment_summary %} {% trans "successfully completed all courses and received passing grades for a Professional Certificate in" as tmsg %} {{ tmsg | force_escape }} diff --git a/credentials/apps/edx_credentials_extensions/edly_credentials_app/api/v1/urls.py b/credentials/apps/edx_credentials_extensions/edly_credentials_app/api/v1/urls.py index 74ee2ffae..a5887f8f1 100644 --- a/credentials/apps/edx_credentials_extensions/edly_credentials_app/api/v1/urls.py +++ b/credentials/apps/edx_credentials_extensions/edly_credentials_app/api/v1/urls.py @@ -4,6 +4,6 @@ router = routers.SimpleRouter() -router.register(r'program-certificate-configuration', ProgramCertificateConfigurationViewSet, base_name='program-certificate-configuration') +router.register(r'program-certificate-configuration', ProgramCertificateConfigurationViewSet, basename='program-certificate-configuration') urlpatterns = router.urls diff --git a/credentials/apps/edx_credentials_extensions/edly_credentials_app/middleware.py b/credentials/apps/edx_credentials_extensions/edly_credentials_app/middleware.py index 1698a941f..b7b82797f 100644 --- a/credentials/apps/edx_credentials_extensions/edly_credentials_app/middleware.py +++ b/credentials/apps/edx_credentials_extensions/edly_credentials_app/middleware.py @@ -1,11 +1,14 @@ from logging import getLogger from django.conf import settings from django.contrib.sites.shortcuts import get_current_site +from django.utils.deprecation import MiddlewareMixin + +from credentials.apps.core.models import SiteConfiguration logger = getLogger(__name__) -class SettingsOverrideMiddleware(object): +class SettingsOverrideMiddleware(MiddlewareMixin): """ Django middleware to override django settings from site configuration. """ diff --git a/credentials/apps/edx_django_extensions/tests/test_views.py b/credentials/apps/edx_django_extensions/tests/test_views.py index 9698af356..b46640573 100644 --- a/credentials/apps/edx_django_extensions/tests/test_views.py +++ b/credentials/apps/edx_django_extensions/tests/test_views.py @@ -1,3 +1,4 @@ +import django import mock from django.contrib import messages from django.test import TestCase @@ -40,14 +41,19 @@ def test_superuser_required(self): user = UserFactory() self.client.login(username=user.username, password=USER_PASSWORD) response = self.client.get(self.path) - self.assertEqual(response.status_code, 302) + + expected_code = 302 + if django.VERSION >= (2, 1): # Authenticated users are denied access with an HTTP 403 Forbidden response + expected_code = 403 + + self.assertEqual(response.status_code, expected_code) def test_invalid_action(self): """ Verify the view responds with an error message if an invalid action is posted. """ - response = self.client.post(self.path, {'action': None}) + response = self.client.post(self.path, {'action': ''}) self.assertEqual(response.status_code, 200) self.assert_message_count(response, 1) - self.assert_first_message(response, messages.ERROR, 'None is not a valid action.') + self.assert_first_message(response, messages.ERROR, ' is not a valid action.') @mock.patch('logging.Logger.info') @mock.patch('django.core.cache.cache.clear') diff --git a/credentials/apps/records/admin.py b/credentials/apps/records/admin.py index 05dd89669..ccea4f6d5 100644 --- a/credentials/apps/records/admin.py +++ b/credentials/apps/records/admin.py @@ -9,7 +9,7 @@ class ProgramCertRecordAdmin(admin.ModelAdmin): """ Admin for the ProgramCertRecord model.""" list_display = ('uuid', 'program', 'user',) - search_fields = ('uuid', 'program', 'user',) + search_fields = ('uuid', 'program__title', 'user__username',) raw_id_fields = ('program', 'user',) exclude = ('certificate',) diff --git a/credentials/apps/records/constants.py b/credentials/apps/records/constants.py index 325b5d74f..c5dff1b0e 100644 --- a/credentials/apps/records/constants.py +++ b/credentials/apps/records/constants.py @@ -1,6 +1,6 @@ RECORDS_RATE_LIMIT = '15/m' # This rate was arbitrarily chosen due to a lack of data. It may need to be changed later. -class UserCreditPathwayStatus(object): +class UserCreditPathwayStatus: """ Allowed values for UserCreditPathway.status """ SENT = 'sent' diff --git a/credentials/apps/records/management/commands/seed-records.py b/credentials/apps/records/management/commands/seed-records.py index 41349b153..546e33807 100644 --- a/credentials/apps/records/management/commands/seed-records.py +++ b/credentials/apps/records/management/commands/seed-records.py @@ -142,9 +142,10 @@ def seed_course_runs(courses, faker): course_run, created = CourseRun.objects.get_or_create( course=course, uuid=faker.uuid4(), - start=datetime(2018, 1, 1), - end=datetime(2018, 6, 1), - key=key) + start_date=datetime(2018, 1, 1), + end_date=datetime(2018, 6, 1), + key=key, + ) Command.log_action("CourseRun for", course.title, created) diff --git a/credentials/apps/records/messages.py b/credentials/apps/records/messages.py index 43250f827..63f208766 100644 --- a/credentials/apps/records/messages.py +++ b/credentials/apps/records/messages.py @@ -2,7 +2,7 @@ class ProgramCreditRequest(MessageType): - def __init__(self, site, *args, **kwargs): + def __init__(self, site, user_email=None, *args, **kwargs): # pylint: disable=keyword-arg-before-vararg super(ProgramCreditRequest, self).__init__(*args, **kwargs) if site.siteconfiguration.partner_from_address: @@ -10,9 +10,14 @@ def __init__(self, site, *args, **kwargs): else: from_address = 'no-reply@' + site.domain - self.options.update({ # pylint: disable=no-member + if user_email: + self.options.update({ + 'reply_to': [user_email], + }) + + self.options.update({ 'from_address': from_address, }) - self.context.update({ # pylint: disable=no-member + self.context.update({ 'platform_name': site.siteconfiguration.platform_name, }) diff --git a/credentials/apps/records/migrations/0017_ProgramCertRecord_unique_constraint.py b/credentials/apps/records/migrations/0017_ProgramCertRecord_unique_constraint.py new file mode 100644 index 000000000..bfbacc343 --- /dev/null +++ b/credentials/apps/records/migrations/0017_ProgramCertRecord_unique_constraint.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.15 on 2019-05-03 12:41 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('catalog', '0010_auto_20180828_1336'), + ('records', '0016_auto_20180822_1655'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='programcertrecord', + unique_together=set([('program', 'user')]), + ), + ] diff --git a/credentials/apps/records/migrations/0018_change_learners_course_run.py b/credentials/apps/records/migrations/0018_change_learners_course_run.py new file mode 100644 index 000000000..3107d25a8 --- /dev/null +++ b/credentials/apps/records/migrations/0018_change_learners_course_run.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.27 on 2020-04-16 15:58 +from __future__ import unicode_literals + +from django.db import migrations + + +def change_learners_course_run(apps, schema_editor): + """ + There are two course runs for course-v1:TUMx+QPLS2x+2T2018 and + one of them is excluded from the program. But user grades are + associated with excluded course run. That's why in program record + status of the course course-v1:TUMx+QPLS2x+2T2018 is not earned for + the learners even though learners program is completed. + PROD-1443 + + Changing course run id in user grade would fix that issue. + + """ + + excluded_run, included_run = get_course_runs(apps, 119, 8112) + + if excluded_run and included_run: + update_course_run_id(apps, excluded_run, included_run) + + +def reverse_change_learners_course_run(apps, schema_editor): + + excluded_run, included_run = get_course_runs(apps, 8112, 119) + + if excluded_run and included_run: + update_course_run_id(apps, excluded_run, included_run) + + +def get_course_runs(apps, excluded_run_id, included_run_id): + """ + get excluded and included course runs based on the id + """ + CourseRun = apps.get_model('catalog', 'CourseRun') + courses = CourseRun.objects.filter(key='course-v1:TUMx+QPLS2x+2T2018') + excluded_run = None + included_run = None + if courses: + excluded_run = courses.filter(id=excluded_run_id).first() + included_run = courses.filter(id=included_run_id).first() + + return excluded_run, included_run + + +def update_course_run_id(apps, excluded_run, included_run): + """ + update the learner course run id in grades + """ + UserGrade = apps.get_model('records', 'UserGrade') + UserGrade.objects.filter(course_run_id=excluded_run.id).update(course_run_id=included_run.id) + + +class Migration(migrations.Migration): + + dependencies = [ + ('records', '0017_ProgramCertRecord_unique_constraint'), + ] + + operations = [ + migrations.RunPython(change_learners_course_run, reverse_code=reverse_change_learners_course_run) + ] diff --git a/credentials/apps/records/models.py b/credentials/apps/records/models.py index 144452970..7b1692d68 100644 --- a/credentials/apps/records/models.py +++ b/credentials/apps/records/models.py @@ -20,12 +20,12 @@ class UserGrade(TimeStampedModel): A grade for a specific user and course run """ username = models.CharField(max_length=150, blank=False) - course_run = models.ForeignKey(CourseRun) + course_run = models.ForeignKey(CourseRun, on_delete=models.CASCADE) letter_grade = models.CharField(max_length=255, blank=True) percent_grade = models.DecimalField(max_digits=5, decimal_places=4, null=False) verified = models.BooleanField(verbose_name='Verified Learner ID', default=True) - class Meta(object): + class Meta: unique_together = ('username', 'course_run') @@ -37,17 +37,19 @@ class ProgramCertRecord(TimeStampedModel): ProgramCertificate, null=True, default=None, - help_text='Note: certificate is deprecated, and is kept around because it is used in an old data migration.' + help_text='Note: certificate is deprecated, and is kept around because it is used in an old data migration.', + on_delete=models.CASCADE ) - program = models.ForeignKey(Program) - user = models.ForeignKey(User) + program = models.ForeignKey(Program, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) def __str__(self): return 'ProgramCertificateRecord: {uuid}'.format(uuid=self.uuid) - class Meta(object): + class Meta: verbose_name = "Shared program record" + unique_together = ('program', 'user') class UserCreditPathway(TimeStampedModel): @@ -61,8 +63,8 @@ class UserCreditPathway(TimeStampedModel): ('', _('other')), ] - user = models.ForeignKey(User) - pathway = models.ForeignKey(Pathway) + user = models.ForeignKey(User, on_delete=models.CASCADE) + pathway = models.ForeignKey(Pathway, on_delete=models.CASCADE) status = models.CharField( max_length=15, choices=STATUS_CHOICES, @@ -79,5 +81,5 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ self.full_clean() return super(UserCreditPathway, self).save(*args, **kwargs) - class Meta(object): + class Meta: unique_together = ('user', 'pathway') diff --git a/credentials/apps/records/tests/factories.py b/credentials/apps/records/tests/factories.py index 62baa646a..6c1e4e33a 100644 --- a/credentials/apps/records/tests/factories.py +++ b/credentials/apps/records/tests/factories.py @@ -9,7 +9,7 @@ class UserGradeFactory(factory.django.DjangoModelFactory): - class Meta(object): + class Meta: model = models.UserGrade username = factory.Sequence(lambda o: 'robot%d' % o) @@ -20,7 +20,7 @@ class Meta(object): class ProgramCertRecordFactory(factory.django.DjangoModelFactory): - class Meta(object): + class Meta: model = models.ProgramCertRecord uuid = factory.LazyFunction(uuid4) @@ -29,7 +29,7 @@ class Meta(object): class UserCreditPathwayFactory(factory.django.DjangoModelFactory): - class Meta(object): + class Meta: model = models.UserCreditPathway user = factory.SubFactory(UserFactory) diff --git a/credentials/apps/records/tests/test_utils.py b/credentials/apps/records/tests/test_utils.py index ba17e4be6..3b335eea3 100644 --- a/credentials/apps/records/tests/test_utils.py +++ b/credentials/apps/records/tests/test_utils.py @@ -1,7 +1,10 @@ +import urllib + from django.core import mail from django.test import TestCase from django.test.utils import override_settings from django.urls import reverse +from rest_framework.test import APIRequestFactory from credentials.apps.catalog.tests.factories import PathwayFactory, ProgramFactory from credentials.apps.core.tests.factories import USER_PASSWORD, UserFactory @@ -29,6 +32,7 @@ def setUp(self): self.pcr = ProgramCertRecordFactory(program=self.program, user=self.user) self.data = {'username': self.USERNAME, 'pathway_id': self.pathway.id} self.url = reverse('records:share_program', kwargs={'uuid': self.program.uuid.hex}) + self.request = APIRequestFactory().get('/') mail.outbox = [] @@ -43,12 +47,17 @@ def test_send_updated_email_when_program_finished(self): status=UserCreditPathwayStatus.SENT) self.assertEqual(0, len(mail.outbox)) - send_updated_emails_for_program(self.USERNAME, self.pc) + send_updated_emails_for_program(self.request, self.USERNAME, self.pc) # Check that another email was sent self.assertEqual(1, len(mail.outbox)) email = mail.outbox[0] + record_path = reverse('records:public_programs', kwargs={'uuid': self.pcr.uuid.hex}) + expected_record_link = self.request.build_absolute_uri(record_path) + expected_csv_link = urllib.parse.urljoin(expected_record_link, "csv") self.assertIn(self.program.title + ' Updated Credit Request for', email.subject) + self.assertIn(expected_record_link, email.body) + self.assertIn(expected_csv_link, email.body) def test_no_previous_email_sent(self): """ @@ -56,7 +65,7 @@ def test_no_previous_email_sent(self): """ self.assertEqual(0, len(mail.outbox)) - send_updated_emails_for_program(self.USERNAME, self.pc) + send_updated_emails_for_program(self.request, self.USERNAME, self.pc) # Check that no email was sent self.assertEqual(0, len(mail.outbox)) diff --git a/credentials/apps/records/tests/test_views.py b/credentials/apps/records/tests/test_views.py index 9ddb10ac1..3b2f31dcd 100644 --- a/credentials/apps/records/tests/test_views.py +++ b/credentials/apps/records/tests/test_views.py @@ -99,7 +99,7 @@ def test_no_anonymous_access(self): """ Verify that the view rejects non-logged-in users. """ self.client.logout() response = self._render_records(status_code=302) - self.assertRegex(response.url, '^/login/.*') # pylint: disable=deprecated-method + self.assertRegex(response.url, '^/login/.*') def test_normal_access(self): """ Verify that the view works in default case. """ @@ -325,7 +325,7 @@ def test_no_anonymous_access(self): """ Verify that the view rejects non-logged-in users. """ self.client.logout() response = self._render_listing(status_code=302) - self.assertRegexpMatches(response.url, '^/login/.*') # pylint: disable=deprecated-method + self.assertRegex(response.url, '^/login/.*') def test_only_superuser_access(self): """ Verify that the view rejects non-superusers. """ @@ -343,7 +343,7 @@ def test_normal_access(self): actual_child_templates = response_context_data['child_templates'] self.assert_matching_template_origin(actual_child_templates['footer'], '_footer.html') self.assert_matching_template_origin(actual_child_templates['header'], '_header.html') - self.assertFalse('masquerade' in actual_child_templates) # no masquerading on this view + self.assertNotIn('masquerade', actual_child_templates) # no masquerading on this view @ddt.data( (Program.ACTIVE, True), @@ -418,7 +418,7 @@ def setUp(self): self.pcr = ProgramCertRecordFactory(program=self.program, user=self.user) self.pathway = PathwayFactory(site=self.site) - self.pathway.programs = [self.program] + self.pathway.programs.set([self.program]) # pylint: disable=no-member def _render_program_record(self, record_data=None, status_code=200): """ Helper method to mock rendering a user certificate.""" @@ -440,7 +440,7 @@ def test_no_anonymous_access_private(self): """ Verify that the private view rejects non-logged-in users. """ self.client.logout() response = self._render_program_record(status_code=302) - self.assertRegex(response.url, '^/login/.*') # pylint: disable=deprecated-method + self.assertRegex(response.url, '^/login/.*') def test_anonymous_access_public(self): """ Verify that the public view does not reject non-logged-in users""" @@ -565,7 +565,7 @@ def test_program_visible_date(self, date, completed): def test_organization_order(self): """ Test that the organizations are returned in the order they were added """ - self.course.owners = self.orgs + self.course.owners.set(self.orgs) # pylint: disable=no-member response = self.client.get(reverse('records:private_programs', kwargs={'uuid': self.program.uuid.hex})) program_data = json.loads(response.context_data['record'])['program'] grade = json.loads(response.context_data['record'])['grades'][0] @@ -576,7 +576,7 @@ def test_organization_order(self): def test_course_run_order(self): """ Test that the course_runs are returned in the program order """ new_course_run = CourseRunFactory() - self.program.course_runs.add(new_course_run) + self.program.course_runs.add(new_course_run) # pylint: disable=no-member UserGradeFactory(username=self.MOCK_USER_DATA['username'], course_run=new_course_run, letter_grade='C', percent_grade=.70) @@ -596,7 +596,7 @@ def test_course_run_order(self): def test_course_run_no_credential(self): """ Adds a course run with no credential and tests that it does appear in the results """ new_course_run = CourseRunFactory() - self.program.course_runs.add(new_course_run) + self.program.course_runs.add(new_course_run) # pylint: disable=no-member UserGradeFactory(username=self.MOCK_USER_DATA['username'], course_run=new_course_run, letter_grade='F', percent_grade=.05) @@ -621,7 +621,7 @@ def test_multiple_attempts_no_cert(self): course_run=course_run, letter_grade='F', percent_grade=0.20) for course_run in new_course_runs] - self.program.course_runs = new_course_runs + self.program.course_runs.set(new_course_runs) # pylint: disable=no-member response = self.client.get(reverse('records:private_programs', kwargs={'uuid': self.program.uuid.hex})) grades = json.loads(response.context_data['record'])['grades'] self.assertEqual(len(grades), 1) @@ -745,7 +745,7 @@ def test_user_creation(self): json_data = response.json() self.assertEqual(response.status_code, 201) - self.assertRegex(json_data['url'], UUID_PATTERN) # pylint: disable=deprecated-method + self.assertRegex(json_data['url'], UUID_PATTERN) def test_different_user_creation(self): """ Verify that the view rejects a User attempting to create a ProgramCertRecord for another """ @@ -853,7 +853,7 @@ def test_from_address_unset(self, mock_ace): response = self.post() self.assertEqual(response.status_code, 200) self.assertEqual(mock_ace.send.call_args[0][0].options['from_address'], - 'no-reply@' + self.site.domain) # pylint: disable=no-member + 'no-reply@' + self.site.domain) def test_email_content_complete(self): """Verify an email is actually sent""" @@ -861,7 +861,7 @@ def test_email_content_complete(self): self.assertEqual(response.status_code, 200) public_record = ProgramCertRecord.objects.get(user=self.user, program=self.program) record_path = reverse('records:public_programs', kwargs={'uuid': public_record.uuid.hex}) - record_link = "http://" + self.site.domain + record_path # pylint: disable=no-member + record_link = "http://" + self.site.domain + record_path csv_link = urllib.parse.urljoin(record_link, "csv") # Check output and make sure it seems correct @@ -875,6 +875,7 @@ def test_email_content_complete(self): self.assertIn("View Program Record", message) self.assertIn("Download Record (CSV)", message) self.assertEqual(self.site_configuration.partner_from_address, email.from_email) + self.assertEqual(self.user.email, email.reply_to[0]) self.assertListEqual([self.pathway.email], email.to) def test_email_content_incomplete(self): @@ -964,6 +965,25 @@ def tests_creates_csv(self, segment_client, track): # pylint: disable=unused-ar for header in headers: self.assertIn(header, csv_headers) + @patch('credentials.apps.records.views.SegmentClient', autospec=True) + def test_filename(self, segment_client): # pylint: disable=unused-argument + """ + Verify that the filename in response Content-Disposition is utf-8 encoded + """ + filename = '{username}_{program_name}_grades'.format( + username=self.user.username, + program_name=self.program_cert_record.program.title + ) + filename = filename.replace(' ', '_').lower().encode('utf-8') + expected = 'attachment; filename="{filename}.csv"'.format(filename=filename) + + response = self.client.get( + reverse('records:program_record_csv', kwargs={'uuid': self.program_cert_record.uuid.hex}) + ) + actual = response['Content-Disposition'] + + self.assertEqual(actual, expected) + @ddt.ddt class MasqueradeBannerFactoryTests(SiteMixin, TestCase): @@ -971,6 +991,7 @@ class MasqueradeBannerFactoryTests(SiteMixin, TestCase): MOCK_USER_DATA = {'username': 'test-user', 'name': 'Test User', 'email': 'test@example.org', } def setUp(self): + super().setUp() self.user = UserFactory(username=self.MOCK_USER_DATA['username']) self.client.login(username=self.user.username, password=USER_PASSWORD) diff --git a/credentials/apps/records/utils.py b/credentials/apps/records/utils.py index aab5b7e01..c8ed0fd8f 100644 --- a/credentials/apps/records/utils.py +++ b/credentials/apps/records/utils.py @@ -14,7 +14,7 @@ logger = logging.getLogger(__name__) -def send_updated_emails_for_program(username, program_certificate): +def send_updated_emails_for_program(request, username, program_certificate): """ If the user has previously sent an email to a pathway org, we want to send an updated one when they finish the program. This function is called from the credentials Program Certificate awarding API """ @@ -42,10 +42,10 @@ def send_updated_emails_for_program(username, program_certificate): pathway = user_pathway.pathway record_path = reverse('records:public_programs', kwargs={'uuid': pcr.uuid.hex}) - record_link = site.domain + record_path + record_link = request.build_absolute_uri(record_path) csv_link = urllib.parse.urljoin(record_link, "csv") - msg = ProgramCreditRequest(site).personalize( + msg = ProgramCreditRequest(site, user.email).personalize( recipient=Recipient(username=None, email_address=pathway.email), language=program_certificate.language, user_context={ diff --git a/credentials/apps/records/views.py b/credentials/apps/records/views.py index 0991dc146..ca076fac8 100644 --- a/credentials/apps/records/views.py +++ b/credentials/apps/records/views.py @@ -14,10 +14,10 @@ from django.shortcuts import get_object_or_404 from django.template.defaultfilters import slugify from django.urls import reverse +from django.utils.decorators import method_decorator from django.utils.translation import ugettext as _ from django.views.generic import TemplateView, View from edx_ace import Recipient, ace -from ratelimit.mixins import RatelimitMixin from credentials.apps.catalog.models import CourseRun, Pathway, Program from credentials.apps.core.models import User @@ -28,18 +28,19 @@ from credentials.apps.records.messages import ProgramCreditRequest from credentials.apps.records.models import ProgramCertRecord, UserCreditPathway, UserGrade from credentials.shared.constants import PathwayType +from ratelimit.decorators import ratelimit from .constants import RECORDS_RATE_LIMIT log = logging.getLogger(__name__) -def rate_limited(request, exception): # pylint: disable=unused-argument +def rate_limited(request, exception): log.warning("Credentials records endpoint is being throttled.") return JsonResponse({'error': 'Too Many Requests'}, status=429) -class RecordsEnabledMixin(object): +class RecordsEnabledMixin: """ Only allows view if records are enabled for the installation & site. Note that the API views are will still be active even if records is disabled. You may want to disable records support in the LMS if you want to stop data being sent over. @@ -375,15 +376,16 @@ def get_context_data(self, **kwargs): return context -class ProgramSendView(LoginRequiredMixin, RatelimitMixin, RecordsEnabledMixin, View): +@method_decorator( + ratelimit( + key='user', rate=RECORDS_RATE_LIMIT, + method='POST', block=True + ), name='dispatch' +) +class ProgramSendView(LoginRequiredMixin, RecordsEnabledMixin, View): """ Sends a program via email to a requested partner """ - ratelimit_key = 'user' - ratelimit_rate = RECORDS_RATE_LIMIT - ratelimit_block = True - ratelimit_method = 'POST' - def post(self, request, **kwargs): body_unicode = request.body.decode('utf-8') body = json.loads(body_unicode) @@ -418,7 +420,7 @@ def post(self, request, **kwargs): record_link = request.build_absolute_uri(record_path) csv_link = urllib.parse.urljoin(record_link, "csv") - msg = ProgramCreditRequest(request.site).personalize( + msg = ProgramCreditRequest(request.site, user.email).personalize( recipient=Recipient(username=None, email_address=pathway.email), language=certificate.language, user_context={ @@ -443,16 +445,17 @@ def post(self, request, **kwargs): return http.HttpResponse(status=200) -class ProgramRecordCreationView(LoginRequiredMixin, RatelimitMixin, RecordsEnabledMixin, View): +@method_decorator( + ratelimit( + key='user', rate=RECORDS_RATE_LIMIT, + method='POST', block=True + ), name='dispatch' +) +class ProgramRecordCreationView(LoginRequiredMixin, RecordsEnabledMixin, View): """ Creates a new Program Certificate Record from given username and program uuid, returns the uuid of the created Program Certificate Record """ - ratelimit_key = 'user' - ratelimit_rate = RECORDS_RATE_LIMIT - ratelimit_block = True - ratelimit_method = 'POST' - def post(self, request, **kwargs): body_unicode = request.body.decode('utf-8') body = json.loads(body_unicode) @@ -551,7 +554,7 @@ def get(self, request, *args, **kwargs): writer.writeheader() writer.writerows(record['grades']) string_io.seek(0) - filename = '{username}_{program_name}_grades.csv'.format( + filename = '{username}_{program_name}_grades'.format( username=record['learner']['username'], program_name=record['program']['name'] ) @@ -564,7 +567,8 @@ def get(self, request, *args, **kwargs): properties=properties, segment_client=segment_client, ) - response['Content-Disposition'] = 'attachment; filename={filename}'.format( - filename=filename.replace(' ', '_').lower() + filename = filename.replace(' ', '_').lower().encode('utf-8') + response['Content-Disposition'] = 'attachment; filename="{filename}.csv"'.format( + filename=filename ) return response diff --git a/credentials/conf/locale/ar/LC_MESSAGES/django.mo b/credentials/conf/locale/ar/LC_MESSAGES/django.mo index 440514cdbfcdecec9440911ebbfbdf6320c69693..0b27bdaa81cba5c1d989a47d4072afbe476f8229 100644 GIT binary patch delta 361 zcmXBPKTCp96vy$OB8sMu{|FU9NK~GsK^hd?T?`Z#t-;YHqoL*uLvRWlB=Z#r76?wh z1h+;feF2F}I0}9*!sUMMJ=}B8IWxOw&tDwfKZlg`N)M7e7t$J^F@d*!DUWT8;uyd1 z8)H}wNI7ib8}`w}-Ii3s6t?glv$#Z3wZ#JNEOiy(kO@jxSU^+g4lnT$efW+k{JQ8WU^PWu8yryq6^ZiWIzw;mCj>d38$|t2gNkNbFiPt!ahrN=;a~#4~ zyux?PVZBfC@C=Xf0bPBjU#j2=Zs93T;Wu(s-DxSrY+4NsvRS1tj@#%OH8G2qn8JH3 z;4|j&11IqZ-MQR=G=~P)Fv5L&!VxTF2#X=Q58a@_ugvEE!(F6Ur{Li-9$^VzZ~}kP z1sEfpj}@f1cF_GOMy5_!TG(p8C2l*t4YPPqjpE&U%`8Q6wOpJHY{}Tb1h!}w%JboL Kz-ZT!7pXr@oiPvq diff --git a/credentials/conf/locale/ar/LC_MESSAGES/django.po b/credentials/conf/locale/ar/LC_MESSAGES/django.po index c82b1b425..f9f02a018 100644 --- a/credentials/conf/locale/ar/LC_MESSAGES/django.po +++ b/credentials/conf/locale/ar/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ # # Translators: # AR R1 , 2016 -# AR C2 , 2016 +# 6e68c7971a89e50e680ae9444d303c8f, 2016 # shefaa abu jabel , 2017 # e2f_ar r3 , 2017 # may , 2018 diff --git a/credentials/conf/locale/ar/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/ar/LC_MESSAGES/djangojs.mo index 1f5e8ad9cdd7e47eed003afd0b1e5b3b4f5dbd26..8d89508b154498d38c4f88a269b38eb82994c946 100644 GIT binary patch delta 58 zcmZ3_GLL0~3geTBs#z0vn5#!-W>h9Bc$B7R7A2M_*knK$4(YjxnK^pN`MGvF3PuKo KmK*;WG6Dd61QhH5 delta 73 zcmbQovYusv3gg#_s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F){ YvB!!(D8Eu6Dlt1dQ3tGM<6A>U061I~nE(I) diff --git a/credentials/conf/locale/ar/LC_MESSAGES/djangojs.po b/credentials/conf/locale/ar/LC_MESSAGES/djangojs.po index 582517267..bd67d2043 100644 --- a/credentials/conf/locale/ar/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/ar/LC_MESSAGES/djangojs.po @@ -4,12 +4,13 @@ # EdX Team , 2018. # # Translators: -# AR C2 , 2018 +# 6e68c7971a89e50e680ae9444d303c8f, 2018 # Nabeel El-Dughailib , 2018 # Sahbi BG , 2018 # Alhamzah Alnufaili , 2018 # shefaa abu jabel , 2018 # Roy Zakka, 2018 +# Yihya Hugirat , 2019 # msgid "" msgstr "" @@ -17,7 +18,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: Roy Zakka, 2018\n" +"Last-Translator: Yihya Hugirat , 2019\n" "Language-Team: Arabic (https://www.transifex.com/open-edx/teams/6205/ar/)\n" "Language: ar\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/bg_BG/LC_MESSAGES/django.mo b/credentials/conf/locale/bg_BG/LC_MESSAGES/django.mo index f5b6cfe9993965e19a1b80b57e00a121d48ed87f..a2159ed6dfb3cff3591f9f521e3bcdc411968f96 100644 GIT binary patch delta 15 XcmbQh@|<~s3gg;|s#zO%L@)vXE+YlQ delta 54 zcmaFPJb`6`3gfkjs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+d Iu_u}l0NB?Lr2qf` diff --git a/credentials/conf/locale/bg_BG/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/bg_BG/LC_MESSAGES/djangojs.mo index 57f095e786824017c572f901e7d2178a97ba068e..65ab8b85dbd02dff55dab5c108222654fa65ac2e 100644 GIT binary patch delta 15 WcmaFHyq9@`3S;v`)vS#>^cVpzSp}K^ delta 54 zcmdnX{ET^m3geoIs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d IvB!`R0NF|pe*gdg diff --git a/credentials/conf/locale/bn_BD/LC_MESSAGES/django.mo b/credentials/conf/locale/bn_BD/LC_MESSAGES/django.mo index 82a1567a489eb06d1ac955ccb391a953d095717d..2526574cc831a2cff0641bf64dec316d4fb64e8d 100644 GIT binary patch delta 55 zcmbQta))_>3ge=Qs#z0vn5+1e7M12GxTO}Q%)=+6F){ zvB%2DH$N}4C{e*XBQY;ASHUO0q_kMUCKoK24Hk2#%uUS9(M!(HwbKC`Gx60Y034(v AcmMzZ diff --git a/credentials/conf/locale/bn_BD/LC_MESSAGES/django.po b/credentials/conf/locale/bn_BD/LC_MESSAGES/django.po index 6328fd887..8253e4b29 100644 --- a/credentials/conf/locale/bn_BD/LC_MESSAGES/django.po +++ b/credentials/conf/locale/bn_BD/LC_MESSAGES/django.po @@ -10,6 +10,7 @@ # sunanda chakraborty , 2017 # Masud Ahmed , 2018 # Monira Khanam Lotus , 2018 +# Nurul Ferdous , 2020 # msgid "" msgstr "" @@ -17,7 +18,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Monira Khanam Lotus , 2018\n" +"Last-Translator: Nurul Ferdous , 2020\n" "Language-Team: Bengali (Bangladesh) (https://www.transifex.com/open-edx/teams/6205/bn_BD/)\n" "Language: bn_BD\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/bn_BD/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/bn_BD/LC_MESSAGES/djangojs.mo index e1751542d1de4f1e6ff9d3dd592e519a64d2de26..6c2e375a8ae67a11118bda36afea04a886f1a0e0 100644 GIT binary patch delta 107 zcmbQsa))_>3ge=Qs#(#xL8)b##hLkex-N+&sa6U`28I^82Bx|OhCrs3p{2F~5ODb< z7MJLT6eZ>r=OmWo7g;Izl@^ueD7d8-rR0|uE7;_LxDII`u3l-eoeog1k-=nb#!mnc CwIU<{ delta 150 zcmcb^JeOsH3ghF6s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x&{`yM#c(;##V;r+6F){ zu}3l%A!KD@pl4xVYH4ZA<&#)kq8n0_m{**WSdw34rBGa&mzbB5sF0kIm|c{ZlwVX* osbG_wUsRA^1Tm), 2018 # Masud Ahmed , 2018 # sunanda chakraborty , 2018 +# Nurul Ferdous , 2020 +# msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2018-08-23 13:17+0000\n" -"PO-Revision-Date: 2018-08-23 13:17:40.805993\n" -"Last-Translator: sunanda chakraborty , 2018\n" +"POT-Creation-Date: 2018-10-01 16:57+0000\n" +"PO-Revision-Date: 2018-05-01 20:19+0000\n" +"Last-Translator: Nurul Ferdous , 2020\n" "Language-Team: Bengali (Bangladesh) (https://www.transifex.com/open-edx/teams/6205/bn_BD/)\n" "Language: bn_BD\n" "MIME-Version: 1.0\n" @@ -117,6 +119,10 @@ msgstr "" msgid "Completed" msgstr "" +#: static/components/ProgramRecord.jsx +msgid "Not Earned" +msgstr "" + #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Partially Completed" msgstr "" @@ -201,6 +207,10 @@ msgstr "" msgid "Back to My Profile" msgstr "" +#: static/components/RecordsList.jsx +msgid "View Example" +msgstr "" + #: static/components/RecordsList.jsx msgid "View Program Record" msgstr "" diff --git a/credentials/conf/locale/bn_IN/LC_MESSAGES/django.mo b/credentials/conf/locale/bn_IN/LC_MESSAGES/django.mo index 2aa51de954d052e447d1d0ea6ea953ed2d717c06..4530e624b70e93381a8936e3fcb85726d7d82f5c 100644 GIT binary patch delta 15 WcmaFJyn}gy3S-?w)vS#>G#LRdn+1jd delta 54 zcmdnN{E&Hq3ghyLs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+d Iu}6my0M;)LXaE2J diff --git a/credentials/conf/locale/bn_IN/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/bn_IN/LC_MESSAGES/djangojs.mo index 0158def09cc0850ea87403d0006513162dcc41ef..e310ab4283902dc8a57173f2195f22372e878bec 100644 GIT binary patch delta 15 WcmaFJyn}gy3S-?w)vS#>G#LRdn+1jd delta 54 zcmdnN{E&Hq3ghyLs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d Iu}6my0Mtti;B~%E}0$jo-KzF7wTtJ9p-snfq(}!}#*r`fPq(#O
Y5I=Dri+dxcIKY>D%>C>yMR;OV)5`{J6?fUq zC#A^#zj!T>7HaVhKQnzWrrE?PR(Okt_>5_TB@Xao?oXz9r9R1Wlq0;(S6pBRPnO$> zohfG#=LN0_T;&ow*&!P_M45_=VODv8>HR$(|3^YIANKQYE8C+L%C(Sq@Xm-xG~vRYW%vGToVuJa#t CeoU5jY_HeTg5#0W(uF(DbHDR#GELqbZ5tSB3gXzVQ5 zD9Ki`P_whM@mN^M|CoEbo!>d%x%Zs!yXQW3esx}ahU(WLBGaiNWg=3XF4BbEIEiny=`_wXF^usutJTc&8T(T8j$2bhAFS)%_>+!0U<1<-@P zs9u<(iNsYb#w{$wD^wdiV+np6nsS2AHDeL+DQv`b+{b&2!wFL`v3Zj&;vld_U=*J* z39HG59Hb2?Qe0Spi>T%gu>k!ThEF(%FQ^jK$SxM0sMZ<7Xk0?|1$>6PI-O+#CzyuT z{NRJFs5W#UNy`$dM0SkvsWHCC1mXeg!gp*(yE&NX3UV5zO9w113+{36%)H0a fI_`Db%~hJM-eS`%n$2vhwb#^DYFz&7)a|4{S;^;*>bRwEJ#Duk2{60sPVB*G@zNvurB8=8oSg{6a`CZjYu zm=Hsgl88lOF*Aulh|%|?UGC?cckes*+XdGTJA*s>JMI28fGGicH%WmU! zFponx&HHTQI!@vX-l5YitzZQ=G0rv4;33+L-ZNMuZ)i94h4!S8P;WJep}k46l&-&Y JOJOe?{0F{BIXnOW delta 586 zcmXZZJxD@P6u|LQWFKOhmS$gS4F%c@U)IZnmWrgIt%SA=3|tE0H8s2!D3;JL54fl7?yYHQI&%Lk4k8v|xZ{63496LoiL}Z~&#E+}U zE7`^tJVY0sU=h#Jho)Pk8*}KuOJqs%sPaA4EMXII8H4zXz3B6ZB@awu9+5t*pi1a! z7YSe(BRG$$;1=%T5voFMR7qk#s`V_YJUg}c6m{Y|Y{XBT#&Rvzy;k*yxR+uqq)>Id ziy_P*wd4w$v4A#wLG@h`RpJs(;XCeQfZYt>1=5o|quSsbLo0rxYEZ0B5jzHP1jDFy zyo_U*!!s=45Uy}PCLUl6|L_s@E|DdyU>1`cV;pZ$o#+>X+K8>E+OQwhmAZ}EWDeDx vr1Fyu*Y?3hZDuo*HnSV++HBg)BtoN6J+A3dEvkofBQZV^iSpz>oNJDM%m+n{ diff --git a/credentials/conf/locale/cs/LC_MESSAGES/django.mo b/credentials/conf/locale/cs/LC_MESSAGES/django.mo index cbe26a1150422e967dd177f2c3b5e561a00e3ac8..c6b70a9572a5b70cd76e6e73887384de4ac033e2 100644 GIT binary patch delta 16 YcmX@YGMiOxK|6}WS_LnZs9RG}>9Y$_NoN-FY!0-fKw3{X=wG`ICHE=E506o|S zUxA(QQ@9VdO;&0Ld>*cYe}@O*iYZF1gipdb@C~?Asgn8yov$!ZKUJye@HCtOe+X}e z??R@i-$5SrA+OuuC-4?HcbZZ;csHztC!pB=8Wcru!rAbb$@gE9Z&6lXe$p@wH*KfC~OR(0~D7_<*=g<04NUxhsC60ccs{7tc8bD>1I3C@LWa60-` znvN_8;7oWNk_Gh**bcu97r;NmiEu(q9H|*lBC`PElvywg;f?U;P$Kth zC=vM>mT*Q*Myn{E$4e}0gcADAF!B(#@STMl;fqide+Xrxe?plz9c_zX9h8XN4`rV( zL(y{rE`z7xe0Y8q@s|l7F(9F@=HmdYhmG(tNd2gnp(uI-5(M=g+ys9QSHi_4eihsW zW!;zHc=#&hQLpooqJ0;#L``Bdadg3K;*V~14+F2kHn%`WDnJ%J%@gs&`;L{Ac@xZHHt@87LRj7vb&jSty}D2dPW-J|x=e z5|l_xTddR|TmW~8izn#F#J@o)jvJQ5isnGFpq4^8Ud`}n*a1cH`%r4(6Nqh*6%yJ- zSVQ{NeNYVTO1_Uk&G%QKIQSxLkr4lY&UW}Il;hEg8=@c&W#Z#-1$-KI!?&O)n9pQ% ztJP4d|B>YTYfygwJ`@MufrsG-P)7uv~`SVWHPc1kv1-ddH`mVzNArVXA5#4vJa6aM|vqj#?<|>|MX~5 zV0R;OcBOqGMsyu-puZE5o3GNgCY@A5IeMQ*rXm{=X@`+J5INR4M2fl@v5|J9UgEQu zjx>rm`j001;3X$we)*Z|m8F93Wqm8(?OJ)$5a><5N!y{`WsVe05bBKQ>m|#If$_UD zMaM~IsU^#8=r z9H;+^pIffAO80q1C#@a3*XYpGR-wQOaUd1@WV@REaHn8}xjw7EJZ@s^ew{d?eUtM1 zw6+789{J+ADWm(nqRv@IjW(9=;-D33$5=$ha}DMc{Qwy3%h)MaX_Q!%J%?H+?z~}o zw9qG_vVTy1YVz^*n_a8N!APu0hIIy=Mc*{(Jq)%#up6ZTI|X*$b}U~9CJb#i+cY$N z$_rJq#gx$<^)}-abl0x-^6OJSD2Z`KAGJ-N#`Cn-=Q^I19wUc;cvZmrGbp?2Vpm> z2esv<^*})4+;lXc2Cgjo!Ndl|6yG*Mx0jJw!C+&&(4kC{WyH!4yMuz|4(`cOVm#tx zR5$&E*a`;>I+(EB9Yke3J@h>nD+Z#&BIlESG%fz2{I8i0mHTJiv#OQIS-Q>9_ok({ zvEWfM0SRBuIlP@-xi$_%gy*!*67U0@b#Vh=H9Sq zId)*xO#TEzP7d|$!DI(hF&k(DQ~S^$lR+kaLS}mNVgxdS&M?-y_MT zSkBh&>Cw%_%rRrfE3loTkp_lzP!w6?m~7a{sOxG@SDGuZ;SjLWN07xUx;=b}r9Za}7j zf?rJKxHgNKC{Zr|2&n4EFT zd+RTDZnlHV4kvz&eP`_}25e(o%kMGh7X71lR^<>*H}SNxyi2oB2)hbC`|pW delta 2222 zcmYk+e@xVM9LMqZ0Ydx|P!SM}L()(#_)>=?ovrP}0bFBG?TdUTlrL#42%gw0SW^P&8n(O&GzHEJV@9*RD{oMDypYP}W zd4KQZ?mwF%H|8W9Gg=!_KwOVAi{PnQ9B3z!%$DFg*o+r&4W>P0=EIGcgJIl?Z{Q)k zfwOUcvRMYcg!AxqOvRI!rl~%nQ^>$LF2F0;jdw5`+fvLDu@5!Tkn2&L&-p0kONOd*Zqc(VmkNfC<6;wj&2k}hHL~&@GvgKv+nno zP$|BJ?P!^1Wv~ayx*b7H^fvOb(;VvYbM#?c*7WuMEb^bgKsy6UaR=%FhEXd!if3^Q zsXN=rvUPnBm7y-wc&|Gi#=V>$KxOU+obs6cgUdL-$<3d|bSB$^-Pz>7lFm5>^n*Lt zi5WcpBo3lh;HUCF!#d2vzfmci!&7R-g{azChT8KcP? zTW}HfyS{}*oKGNGwri-Zn!;BxmHew#-b6jfhqwpN;4aMMmCzRUqTY^!99WATN4?IU zqB0)2N{8&&ZR8>C0cyoLEazn`!EHE(y6!$|i_%z@CMZJYG(S?0){L5HCu(b6L@jI> zd3HOAdW|QLsUr4`JMb%d`NBi~w4z0*DqfCH-~g%^Cs7mJLS6SSmSQq#d=4v66CA_E zcp8`EckX$-cl!IKxImj!N9TDn+l{KuFHkAJhg#Vz_CYK6VmGeDYCMHY@fxZqJuH)F zu|(7ZE=H1NHFzC&qW*X)*+*Spi;2u{jdYaCO{gN-g)iVf6nGQ@DfB z@72~5t7ENRaP)Hl)YyJ|oG8}`?NP$7{ja36QX_8DUe7eW5o+4cnWimK(_X7SYKa!2 ziBO@c_*6$~{BKz+p+Z#C8<0ro&*%}NNCyhv;)wq@6_(yly^7|fC;Oax3+6g|Q<9vo zlKsxzl(o?#shi`R-_u8;qq9HuI9-`PMbBrQi+4WAE^(6Qmpje#$DM6C?>nb*w=Ze+ z)enS%;hz3JUqdh)s`jq%2daDlzt11=1}dv7R+su|&Yj%qhL&)sE7;c)^tKKJJBB(t zyL-GfgYH;)cxJ3N*fGdSXV@1Gb$0jl_xA5UP`)p;t}J%vXj@*s$C+1<B;&&6A T@nr#LY3V9wxb#P7tn9*njy?Hr diff --git a/credentials/conf/locale/de_DE/LC_MESSAGES/django.po b/credentials/conf/locale/de_DE/LC_MESSAGES/django.po index 915e6179c..24a9fecca 100644 --- a/credentials/conf/locale/de_DE/LC_MESSAGES/django.po +++ b/credentials/conf/locale/de_DE/LC_MESSAGES/django.po @@ -14,7 +14,8 @@ # trajan, 2018 # Ron Lucke , 2018 # mc , 2018 -# Stefania Trabucchi , 2018 +# Stefania Trabucchi , 2019 +# Donna Kl , 2019 # msgid "" msgstr "" @@ -22,7 +23,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Stefania Trabucchi , 2018\n" +"Last-Translator: Donna Kl , 2019\n" "Language-Team: German (Germany) (https://www.transifex.com/open-edx/teams/6205/de_DE/)\n" "Language: de_DE\n" "MIME-Version: 1.0\n" @@ -138,19 +139,21 @@ msgstr "URL der Seite für Fragen zu Zertifikaten" #: apps/core/models.py msgid "Enable Learner Records" -msgstr "" +msgstr "Lernaufzeichnungen aktivieren" #: apps/core/models.py msgid "Enable the Records feature. The LMS has a similar setting." msgstr "" +"Aktivieren Sie die Aufzeichnungsfunktion. Das LMS hat eine ähnliche " +"Einstellungen." #: apps/core/models.py msgid "Learner Records Help URL" -msgstr "" +msgstr "URL zur Hilfe für die Lernaufzeichnung" #: apps/core/models.py msgid "URL of page for questions about Learner Records" -msgstr "" +msgstr "URL der Seite für Fragen zu Protokollen über Lernende" #: apps/core/models.py msgid "Facebook App ID" @@ -266,12 +269,12 @@ msgstr "" #: apps/credentials/views.py #, python-brace-format msgid "{first_org} and {second_org}" -msgstr "" +msgstr "{first_org} und {second_org}" #: apps/credentials/views.py #, python-brace-format msgid "{series_of_orgs}, and {last_org}" -msgstr "" +msgstr "{series_of_orgs}, und {last_org}" #: apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html msgid "" @@ -304,7 +307,7 @@ msgstr "Andere" #: apps/records/models.py msgid "User credit pathways can only be connected to credit pathways." -msgstr "" +msgstr "Benutzerkreditpfade können nur mit Kreditpfaden verknüpft werden." #: apps/records/views.py msgid "N/A" @@ -321,6 +324,8 @@ msgid "" "A program record is created once you have earned at least one course " "certificate in a program." msgstr "" +"Die Aufzeichnungen werden erst angelegt, wenn mindestens ein Kurszertifikat " +"eines Programmes vorliegt." #: apps/records/views.py msgid "Program Listing View" @@ -431,6 +436,8 @@ msgid "" "{start_span}This is to certify that{end_span} {start_strong} {user_name} " "{end_strong}" msgstr "" +"{start_span}Hiermit wird zertifiziert, dass{end_span} {start_strong} " +"{user_name} {end_strong}" #. Translators: organization_name is the display name for the provided #. organization e.g (e.g., Test Organization). @@ -440,6 +447,8 @@ msgid "" "a program offered by %(org_name_string)s, in collaboration with " "%(platform_name)s." msgstr "" +"Eisn Programm von %(org_name_string)s, in Zusammenarbeit mit " +"%(platform_name)s. " #: templates/credentials/programs/base.html msgid "Noted by" @@ -486,7 +495,7 @@ msgstr "Cache leeren" #: templates/programs.html #, python-brace-format msgid "{program_name} Record" -msgstr "" +msgstr "{program_name} Protokoll" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -494,7 +503,7 @@ msgstr "" msgid "" "%(user_full_name)s has sent an updated program record for %(program_name)s." msgstr "" -"%(user_full_name)s hat eine aktualisierte Kursaufzeichnung für " +"%(user_full_name)s hat eine aktualisierte Programmprotokoll für " "%(program_name)s gesendet." #: templates/records/edx_ace/programcreditrequest/email/body.html @@ -504,6 +513,8 @@ msgid "" "%(user_full_name)s has sent their completed program record for " "%(program_name)s." msgstr "" +"%(user_full_name)s hat Ihnen sein komplettes Programmprotokoll für " +"%(program_name)s gesendet." #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -512,6 +523,8 @@ msgid "" "%(user_full_name)s has sent their partially completed program record for " "%(program_name)s." msgstr "" +"%(user_full_name)s hat Ihnen fast sein komplettes Programmprotokoll für " +"%(program_name)s gesendet." #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -519,6 +532,7 @@ msgstr "" msgid "" "%(user_full_name)s would like to apply for credit in the %(pathway_name)s." msgstr "" +"%(user_full_name)s möchte eine Anrechnung der Credits im %(pathway_name)s" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -527,11 +541,13 @@ msgid "" "Please view or download %(user_full_name)s’s public program record to " "determine their credit eligibility status." msgstr "" +"Bitte lesen oder downloaden Sie den öffentlichen Programmdatensatz von " +"%(user_full_name)s, um den Status des Anspruchs auf Credits zu ermitteln." #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt msgid "View Program Record" -msgstr "" +msgstr "Programmprotokoll einsehen" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -548,11 +564,12 @@ msgstr "Das %(platform_name)s Team" #, python-format msgid "%(program_name)s Updated Credit Request for %(user_full_name)s" msgstr "" +"%(program_name)s Aktualisierte Anfrage von Credits für %(user_full_name)s" #: templates/records/edx_ace/programcreditrequest/email/subject.txt #, python-format msgid "%(program_name)s Credit Request for %(user_full_name)s" -msgstr "" +msgstr "%(program_name)s Kreditanfrage für %(user_full_name)s" #: urls.py msgid "Credentials Administration" diff --git a/credentials/conf/locale/de_DE/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/de_DE/LC_MESSAGES/djangojs.mo index 0988953001475bf448237c358820b8a3e3218658..7f20cbeeba550e97a8896500b28e3755858cf03c 100644 GIT binary patch literal 7057 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, 2018 # Bert Krohn , 2018 # Muhammad Ayub khan , 2018 -# Stefania Trabucchi , 2018 +# Donna Kl , 2019 +# Stefania Trabucchi , 2019 # msgid "" msgstr "" @@ -16,7 +17,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: Stefania Trabucchi , 2018\n" +"Last-Translator: Stefania Trabucchi , 2019\n" "Language-Team: German (Germany) (https://www.transifex.com/open-edx/teams/6205/de_DE/)\n" "Language: de_DE\n" "MIME-Version: 1.0\n" @@ -54,13 +55,15 @@ msgstr "Sie sehen den Kurs aktuell aus der Sicht von: {user}" #: static/components/MasqueradeBanner.jsx msgid "Masquerading failed" -msgstr "" +msgstr "Maskieren fehlgeschlagen" #: static/components/MasqueradeBanner.jsx msgid "" "You either do not have permission to masquerade as this user, or the user " "could not be found." msgstr "" +"Sie haben entweder keine Berechtigung, sich als dieser Benutzer auszugeben, " +"oder der Benutzer konnte nicht gefunden werden." #: static/components/ProgramRecord.jsx msgid "Earned" @@ -84,27 +87,32 @@ msgstr "Aufzeichnungen herunterladen" #: static/components/ProgramRecord.jsx msgid "We are sending your program record." -msgstr "Wir senden Ihnen die Programmaufzeichnungen zu." +msgstr "Wir senden Ihnen das Programmprotokoll zu." #: static/components/ProgramRecord.jsx msgid "We were unable to send your program record." -msgstr "Wir konnten Ihnen leider nicht Ihre Programmaufzeichnungen senden." +msgstr "Wir konnten Ihnen leider nicht Ihr Programmprotokoll senden." #: static/components/ProgramRecord.jsx msgid "" "We were unable to send your record to {orgs}. You can attempt to send this " "record again. Contact support if this issue persists." msgstr "" +"Wir konnten Ihre Daten nicht an {orgs} senden. Sie können versuchen, diesen " +"Datensatz erneut senden zu lassen. Wenden Sie sich an den Support, wenn " +"dieses Problem weiterhin besteht." #: static/components/ProgramRecord.jsx msgid "You have successfully shared your Learner Record" -msgstr "" +msgstr "Sie haben erfolgreich Ihre Lernaufzeichnungen geteilt." #: static/components/ProgramRecord.jsx msgid "" "You have sent your record to {orgs}. Next, ensure you understand their " "application process." msgstr "" +"Ihre Daten wurden zu {orgs} gesendet. Bitte stellen Sie als nächsten Schritt" +" sicher, dass Sie den Bewerbungsprozess verstehen." #: static/components/ProgramRecord.jsx msgid "{name} Record" @@ -112,7 +120,7 @@ msgstr "{name} Aufzeichnung" #: static/components/ProgramRecord.jsx msgid "{type} Program Record" -msgstr "{type} Programmaufzeichnung" +msgstr "{type} Programmprotokoll" #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Completed" @@ -216,11 +224,11 @@ msgstr "Beispiel ansehen" #: static/components/RecordsList.jsx msgid "View Program Record" -msgstr "" +msgstr "Programmprotokoll einsehen" #: static/components/RecordsList.jsx msgid "Program Records" -msgstr "Kursaufzeichnungen" +msgstr "Programmprotokoll" #: static/components/SendLearnerRecordModal.jsx msgid "{name} - Sent" @@ -240,20 +248,28 @@ msgid "" "accept credit for this {type} Program. Once you send your record you cannot " "unsend it." msgstr "" +"Sie können Ihren Programmdatensatz direkt an {platform} Partner weitergeben," +" die Credits für diesen Programm {type} akzeptieren. Sobald Sie Ihren " +"Datensatz gesendet haben, können Sie ihn nicht mehr zurücknehmen." #: static/components/SendLearnerRecordModal.jsx msgid "Not all credit partners are ready to receive records yet" msgstr "" +"Noch sind nicht alle Kreditpartner bereit, Aufzeichnungen zu erhalten." #: static/components/SendLearnerRecordModal.jsx msgid "" "You can check back in the future or share your record link directly if you " "need to do so immediately." msgstr "" +"Sie können zu einem anderen Zeitpunkt noch einmal nachschauen oder Ihren " +"Datensatz-Link direkt freigeben, wenn Sie dies sofort tun möchten." #: static/components/SendLearnerRecordModal.jsx msgid "Select organization(s) you wish to send this record to:" msgstr "" +"Wählen Sie die Organisation(en) aus, an die Sie diesen Datensatz senden " +"möchten:" #: static/components/SendLearnerRecordModal.jsx msgid "Send" @@ -265,6 +281,9 @@ msgid "" "program record to {platform} partners{end_anchor} for credit or application " "purposes." msgstr "" +"Anstatt einen Link zu teilen, können Sie auch {start_anchor} Ihren " +"Programmdatensatz direkt an {platform} Partner{end_anchor} zu Kredit- oder " +"Antragszwecken senden." #: static/components/ShareProgramRecordModal.jsx msgid "Share Link to Record" @@ -280,7 +299,7 @@ msgstr "Sie können das Fenster schließen und es noch einmal versuchen." #: static/components/ShareProgramRecordModal.jsx msgid "Successfully copied program record link." -msgstr "" +msgstr "Erfolgreich kopierter Programmdatensatz-Link." #: static/components/ShareProgramRecordModal.jsx msgid "" @@ -288,10 +307,13 @@ msgid "" "else of you choosing. Anyone you share this link with will have access to " "your record forever." msgstr "" +"Kopieren Sie diesen Link, um Ihr Protokoll an eine Universität, einen " +"Arbeitgeber oder eine andere Person Ihrer Wahl weiterzugeben. Jeder, mit dem" +" Sie diesen Link teilen, hat für immer Zugriff auf Ihr Protokoll." #: static/components/ShareProgramRecordModal.jsx msgid "Program Record URL" -msgstr "" +msgstr "Programmprotokoll URL" #: static/components/ShareProgramRecordModal.jsx msgid "Copy Link" diff --git a/credentials/conf/locale/el/LC_MESSAGES/django.mo b/credentials/conf/locale/el/LC_MESSAGES/django.mo index 0033414b16de3f9ef14b1649faacf245ba142a23..b2c58098a6750cf41b344b30c00c5b8df6f8e2bd 100644 GIT binary patch delta 35 ocmZo-y~#4cM074A149)91A`P0OE57or~+x@iJt8ne|9ng0DHIxcK`qY delta 74 zcmcb~(!@H!MD!RV149)91A`P0J2Ejar~>JjiJt9hA-c{*sfi_-`FXl7i6yC43PuKo Z7PjUBLHiT5wZXP diff --git a/credentials/conf/locale/el/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/el/LC_MESSAGES/djangojs.mo index 110edc62b4cf6d1bbf5571fe0132fede15b0564e..e7284a0348ebd4d85eba577f7556ef39825cc672 100644 GIT binary patch delta 15 Xcmey*e2sa63geuKs#zO%xHAF(GY3S<66)vS#>q!%)=+6F+d Iu}78>0L;`6DgXcg diff --git a/credentials/conf/locale/en/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/en/LC_MESSAGES/djangojs.mo index 84dcb3dce90739bc15e3754f8ff60283727e0c8e..b00e665726466df648737c5ef8e7e30853aaf7a2 100644 GIT binary patch delta 15 Wcmcb_ypnl>3S<66)vS#>q!%)=+6F+d Iu}78>0L;`6DgXcg diff --git a/credentials/conf/locale/eo/LC_MESSAGES/django.mo b/credentials/conf/locale/eo/LC_MESSAGES/django.mo index 9f11ebec1511c725a99b437e70e1b29bc0bf599c..190307b9cadb07ee692302964e703693c00f61dc 100644 GIT binary patch delta 871 zcmXBSOGwmF6vy%3U_@a^7^UT-B5iz3WloNd%BC`3iTJ3dDJnE6Gg0UP8R5T$5D^m? zF|;T%5sWEHR8UdWW-Tp(HVG}FXwg6*v3OQuE@{Iy=^oz3eq6>8>`Rm0p(kCM#U-@$XBpBk`ZJ{u_yZr~$ad)*`gTZD*pIOU zsdlGyjJ!BoI)Qhy<>(Z_0t+R$dAC%C^>_gvVjKR%IjnO_&6t=YQA3yTB03nvuXr9y za-{%1#SV<&RSe`wmvJ5ka96$~RT4z=rCYe(BYnbN+=Gq$m7;^AIE}BdW3O}^H|>*} zFn|>p!E5*%t$|>HbQCAip8v&qEZ;9BW5^*GBN)UkY%Y}8tfF`XV_1Y9uXG4o@gTm$ zkGO=57&#y@CH=!2n94_I@e$gQf5!%N`=l1UgZ7>Cg}^%W6vt+S26lw5<(=D}&X1`~Uy| delta 910 zcmXBST}YEr9LMqB*r*_i5UkP6RyZ@)YFpE}Wu|GbM9h~}mSw}lR$*b2D5Qx51z}-< zbRp6kxQbF#hD9ZI(JMtH-A8nhEaDzK_pisCsujN^C=m#mRqVmCHos6=87UBM%m!DgJlRyu?U z+>bBt1m>4Y9oUO)_!Eb5k2fuCXOQzsDGaWYzTgP@@B*(0W^fF@V+L=omuj(cgVcb% zSckKiM5o_spbu-PzoGfQIw0-E4$Q-Bn!z}Oci4$NL5WQ(hg&dzBe7r{EAc9B!ngPd z9U-Y6XOWcT;m2KAg9mUD&5q72J;v_;XO2_Ub9kBcb$7Ew^D3>fGHk&e)LDFp zf3Y7Qa2z)gwqpG=SA|0n|!rh?TJ+Kbf>Q_ fk!p{+gZ@B;FW~q21MWb1v@GoP^Gy79HU|Fzm!XHf diff --git a/credentials/conf/locale/eo/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/eo/LC_MESSAGES/djangojs.mo index d3ea3eabc7f7f5eb4e9bc8fb9f783b265697c517..cd205c236b0e6007d20c3b7e92d3fee34f21a8d4 100644 GIT binary patch delta 547 zcmXZYPbhzFKJI;wne(;e5y^l!SZ%#AF~+3g$}mGbDYBu Zv|i8=X1bqZ4JR>`4iuz{)Aq6|&mX^DMiT%4 delta 565 zcmXZZODIH99LMpW$E2vqC}s@9crNDQ5qa+hp*)iXLlLpCAqrDw#R3abQWg?5S&$k# zEGQxtR_v6ro!Ln?_};Et=YIbGbIv{I-~av$MS^F(#PDu{v}KjLBze+J7qN!?9?S6e z-!hx@mLv^hIrB}K44lSVJVxWYcMPB-OKQd-y72(}@CJjJX;)a!1haOj4Fe9T4mYp` zZ*c(AvZY=e!#h03N(?!reLO*9y)%b_`)Cvp!+EUCH4BK~9{D{|gT`H9sfQrsk|yy9 zSyOeMS!pk}lOG{xDUSJ=%FF102i-V`D;UBDOmLe8Jz|F>SAlun6xNeppmFbKn7~ia zS|}~z9Ij&=*Knzb&1fl>7VsKxv7yAgP^Horzho6JnP2uuSJ>#4cJLdGPHy^`!xtRE q3@$Y~XqzFZA~?iue2vy8pQfht`vbERqjTFatJC7G;E8@%o%w$>WKEL* diff --git a/credentials/conf/locale/es_419/LC_MESSAGES/django.mo b/credentials/conf/locale/es_419/LC_MESSAGES/django.mo index 1b5b907f3ddb8523a2ee6e3d7f47319d28dbb2e1..a00bced0cb3b435f529666443456e714e1f10bcb 100644 GIT binary patch delta 902 zcmXZaPe@cz6vy$OOlU*UAJb|ao1T9v|0RkdY6T(8A`_J`(7}``hZ083$;E)mTeJwG zjTRCnR3h3Y69&O_nJt4hQDB4wK?JpQQ)J)cd+>OlbI-f)-h1x7Ut&wKm1MwunI+Ag zkj_Yo7D!hxiS4+CJ(v@gx-pJ7aUCyXq)_U`+c=ExumO0$rTO?|2Z?5s4{v zKO$Ep1kXtv#!bw_kI18+yiQ?|R}#z7ew@Jr_!@b%!K)az{PsVz|09m~{i5%6wEQIY z;hf`o1O6d#jznIO^b}k05Pn5ALBH`l2AQQ7qnL~HXx~?{5I5906umJItZSigFnzJ3zkaxn2-Ch(r?$}aoVjohBweE`h?bR2N^=UXggVQ+Uwtl z)SynkokCl%`+(pG!4kIM8*IS8e!h;2VFO0dDjCGHIDxi+1+=pck+jO{&~~x|%Wx8H zqD8cYzQs}ehSkikE_xj!aSt7wMf+g`?J|BwyHus+Ui%t0(SCvZa0}UOZKGYDQeq%oJ(9YWnFwtk{SQ4SZZ!Y^ delta 953 zcmXZaT}TvB6u|LQ_C=qYW`2}9X=$~M%=Dp~*%BIr3HqQ9u7tD<8#^%T(6$lm%McV% z5P?NOUyv_83Fh_?5kxORNJvmgXb{ztQ2JOs^*_xF!~D)YGjs1f_uh%&h2bf$EcmiQ z=ZeJk8l+KqJ?gqNDbb{3_im#tgRQ> zjFZTa^0Hn6X(ISYB8Go(8?J8mLv}kM^Jy9!43EWxn-G0BgTxdeF*jcfvBC2 z9EgvT(6T5%D}aV~nlh!iiYspbGSqK*Bi z-(5zX_#I^NWD?8pZ9vdZ@D2^E+Z|Sx#7f$yaUGtG+Adbn4zPgtP%GWIC;TpsbR~yT zS8@pT`y5h}T#ecjs1pue5o{$`z%KlSZD?~KG@nK7u!vg8V{E~vs1x{%y4QUqwX!Vg zN?yUeIE^~cJnBS$<9V#&Wh>*$b%O0Ao}+=Ys6Q;D9>z`k!b6opZQsD7_z72GY=5}- zm8gd{fx2baPzM}Go#1OM;XG=Ax8k~DgCcJTw4>R`5C30K8LlP&4aad2*=D)P2I~M* zs7pJ8I?#7a;YzdgVojpFdC=_6XVL}NA2m;=3z-h%pk*ga+cGWNu-iJ0w8t&3+e_8Q rTO9v_(RI;rGM<+&sgmzvVI|#cD+`|&y`Nqe69Eg95#Oz diff --git a/credentials/conf/locale/es_419/LC_MESSAGES/django.po b/credentials/conf/locale/es_419/LC_MESSAGES/django.po index 118de0ddc..e8b138dae 100644 --- a/credentials/conf/locale/es_419/LC_MESSAGES/django.po +++ b/credentials/conf/locale/es_419/LC_MESSAGES/django.po @@ -4,17 +4,17 @@ # EdX Team , 2018. # # Translators: -# Juan Camilo Montoya Franco , 2017 # Diego Rojas , 2017 +# Juan Camilo Montoya Franco , 2017 # Diego Tejada , 2017 # Eduardo Zambrano , 2017 -# Bill DeRusha , 2017 # Michael Terry , 2018 +# Bill DeRusha , 2018 # Marco Morales, 2018 # Albeiro Gonzalez , 2018 # Cristian Salamea , 2018 -# Marina Pastor , 2018 # Marcos Buccella , 2018 +# Marina Pastor , 2018 # msgid "" msgstr "" @@ -22,7 +22,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Marcos Buccella , 2018\n" +"Last-Translator: Marina Pastor , 2018\n" "Language-Team: Spanish (Latin America) (https://www.transifex.com/open-edx/teams/6205/es_419/)\n" "Language: es_419\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/es_419/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/es_419/LC_MESSAGES/djangojs.mo index cc311e420e51b363e0a572a74f35c0c5ad8c31c7..770c1ecd1462fa2e969b430606063e531096f21d 100644 GIT binary patch delta 1562 zcmXxkOKeP09LMoPTlK1%8LIW1R@JMfRg_Y+O}!G*wW!F|xnyD<(P?RFF(D!(CL$9A z@!CjgVPTZSLef-2ZB$7^S`s!a+K3ez#P@gSI&tpj{BP%;d;b4(uKU%msuLf**_RBh zfj*yJKGB%HSewm-c00!yFWzx|h|`Fl;}m@3`T@I$zqs+mNyg+6Uq}7E%e5QxiF-08 z%xebxGoQH3!mn6}fAA(29<;C++mYnV71Tm+pceALz2A$PrysR|0XH7TWyGJ+ z!fE_b^G49a`^`xPvoVFru*?0yBjg$L5*OoJT!BAvB`&6}r?3f2aS(0%fqLP7CcA}a za6gu@AI*3gEAbO1wCCO#nT#rME^#Aj?+&;|PzyYPk0%&&5mm}vq^~V#MsArnvNm%H zl}Hi?@R}PR@nlNhf!s1@J=9-=tK3jY?x7}ripu;o`f(VQ;ct9`#r$Xij^G>|$9d>w z65S7=7PcGDVGB|eGlot03-#QtS=3(-SZt^sNMHajyKx^j5|5zvzL+woVoOmaZbU6K zf|~F+YMu+I#O|Wb#A8%tp1N^A))EgS7$h0w@zhehj=anCqP~K6sFi<5ZOtFl3k!MM zaxB3z+>JULaZKS&)B>xRHX^IwV6_%fZUx}md`N|Kf^o4G3UEedHBbS>b2%VwYw zYJauBt#q|@8Jz19uH|%!>13&!E9o2PI+&`Ms--PZwbaPl==?d&&sFDusyR&=Dtmne z+9#@Js_8X!mAQ_tZCOpX)13fsZ-SnklI)aZ=$YwN=Y?NDpXzatjyw8D0$ zzt+m!2^@Cs)Y-xIsNK;PXo+np_g7V}u1W9AAI~b-ZiU0WY5%rp#EQ3AtyUytN84iQ Idf!;?Ki$TtZvX%Q delta 1523 zcmX}sT};h!9LMo*sZ*XxMako#^3dtzoY2GRnQm+h&6rI%Xf!&JSZthOF3dbOHWw*Y z9yUXZH5!{STzJaE>cU)@%}uPiF>ZK&PJe#u`+xm@zy1He|Nr~_{Z6m*Z{~+TCC8jI zS|u@s$c#2yi#ah|Xs2V%lJSgV2aaakgCp^=<4e5GxZfEca+!@~d>nQE1;=R86UX2^9EVRa75g!S-*FVy#G4i3Hq>*MPzl^cz4$r0 zaR6NwHv2`VfC;6eqzh3Gmf{Rti6mvaQ6IPu^#LcH`8HGnov07E;f#B54&#Ry#ILAz zmvB=O*5Cx*Zw+*m*-_LDXOVmD3TEJK%*OZV!9REyJ=8T5Z(=RJL%pzw#oBNa)?hUI zunsq(4U&UM{sy%rA5ky-iE}ZEx6VQjW@9zB z-~rSJCb7s~44@CY@Ckm#4R|%(YzxM*_#!s$dFQAVgO7$$Pf z, 2018 # Diego Rojas , 2018 -# Albeiro Gonzalez , 2018 # Michael Terry , 2018 # Laura Silva , 2018 # Marco Morales, 2018 # Juan Camilo Montoya Franco , 2018 -# Waheed Ahmed , 2018 # Luis Manuel Moreno , 2018 -# Marcos Buccella , 2018 # Marina Pastor , 2018 +# Waheed Ahmed , 2018 +# Albeiro Gonzalez , 2019 # msgid "" msgstr "" @@ -22,7 +21,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: Marina Pastor , 2018\n" +"Last-Translator: Albeiro Gonzalez , 2019\n" "Language-Team: Spanish (Latin America) (https://www.transifex.com/open-edx/teams/6205/es_419/)\n" "Language: es_419\n" "MIME-Version: 1.0\n" @@ -60,7 +59,7 @@ msgstr "Actualmente lo está viendo como: {user}" #: static/components/MasqueradeBanner.jsx msgid "Masquerading failed" -msgstr "" +msgstr "Falló Enmascaramiento" #: static/components/MasqueradeBanner.jsx msgid "" diff --git a/credentials/conf/locale/es_AR/LC_MESSAGES/django.mo b/credentials/conf/locale/es_AR/LC_MESSAGES/django.mo index 5a2ee216e1a41c7e934ea75b20919e641f90be8c..f8aba5d08aa1f21fa5b0b2ea83a65e89ec359194 100644 GIT binary patch delta 15 XcmbQh@|<~s3gg;|s#zO%L@)vXE+YlQ delta 54 zcmaFPJb`6`3gfkjs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(4RDM&=5JW>%&~+6F+d Iu_u}l0NGLxtN;K2 diff --git a/credentials/conf/locale/es_AR/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/es_AR/LC_MESSAGES/djangojs.mo index 248295a4f973239a10fb7432daa859dea04c1083..594d3ac2661682dc8e023e53e590107cd0c7fd12 100644 GIT binary patch delta 15 WcmaFLyoY&$3S-km)vS#>bQu9JGX<6a delta 54 zcmdnP{FHfu3ghaDs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d IvB!WB0NAe&djJ3c diff --git a/credentials/conf/locale/es_EC/LC_MESSAGES/django.mo b/credentials/conf/locale/es_EC/LC_MESSAGES/django.mo index d9a0ae34d36bd1e8b0d547c808442f79b1862635..e96370d58573740eb86b71ed29ebafa6caa45d47 100644 GIT binary patch delta 15 WcmeBYdB!|Jg>lV9)vS#>!WjWA{RO`O delta 54 zcmaFH+|M#Wh4Jb{)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12HV?zZ)Q!67gZ37_K I*b~JF0N6Yap#T5? diff --git a/credentials/conf/locale/es_EC/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/es_EC/LC_MESSAGES/djangojs.mo index 9fbc056edf5efa32f683cfb803af5f043860994e..aae3ce9b804258fa3bb13c70327c2d74bd39c810 100644 GIT binary patch delta 15 WcmeBYdB!|Jg>lV9)vS#>!WjWA{RO`O delta 54 zcmaFH+|M#Wh4Jb{)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12H149KvODjW5Z37_K I*b~JF0N79uqyPW_ diff --git a/credentials/conf/locale/es_ES/LC_MESSAGES/django.mo b/credentials/conf/locale/es_ES/LC_MESSAGES/django.mo index c82f0466b979200162d1f11fcc0740748e94bb96..ea3a530e298a21bb8a34a5b77730922b68c5df31 100644 GIT binary patch delta 15 XcmbQl@{)Oi3gh~Ts#zO%L@@#YE^h_P delta 54 zcmaFKJc(t33geB5s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d Iu_u-h0NKY6s{jB1 diff --git a/credentials/conf/locale/es_ES/LC_MESSAGES/django.po b/credentials/conf/locale/es_ES/LC_MESSAGES/django.po index ccd5d5f0f..edf35ea16 100644 --- a/credentials/conf/locale/es_ES/LC_MESSAGES/django.po +++ b/credentials/conf/locale/es_ES/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ # URJConline , 2016 # Javier Galnares Arias , 2016 # Javier Orts , 2016 -# Paco López , 2016 +# 06052463cea5d8c38d564d090d2691cb, 2016 # Mariangeles Fernandez , 2016 # Miguel Angel Cordova , 2017 # Walter Ronald Perez Estrada , 2018 diff --git a/credentials/conf/locale/es_ES/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/es_ES/LC_MESSAGES/djangojs.mo index 6a54c00652abcfd04eac800d1391acd8a609ed85..e4196b873fe0bac2c6b01d85c5e177b62ef5e75c 100644 GIT binary patch delta 57 zcmbQla+7(23gi5Vs#z0vn5%^bc{%6j%)=+6F){ zvB%2HF|#DUNFgXSFFz?UC$(6?v#7MVBvHX85hSix1QT~i%_!ANEiJaw0UI^(&07Gj Cz$E_w diff --git a/credentials/conf/locale/es_ES/LC_MESSAGES/djangojs.po b/credentials/conf/locale/es_ES/LC_MESSAGES/djangojs.po index 71d4b96e2..3c67ba234 100644 --- a/credentials/conf/locale/es_ES/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/es_ES/LC_MESSAGES/djangojs.po @@ -5,12 +5,12 @@ # # Translators: # Rafael Puig-Durán , 2018 -# Paco López , 2018 +# 06052463cea5d8c38d564d090d2691cb, 2018 # Mariangeles Fernandez , 2018 # Gonzalo Rodrigo Ruiz , 2018 -# URJConline , 2018 # Javier Orts , 2018 # Aitor Renobales Irusta , 2018 +# URJConline , 2019 # msgid "" msgstr "" @@ -18,7 +18,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: Aitor Renobales Irusta , 2018\n" +"Last-Translator: URJConline , 2019\n" "Language-Team: Spanish (Spain) (https://www.transifex.com/open-edx/teams/6205/es_ES/)\n" "Language: es_ES\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/es_MX/LC_MESSAGES/django.mo b/credentials/conf/locale/es_MX/LC_MESSAGES/django.mo index 0e34ffe0ff1863371b5ab0f5b85ea7b96a5f75c4..c34892897e5f6c9bb94437d10db2dd87f9630fce 100644 GIT binary patch delta 15 XcmbQp@`8DS3gfzos#zO%L^1*ZE=dK) delta 54 zcmaFCJdtIB3gh*Os#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+d Iu_uNR0NHX6sQ>@~ diff --git a/credentials/conf/locale/es_MX/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/es_MX/LC_MESSAGES/djangojs.mo index 720c18cf1f73054bf125010082b9ccca9ff764dd..64b4deedab102c54877d54ee300681a6293f1bfc 100644 GIT binary patch delta 15 WcmZo*xz0R6g>mjg)vS#>JQx8h!v%u? delta 54 zcmcc4+`uwHh4I)#)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12H149KvODjW5Z37_K I*yGIz0M9!QXaE2J diff --git a/credentials/conf/locale/es_PE/LC_MESSAGES/django.mo b/credentials/conf/locale/es_PE/LC_MESSAGES/django.mo index 727ff5d2c6b1bdb0bbe25dd9d74ce3c2d624ed22..1df9f7f42dc01cdcf52383c669b9071c79a8665d 100644 GIT binary patch delta 15 XcmbQk@|k&p3gga+s#zO%q%Z;iFY5*K delta 54 zcmey&Jcnh13ge@Rs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+d Iu_v7o0N@)B)c^nh diff --git a/credentials/conf/locale/es_PE/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/es_PE/LC_MESSAGES/djangojs.mo index 65aa2c16c34316fd434a0ea108203122f919aa54..5f5969c142d3a2a28a0ab65465942a76742ae1a2 100644 GIT binary patch delta 15 WcmaFByq$T13S;d=)vS#>G#CLcbp?U| delta 54 zcmdna{D66a3gfbgs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d Iu}7N`0M)1uX8-^I diff --git a/credentials/conf/locale/et_EE/LC_MESSAGES/django.mo b/credentials/conf/locale/et_EE/LC_MESSAGES/django.mo index 582be55533ed9e17f695d2ca6f6699c777346a8d..695405a1a3f88ebc6ef23c3250f1c467e35a62d3 100644 GIT binary patch delta 15 WcmZol|Q)vS#>JQ)Ei=>>-X delta 54 zcmcb?+{iLPh4J`A)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12HV?zZ)Q!67gZ37_K I*yFbQl3I4F!?_ delta 54 zcmdnZ{DgUe3gfDYs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d Iu}7Z~0N4}{cK`qY diff --git a/credentials/conf/locale/eu_ES/LC_MESSAGES/django.mo b/credentials/conf/locale/eu_ES/LC_MESSAGES/django.mo index 1260524b02a0e3c57a6f4c0d4b3d910b56d704b6..a0fd5d69ab088c97b4c0e8d92e7711915d793a8c 100644 GIT binary patch literal 4344 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, 2018 -# Abel Camacho , 2018 +# Abel Camacho , 2020 # msgid "" msgstr "" @@ -14,7 +14,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Abel Camacho , 2018\n" +"Last-Translator: Abel Camacho , 2020\n" "Language-Team: Basque (Spain) (https://www.transifex.com/open-edx/teams/6205/eu_ES/)\n" "Language: eu_ES\n" "MIME-Version: 1.0\n" @@ -40,7 +40,7 @@ msgstr "URLak" #: apps/core/admin.py msgid "Social Sharing" -msgstr "" +msgstr "Partekatu sare sozialetan" #: apps/core/forms.py msgid "A Facebook app ID is required to enable Facebook sharing." @@ -89,11 +89,11 @@ msgstr "" #: apps/core/models.py msgid "Terms of Service URL" -msgstr "" +msgstr "Zerbitzu-baldintzen URLa" #: apps/core/models.py msgid "Privacy Policy URL" -msgstr "" +msgstr "Pribatutasun-politikaren URLa" #: apps/core/models.py msgid "Homepage URL" @@ -101,7 +101,7 @@ msgstr "Hasiera-orriaren URLa" #: apps/core/models.py msgid "Company Name" -msgstr "Konpainiaren izena" +msgstr "Enpresaren izena" #: apps/core/models.py msgid "Verified Certificate URL" @@ -109,11 +109,11 @@ msgstr "Egiaztatutako ziurtagiriaren URLa" #: apps/core/models.py msgid "Certificate Help URL" -msgstr "" +msgstr "Ziurtagirien laguntzarako URLa" #: apps/core/models.py msgid "URL of page for questions about certificates" -msgstr "" +msgstr "Ziurtagiriei buruzko galdera-orriaren URLa" #: apps/core/models.py msgid "Enable Learner Records" @@ -133,7 +133,7 @@ msgstr "" #: apps/core/models.py msgid "Facebook App ID" -msgstr "" +msgstr "Facebook aplikazioaren IDa" #: apps/core/models.py msgid "Facebook app ID used for sharing" @@ -153,7 +153,7 @@ msgstr "Gaitu Facebook bidez partekatzea" #: apps/core/models.py msgid "Enable sharing via Facebook" -msgstr "" +msgstr "Gaitu partekatzea Facebook bidez" #: apps/core/models.py msgid "Enable LinkedIn sharing" @@ -161,15 +161,15 @@ msgstr "Gaitu LinkedIn bidez partekatzea" #: apps/core/models.py msgid "Enable sharing via LinkedIn" -msgstr "" +msgstr "Gaitu partekatzea Linkedin bidez" #: apps/core/models.py msgid "Enable Twitter sharing" -msgstr "" +msgstr "Gaitu Twitter bidez partekatzea" #: apps/core/models.py msgid "Enable sharing via Twitter" -msgstr "" +msgstr "Gaitu partekatzea Twitter bidez " #: apps/core/models.py msgid "Full Name" @@ -183,7 +183,7 @@ msgstr "" #: apps/credentials/models.py msgid "The image file size must be less than 250KB." -msgstr "" +msgstr "Irudiaren fitxategiaren tamainak 250KB baino txikiagoa izan behar du." #: apps/credentials/models.py msgid "Invalid course key." @@ -200,11 +200,11 @@ msgstr "" #: apps/credentials/models.py msgid "awarded" -msgstr "" +msgstr "saritua" #: apps/credentials/models.py msgid "revoked" -msgstr "" +msgstr "ezeztatua" #: apps/credentials/models.py msgid "URL at which the credential can be downloaded" @@ -233,12 +233,12 @@ msgstr "" #: apps/credentials/views.py #, python-brace-format msgid "{first_org} and {second_org}" -msgstr "" +msgstr "{first_org} eta {second_org}" #: apps/credentials/views.py #, python-brace-format msgid "{series_of_orgs}, and {last_org}" -msgstr "" +msgstr "{series_of_orgs}, eta {last_org}" #: apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html msgid "" @@ -261,11 +261,11 @@ msgstr "" #: apps/records/models.py msgid "sent" -msgstr "" +msgstr "bidalita" #: apps/records/models.py msgid "other" -msgstr "" +msgstr "beste bat" #: apps/records/models.py msgid "User credit pathways can only be connected to credit pathways." @@ -273,7 +273,7 @@ msgstr "" #: apps/records/views.py msgid "N/A" -msgstr "" +msgstr "Ez da aplikatzen" #. Translators: A 'record' here means something like a transcript -- a list of #. courses and grades. @@ -307,15 +307,15 @@ msgstr "Zerbitzariaren errorea" #: templates/_actions.html msgid "Print or share your certificate" -msgstr "" +msgstr "Inprimatu edo partekatu zure ziurtagiria" #: templates/_actions.html msgid "Share this certificate via Facebook" -msgstr "" +msgstr "Partekatu ziurtagiri hau Facebook bidez" #: templates/_actions.html msgid "Tweet this certificate" -msgstr "" +msgstr "Txiokatu ziurtagiri hau" #: templates/_actions.html msgid "Add to LinkedIn profile" @@ -323,7 +323,7 @@ msgstr "Gehitu Lindedln-eko profilera" #: templates/_actions.html msgid "Add this certificate to your LinkedIn profile" -msgstr "" +msgstr "Gehitu ziurtagiri hau Linkendin profilera" #: templates/_actions.html msgid "Print" @@ -335,11 +335,11 @@ msgstr "Inprimatu ziurtagiri hau" #: templates/_footer.html msgid "Legal Policies" -msgstr "" +msgstr "Lege-politikak" #: templates/_footer.html msgid "Terms of Service & Honor Code" -msgstr "" +msgstr "Zerbitzu-baldintzak eta Portaera-kodea" #: templates/_footer.html msgid "Privacy Policy" @@ -357,12 +357,12 @@ msgstr "Open edX-ek sortua" #: templates/base.html templates/credentials/base.html msgid "Skip to main content" -msgstr "" +msgstr "Egin jauzi eduki nagusira" #: templates/credentials/base.html #, python-format msgid "Congratulations, %(user_name)s!" -msgstr "" +msgstr "Zorionak, %(user_name)s!" #: templates/credentials/base.html #, python-format @@ -420,8 +420,8 @@ msgstr "" #, python-format msgid "%(num_hours)s hour" msgid_plural "%(num_hours)s hours" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "ordu %(num_hours)s" +msgstr[1] "%(num_hours)s ordu" #: templates/credentials/programs/base.html #, python-brace-format @@ -436,12 +436,12 @@ msgstr "" #: templates/management.html msgid "Clear cache" -msgstr "" +msgstr "Garbitu cachea" #: templates/programs.html #, python-brace-format msgid "{program_name} Record" -msgstr "" +msgstr "{program_name} Erregistroa" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -484,18 +484,18 @@ msgstr "" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt msgid "View Program Record" -msgstr "" +msgstr "Ikusi programaren erregistroa " #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt msgid "Download Record (CSV)" -msgstr "" +msgstr "Deskargatu erregistroa (CSV)" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt #, python-format msgid "The %(platform_name)s Team" -msgstr "" +msgstr "%(platform_name)s taldea" #: templates/records/edx_ace/programcreditrequest/email/subject.txt #, python-format diff --git a/credentials/conf/locale/eu_ES/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/eu_ES/LC_MESSAGES/djangojs.mo index f498de686678c90ea98c179655c80915c7106ac1..0a1ddfe232318ba9d93895812da87eb866eb6cc4 100644 GIT binary patch literal 2593 zcmZvcJ8T?97{>=lcpMNQknl?23yF|JvUg`oM&S~p_;Ep}*ol?10|`jc-0j@ncK2)rd!*gp&g?he<3Epk2lst0 z&`#hvg6G^rLQI1nJ&YGx{ShG!f|KBr-~#vrxLopj$q;-R;|65AJK!_mC*bqo7vNLi zci^+&59RnT;G^i@1NVV{g8RYy;0xeiAlvW9PaZ!Evi%XT0v<2>RgmLMgU^92kmG$= zj`u;%`wn;<{1`k1ehZ!ee*-7Le?ZpZ1cGt=S@0F`D#-QSDtQRF=SYba8 za(*9x2f!O3=WojYcR|+kGw?9@4frznBglFG4zj=dAjcPv7yb^FJOXkZ$H7;@0A&9y zkafQfa{M01`V7ndI`|s;cfl9IFG0@l`*Qqekn8&cd<*;=tb#`ojCEQBd3+sYeZB@c zzn{Q2!Cyhn|6h>nJAmMjN1%-D##@SO<#}+Q*TFfmzI!#+f!C1RO9k@xQM`F?aXnZr z@dh5&lG`ynti!l*t*i&<^g141dv07iuRGzv7RzhGQs#|y zNLN(Xx=<|2)YEDbO1Pp;#+F!A-YZA%IT@-4I=Q!H>AaS4yg@rTw7Y$4W6@S76z#rr zs(9yVaS>&(cA~nvr@OSMq%)|Z?PagWZ=aVt<(9>%^1tW16$?KtztMut|r7FtFuJ`U9z68DNnPvp%Ul4^9zgO z$GvhLpwCU!^+78PAZMzrX;8g7DJxf0Vx13`(nyEFTo$Flifxchlu_Y&;GHySEIsVc zN7hEMik!`owA!`9%JSvlin^`YS+Ia@)S#*QfB*_eFiR2{9t8!lKWJz3YF z*^Y{7UJfPp5S^3gbAvRISR*oN4ieE6LdvoVfmM)>UL*pDEOlHziT zF)1$EMCU(KuLe@I22fc#i_LCuAM75;PuT-jdZdgL3o0E*7Zq`wigfDXMa+%{aHOIP zhgJ@hmzZDN6&GcU<2aBbZ3Em#NORXn9_y0A5z*Aa&cIQyMb^!9U}GufbOxT{YxPJ2CUDwZogzyH-Luy1JI#{>?Rt42L SJ-&PbDi*`b@knz=J@Fs-Y@ss% delta 115 zcmZ1|(!^qMPl#nI0}wC*u?!Ha05LNV>i{tbSOD<}prj>`2C0F8$vv#$Y9YGLMX8A; tnfZCTE{P?nRtiQ2h8DVp2D%1@3WjD@rsmoPKrs0MYdn*o#bigeHvob96bk?V diff --git a/credentials/conf/locale/eu_ES/LC_MESSAGES/djangojs.po b/credentials/conf/locale/eu_ES/LC_MESSAGES/djangojs.po index 2eb80f45f..d004bc45d 100644 --- a/credentials/conf/locale/eu_ES/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/eu_ES/LC_MESSAGES/djangojs.po @@ -4,7 +4,7 @@ # EdX Team , 2018. # # Translators: -# Abel Camacho , 2018 +# Abel Camacho , 2020 # msgid "" msgstr "" @@ -12,7 +12,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: Abel Camacho , 2018\n" +"Last-Translator: Abel Camacho , 2020\n" "Language-Team: Basque (Spain) (https://www.transifex.com/open-edx/teams/6205/eu_ES/)\n" "Language: eu_ES\n" "MIME-Version: 1.0\n" @@ -22,31 +22,31 @@ msgstr "" #: static/components/MasqueradeBanner.jsx msgid "Change user" -msgstr "" +msgstr "Aldatu erabiltzailea" #: static/components/MasqueradeBanner.jsx msgid "Submit" -msgstr "" +msgstr "Bidali" #: static/components/MasqueradeBanner.jsx msgid "View as: " -msgstr "" +msgstr "Ikusi honela:" #: static/components/MasqueradeBanner.jsx msgid "Staff" -msgstr "" +msgstr "Arduradunak" #: static/components/MasqueradeBanner.jsx msgid "Specific Learner" -msgstr "" +msgstr "Ikasle jakin bat" #: static/components/MasqueradeBanner.jsx msgid "Username or email: " -msgstr "" +msgstr "Erabiltzaie-izena edo e-posta:" #: static/components/MasqueradeBanner.jsx msgid "You are currently viewing as: {user}" -msgstr "" +msgstr "Une honetan honela ikusten ari zara: {user}" #: static/components/MasqueradeBanner.jsx msgid "Masquerading failed" @@ -72,19 +72,19 @@ msgstr "" #: static/components/ProgramRecord.jsx msgid "Share" -msgstr "" +msgstr "Partekatu" #: static/components/ProgramRecord.jsx msgid "Download Record" -msgstr "" +msgstr "Deskargatu erregistroa" #: static/components/ProgramRecord.jsx msgid "We are sending your program record." -msgstr "" +msgstr "Programaren erregistroa bidaltzen ari gara" #: static/components/ProgramRecord.jsx msgid "We were unable to send your program record." -msgstr "" +msgstr "Ezin izan dugu programaren erregistroa bidali." #: static/components/ProgramRecord.jsx msgid "" @@ -104,7 +104,7 @@ msgstr "" #: static/components/ProgramRecord.jsx msgid "{name} Record" -msgstr "" +msgstr "{name} Erregistroa" #: static/components/ProgramRecord.jsx msgid "{type} Program Record" @@ -112,7 +112,7 @@ msgstr "" #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Completed" -msgstr "" +msgstr "Osatua" #: static/components/ProgramRecord.jsx msgid "Not Earned" @@ -120,23 +120,23 @@ msgstr "" #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Partially Completed" -msgstr "" +msgstr "Neurri batean osatua" #: static/components/ProgramRecord.jsx msgid "Last Updated {date}" -msgstr "" +msgstr "Azken eguneraketa: {date}" #: static/components/ProgramRecord.jsx msgid "Course Name" -msgstr "" +msgstr "Ikastaroaren izena" #: static/components/ProgramRecord.jsx msgid "School" -msgstr "" +msgstr "Eskola" #: static/components/ProgramRecord.jsx msgid "Course ID" -msgstr "" +msgstr "Ikastaroaren IDa" #: static/components/ProgramRecord.jsx msgid "Highest Grade Earned" @@ -144,11 +144,11 @@ msgstr "" #: static/components/ProgramRecord.jsx msgid "Letter Grade" -msgstr "" +msgstr "Kalifikazio-letra" #: static/components/ProgramRecord.jsx msgid "Verified Attempts" -msgstr "" +msgstr "Egiaztatutako saiakerak" #: static/components/ProgramRecord.jsx msgid "Date Earned" @@ -156,11 +156,11 @@ msgstr "" #: static/components/ProgramRecord.jsx msgid "Status" -msgstr "" +msgstr "Egoera" #: static/components/ProgramRecord.jsx msgid "Course ID: {}" -msgstr "" +msgstr "Ikastaroaren IDa: {}" #: static/components/ProgramRecord.jsx msgid "Highest Grade Earned: {}" @@ -168,7 +168,7 @@ msgstr "" #: static/components/ProgramRecord.jsx msgid "Letter Grade: {}" -msgstr "" +msgstr "Kalifikazio-letra: {}" #: static/components/ProgramRecord.jsx msgid "Verified Attempts: {}" @@ -180,7 +180,7 @@ msgstr "" #: static/components/ProgramRecord.jsx msgid "Status: {}" -msgstr "" +msgstr "Egoera: {}" #: static/components/RecordsHelp.jsx msgid "Questions about Learner Records?" @@ -200,15 +200,15 @@ msgstr "" #: static/components/RecordsList.jsx msgid "Back to My Profile" -msgstr "" +msgstr "Itzuli nire profilera" #: static/components/RecordsList.jsx msgid "View Example" -msgstr "" +msgstr "Ikusi adibidea" #: static/components/RecordsList.jsx msgid "View Program Record" -msgstr "" +msgstr "Ikusi programaren erregistroa " #: static/components/RecordsList.jsx msgid "Program Records" @@ -216,11 +216,11 @@ msgstr "" #: static/components/SendLearnerRecordModal.jsx msgid "{name} - Sent" -msgstr "" +msgstr "{name} - Bidalita" #: static/components/SendLearnerRecordModal.jsx msgid "{name} - Not Yet Available" -msgstr "" +msgstr "{name} - Dagoeneko ez dago eskuragarri" #: static/components/SendLearnerRecordModal.jsx msgid "Send to {platform} Credit Partner" @@ -249,7 +249,7 @@ msgstr "" #: static/components/SendLearnerRecordModal.jsx msgid "Send" -msgstr "" +msgstr "Bidali" #: static/components/ShareProgramRecordModal.jsx msgid "" @@ -260,7 +260,7 @@ msgstr "" #: static/components/ShareProgramRecordModal.jsx msgid "Share Link to Record" -msgstr "" +msgstr "Partekatu esteka grabatzeko" #: static/components/ShareProgramRecordModal.jsx msgid "We were unable to create your record link." @@ -268,7 +268,7 @@ msgstr "" #: static/components/ShareProgramRecordModal.jsx msgid "You can close this window and try again." -msgstr "" +msgstr "Leiho hau itxi dezakezu eta berriz saiatu." #: static/components/ShareProgramRecordModal.jsx msgid "Successfully copied program record link." @@ -287,7 +287,7 @@ msgstr "" #: static/components/ShareProgramRecordModal.jsx msgid "Copy Link" -msgstr "" +msgstr "Kopiatu esteka" #: static/components/ShareProgramRecordModal.jsx msgid "Loading record link..." @@ -295,8 +295,8 @@ msgstr "" #: static/components/Utils.jsx msgid "{first} and {second}" -msgstr "" +msgstr "{first} eta {second}" #: static/components/Utils.jsx msgid "{firstItems}, and {lastItem}" -msgstr "" +msgstr "{firstItems}, eta {lastItem}" diff --git a/credentials/conf/locale/fa_IR/LC_MESSAGES/django.mo b/credentials/conf/locale/fa_IR/LC_MESSAGES/django.mo index fee62bb2b171d9d6bff5aab1e581b3ae5172f328..5aed30c2ade99aa0f547bf74ff1ca37f27a8bb23 100644 GIT binary patch delta 15 WcmaFPypMT;3S-Me)vS#>^cev!e+8ZZ delta 54 zcmdnT{G54$3gg;|s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d IvB!uJ0NINVegFUf diff --git a/credentials/conf/locale/fa_IR/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/fa_IR/LC_MESSAGES/djangojs.mo index dc9b0cb9a789aedadf637efd6fbac59e1e3a5ef3..fda5b1e59b2210b55d67ce36445578874fca132a 100644 GIT binary patch delta 15 WcmeBRxz9X7g>mUb)vS#>0vG`-^97>- delta 54 zcmcc5+`%$Ih4I`()hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT>}eUBVz?aV=F^*Z37_K I*b~eM0MslGhX4Qo diff --git a/credentials/conf/locale/fi_FI/LC_MESSAGES/django.mo b/credentials/conf/locale/fi_FI/LC_MESSAGES/django.mo index 1c765ec049a1abd912d742d9c9da0e50269aefea..0b90516909d0a760b70a32924afb0fe6718c6e85 100644 GIT binary patch delta 62 zcmey!{D66a3gfbgs#z0vm}`5aW@cw+DuiWbmL%q-<|)`@fCU_KQ*-l^6H8L{lJj%z PbQFvX3@s-!F}?=?Nr3gh;Ps#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F){ bvByd{EHfu3Rly@QGbb@8Q3tGU;>Y&@l=>I~ diff --git a/credentials/conf/locale/fi_FI/LC_MESSAGES/django.po b/credentials/conf/locale/fi_FI/LC_MESSAGES/django.po index 05609b058..b3d5b3693 100644 --- a/credentials/conf/locale/fi_FI/LC_MESSAGES/django.po +++ b/credentials/conf/locale/fi_FI/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ # Henri Juvonen , 2016 # Matti Peltoniemi , 2016 # Daniel Landau , 2018 -# Heikki Viitanen , 2018 # Ville Heilala, 2018 +# Heikki Viitanen , 2019 # msgid "" msgstr "" @@ -16,7 +16,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Ville Heilala, 2018\n" +"Last-Translator: Heikki Viitanen , 2019\n" "Language-Team: Finnish (Finland) (https://www.transifex.com/open-edx/teams/6205/fi_FI/)\n" "Language: fi_FI\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/fi_FI/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/fi_FI/LC_MESSAGES/djangojs.mo index 56d5d715de69533290ad23ba2a4b6e9a46731081..d8a955f685beaee9db900462721a88373a0ef2c6 100644 GIT binary patch delta 15 WcmeBWdBQwFg>ltH)vS#>LKy)qu?4pP delta 54 zcmaFD+{-dSh4J!4)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12H149KvODjW5Z37_K I*b~7B0M{B1oB#j- diff --git a/credentials/conf/locale/fr/LC_MESSAGES/django.mo b/credentials/conf/locale/fr/LC_MESSAGES/django.mo index 39c308e8c8a8fb362ce3649a9664a7870226c783..2b1eff543f452ca0d81e93778019349fe43c1969 100644 GIT binary patch literal 8281 zcmd6rTZ|mpS;tS70Eq)hSYQKWlbnTk$0qI`dsrKL94{WvjMtMLj|b0;lZZu2P4(%X zimR)7>(Vo0%OpG`+8`bx#Ve7QBmxmUNG!Jp#6t*4KSex*2c$?SAVGvsKtf0$q2+<) z_diwD({nN6k)uBSugkf7=evLPcTc|cHADL-{b~ANKVi%#!Qc3P{%EiMpfS&ap9L>~ ze+XUz{|Rh?&%Di;_kg#+SHT|mPVnErcY$w$KL)<*?Z&(V{2=&4;92kk;1%$vz+VQp zz#Z_%!M_K80Q^r->;E_S6!_GM>b?c)ei3{x_)?8Kpw@c`&VqjcijIE-lw7_D@-u(J-zo4L;QPRT0rmdv98&yzKX?Yb1j0ho z1-1Sw;Jd+B!FlitU<>?PP~+dh%eR6TLGkelsCCysons5s`F{=kWAHT))iO7Jq?)%1 ziZ2_Wp8rz)JOqD*`z=uX`X;FLzXv{i3;Y9ho`)=c8T=}E3w#4?gG&&hbM1hyf}aP) zx2IV|d_DzQ@G=N1%uAr?ybEf-PuB0h1SZ`71t_^s!xZW53Mf8qfYO`1zW-c(|2p{7 zJpW7Z8Sr003%(m=e-S(fo&+C&KL`FcDEht)!Xoos@CNuF;2rP^%;d=CA;=^16;O8Z z=ioH>H=xdS;wLNqzYmn&&VVc61yFQ+8kBwfE~xeX2-LnKke~S$e;n0(57fEd1m6Ze z4KqdWNsyzM7eSqW6PyA+1ww*(1d5+u2d{&F1que5vXyW0j0OEfiHo7 z2J$mcF-SNAo(5NH+yVKS->UIz;Cb%917aHHNr)2t7SwqzfL(AEybS(njsFfxZ>JHe z`0^acBXbATz6Gd#J_mjW{C!Y-_;r*+^F9wsZ+{b%-hQ#huYi5-UkAmnr)|ZrYv5bB zpKSB>%|~h!{c2a}I+NNmU3`*_Yai`7OK;QFbSCNjRr=4<)g%u!>6LU^`X-ycM1O(4 zL04O$pYvCh?nqzMbnbP3RsCT)<0e^ti2k$moAp!iMLH@yQoBgM;IEANaE*dh`g8TY zbX#qSewHqt%3h?KYS-v%^k1M`x*9Asi}aRmXp*mVUu~uW|1Z66nsic4I@>hqn;IhZ zvVI=?Mfw6=IxG8CzqP%@g!z=^v%5ouW$*rSra>Je8FF zm$%X~%TMQ4XpTP0c{a$>UKaFUPJ+H;+vr3D-1He*i2bX%m>CBbIw_DAJgK^7&wwwdejev8qe%ZjKQg+bvo zVHXPRa+KV4ou$M=R5yxUH99@!c^um;>TMO;72<~M>UKgmDD3u@%bZQp!gbo#bv|~+ z-Ws?>S2jqrw;NxNU&LuI&27NE%=MzYU~3yh!sK1oudtMaZ8IN)EEvX) z$sOGm0gKtW)g`-ed)drS9P);X2h~H3wxMR4`LsU>l3lw3_qjheH)TO*C?Lh_PV>0FwZkYF>(_Db8f7}}UA=Vk zN=i|kC%0XP1TxpQ>pWb(xn{QlRKw;`KZ+4s?ux=I^4I|8yaAZ;eQglfOvjr~L;iK_ zr;_uHwC@H%&v|WFvLQMUJBcgc8iREjIO;TH-F3T+$@3Cg@AuQB*m_{^?;_O)Mx5FVAid0Df9w!S(muNt&MdoL5Zl!> zj>3plZw3jp_EBYfCvw|nWqQuY0Hk~TBn>+E_yySLm-9YDi|Hh|59lfAc6^9_}z^&JkRUD%8q(RbzS7EGo-*#hT zX~Eo1ISYQL;o_t_s?2_?Cv_q(=yu1~+Zl^Y%PD#dT9d=h>|&OsnOXCkI?R%T*1Z<>lAuVl z-2)D-@=j6ZuR2k;i%?LhZid<+#u76OlbUG;*Z`R~2V=FqCVb;ol zY@iaaRI!`JDA(b|a$AlGy z7U4474=`=JklK}7>&-ZOwQZzXc&6>GDBSW^x<8(9mdKlQ>hRz?el*?n8OZ}APd*om+Gf0x##S;=Vs5(eCRCg z#4-$Rt=B81*{q$5-3~Ir4==3Q4+qWNRkAhQPyeD;fBp=s&1iDcEAeA38RM+IjweCK zPc`mxdwaX>iHy2#NAspddJEd8Nt*ik^o5zT=cl{b^y%?Rvna-in@cwr_c?K{ef9)7 zp1e+LeRse~3b#{C;}4?bvJKHqvWqJl>(^Ts_s8i#c(&GJ5~dwQIBPF%M#YKMxXgmM zb)5tupS8)r-{x0lF5Bwz%BjRYZ_k~+e8L>c`yJWManFt^1{TPFNe?W)FdOZuFc>IL zKc*sBo}vg~Ts%eoL!9@*9Oq|NwS=z^FmbhWGf@ z{Qzo5U$(=vK%!_LesWwGjE4v1(O3V!YK39c8SOQN0x67~sv@KJm!5%h5#X5wbM_C9 zYg4ACXTEe;AtMJ@ETtL`o%nQf?J7Qigwg>&uBj)C|GZlp4 z=uvLwx|C#i8*P&`8&p0yuS6O!B92CTDkOH=-}o|icDR&42yx^}QAZBt*9kV!pWbNC zPqNhz9qmc;d)1zu6hFn~fwE$5tSg>sDN8<~DdV?W2yKMv=_&RFwgBQXI|b_hjcvTeK|~ZLKX}-G2MW0-YI=f z?Cn%JvK{z}LBh*Ndu^j?r-+pFSazfS;cJFBF1b4PMu&an?u#E;T1iSZdPHUhZ47~1!+;^ z{Ii1p)b*N;-ujwaprjDXS`#E&2Z7%d1L- zp<#OT*)YTW<*@5_Z9`=x4MBm1ATqAZ_)sv_&f|ECgE`*c=f3s|v_(Ee$W%uVB~Bl-s!${cL)}b1g+-?c*@;)v&QxVRRk(WAmt)9lD@_tHL!x-xXgttpkB^uG zXO)v5tkcYd%h73%_^RW*j}Oz0ZCB+QtT?FCe$1F`iLMC`k9||sWkhb$L(dtcS%>p+ zG`C)7DGv0BRV5zmZQGSayE?a4&6nOxQQ0lS+B(C-&T-xuX=?3)nbpK-=F2<9Q(|Jx z{0WourCy!5MYwg)>!4&xfG;z!(#=kr7gaREm#hkNiP?!`B#>un-G`Nc)|*uny=4~dkD ztVlD1Vjgs37>5E+qAoOzyU@c*yn+LG6HD+NvXE@x0sMk>Siz)nJc#@77`CB}MR*HC z>@WBH0h!_pvVMpscwsvKU&iaG8TSyj2HKBja2zAJhE@0!JMb;~8Dj(YTd0+4;G*xh zqONlUtJz=17*wE*TC!zij@(7f_&yHeGt^AKV-4n$Mhor_JdQg5a^P+3;{GveWj`au zk#B)NaV0orKSTc8Zo09SQ8(1!wCUoyv7R%Zfu9BVO^-^C?YT4+_jO%6PXfB<$CQ}*5EEmt4AH{Wf zlO2QRM#)vv7>=0N;o&0hlH*yUp67bzN$IP?(^C^ml6Eh+=1=5o)2aBd=cKZ8?qYl- zm34Zp&P4K1Jei0ml2)?2x2vZ$K{Hd)-llPPcF`Kjc?*u!H_!OoK!%Bnj?-?t?YV{i zHh->p9^D8{G>?s#^{N^ZEAP}`qd~BfZpO0R%*C{mHGQ#b=0hyuhkL#HQP_4Y+wrn< fGjnz-YbtAoONrUZdDfCc2070pYrht42QPmCE#Hci diff --git a/credentials/conf/locale/fr/LC_MESSAGES/django.po b/credentials/conf/locale/fr/LC_MESSAGES/django.po index cdfe5c027..e0ff52373 100644 --- a/credentials/conf/locale/fr/LC_MESSAGES/django.po +++ b/credentials/conf/locale/fr/LC_MESSAGES/django.po @@ -12,6 +12,8 @@ # Gérard Vidal , 2018 # moocit-france , 2018 # John Burke , 2018 +# willyedoo , 2019 +# Alexandre DS , 2020 # msgid "" msgstr "" @@ -19,7 +21,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: John Burke , 2018\n" +"Last-Translator: Alexandre DS , 2020\n" "Language-Team: French (https://www.transifex.com/open-edx/teams/6205/fr/)\n" "Language: fr\n" "MIME-Version: 1.0\n" @@ -63,11 +65,11 @@ msgstr "Nom de votre plateforme Open edX." #: apps/core/models.py msgid "Segment Key" -msgstr "" +msgstr "Clé du segment" #: apps/core/models.py msgid "Segment write/API key." -msgstr "" +msgstr "Clé API/Écrire le segment." #: apps/core/models.py msgid "Theme Name" @@ -77,6 +79,7 @@ msgstr "Nom du thème" msgid "" "Name of of the theme to use for this site. This value should be lower-cased." msgstr "" +"Nom du thème à utiliser pour ce site. Cette valeur doit être en minuscules." #: apps/core/models.py msgid "LMS base url for custom site" @@ -88,11 +91,11 @@ msgstr "URL racine du site de LMS (par ex. https://courses.stage.edx.org)" #: apps/core/models.py msgid "Catalog API URL" -msgstr "" +msgstr "Catalogue URL API " #: apps/core/models.py msgid "Root URL of the Catalog API (e.g. https://api.edx.org/catalog/v1/)" -msgstr "" +msgstr "URL racine du catalogue d'API (ex. : https://api.edx.org/catalog/v1/)" #: apps/core/models.py msgid "Terms of Service URL" @@ -100,35 +103,36 @@ msgstr "URL Conditions d'utilisation" #: apps/core/models.py msgid "Privacy Policy URL" -msgstr "" +msgstr "URL de la Politique de confidentialité" #: apps/core/models.py msgid "Homepage URL" -msgstr "" +msgstr "URL de l'Accueil" #: apps/core/models.py msgid "Company Name" -msgstr "" +msgstr "Nom de l'entreprise" #: apps/core/models.py msgid "Verified Certificate URL" -msgstr "" +msgstr "URL du Certificat vérifié" #: apps/core/models.py msgid "Certificate Help URL" -msgstr "" +msgstr "URL de l'Aide pour le certificat" #: apps/core/models.py msgid "URL of page for questions about certificates" -msgstr "" +msgstr "URL de la page pour les questions à propos des certificats" #: apps/core/models.py msgid "Enable Learner Records" -msgstr "" +msgstr "Activer le Dossier Apprenant" #: apps/core/models.py msgid "Enable the Records feature. The LMS has a similar setting." msgstr "" +"Activer la fonctionnalité de Dossier. Le LMS a des paramètres similaires." #: apps/core/models.py msgid "Learner Records Help URL" @@ -140,43 +144,45 @@ msgstr "" #: apps/core/models.py msgid "Facebook App ID" -msgstr "" +msgstr "Identifiant de l'application Facebook" #: apps/core/models.py msgid "Facebook app ID used for sharing" -msgstr "" +msgstr "Identifiant de l'application Facebook pour partager" #: apps/core/models.py msgid "Twitter Username" -msgstr "" +msgstr "Nom d'utilisateur Twitter" #: apps/core/models.py msgid "Twitter username included in tweeted credentials. Do NOT include @." msgstr "" +"Nom d'utilisateur Twitter inclus dans les paramètre d'accès du compte " +"tweetés. NE PAS inclure l'@." #: apps/core/models.py msgid "Enable Facebook sharing" -msgstr "" +msgstr "Activer les partages Facebook" #: apps/core/models.py msgid "Enable sharing via Facebook" -msgstr "" +msgstr "Activer les partages par Facebook" #: apps/core/models.py msgid "Enable LinkedIn sharing" -msgstr "" +msgstr "Activer les partages LinkedIn" #: apps/core/models.py msgid "Enable sharing via LinkedIn" -msgstr "" +msgstr "Activer les partages par LinkedIn" #: apps/core/models.py msgid "Enable Twitter sharing" -msgstr "" +msgstr "Activer les partages Twitter" #: apps/core/models.py msgid "Enable sharing via Twitter" -msgstr "" +msgstr "Activer les partages par Twitter" #: apps/core/models.py msgid "Full Name" @@ -200,6 +206,8 @@ msgstr "Clé de cours invalide." msgid "" "Signatory organization name if its different from issuing organization." msgstr "" +"Le nom de l'organisation signataire s'il est distinct de celui de " +"l'organisation émettrice." #: apps/credentials/models.py msgid "Image must be square PNG files. The file size should be under 250KB." @@ -209,35 +217,37 @@ msgstr "" #: apps/credentials/models.py msgid "awarded" -msgstr "" +msgstr "accordé" #: apps/credentials/models.py msgid "revoked" -msgstr "" +msgstr "refusé" #: apps/credentials/models.py msgid "URL at which the credential can be downloaded" -msgstr "" +msgstr "Les crédits pourront être téléchargés à l'URL suivant" #: apps/credentials/models.py msgid "Program UUID" -msgstr "" +msgstr "Programme UUID" #: apps/credentials/models.py msgid "" "Display the associated organization's name (e.g. ACME University) instead of" " its short name (e.g. ACMEx)" msgstr "" +"Afficher le nom de l'organisation affiliée (ex. : ACME University) au lieu " +"de son nom abrégé (ex. : ACMEx)" #: apps/credentials/models.py msgid "Use organization name" -msgstr "" +msgstr "Utiliser le nom de l'organisation" #: apps/credentials/views.py #, python-brace-format msgid "" "I completed a course at {platform_name}. Take a look at my certificate:" -msgstr "" +msgstr "J'ai complété un cours sur {platform_name}. Regardez mon certificat :" #: apps/credentials/views.py #, python-brace-format @@ -254,27 +264,29 @@ msgid "" "successfully completed all courses and received passing grades for a " "Professional Certificate in" msgstr "" +"a complété avec succès tous les cours et a obtenu les notes requises pour " +"un Certificat professionnel en " #: apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html msgid "Professional Certificate" -msgstr "" +msgstr "Certificat professionnel" #: apps/edx_django_extensions/views.py msgid "Cache cleared." -msgstr "" +msgstr "Cache vidé" #: apps/edx_django_extensions/views.py #, python-brace-format msgid "{action} is not a valid action." -msgstr "" +msgstr "{action} n'est pas une action valide." #: apps/records/models.py msgid "sent" -msgstr "" +msgstr "envoyé" #: apps/records/models.py msgid "other" -msgstr "" +msgstr "autre" #: apps/records/models.py msgid "User credit pathways can only be connected to credit pathways." @@ -316,23 +328,23 @@ msgstr "Erreur serveur" #: templates/_actions.html msgid "Print or share your certificate" -msgstr "" +msgstr "Imprimer ou partager votre certificat" #: templates/_actions.html msgid "Share this certificate via Facebook" -msgstr "" +msgstr "Partager ce certificat par Facebook" #: templates/_actions.html msgid "Tweet this certificate" -msgstr "" +msgstr "Tweeter ce certificat" #: templates/_actions.html msgid "Add to LinkedIn profile" -msgstr "" +msgstr "Ajouter au profil LinkedIn" #: templates/_actions.html msgid "Add this certificate to your LinkedIn profile" -msgstr "" +msgstr "Ajouter ce certificat à votre profil LinkedIn" #: templates/_actions.html msgid "Print" @@ -344,7 +356,7 @@ msgstr "Imprimer ce certificat" #: templates/_footer.html msgid "Legal Policies" -msgstr "" +msgstr "Politiques légale" #: templates/_footer.html msgid "Terms of Service & Honor Code" @@ -352,7 +364,7 @@ msgstr "Conditions d'utilisation & Code d'honneur" #: templates/_footer.html msgid "Privacy Policy" -msgstr "" +msgstr "Politique de confidentialité" #: templates/_footer.html msgid "" @@ -368,12 +380,12 @@ msgstr "Site réalisé avec Open edX" #: templates/base.html templates/credentials/base.html msgid "Skip to main content" -msgstr "" +msgstr "Passer au contenu principal" #: templates/credentials/base.html #, python-format msgid "Congratulations, %(user_name)s!" -msgstr "" +msgstr "Félicitations, %(user_name)s !" #: templates/credentials/base.html #, python-format @@ -382,6 +394,9 @@ msgid "" "with colleagues, friends, and family to get the word out about what you " "mastered in %(program_name)s." msgstr "" +"Vous avez travaillé dur pour obtenir votre certificat de %(platform_name)s —" +" partagez le avec vos collègues, amis et membres de votre famille afin leur " +"faire savoir ce que vous venez d'apprendre sur %(program_name)s." #: templates/credentials/programs/base.html msgid "Supported by the following organizations" @@ -417,11 +432,11 @@ msgstr "Noté par" #: templates/credentials/programs/base.html #, python-brace-format msgid "Issued {month} {year}" -msgstr "" +msgstr "décerné en {month} {year}" #: templates/credentials/programs/base.html msgid "Valid Certificate ID" -msgstr "" +msgstr "Certificat d'identité valide" #: templates/credentials/programs/base.html msgid "Effort" @@ -431,8 +446,8 @@ msgstr "Effort" #, python-format msgid "%(num_hours)s hour" msgid_plural "%(num_hours)s hours" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(num_hours)s heure" +msgstr[1] "%(num_hours)s heures" #: templates/credentials/programs/base.html #, python-brace-format @@ -443,16 +458,16 @@ msgstr "" #: templates/management.html msgid "Management View" -msgstr "" +msgstr "Affichage de gestion" #: templates/management.html msgid "Clear cache" -msgstr "" +msgstr "Vider le cache" #: templates/programs.html #, python-brace-format msgid "{program_name} Record" -msgstr "" +msgstr "Dossier {program_name}" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -495,12 +510,12 @@ msgstr "" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt msgid "View Program Record" -msgstr "" +msgstr "Voir le Dossier de programme" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt msgid "Download Record (CSV)" -msgstr "" +msgstr "Télécharger le dossier (CSV)" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -516,8 +531,8 @@ msgstr "" #: templates/records/edx_ace/programcreditrequest/email/subject.txt #, python-format msgid "%(program_name)s Credit Request for %(user_full_name)s" -msgstr "" +msgstr "%(program_name)s Demande de crédit pour %(user_full_name)s" #: urls.py msgid "Credentials Administration" -msgstr "" +msgstr "Gestion des paramètres d'accès" diff --git a/credentials/conf/locale/fr/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/fr/LC_MESSAGES/djangojs.mo index 7df0312fd63869a506dfa7c0edda543b6ee0721e..d6462db348cd0722740b20bf0440de8b2e4c6ac8 100644 GIT binary patch delta 332 zcmeBWTgz5|Pl#nI0}yZmu?!HW05LBRuK{8ZcmTv8xrb2tHIU{8;;%r=3dD?z5SkrG zvjO?MP+9~?3j_JmK)Mfz^?{fJ$lu2V(SICB^Dr>PFc&7-WGoKad8R0hR+&U;vV50b-DTpmMN-fD{-2l{2tUJZdK#TAW&xmzbNXkYA*b znwyxJW2G>ei7|X~6Qj9md1g*dWok-(zJd*$>5vXmq?eqZYp0`NWMF8y`97l_qnuxU eu0o1>X-Q^IW^rOkYH5)|eyKvLE=a9{6$1c52s69@ delta 297 zcmZ3>*2`9ZPl#nI0}yZku?!H$05LZZ&jDf(I03|*KztHPUk1`3`MW^O3dAp=^m`!9 z2E<>X^dBI-gOP!O8Hhn, 2018 # Eric Fischer , 2018 # John Burke , 2018 +# willyedoo , 2019 # msgid "" msgstr "" @@ -17,7 +18,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: John Burke , 2018\n" +"Last-Translator: willyedoo , 2019\n" "Language-Team: French (https://www.transifex.com/open-edx/teams/6205/fr/)\n" "Language: fr\n" "MIME-Version: 1.0\n" @@ -47,7 +48,7 @@ msgstr "" #: static/components/MasqueradeBanner.jsx msgid "Username or email: " -msgstr "" +msgstr "Nom d'utilisateur ou e-mail :" #: static/components/MasqueradeBanner.jsx msgid "You are currently viewing as: {user}" diff --git a/credentials/conf/locale/fr_CA/LC_MESSAGES/django.mo b/credentials/conf/locale/fr_CA/LC_MESSAGES/django.mo index 0bba9c1090d73c7dc0e78a3091200665d6474a58..0152c621475390a7eecf05cef5d57e360245e1e5 100644 GIT binary patch delta 2050 zcmZwHe@vBC9LMqRl?xZp_@OD#O?*O)1SBn|MolsZOH{y(6j0)F?~Na^OWq$P|8yD6 z)mmGdceQ5xlMW1>xu7<2wzXKlG%HQ5^+(wj%jsH0xm**gy&NAE2+AId5LcYvjsXpb=ZJa*ozv-2s-#S4&WtZvew8$ z4X712VHeioapY&eaan-wyyUc0=gKBCtk-#aGRH4ID-1Dvgus|Y{nSAj(1@(`J{<8p+2`hd=bx}mZpsjRD|8=;1Fs_ z;)f_G1LMfgKH#F6Orv&tIu&AD9Q9efih6#K%We2xs{TEyz1vAXUz)N8b>EMgzz~+; zS!CjI`ryyn$MRCCih$ zy9!IG??h#A5Y^8xD$bj@iTE}}p%&-Ti~a{1k!)HA>cv4+NBi+1Jc&wa8aK6uCAb1t zr))!q`aaZV9L2pjg&XlczCrz8JdJT}iWexzQS{-vxDHF`wHjM-EgnEOo}U0L>cM67@Zc)$#u+3z*2qV#6h4Vc z{eD!SF>Jw)6Jr@(S6kx!j8fO5i61i>($%^%vC&iDdMwf6smTvg*FlG~hc9u=<6Rt} z{4l5fJ2atxj$ITw64Rb~S65Op&=DYL=lPrR1tE$ZdoDo!nFH)j>6vfDFp zF>`v}hUQ2x?D)gMKu^?p!WZp+$~O>k{JxOWAL<)$I)jeCKNJf3qrrd^?f>tKl@pT- zug;nHq%bXi$Ce=N!~XtQIN}6o7M>bYpRs69Ur)pr4aUMwh*yHKs55mW8veKAsWE3y Ttkf6V`+vFJh-0rwAx$0%50zFAYmWA9vya*{x!{ekOm_xF39bNKzv_nhxJ zhrNlxR%bLfeVcLhaL?lInqsDg&%8Wz1~}W|}RikI)qhFVFJX}Q<|As57fMkbF2Xn_Py^Y9Asob4@F!%lwwOO^Kuhsq zY{3THjr{B!kLh?BH8B63lNm0-8PsbroA|bzf<9P{xwrvY1$!GG!*{U+e?~uEMaE#a zP?^cjF=H||Gi5pUQjehqa1b-_2r6?wpfd6oI_l^K1qBZ9(8y+?QeT4;GsHUTEw~tW zq5_{qb#xx}-hWt%SwvMvDpCDxLdEIFa@>W*_~Uf)uNVHLL8-rn?U+mBeYhUkKQ@30 zbO4#O9mRS)g%Qjnzm-^r`mPt#aX0d_eLQ&84x=VGjD)fa`Q-l)g>f35#4m{A!!&k| zI>Dh~PWydgtXYgsfhNnDcO+?peDq*&@_@G0fiNYJW z0*CP#EN4e{p@TY3=THNzB+5p76RYt$@`;7Xr;gb}_#O_TmZXM_Q-pOG!Zy@WypGC% z(@%k)eZ)gEIf~llzaufNl|~(>ji|rB#^Y}MAXWbs_5En7elulpXtMo&)CAgaHtxq- zJb?_isfQe=(eb z&8SR2g(3VH13LfTQ_u$|Q3Dx8rR*YV)1;S~y^lqxz=u%B@HlEMM^H=fFOqf3yT@!U zR--bQK=tzyD$W+v=@`HU;@cPno&O4YBYPG{y_i6Cv<@G^t*BK0hFZgMyce^0N!DNp zJCJQ*oAG5Fz$I9~+0*}t6{rcWK}Utn6q2|T=i@jo!g=%>!#+&MPf-K-615b=r~#ix z1@y5VBUpwLyB-77o5GXdw_pbKHdK3OnC^WP*3r<8eMmBF1eLn$sFde%s1+!Tt=Q;( z?G1Vo?rHB_&r0{Ew>hJY)}*`4SLo?>dwlgdUDS2a!Nj%7J>m-%ucrJcxBgdXBDb$+ zDRj73e2tzp?h=2Y#x`-ayGehur_=q)A1vyjCCaU(e2zQLt*ovc8uO1$9SRiuoi=BA xq`tR1+1K;p3z3FoUw1rI9j#grsftFTRiUa_yk>ql$~E-Q3~$a*Q^lFI{{e=d)CvFq diff --git a/credentials/conf/locale/fr_CA/LC_MESSAGES/django.po b/credentials/conf/locale/fr_CA/LC_MESSAGES/django.po index 5551827a3..7077f497e 100644 --- a/credentials/conf/locale/fr_CA/LC_MESSAGES/django.po +++ b/credentials/conf/locale/fr_CA/LC_MESSAGES/django.po @@ -287,6 +287,8 @@ msgstr "autre" #: apps/records/models.py msgid "User credit pathways can only be connected to credit pathways." msgstr "" +"Le parcours de crédit utilisateur ne peut être connecté qu'aux parcours de " +"crédits." #: apps/records/views.py msgid "N/A" diff --git a/credentials/conf/locale/fr_CA/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/fr_CA/LC_MESSAGES/djangojs.mo index 1fb45c4a2407e3717a58b864a7b0783c9c5bd951..6470c228f5df642c82bf7ee8478a34b2fe84225b 100644 GIT binary patch delta 547 zcmXZZy)Q#i7{~D+k+!O#x3s9YMl4c->GtkMX&Q?}VxkEP8-thI!G?(30e^r<_#Zz-+vfr!c#qxqfc^O9$XAWvhk*h6k!mb>$1MGC%weXp zaWNN<=@&7Mb)3Zyw1cL}q7UcM`s+B3`)C)q#S&IAim9|O4H9IToktHX<1*%O75Q{X z!y298-$rPeRN1MMIE6DkHHc z&JGbD4r2gkP!n6m66Vm3F0x)kAL_al?8BY<_z(@o*Qg1+;{w*O1*g4vkpY4@tAm(A zJ#hg8SVEfI(y>M!@P8xJlh&{me=vla1~ui;kknJnDgys8{FSCeXVr xB2A7e(WVRgXu^oEr<2*t+Nv>|%%)?33Db%gmT8z)z?zCphJz+g<->XD_ycvFMc)7b diff --git a/credentials/conf/locale/gl/LC_MESSAGES/django.mo b/credentials/conf/locale/gl/LC_MESSAGES/django.mo index 38204de9983b8f25832141367b252d56464ab678..4bef14cb3d58c5782faac41aff9959283a62ad63 100644 GIT binary patch delta 15 WcmeBWdBQwFg>ltH)vS#>LKy)qu?4pP delta 54 zcmaFD+{-dSh4J!4)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12HV?zZ)Q!67gZ37_K I*b~7B0M`Z&nE(I) diff --git a/credentials/conf/locale/gl/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/gl/LC_MESSAGES/djangojs.mo index 0a4aa28982f8d320cda8064761dfc6d625959d55..738e8b89b021855fb7633950d6cb411d18661c11 100644 GIT binary patch delta 15 WcmaFQe2jU53S-Yi)vS#>%ozbNuLZsU delta 54 zcmX@c{GNG&3ggy^s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d IvB!!L0N%Y1qW}N^ diff --git a/credentials/conf/locale/gu/LC_MESSAGES/django.mo b/credentials/conf/locale/gu/LC_MESSAGES/django.mo index 6dc5964d77c22543ba10993ccb4fb5d4ef502795..6f4116568792e9dc968a08b0f7189a8537a65db7 100644 GIT binary patch delta 15 Wcmcc5yp4H+3S-Sg)vS#>)ENOTPX&Ge delta 54 zcmdnSe4lxO3ggm=s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+d Iu}6y$0Mz*pU;qFB diff --git a/credentials/conf/locale/gu/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/gu/LC_MESSAGES/djangojs.mo index 2faa0597113acc045f3976a155258dc6978f81d7..b5891994742f6a1fc0e05c70f239bc7402edb7e9 100644 GIT binary patch delta 15 Wcmcb@yqbA}3S;3!)vS#>WEcS~tp!p5 delta 54 zcmZ3@e1&;}3gfJas#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d Iu}7W}0M29&Hvj+t diff --git a/credentials/conf/locale/he/LC_MESSAGES/django.mo b/credentials/conf/locale/he/LC_MESSAGES/django.mo index 70d5a7ac347e0a7d827a0556bb5ceea7effaba41..e252a2a9fe19a40be965feb5f6c65c0fea1d33ea 100644 GIT binary patch delta 563 zcmXZZJ1oOd6vy$OSJHTtDpfS3UiAo~gQP-OkTS7JhbA4`O}a2Nl7GS=A)$#?JY(xX zy4eUZ7(`+atHr>8#L&t2(Bvkcdv5MM=bn4-A_tN4@lxlbL~^;Mw4|R}DT1jwsS}Se zj<@)Q-5#k2{q<4@PGSfX=*KOr!BgDE3+%!PuhfnSv~%m&hi6_ne}x`cuteWDh^-A$ z6r)&$dl z%V;mMU-Sm2iQlmo!!1%9#*iMA!BH%rogd+r{F>ru`_gEg@8SUFkl$ADnTd7!R`e4y T#09iMEBU4Jipu<^d&>O_L3ut0 delta 602 zcmXZZJuE{}6u|MLNE)hCwWx+xrAR$miPENvR3i*RSm+=f2#ZS72}u`&qyw8+YA`ZL zI?%)_K4Y_3ZG=G#B9ZtX`tp+BJtyy;bI*N+$W`PtX39BDB4(?|x`=c%h(vG?eRzwD z_>LbqWfSq^Xro9cu3$Iju?34*hxeGrC+xyyJGC*7y6+f!@xd;+zeZmqw2-?=#K19} zzzo*n1$yxsXYd8LFy;_x!xCzvN1ViO?Q zMl)8D)B{;iCyb&toW%i5V=EqF6ib-IPh7@nHfjAc)B$eF@hkQd|KwPVv*@FWUQHIY z;Xdk(T$X*q1hK;<(t}BC$8BT=Il?gdIGyfaK>nKSpni9NI{7&U@Dc0LTR~PQwUu?@ z2C)ydQK2|rRbAtsGvb-0RCYDJW{jn>OHt2YFf?L>f<`dp35BCW!+{`AF=>rke*rih BN38$= diff --git a/credentials/conf/locale/he/LC_MESSAGES/django.po b/credentials/conf/locale/he/LC_MESSAGES/django.po index 35b86842f..959b1a7a2 100644 --- a/credentials/conf/locale/he/LC_MESSAGES/django.po +++ b/credentials/conf/locale/he/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ # # Translators: # Natalia Berdnikov , 2016 -# e2f he_r1 , 2016 +# da4b3b3e32ebb4f0ec5430359fde7fbb, 2016 # qualityalltext , 2017 # e2f_HE c1 , 2017 # Nadav Stark , 2018 diff --git a/credentials/conf/locale/he/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/he/LC_MESSAGES/djangojs.mo index fa6bfac77e2324e1e817b4ad5a8942e50ada856f..8b41a5acf81cfabb07c8c96554d756b311b043d9 100644 GIT binary patch delta 16 Ycmcb_vXW(j3e$VWiE5b}cg$i005rA+P5=M^ delta 55 zcmZ3-KrU;`7-g9td}q$U>SWfo*)>Lusr g+UbDIwFDZImtLBfo~j#?nwV>);E|a(S%&cx03zEjWdHyG delta 142 zcmcb?{ET^m3geoIs#*0Ry3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+t z72vNMlv, 2018. # # Translators: -# Akshay Garg <64akshay@gmail.com>, 2016 -# Goutam Kumar Dey , 2016 # Amol Bhave , 2016 -# Devesh Parashar, 2016 +# Akshay Garg <64akshay@gmail.com>, 2016 +# Goutam Kumar Dey , 2018 +# Devesh Parashar, 2018 +# Gaurav Mehta , 2019 +# abhishek tiwari , 2019 +# msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2018-05-31 15:26+0000\n" -"PO-Revision-Date: 2018-05-31 15:26:45.721375\n" -"Last-Translator: Devesh Parashar, 2016\n" +"POT-Creation-Date: 2018-10-01 16:57+0000\n" +"PO-Revision-Date: 2016-09-14 14:52+0000\n" +"Last-Translator: abhishek tiwari , 2019\n" "Language-Team: Hindi (https://www.transifex.com/open-edx/teams/6205/hi/)\n" "Language: hi\n" "MIME-Version: 1.0\n" @@ -115,6 +118,22 @@ msgstr "" msgid "URL of page for questions about certificates" msgstr "" +#: apps/core/models.py +msgid "Enable Learner Records" +msgstr "" + +#: apps/core/models.py +msgid "Enable the Records feature. The LMS has a similar setting." +msgstr "" + +#: apps/core/models.py +msgid "Learner Records Help URL" +msgstr "" + +#: apps/core/models.py +msgid "URL of page for questions about Learner Records" +msgstr "" + #: apps/core/models.py msgid "Facebook App ID" msgstr "" @@ -214,6 +233,16 @@ msgid "" "I completed a course at {platform_name}. Take a look at my certificate:" msgstr "" +#: apps/credentials/views.py +#, python-brace-format +msgid "{first_org} and {second_org}" +msgstr "" + +#: apps/credentials/views.py +#, python-brace-format +msgid "{series_of_orgs}, and {last_org}" +msgstr "" + #: apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html msgid "" "successfully completed all courses and received passing grades for a " @@ -233,6 +262,44 @@ msgstr "" msgid "{action} is not a valid action." msgstr "" +#: apps/records/models.py +msgid "sent" +msgstr "" + +#: apps/records/models.py +msgid "other" +msgstr "" + +#: apps/records/models.py +msgid "User credit pathways can only be connected to credit pathways." +msgstr "" + +#: apps/records/views.py +msgid "N/A" +msgstr "" + +#. Translators: A 'record' here means something like a transcript -- a list of +#. courses and grades. +#: apps/records/views.py +msgid "My Learner Records" +msgstr "" + +#: apps/records/views.py +msgid "" +"A program record is created once you have earned at least one course " +"certificate in a program." +msgstr "" + +#: apps/records/views.py +msgid "Program Listing View" +msgstr "" + +#: apps/records/views.py +msgid "" +"The following is a list of all active programs for which program records are" +" being generated." +msgstr "" + #: templates/404.html msgid "Page Not Found" msgstr "" @@ -269,6 +336,10 @@ msgstr "" msgid "Print this certificate" msgstr "" +#: templates/_footer.html +msgid "Legal Policies" +msgstr "" + #: templates/_footer.html msgid "Terms of Service & Honor Code" msgstr "" @@ -287,6 +358,10 @@ msgstr "" msgid "Powered by Open edX" msgstr "" +#: templates/base.html templates/credentials/base.html +msgid "Skip to main content" +msgstr "" + #: templates/credentials/base.html #, python-format msgid "Congratulations, %(user_name)s!" @@ -322,7 +397,7 @@ msgstr "" #: templates/credentials/programs/base.html #, python-format msgid "" -"a program offered by %(organization_name)s, in collaboration with " +"a program offered by %(org_name_string)s, in collaboration with " "%(platform_name)s." msgstr "" @@ -371,8 +446,68 @@ msgstr "" msgid "{program_name} Record" msgstr "" -#: templates/records.html -msgid "My Records" +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"%(user_full_name)s has sent an updated program record for %(program_name)s." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"%(user_full_name)s has sent their completed program record for " +"%(program_name)s." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"%(user_full_name)s has sent their partially completed program record for " +"%(program_name)s." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"%(user_full_name)s would like to apply for credit in the %(pathway_name)s." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"Please view or download %(user_full_name)s’s public program record to " +"determine their credit eligibility status." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +msgid "View Program Record" +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +msgid "Download Record (CSV)" +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "The %(platform_name)s Team" +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/subject.txt +#, python-format +msgid "%(program_name)s Updated Credit Request for %(user_full_name)s" +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/subject.txt +#, python-format +msgid "%(program_name)s Credit Request for %(user_full_name)s" msgstr "" #: urls.py diff --git a/credentials/conf/locale/hi/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/hi/LC_MESSAGES/djangojs.mo index 9f718bdaebeaa0ab5c8919331f918799c3a8e971..234471c111d10734c64664572966e8cc07a01ac3 100644 GIT binary patch delta 143 zcmeyxe1my{3gf(qs#*2AL8)b##hLkex-N+&sa6U`28I^82Bx|OhCrs3p{2F~5ODb< z7MJLT6eZ>r=OmWo7g;GJCS_z6XQXB;lw_7C7G)~fzy$Ol0uDK;iA8yt1sR!o$@#f< fIv{f`fyU&emnNpC>V~8y=2|IuWadpaU_1o?E4nX- delta 131 zcmcb?{EK;l3gdx^s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F){ zu}97e%(F5!*E2J;Fflab@<}W%(G4j|%qz}GEXgmjQYgwyG&C|cQLstQ&nrnxE-6T? bOw4hpOw7p7*GtaN)k`e3(*YYl@%LQ-C(9`? diff --git a/credentials/conf/locale/hi/LC_MESSAGES/djangojs.po b/credentials/conf/locale/hi/LC_MESSAGES/djangojs.po index e42560a30..e97bb8a57 100644 --- a/credentials/conf/locale/hi/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/hi/LC_MESSAGES/djangojs.po @@ -5,13 +5,16 @@ # # Translators: # ria1234 , 2018 +# Nitin Madhok , 2018 +# abhishek tiwari , 2019 +# msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2018-05-01 19:19+0000\n" -"PO-Revision-Date: 2018-05-01 19:19:37.618411\n" -"Last-Translator: ria1234 , 2018\n" +"POT-Creation-Date: 2018-10-01 16:57+0000\n" +"PO-Revision-Date: 2018-05-01 20:19+0000\n" +"Last-Translator: abhishek tiwari , 2019\n" "Language-Team: Hindi (https://www.transifex.com/open-edx/teams/6205/hi/)\n" "Language: hi\n" "MIME-Version: 1.0\n" @@ -19,32 +22,283 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: static/components/RecordsList.jsx -msgid "FAQ" +#: static/components/MasqueradeBanner.jsx +msgid "Change user" +msgstr "" + +#: static/components/MasqueradeBanner.jsx +msgid "Submit" +msgstr "" + +#: static/components/MasqueradeBanner.jsx +msgid "View as: " +msgstr "" + +#: static/components/MasqueradeBanner.jsx +msgid "Staff" +msgstr "" + +#: static/components/MasqueradeBanner.jsx +msgid "Specific Learner" +msgstr "" + +#: static/components/MasqueradeBanner.jsx +msgid "Username or email: " +msgstr "" + +#: static/components/MasqueradeBanner.jsx +msgid "You are currently viewing as: {user}" +msgstr "" + +#: static/components/MasqueradeBanner.jsx +msgid "Masquerading failed" +msgstr "" + +#: static/components/MasqueradeBanner.jsx +msgid "" +"You either do not have permission to masquerade as this user, or the user " +"could not be found." +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Earned" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Back to My Records" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Send Learner Record" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Share" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Download Record" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "We are sending your program record." +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "We were unable to send your program record." +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "" +"We were unable to send your record to {orgs}. You can attempt to send this " +"record again. Contact support if this issue persists." +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "You have successfully shared your Learner Record" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "" +"You have sent your record to {orgs}. Next, ensure you understand their " +"application process." +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "{name} Record" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "{type} Program Record" +msgstr "" + +#: static/components/ProgramRecord.jsx static/components/RecordsList.jsx +msgid "Completed" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Not Earned" +msgstr "" + +#: static/components/ProgramRecord.jsx static/components/RecordsList.jsx +msgid "Partially Completed" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Last Updated {date}" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Course Name" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "School" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Course ID" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Highest Grade Earned" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Letter Grade" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Verified Attempts" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Date Earned" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Status" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Course ID: {}" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Highest Grade Earned: {}" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Letter Grade: {}" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Verified Attempts: {}" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Date Earned: {}" +msgstr "" + +#: static/components/ProgramRecord.jsx +msgid "Status: {}" +msgstr "" + +#: static/components/RecordsHelp.jsx +msgid "Questions about Learner Records?" +msgstr "" + +#: static/components/RecordsHelp.jsx +msgid "" +"To learn more about records you can {start_anchor}read more in our records " +"help area.{end_anchor}" msgstr "" #: static/components/RecordsList.jsx -msgid "View Program Record" +msgid "" +"No records yet. Program records are created once you have earned at least " +"one course certificate in a program." msgstr "" #: static/components/RecordsList.jsx -msgid "Program Name" +msgid "Back to My Profile" msgstr "" #: static/components/RecordsList.jsx -msgid "School" +msgid "View Example" msgstr "" #: static/components/RecordsList.jsx -msgid "Record Link" +msgid "View Program Record" msgstr "" #: static/components/RecordsList.jsx msgid "Program Records" msgstr "" -#. Translators: a 'record' here means something like a transcript -- a list of -#. courses and grades -#: static/components/RecordsList.jsx -msgid "My Records" +#: static/components/SendLearnerRecordModal.jsx +msgid "{name} - Sent" +msgstr "" + +#: static/components/SendLearnerRecordModal.jsx +msgid "{name} - Not Yet Available" +msgstr "" + +#: static/components/SendLearnerRecordModal.jsx +msgid "Send to {platform} Credit Partner" +msgstr "" + +#: static/components/SendLearnerRecordModal.jsx +msgid "" +"You can directly share your program record with {platform} partners that " +"accept credit for this {type} Program. Once you send your record you cannot " +"unsend it." +msgstr "" + +#: static/components/SendLearnerRecordModal.jsx +msgid "Not all credit partners are ready to receive records yet" +msgstr "" + +#: static/components/SendLearnerRecordModal.jsx +msgid "" +"You can check back in the future or share your record link directly if you " +"need to do so immediately." +msgstr "" + +#: static/components/SendLearnerRecordModal.jsx +msgid "Select organization(s) you wish to send this record to:" +msgstr "" + +#: static/components/SendLearnerRecordModal.jsx +msgid "Send" +msgstr "" + +#: static/components/ShareProgramRecordModal.jsx +msgid "" +"Instead of sharing a link, you can also {start_anchor}directly send your " +"program record to {platform} partners{end_anchor} for credit or application " +"purposes." +msgstr "" + +#: static/components/ShareProgramRecordModal.jsx +msgid "Share Link to Record" +msgstr "" + +#: static/components/ShareProgramRecordModal.jsx +msgid "We were unable to create your record link." +msgstr "" + +#: static/components/ShareProgramRecordModal.jsx +msgid "You can close this window and try again." +msgstr "" + +#: static/components/ShareProgramRecordModal.jsx +msgid "Successfully copied program record link." +msgstr "" + +#: static/components/ShareProgramRecordModal.jsx +msgid "" +"Copy this link to share your record with a university, employer, or anyone " +"else of you choosing. Anyone you share this link with will have access to " +"your record forever." +msgstr "" + +#: static/components/ShareProgramRecordModal.jsx +msgid "Program Record URL" +msgstr "" + +#: static/components/ShareProgramRecordModal.jsx +msgid "Copy Link" +msgstr "" + +#: static/components/ShareProgramRecordModal.jsx +msgid "Loading record link..." +msgstr "" + +#: static/components/Utils.jsx +msgid "{first} and {second}" +msgstr "" + +#: static/components/Utils.jsx +msgid "{firstItems}, and {lastItem}" msgstr "" diff --git a/credentials/conf/locale/hr/LC_MESSAGES/django.mo b/credentials/conf/locale/hr/LC_MESSAGES/django.mo index aa9bbc602a6437292a552c2028a15b70d69b983c..7e180ae55f5bcb7b496caee522f5a93fdf0794e2 100644 GIT binary patch delta 15 WcmZ3_(#A4Dh4IWp)vS#>${7JHNd>?F delta 54 zcmZo;Sl2j`NBLhPVT?12HV?zZ)Q!67gZ37_K I*i*#_0MOA7q5uE@ diff --git a/credentials/conf/locale/hr/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/hr/LC_MESSAGES/djangojs.mo index 931ef83f485c664f3d1c39cb4dbbedac2f422c3c..f09fb65f87a77c37a4945c842289b114e868225b 100644 GIT binary patch delta 16 YcmdnXGKFP=3ezpdiE5b}ceFAB05Mqw`Tzg` delta 55 zcmbQjvX^Co3KKKaM72z{5MAe@)Wnj^{5)Nk#FA7i1tSAP3ta{F8Zt3giBXs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d IvB!lG0OWBG#sB~S diff --git a/credentials/conf/locale/hu/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/hu/LC_MESSAGES/djangojs.mo index 425ca0a4f736d760e4543e1ba5b3689633773a85..1dbc6c98691b22cbc86c8d3c9d088600bf096ac6 100644 GIT binary patch delta 15 Xcmey#e1Um_3gfhis#zO%I5GkNGAjk< delta 54 zcmcb>{F8Zt3giBXs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x&{`yM#c(;##V;r+6F+d IvB!lG0OaHk$^ZZW diff --git a/credentials/conf/locale/hy_AM/LC_MESSAGES/django.mo b/credentials/conf/locale/hy_AM/LC_MESSAGES/django.mo index 7ef9b789c5fdf445ac7afa7d2626397a9ee1adc1..162ca96197296aafc7750e01ea802f1c55c88d89 100644 GIT binary patch delta 15 WcmZo=xx+j`g>lhD)vS#>d>H{Ofd!WU delta 54 zcmcb^+{!XRh4J)6)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12HV?zZ)Q!67gZ37_K I*b~4A0MafGc>n+a diff --git a/credentials/conf/locale/hy_AM/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/hy_AM/LC_MESSAGES/djangojs.mo index c02c6bec6be57e9ea12dd7ac310606bc7822bc18..33624884c52b095c31bf7588ee0167f45a4a38b5 100644 GIT binary patch delta 15 XcmbQp@`8DS3gfzos#zO%L^1*ZE=dK) delta 54 zcmaFCJdtIB3gh*Os#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d Iu_uNR0NI8QtN;K2 diff --git a/credentials/conf/locale/id/LC_MESSAGES/django.mo b/credentials/conf/locale/id/LC_MESSAGES/django.mo index bd137fc6eae0b51e4ac7ddb3b6ffa1aa26d9890e..5843a11e0a88c860fff0c01d5efdebacabad551f 100644 GIT binary patch literal 10825 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 FzX7WpoVx%3 delta 162 zcmX>Z@|)S>o)F7a1|VPrVi_P-0b*t#)&XJ=umIwNKuJp=4N?OGlV?kWtA*$~7o{ea zWaj7Tx+IpQS}7PA7+UBW8t57rDj1qsnVM@G0Kw$<5>|$h`FV*Y8HsrcZbgZC#hJy) mrNs(1IjQ-1`9&#uIhnZ*>A8uSIeN+Yxpq2WV, 2016 # Sari Rahmawati , 2018 -# el greshard , 2018 +# El Greshard , 2018 # cholif yulian , 2018 # Yonathan Fransiscus , 2018 +# Aprisa Chrysantina , 2019 # msgid "" msgstr "" @@ -21,7 +22,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Yonathan Fransiscus , 2018\n" +"Last-Translator: Aprisa Chrysantina , 2019\n" "Language-Team: Indonesian (https://www.transifex.com/open-edx/teams/6205/id/)\n" "Language: id\n" "MIME-Version: 1.0\n" @@ -31,345 +32,362 @@ msgstr "" #: apps/core/admin.py msgid "Personal info" -msgstr "" +msgstr "Informasi Pribadi" #: apps/core/admin.py msgid "Permissions" -msgstr "" +msgstr "izin" #: apps/core/admin.py msgid "Important dates" -msgstr "" +msgstr "Tanggal penting" #: apps/core/admin.py msgid "URLs" -msgstr "" +msgstr "URL" #: apps/core/admin.py msgid "Social Sharing" -msgstr "" +msgstr "berbagi sosial" #: apps/core/forms.py msgid "A Facebook app ID is required to enable Facebook sharing." -msgstr "" +msgstr "ID app Facebook diperlukan untuk berbagi melalui Facebook." #: apps/core/models.py msgid "Platform Name" -msgstr "" +msgstr "Nama platform" #: apps/core/models.py msgid "Name of your Open edX platform" -msgstr "" +msgstr "Beri nama platform Open edX Anda" #: apps/core/models.py msgid "Segment Key" -msgstr "" +msgstr "Kunci Segmen" #: apps/core/models.py msgid "Segment write/API key." -msgstr "" +msgstr "Segment write/API key." #: apps/core/models.py msgid "Theme Name" -msgstr "" +msgstr "Nama tema" #: apps/core/models.py msgid "" "Name of of the theme to use for this site. This value should be lower-cased." msgstr "" +"Nama tema yang digunakan untuk situs ini. Nilainya harus ditulis dengan " +"huruf kecil." #: apps/core/models.py msgid "LMS base url for custom site" -msgstr "" +msgstr "Url dasar LMS untuk situs kustom" #: apps/core/models.py msgid "Root URL of this site's LMS (e.g. https://courses.stage.edx.org)" -msgstr "" +msgstr "Root URL untuk LMS situs ini (contoh https://courses.stage.edx.org)" #: apps/core/models.py msgid "Catalog API URL" -msgstr "" +msgstr "URL API Katalog" #: apps/core/models.py msgid "Root URL of the Catalog API (e.g. https://api.edx.org/catalog/v1/)" -msgstr "" +msgstr "Root URL untuk API Katalog (e.g. https://api.edx.org/catalog/v1/)" #: apps/core/models.py msgid "Terms of Service URL" -msgstr "" +msgstr "URL Syarat dan Ketentuan" #: apps/core/models.py msgid "Privacy Policy URL" -msgstr "" +msgstr "URL Kebijakan Privasi" #: apps/core/models.py msgid "Homepage URL" -msgstr "" +msgstr "URL Beranda" #: apps/core/models.py msgid "Company Name" -msgstr "" +msgstr "Nama Perusahaan" #: apps/core/models.py msgid "Verified Certificate URL" -msgstr "" +msgstr "URL Sertifikat Terverifikasi" #: apps/core/models.py msgid "Certificate Help URL" -msgstr "" +msgstr "URL Bantuan Sertifikat" #: apps/core/models.py msgid "URL of page for questions about certificates" -msgstr "" +msgstr "URL halaman pertanyaan terkait sertifikat" #: apps/core/models.py msgid "Enable Learner Records" -msgstr "" +msgstr "Aktifkan Rekaman Peserta" #: apps/core/models.py msgid "Enable the Records feature. The LMS has a similar setting." -msgstr "" +msgstr "Aktifkan fitur Rekaman. LMS memiliki penyetelan yang sama." #: apps/core/models.py msgid "Learner Records Help URL" -msgstr "" +msgstr "URL Bantuan Records Peserta" #: apps/core/models.py msgid "URL of page for questions about Learner Records" -msgstr "" +msgstr "URL halaman pertanyaan terkait Records Peserta" #: apps/core/models.py msgid "Facebook App ID" -msgstr "" +msgstr "ID App Facebook" #: apps/core/models.py msgid "Facebook app ID used for sharing" -msgstr "" +msgstr "ID app Facebook untuk berbagi" #: apps/core/models.py msgid "Twitter Username" -msgstr "" +msgstr "Nama Pengguna Twitter" #: apps/core/models.py msgid "Twitter username included in tweeted credentials. Do NOT include @." msgstr "" +"Nama pengguna Twitter dalam kredensial yang dicuitkan. JANGAN masukkan @." #: apps/core/models.py msgid "Enable Facebook sharing" -msgstr "" +msgstr "Aktifkan berbagi melalui Facebook" #: apps/core/models.py msgid "Enable sharing via Facebook" -msgstr "" +msgstr "Aktifkan berbagi melalui Facebook" #: apps/core/models.py msgid "Enable LinkedIn sharing" -msgstr "" +msgstr "Aktifkan berbagi melalui LinkedIn" #: apps/core/models.py msgid "Enable sharing via LinkedIn" -msgstr "" +msgstr "Aktifkan berbagi melalui LinkedIn" #: apps/core/models.py msgid "Enable Twitter sharing" -msgstr "" +msgstr "Aktifkan berbagi melalui Twitter" #: apps/core/models.py msgid "Enable sharing via Twitter" -msgstr "" +msgstr "Aktifkan berbagi melalui Twitter" #: apps/core/models.py msgid "Full Name" -msgstr "" +msgstr "Nama Lengkap" #: apps/credentials/forms.py msgid "" "All authoring organizations of the program MUST have a certificate image " "defined!" msgstr "" +"Semua organisasi yang menyusun program HARUS memiliki gambar sertifikat!" #: apps/credentials/models.py msgid "The image file size must be less than 250KB." -msgstr "" +msgstr "Ukuran file harus kurang dari 250KB." #: apps/credentials/models.py msgid "Invalid course key." -msgstr "" +msgstr "Kunci kursus tidak valid." #: apps/credentials/models.py msgid "" "Signatory organization name if its different from issuing organization." msgstr "" +"Nama organisasi jika berbeda dengan organisasi yang mengeluarkan sertifikat." #: apps/credentials/models.py msgid "Image must be square PNG files. The file size should be under 250KB." -msgstr "" +msgstr "Gambar harus berupa file PNG persegi. Ukuran harus kurang dari 250KB." #: apps/credentials/models.py msgid "awarded" -msgstr "" +msgstr "diberikan" #: apps/credentials/models.py msgid "revoked" -msgstr "" +msgstr "dibatalkan" #: apps/credentials/models.py msgid "URL at which the credential can be downloaded" -msgstr "" +msgstr "URL di mana kredensial dapat diunduh" #: apps/credentials/models.py msgid "Program UUID" -msgstr "" +msgstr "UUID Program" #: apps/credentials/models.py msgid "" "Display the associated organization's name (e.g. ACME University) instead of" " its short name (e.g. ACMEx)" msgstr "" +"Tampilkan nama organisasi yang terkait (misal Universitas ACME) bukan nama " +"singkatnya (misal ACMEx)" #: apps/credentials/models.py msgid "Use organization name" -msgstr "" +msgstr "Gunakan nama organisasi" #: apps/credentials/views.py #, python-brace-format msgid "" "I completed a course at {platform_name}. Take a look at my certificate:" msgstr "" +"Saya telah menyelesaikan kursus di {platform_name}. Lihat sertifikat saya:" #: apps/credentials/views.py #, python-brace-format msgid "{first_org} and {second_org}" -msgstr "" +msgstr "{first_org} dan {second_org}" #: apps/credentials/views.py #, python-brace-format msgid "{series_of_orgs}, and {last_org}" -msgstr "" +msgstr "{series_of_orgs}, dan {last_org}" #: apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html msgid "" "successfully completed all courses and received passing grades for a " "Professional Certificate in" msgstr "" +"telah berhasil menyelesaikan semua kursus dan melewati nilai batas kelulusan" +" untuk Sertifikat Profesional dalam" #: apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html msgid "Professional Certificate" -msgstr "" +msgstr "Sertifikat Profesional" #: apps/edx_django_extensions/views.py msgid "Cache cleared." -msgstr "" +msgstr "Cache dihapus" #: apps/edx_django_extensions/views.py #, python-brace-format msgid "{action} is not a valid action." -msgstr "" +msgstr "{action} tidak valid." #: apps/records/models.py msgid "sent" -msgstr "" +msgstr "terkirim" #: apps/records/models.py msgid "other" -msgstr "" +msgstr "lainnya" #: apps/records/models.py msgid "User credit pathways can only be connected to credit pathways." -msgstr "" +msgstr "Jalur kredit pengguna hanya dapat digunakan untuk jalur kredit." #: apps/records/views.py msgid "N/A" -msgstr "" +msgstr "N/A" #. Translators: A 'record' here means something like a transcript -- a list of #. courses and grades. #: apps/records/views.py msgid "My Learner Records" -msgstr "" +msgstr "Record Peserta Saya" #: apps/records/views.py msgid "" "A program record is created once you have earned at least one course " "certificate in a program." msgstr "" +"Record program akan dibuat setelah Anda mendapatkan satu sertifikat kursus " +"dalam satu program." #: apps/records/views.py msgid "Program Listing View" -msgstr "" +msgstr "Tampilan Daftar Program" #: apps/records/views.py msgid "" "The following is a list of all active programs for which program records are" " being generated." msgstr "" +"The following is a list of all active programs for which program records are" +" being generated.am." #: templates/404.html msgid "Page Not Found" -msgstr "" +msgstr "Halaman Tidak Ditemukan" #: templates/500.html msgid "Server Error" -msgstr "" +msgstr "Server Error" #: templates/_actions.html msgid "Print or share your certificate" -msgstr "" +msgstr "Cetak atau bagikan sertifikat Anda" #: templates/_actions.html msgid "Share this certificate via Facebook" -msgstr "" +msgstr "Bagikan sertifikat ini melalui Facebook" #: templates/_actions.html msgid "Tweet this certificate" -msgstr "" +msgstr "Cuitkan sertifikat ini" #: templates/_actions.html msgid "Add to LinkedIn profile" -msgstr "" +msgstr "Tambahkan ke profil LInkedIn" #: templates/_actions.html msgid "Add this certificate to your LinkedIn profile" -msgstr "" +msgstr "Tambahkan sertifikat ini ke profil LinkedIn Anda" #: templates/_actions.html msgid "Print" -msgstr "" +msgstr "cetak" #: templates/_actions.html msgid "Print this certificate" -msgstr "" +msgstr "Cetak sertifikat" #: templates/_footer.html msgid "Legal Policies" -msgstr "" +msgstr "Kebijakan Hukum" #: templates/_footer.html msgid "Terms of Service & Honor Code" -msgstr "" +msgstr "Ketentuan Layanan dan Kode Etik" #: templates/_footer.html msgid "Privacy Policy" -msgstr "" +msgstr "Kebijakan Privasi" #: templates/_footer.html msgid "" "All rights reserved except where noted. edX, Open edX and the edX and Open " "edX logos are registered trademarks or trademarks of edX Inc." msgstr "" +"Semua hak cipta dilindungi kecuali yang dituliskan. edX, Open edX dan edX " +"dan Open edX logo adalah merek dagang terdaftar atau merek dagang dari edX " +"Inc." #: templates/_footer.html msgid "Powered by Open edX" -msgstr "" +msgstr "Powered by Open edX" #: templates/base.html templates/credentials/base.html msgid "Skip to main content" -msgstr "" +msgstr "Lewati ke konten utama" #: templates/credentials/base.html #, python-format msgid "Congratulations, %(user_name)s!" -msgstr "" +msgstr "Selamat, %(user_name)s!" #: templates/credentials/base.html #, python-format @@ -378,14 +396,17 @@ msgid "" "with colleagues, friends, and family to get the word out about what you " "mastered in %(program_name)s." msgstr "" +"Anda telah bekerja keras untuk mendapatkan sertifikat dari %(platform_name)s" +" — bagikan dengan kolega, teman, dan keluarga tentang apa yang telah Anda " +"pelajari di %(program_name)s." #: templates/credentials/programs/base.html msgid "Supported by the following organizations" -msgstr "" +msgstr "Didukung oleh institusi berikut" #: templates/credentials/programs/base.html msgid "logo" -msgstr "" +msgstr "logo" #. Translators: This phrase is followed by a statement of the learner's #. achievement. e.g. 'has completed the course' @@ -395,6 +416,8 @@ msgid "" "{start_span}This is to certify that{end_span} {start_strong} {user_name} " "{end_strong}" msgstr "" +"{start_span}Menyatakan bahwa{end_span} {start_strong} {user_name} " +"{end_strong}telah menyelesaikan" #. Translators: organization_name is the display name for the provided #. organization e.g (e.g., Test Organization). @@ -404,30 +427,32 @@ msgid "" "a program offered by %(org_name_string)s, in collaboration with " "%(platform_name)s." msgstr "" +"program yang ditawarkan oleh %(org_name_string)s, bekerja sama dengan " +"%(platform_name)s." #: templates/credentials/programs/base.html msgid "Noted by" -msgstr "" +msgstr "Dicatat oleh" #. Translators: This phrase appears on certificates to show the issue date #: templates/credentials/programs/base.html #, python-brace-format msgid "Issued {month} {year}" -msgstr "" +msgstr "Diterbitkan {month} {year}" #: templates/credentials/programs/base.html msgid "Valid Certificate ID" -msgstr "" +msgstr "ID Sertifikat Valid" #: templates/credentials/programs/base.html msgid "Effort" -msgstr "" +msgstr "Karya" #: templates/credentials/programs/base.html #, python-format msgid "%(num_hours)s hour" msgid_plural "%(num_hours)s hours" -msgstr[0] "" +msgstr[0] "%(num_hours)s jam" #: templates/credentials/programs/base.html #, python-brace-format @@ -435,19 +460,21 @@ msgid "" "For tips and tricks on printing your certificate, view the {start_anchor}Web" " Certificates help documentation{end_anchor}." msgstr "" +"Untuk panduan mencetak sertifikat Anda, lihat {start_anchor}dokumentasi " +"bantuan Sertifikat Web{end_anchor}." #: templates/management.html msgid "Management View" -msgstr "" +msgstr "Tampilan Manajemen" #: templates/management.html msgid "Clear cache" -msgstr "" +msgstr "Hapus cache" #: templates/programs.html #, python-brace-format msgid "{program_name} Record" -msgstr "" +msgstr "Record {program_name} " #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -455,6 +482,8 @@ msgstr "" msgid "" "%(user_full_name)s has sent an updated program record for %(program_name)s." msgstr "" +"%(user_full_name)s telah mengirimkan pembaruan untuk record program " +"%(program_name)s." #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -463,6 +492,8 @@ msgid "" "%(user_full_name)s has sent their completed program record for " "%(program_name)s." msgstr "" +"%(user_full_name)s telah mengirimkan record program lengkap untuk " +"%(program_name)s." #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -471,13 +502,15 @@ msgid "" "%(user_full_name)s has sent their partially completed program record for " "%(program_name)s." msgstr "" +"%(user_full_name)s telah mengirimkan record program yang lengkap sebagian " +"untuk %(program_name)s." #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt #, python-format msgid "" "%(user_full_name)s would like to apply for credit in the %(pathway_name)s." -msgstr "" +msgstr "%(user_full_name)s ingin mendaftar kredit di %(pathway_name)s." #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -486,33 +519,35 @@ msgid "" "Please view or download %(user_full_name)s’s public program record to " "determine their credit eligibility status." msgstr "" +"Silakan lihat atau unduh record program publik milik %(user_full_name)s " +"untuk menentukan status kelayakan kreditnya." #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt msgid "View Program Record" -msgstr "" +msgstr "Lihat Record Program" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt msgid "Download Record (CSV)" -msgstr "" +msgstr "Unduh Record (CSV)" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt #, python-format msgid "The %(platform_name)s Team" -msgstr "" +msgstr "Tim %(platform_name)s " #: templates/records/edx_ace/programcreditrequest/email/subject.txt #, python-format msgid "%(program_name)s Updated Credit Request for %(user_full_name)s" -msgstr "" +msgstr "%(program_name)s Permintaan Kredit Terbaru untuk %(user_full_name)s" #: templates/records/edx_ace/programcreditrequest/email/subject.txt #, python-format msgid "%(program_name)s Credit Request for %(user_full_name)s" -msgstr "" +msgstr "%(program_name)s Permintaan Kredit untuk %(user_full_name)s" #: urls.py msgid "Credentials Administration" -msgstr "" +msgstr "Administrasi Kredensial" diff --git a/credentials/conf/locale/id/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/id/LC_MESSAGES/djangojs.mo index d6434db0dd2cc784e06d0e5464ef7749494bc4dc..c9650eec2512a789a2efa209afa39e131cb5ef31 100644 GIT binary patch literal 6671 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 delta 227 zcmeA-X=AatC&V(90SFj@SO$nyfS4JGb$}QIEP(h7P|^}egVeylMX8A; znfZCTE{P?nRtiQ2h8DU87P>~p3Wmm3hUVG^K)@B?uN#zFmRXEjot24!o`r#_rKK^K zPhxS2Zb(sLUU5!hNq&))f@48ZW^tl|b4F2RabjLcW?rI#O(IxKRxcSL=#ZY9n3vj~uy!}x diff --git a/credentials/conf/locale/id/LC_MESSAGES/djangojs.po b/credentials/conf/locale/id/LC_MESSAGES/djangojs.po index 82b9faaa0..4664e01e0 100644 --- a/credentials/conf/locale/id/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/id/LC_MESSAGES/djangojs.po @@ -4,17 +4,19 @@ # EdX Team , 2018. # # Translators: -# Rizky Ariestiyansyah , 2018 # Ahmad Sofyan , 2018 # Anggun Dewara, 2018 -# Aprisa Chrysantina , 2018 +# Rizky Ariestiyansyah , 2018 +# Aprisa Chrysantina , 2019 +# Stefania Trabucchi , 2019 +# msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2018-08-23 13:17+0000\n" -"PO-Revision-Date: 2018-08-23 13:17:40.805993\n" -"Last-Translator: Aprisa Chrysantina , 2018\n" +"POT-Creation-Date: 2018-10-01 16:57+0000\n" +"PO-Revision-Date: 2018-05-01 20:19+0000\n" +"Last-Translator: Stefania Trabucchi , 2019\n" "Language-Team: Indonesian (https://www.transifex.com/open-edx/teams/6205/id/)\n" "Language: id\n" "MIME-Version: 1.0\n" @@ -24,201 +26,219 @@ msgstr "" #: static/components/MasqueradeBanner.jsx msgid "Change user" -msgstr "" +msgstr "Ubah pengguna" #: static/components/MasqueradeBanner.jsx msgid "Submit" -msgstr "" +msgstr "Kirim" #: static/components/MasqueradeBanner.jsx msgid "View as: " -msgstr "" +msgstr "Lihat sebagai:" #: static/components/MasqueradeBanner.jsx msgid "Staff" -msgstr "" +msgstr "Staf" #: static/components/MasqueradeBanner.jsx msgid "Specific Learner" -msgstr "" +msgstr "Peserta tertentu" #: static/components/MasqueradeBanner.jsx msgid "Username or email: " -msgstr "" +msgstr "Nama pengguna atau email:" #: static/components/MasqueradeBanner.jsx msgid "You are currently viewing as: {user}" -msgstr "" +msgstr "Anda sedang melihat sebagai: {user}" #: static/components/MasqueradeBanner.jsx msgid "Masquerading failed" -msgstr "" +msgstr "Penyamaran gagal" #: static/components/MasqueradeBanner.jsx msgid "" "You either do not have permission to masquerade as this user, or the user " "could not be found." msgstr "" +"Anda mungkin tidak memiliki izin untuk menyamar sebagai pengguna ini, atau " +"pengguna tidak ditemukan." #: static/components/ProgramRecord.jsx msgid "Earned" -msgstr "" +msgstr "Didapatkan" #: static/components/ProgramRecord.jsx msgid "Back to My Records" -msgstr "" +msgstr "Kembali ke Record Saya" #: static/components/ProgramRecord.jsx msgid "Send Learner Record" -msgstr "" +msgstr "Kirimkan Record Peserta" #: static/components/ProgramRecord.jsx msgid "Share" -msgstr "" +msgstr "Bagikan" #: static/components/ProgramRecord.jsx msgid "Download Record" -msgstr "" +msgstr "Unduh Record" #: static/components/ProgramRecord.jsx msgid "We are sending your program record." -msgstr "" +msgstr "Kami sedang mengirimkan record program Anda." #: static/components/ProgramRecord.jsx msgid "We were unable to send your program record." -msgstr "" +msgstr "Kami tidak dapat menemukan record program Anda." #: static/components/ProgramRecord.jsx msgid "" "We were unable to send your record to {orgs}. You can attempt to send this " "record again. Contact support if this issue persists." msgstr "" +"Kami tidak dapat mengirimkan record ke {orgs}. Anda dapat mencoba " +"mengirimkan record ini lagi. Hubungi bantuan jika masalah masih terjadi." #: static/components/ProgramRecord.jsx msgid "You have successfully shared your Learner Record" -msgstr "" +msgstr "Anda telah membagikan Record Peserta Anda." #: static/components/ProgramRecord.jsx msgid "" "You have sent your record to {orgs}. Next, ensure you understand their " "application process." msgstr "" +"Anda telah mengirimakn record Anda ke {orgs}. Selanjutnya, pastikan Anda " +"memahami proses aplikasi mereka." #: static/components/ProgramRecord.jsx msgid "{name} Record" -msgstr "" +msgstr "Record {name} " #: static/components/ProgramRecord.jsx msgid "{type} Program Record" -msgstr "" +msgstr "Record Program {type} " #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Completed" -msgstr "" +msgstr "Selesai" + +#: static/components/ProgramRecord.jsx +msgid "Not Earned" +msgstr "Belum Didapatkan" #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Partially Completed" -msgstr "" +msgstr "Selesai Sebagian" #: static/components/ProgramRecord.jsx msgid "Last Updated {date}" -msgstr "" +msgstr "Terakhir Diperbarui {date}" #: static/components/ProgramRecord.jsx msgid "Course Name" -msgstr "" +msgstr "Nama Kursus" #: static/components/ProgramRecord.jsx msgid "School" -msgstr "" +msgstr "Sekolah" #: static/components/ProgramRecord.jsx msgid "Course ID" -msgstr "" +msgstr "Kursus ID" #: static/components/ProgramRecord.jsx msgid "Highest Grade Earned" -msgstr "" +msgstr "Nilai Tertinggi yang Didapatkan" #: static/components/ProgramRecord.jsx msgid "Letter Grade" -msgstr "" +msgstr "Nilai Huruf" #: static/components/ProgramRecord.jsx msgid "Verified Attempts" -msgstr "" +msgstr "Percobaan Terverifikasi" #: static/components/ProgramRecord.jsx msgid "Date Earned" -msgstr "" +msgstr "Tanggal Didapatkan" #: static/components/ProgramRecord.jsx msgid "Status" -msgstr "" +msgstr "Status" #: static/components/ProgramRecord.jsx msgid "Course ID: {}" -msgstr "" +msgstr "ID Kursus: {}" #: static/components/ProgramRecord.jsx msgid "Highest Grade Earned: {}" -msgstr "" +msgstr "Nilai Tertinggi yang Didapatkan: {}" #: static/components/ProgramRecord.jsx msgid "Letter Grade: {}" -msgstr "" +msgstr "Nilai Huruf: {}" #: static/components/ProgramRecord.jsx msgid "Verified Attempts: {}" -msgstr "" +msgstr "Percobaan Terverifikasi: {}" #: static/components/ProgramRecord.jsx msgid "Date Earned: {}" -msgstr "" +msgstr "Tanggal Didapatkan: {}" #: static/components/ProgramRecord.jsx msgid "Status: {}" -msgstr "" +msgstr "Status: {}" #: static/components/RecordsHelp.jsx msgid "Questions about Learner Records?" -msgstr "" +msgstr "Pertanyaan terkait Record Peserta?" #: static/components/RecordsHelp.jsx msgid "" "To learn more about records you can {start_anchor}read more in our records " "help area.{end_anchor}" msgstr "" +"Untuk lebih lanjut mempelajari tentang record Anda dapat {start_anchor} " +"membaca lebih lanjut di area bantuan.{end_anchor}" #: static/components/RecordsList.jsx msgid "" "No records yet. Program records are created once you have earned at least " "one course certificate in a program." msgstr "" +"Belum ada record. Record program akan dibuat setelah Anda mendapatkan satu " +"sertifikat kursus dalam satu program." #: static/components/RecordsList.jsx msgid "Back to My Profile" -msgstr "" +msgstr "Kembali ke Profil Saya" + +#: static/components/RecordsList.jsx +msgid "View Example" +msgstr "Lihat Contoh" #: static/components/RecordsList.jsx msgid "View Program Record" -msgstr "" +msgstr "Lihat Record Program" #: static/components/RecordsList.jsx msgid "Program Records" -msgstr "" +msgstr "Record Program" #: static/components/SendLearnerRecordModal.jsx msgid "{name} - Sent" -msgstr "" +msgstr "{name} - Terkirim" #: static/components/SendLearnerRecordModal.jsx msgid "{name} - Not Yet Available" -msgstr "" +msgstr "{name} - Belum Tersedia" #: static/components/SendLearnerRecordModal.jsx msgid "Send to {platform} Credit Partner" -msgstr "" +msgstr "Kirim ke Mitra Kredit {platform}" #: static/components/SendLearnerRecordModal.jsx msgid "" @@ -226,24 +246,29 @@ msgid "" "accept credit for this {type} Program. Once you send your record you cannot " "unsend it." msgstr "" +"Anda dapat membagikan record program Anda dengan mitra {platform} yang " +"menerima kredit untuk Program {type} ini. Anda tidak dapat membatalkan " +"kiriman record setelah terkirim." #: static/components/SendLearnerRecordModal.jsx msgid "Not all credit partners are ready to receive records yet" -msgstr "" +msgstr "Tidak semua mitra kredit siap menerima record." #: static/components/SendLearnerRecordModal.jsx msgid "" "You can check back in the future or share your record link directly if you " "need to do so immediately." msgstr "" +"Anda dapat memeriksa kembali atau membagikan tautan record Anda secara " +"langsung jika Anda perlu melakukannya segera." #: static/components/SendLearnerRecordModal.jsx msgid "Select organization(s) you wish to send this record to:" -msgstr "" +msgstr "Pilih organisasi(-organisasi) yang ingin Anda kirimi record:" #: static/components/SendLearnerRecordModal.jsx msgid "Send" -msgstr "" +msgstr "Kirim" #: static/components/ShareProgramRecordModal.jsx msgid "" @@ -251,22 +276,25 @@ msgid "" "program record to {platform} partners{end_anchor} for credit or application " "purposes." msgstr "" +"Selain membagikan tautan, Anda juga dapat {start_anchor} mengirimkan record " +"program secara langsung ke mitra {platform}{end_anchor} untuk kepentingan " +"kredit atau aplikasi." #: static/components/ShareProgramRecordModal.jsx msgid "Share Link to Record" -msgstr "" +msgstr "Bagikan Tautan ke Record" #: static/components/ShareProgramRecordModal.jsx msgid "We were unable to create your record link." -msgstr "" +msgstr "Kami tidak dapat membuat tautan record Anda." #: static/components/ShareProgramRecordModal.jsx msgid "You can close this window and try again." -msgstr "" +msgstr "Anda dapat menutup window ini dan mencoba kembali." #: static/components/ShareProgramRecordModal.jsx msgid "Successfully copied program record link." -msgstr "" +msgstr "Berhasil menyalin tautan record program." #: static/components/ShareProgramRecordModal.jsx msgid "" @@ -274,23 +302,26 @@ msgid "" "else of you choosing. Anyone you share this link with will have access to " "your record forever." msgstr "" +"Salin tautan ini untuk membagikan record Anda dengan universitas, " +"perusahaan, atau siapapun yang Anda inginkan. Siapapun yang memiliki tautan " +"ini akan memiliki akses ke record Anda selamanya." #: static/components/ShareProgramRecordModal.jsx msgid "Program Record URL" -msgstr "" +msgstr "URL Record Program" #: static/components/ShareProgramRecordModal.jsx msgid "Copy Link" -msgstr "" +msgstr "Salin Tautan" #: static/components/ShareProgramRecordModal.jsx msgid "Loading record link..." -msgstr "" +msgstr "Membaca tautan record..." #: static/components/Utils.jsx msgid "{first} and {second}" -msgstr "" +msgstr "{first} dan {second}" #: static/components/Utils.jsx msgid "{firstItems}, and {lastItem}" -msgstr "" +msgstr "{firstItems}, dan {lastItem}" diff --git a/credentials/conf/locale/it_IT/LC_MESSAGES/django.mo b/credentials/conf/locale/it_IT/LC_MESSAGES/django.mo index bcbde7893abe66b491a29515325f21a71d6d8a17..5455eea6124a14d76cc9e5d953b65f043ef48e6c 100644 GIT binary patch delta 103 zcmbQla+!I83ggU)s#)Q>L8)b##hLkex-N+&sa6U`28L$329~;pCJKfoR;EVU20*~& zlUQ7$8&Z^*SDcerl3!${5L}X)mYA2XV3Sr_l$lrTke-{EnWLASpKGTBG{VSWvMS?S E0D7+=asU7T delta 166 zcmcc2Jc(t33geB5s#*0Ry3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+t z72vNMlvsclUZR3eeqMTN iQKo`T2}D3IM;s#JP>`RKnVYGXSz@OHa)H^zXKw-ihBLbW diff --git a/credentials/conf/locale/it_IT/LC_MESSAGES/django.po b/credentials/conf/locale/it_IT/LC_MESSAGES/django.po index 6e2c03599..58223d24c 100644 --- a/credentials/conf/locale/it_IT/LC_MESSAGES/django.po +++ b/credentials/conf/locale/it_IT/LC_MESSAGES/django.po @@ -4,19 +4,21 @@ # EdX Team , 2018. # # Translators: -# Claude Almansi , 2016 # CarlaGratta , 2016 +# Claude Almansi , 2016 # Nicola Moretto , 2016 # Monica Maria Crapanzano , 2016 # mZakk , 2016 -# Tiziana Longeri , 2016 +# Tiziana Longeri , 2018 +# Stefano , 2020 +# msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2018-05-31 15:26+0000\n" -"PO-Revision-Date: 2018-05-31 15:26:45.721375\n" -"Last-Translator: Tiziana Longeri , 2016\n" +"POT-Creation-Date: 2018-10-01 16:57+0000\n" +"PO-Revision-Date: 2016-09-14 14:52+0000\n" +"Last-Translator: Stefano , 2020\n" "Language-Team: Italian (Italy) (https://www.transifex.com/open-edx/teams/6205/it_IT/)\n" "Language: it_IT\n" "MIME-Version: 1.0\n" @@ -117,6 +119,22 @@ msgstr "" msgid "URL of page for questions about certificates" msgstr "" +#: apps/core/models.py +msgid "Enable Learner Records" +msgstr "" + +#: apps/core/models.py +msgid "Enable the Records feature. The LMS has a similar setting." +msgstr "" + +#: apps/core/models.py +msgid "Learner Records Help URL" +msgstr "" + +#: apps/core/models.py +msgid "URL of page for questions about Learner Records" +msgstr "" + #: apps/core/models.py msgid "Facebook App ID" msgstr "" @@ -216,6 +234,16 @@ msgid "" "I completed a course at {platform_name}. Take a look at my certificate:" msgstr "" +#: apps/credentials/views.py +#, python-brace-format +msgid "{first_org} and {second_org}" +msgstr "" + +#: apps/credentials/views.py +#, python-brace-format +msgid "{series_of_orgs}, and {last_org}" +msgstr "" + #: apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html msgid "" "successfully completed all courses and received passing grades for a " @@ -235,6 +263,44 @@ msgstr "" msgid "{action} is not a valid action." msgstr "" +#: apps/records/models.py +msgid "sent" +msgstr "" + +#: apps/records/models.py +msgid "other" +msgstr "" + +#: apps/records/models.py +msgid "User credit pathways can only be connected to credit pathways." +msgstr "" + +#: apps/records/views.py +msgid "N/A" +msgstr "" + +#. Translators: A 'record' here means something like a transcript -- a list of +#. courses and grades. +#: apps/records/views.py +msgid "My Learner Records" +msgstr "" + +#: apps/records/views.py +msgid "" +"A program record is created once you have earned at least one course " +"certificate in a program." +msgstr "" + +#: apps/records/views.py +msgid "Program Listing View" +msgstr "" + +#: apps/records/views.py +msgid "" +"The following is a list of all active programs for which program records are" +" being generated." +msgstr "" + #: templates/404.html msgid "Page Not Found" msgstr "" @@ -271,6 +337,10 @@ msgstr "" msgid "Print this certificate" msgstr "" +#: templates/_footer.html +msgid "Legal Policies" +msgstr "" + #: templates/_footer.html msgid "Terms of Service & Honor Code" msgstr "" @@ -289,6 +359,10 @@ msgstr "" msgid "Powered by Open edX" msgstr "" +#: templates/base.html templates/credentials/base.html +msgid "Skip to main content" +msgstr "" + #: templates/credentials/base.html #, python-format msgid "Congratulations, %(user_name)s!" @@ -324,7 +398,7 @@ msgstr "" #: templates/credentials/programs/base.html #, python-format msgid "" -"a program offered by %(organization_name)s, in collaboration with " +"a program offered by %(org_name_string)s, in collaboration with " "%(platform_name)s." msgstr "" @@ -373,8 +447,68 @@ msgstr "" msgid "{program_name} Record" msgstr "" -#: templates/records.html -msgid "My Records" +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"%(user_full_name)s has sent an updated program record for %(program_name)s." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"%(user_full_name)s has sent their completed program record for " +"%(program_name)s." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"%(user_full_name)s has sent their partially completed program record for " +"%(program_name)s." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"%(user_full_name)s would like to apply for credit in the %(pathway_name)s." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "" +"Please view or download %(user_full_name)s’s public program record to " +"determine their credit eligibility status." +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +msgid "View Program Record" +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +msgid "Download Record (CSV)" +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/body.html +#: templates/records/edx_ace/programcreditrequest/email/body.txt +#, python-format +msgid "The %(platform_name)s Team" +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/subject.txt +#, python-format +msgid "%(program_name)s Updated Credit Request for %(user_full_name)s" +msgstr "" + +#: templates/records/edx_ace/programcreditrequest/email/subject.txt +#, python-format +msgid "%(program_name)s Credit Request for %(user_full_name)s" msgstr "" #: urls.py diff --git a/credentials/conf/locale/it_IT/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/it_IT/LC_MESSAGES/djangojs.mo index ec4a3fddb59c2e47b514658bf135264672ee5c02..f099af51254d6e42d1477dcb4a20c2aab898d6aa 100644 GIT binary patch delta 15 WcmaFIe3W^D3S;*~)vS#>%oqVMi3Pd< delta 54 zcmX@g{Em5o3gecEs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d IvB#1T0Nx@Gp8x;= diff --git a/credentials/conf/locale/ja_JP/LC_MESSAGES/django.mo b/credentials/conf/locale/ja_JP/LC_MESSAGES/django.mo index fd4cc8d4aaf237625d6b05517fe8f54de801794c..730ec0138bc6eaadb59f8d503f56eb2f3a45084d 100644 GIT binary patch delta 595 zcmXZYzb`{k6u|M*`jOC=G+v)qEfK$>Dx}2Xr3ey91%ptb(cQr04|ro2G1v^1u4R#Q z)IlT$n@BJi321a48E=mlr+2U9qm5She1 z)Tb|@HmKqT)=>xUOa=qQk=bPq4{!^0;JWb>TR7Wnr*)QGet2C;qi(#w=MoF38@KfZ XTE-Y|5>KKIdRRMeor=`HqOaB;P6#_% delta 634 zcmXZY-77U^S*Oh#8FI18O}RN&V=>K3r*#?EgZ>3fx+OXKdn!ZyZEt zqevxAp>7z%1a9Ig7LXalNs`99Q8$R79k*~9kFW=e$Sm^Flr2s4!-8($YAz-8qE0l7 zG%pLN@h$Y=K6c`j>4tCLBnSnrqAUY2?s{KCRe`b Hc(wllr@Td^ diff --git a/credentials/conf/locale/ja_JP/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/ja_JP/LC_MESSAGES/djangojs.mo index 3633bca9a449af19d55950d400e446226ada074b..7dc35822c5d178b66f79db87d89d3b0991e26f01 100644 GIT binary patch delta 15 WcmZo>xy3v|g>k_|)vS#>ycq#3H3g3V delta 54 zcmcb`+{`jTh4JJ>)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT|)z1149KvGb>YbZ37_K I*yG0t0MN1zZvX%Q diff --git a/credentials/conf/locale/ka/LC_MESSAGES/django.mo b/credentials/conf/locale/ka/LC_MESSAGES/django.mo index e17ee9549ec7b070c170f49f99af42ad71262989..345d674840055139656ea603424efe18a85180b3 100644 GIT binary patch delta 239 zcmXZWJqtkr6vpxAmX}=bgP4$)6r~J|x{Ebo~%=y69rEBFOLvmNOG delta 278 zcmXZWu}cDR7{>AEAeA(=wkA0&4UCif6_;{D99U4Bg9t8)gGf*Y;jIPt2Z&2!RM1wN z;6Kpf>d+umY4Sj>&;r+bNyS(pr{&{EobN6aV%b4_nql8qFbhu6mdu#81rO*@om{AGh%V4NkFutN(o_Ej7t|X|-iD+_HGWM~pDYNM(G-5B$Oq zXIbeAk2j?jzTr8>x1?RHBR3S{-$T)4VRhDu4X1X~>kcoj2hK@%*z@fJ*Hj(jIbQu9JGX<6a delta 54 zcmdnP{FHfu3ghaDs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d IvB!WB0N7Ozb^rhX diff --git a/credentials/conf/locale/kk_KZ/LC_MESSAGES/django.mo b/credentials/conf/locale/kk_KZ/LC_MESSAGES/django.mo index 14dc8d6be901888e7190607c451201dc93d26ded..8e020b39e2e481a600d4b9d0801c9eeaef70937d 100644 GIT binary patch delta 15 WcmeBXdBi+Hg>l71)vS#>f*AoVWd*MQ delta 54 zcmaFF+|4pUh4JD<)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?2DnLt_O)Q!5igZ37_K I*b~MG0M*zJkN^Mx diff --git a/credentials/conf/locale/kk_KZ/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/kk_KZ/LC_MESSAGES/djangojs.mo index ee1b3196bb0565b75951d051cd2d4785872a9331..7e0130a0c294353b52e46ebd24825c2415933ac6 100644 GIT binary patch delta 15 WcmZo*xz0R6g>mjg)vS#>JQx8h!v%u? delta 54 zcmcc4+`uwHh4I)#)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT|)z1149KvGb>YbZ37_K I*yGIz0M6kLV*mgE diff --git a/credentials/conf/locale/km_KH/LC_MESSAGES/django.mo b/credentials/conf/locale/km_KH/LC_MESSAGES/django.mo index 5fcc548c09d2c74ae1e48d1cfbac4a30096554dc..941063d586ecf1f7ba2947d0586cd489a7febedf 100644 GIT binary patch delta 15 Wcmcc3yoq^&3S-4Y)vS#>lolo^~3ge`Ss#zO%*fIhDF_Q(> delta 54 zcmX@Z{FQlv3ghmHs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d IvB!ZC0OAD?wg3PC diff --git a/credentials/conf/locale/ko_KR/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/ko_KR/LC_MESSAGES/djangojs.mo index 00f11801eeb60b203b68015c78a03a4745ec2e2b..b06ab89b3828c57865aa95b9eee1c3237d2e46fd 100644 GIT binary patch delta 15 Xcmey$e1>^~3ge`Ss#zO%*fIhDF_Q(> delta 54 zcmX@Z{FQlv3ghmHs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d IvB!ZC0OAD?wg3PC diff --git a/credentials/conf/locale/lt_LT/LC_MESSAGES/django.mo b/credentials/conf/locale/lt_LT/LC_MESSAGES/django.mo index 789acacb6f96b8db01f8749db9b58e869a6f7a49..6924b0dbc8330c3a105d2f5e747d0d9afacf79e3 100644 GIT binary patch delta 15 WcmeBRz0Wd1g;8~)YSzXb7Z?F54F#nD delta 54 zcmcc5(!n}Gh0$-KYL;4vu5(dpVo7Fxo~}z`Nvf5Ck%6IwuAzahfuVw-nU$%zwgC`q I?77Sc0KK6Pg#Z8m diff --git a/credentials/conf/locale/lt_LT/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/lt_LT/LC_MESSAGES/djangojs.mo index 29f50503c983e5ff331258379899553fc068b525..5a2bf4548e4b169ec9dc8470685fa2ade42a1fbc 100644 GIT binary patch delta 15 WcmeBWeZn$9g;8swYSzXbR~Z2+%LTar delta 54 zcmaFD(#twQg)w-dYL;4vu5(dpVo7Fxo~}z`Nvf5Ck%6Iwu7QQFk+FiIv6Z2@wgC`q I?76`R0Kpp%od5s; diff --git a/credentials/conf/locale/ml/LC_MESSAGES/django.mo b/credentials/conf/locale/ml/LC_MESSAGES/django.mo index eb5043cff5d3995505ad0d77c04b7706856af2c9..8e807864429d689578929a83221bd6f2ce082e19 100644 GIT binary patch delta 15 WcmaFDyqkG~3S;9$)vS#>bQl3I4F!?_ delta 54 zcmdnZ{DgUe3gfDYs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+d Iu}7Z~0N4NzbN~PV diff --git a/credentials/conf/locale/ml/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/ml/LC_MESSAGES/djangojs.mo index e5956d139a0510b30ece4c9afc3bc931efa753da..eb353ff70e9bba4f034aef472d1ab604a5b99f70 100644 GIT binary patch delta 15 Wcmcc0yoPy#3S-ek)vS#>WElZ0(*;%l delta 54 zcmZ3(e3f~E3ghgFs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d Iu}6Us0M7ppI{*Lx diff --git a/credentials/conf/locale/mn/LC_MESSAGES/django.mo b/credentials/conf/locale/mn/LC_MESSAGES/django.mo index 0beed81a15f9647c34521aea1482df7000e4b7c4..68d7192c0b1140aa90b35bda9e7b07820a419635 100644 GIT binary patch delta 582 zcmZvXzfR*o5XKiSK!Iowq5^3iAZOh{fsi5u1qH?3QBu&Dc+W9$?65Wgs{BEbkPZrj z(6zMCTyhQ)$OMTum?b@kcc4neXBUw`?CLkOvoky2Y(MinR(+bxoC~ZBm;pI30yaUi z%HRm>fN{_V6JT^mhzl?UUJ+jp3o#9U^+kwT_y>5T1YEk1PBp}e>ymQpLKu*eK~<`X zv`FsfWjUn1f>e?`Ka`e1(%Q((!T`@VQ)4ZTzcQ*g}p9PvGI8;2z5e)s?qEnVXlc31x)-Owj|#~s*+aC1DE z>nq>zEuM4x@t>~g21lrg)B0P|O?}86^oSE%{j^!Tcga!j61GDSID{%{bu-b{b@atm u9Rae5M3eLhbegGblqx_YMu>S4qVf)3O9qd*%Pm9jU-bU3k9uc6hqG^E*Z7P8 delta 207 zcmcc5{);8yo)F7a1|VPtVi_Pd0b*7l_5orLNC0APAWj5gULei|;**RF4Anqd42WHs z7#L)MbPSLN3NrvHH~^VB@n@l0h^}){YGO%dex9yNVo9o%f{}rtg|4B2u7RO~p_!Gb zxwZiiOipLCG7c`y&8$q!&PdElQE*92Pf5%xC@o4YQ?MxpN@N, 2016 # Oyunmaa Khorloo , 2017 # Mooc num , 2017 -# Myagmarjav Enkhbileg , 2018 # Munkhtsetseg , 2018 # Narantsogt Baatarkhuu , 2018 # Otgontsetseg Sukhbaatar , 2018 # Gerelmaa Byambatsogt , 2018 # Sumiyakhand Dagdanpurev , 2018 +# Myagmarjav Enkhbileg , 2019 # msgid "" msgstr "" @@ -20,7 +20,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Sumiyakhand Dagdanpurev , 2018\n" +"Last-Translator: Myagmarjav Enkhbileg , 2019\n" "Language-Team: Mongolian (https://www.transifex.com/open-edx/teams/6205/mn/)\n" "Language: mn\n" "MIME-Version: 1.0\n" @@ -356,6 +356,8 @@ msgid "" "All rights reserved except where noted. edX, Open edX and the edX and Open " "edX logos are registered trademarks or trademarks of edX Inc." msgstr "" +"Open edX лого болон edX, Open edX барааны тэмдэг нь edX Inc.-ийн бүртгэлтэй " +"худалдааны тэмдэг бөгөөд дурдахаас бусад бүх эрхийг хуулиар хамгаална." #: templates/_footer.html msgid "Powered by Open edX" diff --git a/credentials/conf/locale/mn/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/mn/LC_MESSAGES/djangojs.mo index 52aacf5e5b11a726138d83c897dcad44a8d7c926..98315a956387dcab93193436591072ab48895edb 100644 GIT binary patch delta 28 icmcb}vVvuT4&PEn28K`u1_m7<{>?bis&M0#T1Eh8oCmG| delta 45 ycmZ3%a*<_%4&OOO28K`u1_m7s*wYSTb>=6sxuY5Nur0zz6{M^$VK- diff --git a/credentials/conf/locale/ms/LC_MESSAGES/django.mo b/credentials/conf/locale/ms/LC_MESSAGES/django.mo index e109aa6c521458ad98f0046fab1ec6b09cc682f0..bf2767fbcd9468099469c4ad6569f68310775201 100644 GIT binary patch delta 15 WcmaFIe3W^D3S;*~)vS#>%oqVMi3Pd< delta 54 zcmX@g{Em5o3gecEs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d IvB#1T0NuzBng9R* diff --git a/credentials/conf/locale/ms/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/ms/LC_MESSAGES/djangojs.mo index a7448e96fd50339eafc3b522c869a42d128da3f3..de63b3494b35587bd11d653d0ea5c72832995885 100644 GIT binary patch delta 15 WcmaFIe3W^D3S;*~)vS#>%oqVMi3Pd< delta 54 zcmX@g{Em5o3gecEs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d IvB#1T0NuzBng9R* diff --git a/credentials/conf/locale/nb/LC_MESSAGES/django.mo b/credentials/conf/locale/nb/LC_MESSAGES/django.mo index 1811f2c61d27b7ba69e76a4c8dfaa80d7554bfcd..6047a43713546c01875c9ed976f9a639e9674e96 100644 GIT binary patch delta 518 zcmXZYyGz4R6vy#Xszj}(K2WShNNsHwExu4HMJk9WPIc&{V6dG6DbyE;7=mMI?&j*oup&-dok~VmJ9r=UeTg_7fx2gQkC8FUHA(rg%bc$+N*DOIimz$=j#~ zIKl#+V+?<=5hE1UKyeJ=7^?4bNKYwXI~K74w@@|SLv?|xu;(A}zy@c^lPLv`aBOyDw(;(@N;VUqlUY7lAj-#1YWl)_n@oMTbXVjtBnKEw{Z)cGC_ z@-x=qD>9S3;|hME8X#Z04$d1V(a=O{+FGcT9ox0Cwp*-}?6tJ>pL6cIyODOwQv=q> L&~T=<6G|F?Itx0| delta 560 zcmYMw-z!657{KvovBox=pXJxZ*zAIhGs}<;K;N zBBXZ9rE;P83tZsNg*&Ab-`7q#ozCZdp7(v;_j%8!uh4P*X35`MM2_tuQ4!IdBF$L9 zL%c;d&Q^)E;38Jz7OHi7rsvp5d}qebreCJ@E|DhYdr<8cLoY74BrlvTITF0GV|s)Q z#HXkVZZM7y*oIz`l{k*7p=oqr0##iKIa{`|7Wc6NPf#6vf$9QJ?tE#3cM|L^-{ypa zUAesYQRTy^8XduQoI-Wu4Ek^v!+2%Rzo4JkT2pEyfNFgNRYP+)fy, 2016 # Frode Arntsen , 2018 # Vangenplotz edx , 2018 +# A Krokan , 2019 # msgid "" msgstr "" @@ -14,7 +15,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Vangenplotz edx , 2018\n" +"Last-Translator: A Krokan , 2019\n" "Language-Team: Norwegian Bokmål (https://www.transifex.com/open-edx/teams/6205/nb/)\n" "Language: nb\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/nb/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/nb/LC_MESSAGES/djangojs.mo index 16c2ac390d8c9ebf41f4d52578c3c7faa0591bfa..767c2c21fd5472b72646543682aa22c8f0eef76d 100644 GIT binary patch delta 15 WcmZo-xyd|1g>n8w)vS#>ychv24+V<= delta 54 zcmcb~+{7|Lh4I8h)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT|)z1149KvGb>YbZ37_K I*yGCx0MHi?YXATM diff --git a/credentials/conf/locale/nl_NL/LC_MESSAGES/django.mo b/credentials/conf/locale/nl_NL/LC_MESSAGES/django.mo index 586bec386fecc0f94c40ee2df29b981358db3d0f..9127b1513d61e758bb224b7ee0a2344db4d42858 100644 GIT binary patch delta 15 XcmbQj@|t;q3ggC!s#zO%#4rK?F1rQO delta 54 zcmaFOJcVU~3gfMbs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+d Iu_vAp0NXlhD)vS#>d>H{Ofd!WU delta 54 zcmcb^+{!XRh4J)6)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT>}eUBVz?aV=F^*Z37_K I*b~4A0Mc6zdjJ3c diff --git a/credentials/conf/locale/pl/LC_MESSAGES/django.mo b/credentials/conf/locale/pl/LC_MESSAGES/django.mo index 098b5b0d8d5417b2fefc02a7c1e10fccffb94496..5a4b7ffa0ca8e7e6f316e189a7b01fe10f44518d 100644 GIT binary patch delta 657 zcmXZZJxfAi6u|M5q`g?0W|n1d6iO;cQ7vj{5JZbZSr8Pa4mC8i6hU;s!3Y}yrYliiozLzCne1i{GI79~3V4cbxW^fhr_>4}BcZqc1B93AX?Rbpcc!s_Bfc^M^Is8EzCf#Dl2*J8rWEk_P>r3?E z8*1WeeeEKveiy+xT*D$>pcWGD5urAT<2dd)BA4uN`@&Of!ELfN;vVYy01vT%n$J8WvV`Nfa#Im+U0vN}8BAdopP?PpL`!T7tz};^jQe;CJNZAOco!{!w;00>v`+rOEV}5w zos&aLXbP=B=P83H3~HFfKN!LAIq4`)p=CUS*7`DXYn8`$+(b9NCt3%7K%0NW3O3N< zT?$Hhyn}6MF~+1;*OBMlp?viE;#{FrT$qne6iRdHU^1S#7EQ#X@kB6jHGL%&iF37< HJnr6q?Uqfn diff --git a/credentials/conf/locale/pl/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/pl/LC_MESSAGES/djangojs.mo index 3de3f9ae998a9bbf471b6d8eea0a87fbaa24ee30..c548016fe885d8fe5a90dc56cd5290a0ece1071f 100644 GIT binary patch delta 15 WcmeBYea13Dg;8gsYSzXb*BJpT7X`rp delta 54 zcmaFH($6|Ug)wxZYL;4vu5(dpVo7Fxo~}z`Nvf5Ck%6IwuAzahfuVw-nU$%zwgC`q I?777V0Kwi5p#T5? diff --git a/credentials/conf/locale/pt_BR/LC_MESSAGES/django.mo b/credentials/conf/locale/pt_BR/LC_MESSAGES/django.mo index f4058dec76d752e0d1d08565913cf6b394abd747..7e25ec348bbc16516d38245cb032c238ae097293 100644 GIT binary patch delta 28 icmX@lx{P(}>P28JaJ3=CmF>_5@+;AUmUHH-jjMF&g( delta 66 zcmZ3+dY*NH4)0|~28JaJ3=CmFTrtt|pjwEob5UwyNoIbYu1jJ`s+EF~fuV)2p@FV} Tp@N~Am8rS50T68bzMc^PU*8f^ diff --git a/credentials/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo index 0932dc659e5dbbb27225a6ac26e9999fa0e4bd47..b642c219bf08cee36540de29847ed10f0f633ee2 100644 GIT binary patch delta 15 WcmZo-xyd|1g>n8w)vS#>ychv24+V<= delta 54 zcmcb~+{7|Lh4I8h)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT|)z1149KvGb>YbZ37_K I*yGCx0MHi?YXATM diff --git a/credentials/conf/locale/pt_PT/LC_MESSAGES/django.mo b/credentials/conf/locale/pt_PT/LC_MESSAGES/django.mo index cc8fdbb20d308a3e8e68ee1987558ad55e3ece34..3fe1a3020fe6cbb286c31da310f0f3002329df16 100644 GIT binary patch delta 187 zcmeBYS=appCDpXgdCKlCBvi{tbSOD==prj>`2C0F8iD}_#A-c{*sfi_- z`FXl7i6yC43PuKo7P^K8x(0>{hGtf#=Gq28FmacauJhrAC7Fo|KKTWy#R@jbKs||i cIUtThQc-3~dTLI#UUGh}oetQ9$, 2016 # Carlos , 2016 # Developer QUEO , 2018 -# MS , 2018 +# Manuela Silva , 2018 # BridgeLK , 2018 -# Rui Ribeiro , 2018 -# Beatriz Sousa , 2018 # Cátia Lopes , 2018 +# Beatriz Sousa , 2018 +# Rui Ribeiro , 2019 # msgid "" msgstr "" @@ -19,7 +19,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Cátia Lopes , 2018\n" +"Last-Translator: Rui Ribeiro , 2019\n" "Language-Team: Portuguese (Portugal) (https://www.transifex.com/open-edx/teams/6205/pt_PT/)\n" "Language: pt_PT\n" "MIME-Version: 1.0\n" @@ -411,7 +411,7 @@ msgstr "" #: templates/credentials/programs/base.html #, python-brace-format msgid "Issued {month} {year}" -msgstr "" +msgstr "Emitido em {month} de {year}" #: templates/credentials/programs/base.html msgid "Valid Certificate ID" diff --git a/credentials/conf/locale/pt_PT/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/pt_PT/LC_MESSAGES/djangojs.mo index 05e25ba17ee75fa98f985d394dc74d54cb2a20d0..962b0a41aabf7c5e084d01b958d609c8d65fba6a 100644 GIT binary patch delta 123 zcmZ3%*1||nK;04mhfV2>hz5=84;04mpfV2>h{sN>qfiyca0|Oh77KG9gK$-){ zmuH?h>!@0Yu5(dpVo7Fxo~}z`Nvf5Ck%6IwuAzahfuVw-nU$%zwgC`Kj$yP?2rA7~ i2+B-K%`D1Suqi6d)GLB;9MY1L^YjWzHt%QjWCQ@xl_5d^ diff --git a/credentials/conf/locale/pt_PT/LC_MESSAGES/djangojs.po b/credentials/conf/locale/pt_PT/LC_MESSAGES/djangojs.po index 885fbbeaf..677db27c8 100644 --- a/credentials/conf/locale/pt_PT/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/pt_PT/LC_MESSAGES/djangojs.po @@ -9,10 +9,10 @@ # Waldo Luís Ribeiro, 2018 # Carlos , 2018 # Developer QUEO , 2018 -# MS , 2018 -# Beatriz Sousa , 2018 -# Cátia Lopes , 2018 +# Manuela Silva , 2018 # Rui Ribeiro , 2018 +# Cátia Lopes , 2018 +# Beatriz Sousa , 2018 # msgid "" msgstr "" @@ -20,7 +20,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: Rui Ribeiro , 2018\n" +"Last-Translator: Beatriz Sousa , 2018\n" "Language-Team: Portuguese (Portugal) (https://www.transifex.com/open-edx/teams/6205/pt_PT/)\n" "Language: pt_PT\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/ro/LC_MESSAGES/django.mo b/credentials/conf/locale/ro/LC_MESSAGES/django.mo index 836ebbe3275e41c14829a652abf09ffb209a13b2..21e6b904abfc45419dd492b4976fdc75123ec384 100644 GIT binary patch delta 40 wcmeBWdCWXPg>mIX)vSp-%td??^GZ{56ao^HON$bVbQFvXj0`rjFmf^i03}EatN;K2 delta 79 zcmaFN+{-dSh4J!4)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT|)z1149KvGb>YbZ37^f d*kdK?l%JlGn5W, 2016 # John Doe , 2017 # Bogdan Mateescu, 2018 +# Alex Coman , 2019 +# Manuel Pacurar, 2020 # msgid "" msgstr "" @@ -17,7 +19,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Bogdan Mateescu, 2018\n" +"Last-Translator: Manuel Pacurar, 2020\n" "Language-Team: Romanian (https://www.transifex.com/open-edx/teams/6205/ro/)\n" "Language: ro\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/ro/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/ro/LC_MESSAGES/djangojs.mo index 8649f3367da1ce559ad888c5272af93fe0bc3339..79cfc3fd1604fd78d2ff5fd7ccf00c638df4cc41 100644 GIT binary patch delta 15 XcmZ3=@`rhX3ge-Ps#zO%6vpvK3>xK5U=D$K@iM^ zi*!U3)F~9qg%z~WT!~^TxKqj&=^_Y>AcFp<8Mxfv`*Atvyyv~^!SUc?TdeaeMtYSl z`6MM}NR@aK?_)1M!cFWzf2QPM2p^+2OKQLuIEe>XgMAmItGJEB_#1a|;G#q-J@iU^ zsxB|#?8BEVoWplfx3G};5nji$m!vROAdgOXxzKevlE{NsnBR)dgP6yB2#fJ;)URmq z+}TnS2C|(GCms-4ywVG`&OX z$P)hFrwyL%BhAr8!P%Q0%cK%5uGi^!uH$+MuRYb)(d6=( J`4+oh^ABlVb8P?s diff --git a/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.mo index f6b5bcbb9886f094922623616835d0ad4c9f84d7..5e63e564ad46e805f45728129648269fac800de0 100644 GIT binary patch delta 548 zcmXZZKP&@r6u|MPA))@<^-BG@URyL}=s+z<6QnkgSaj%O5eA8ff$43Y_B2w%T85WU%DH6jZ zT9`uv^T;C`EH>x&{rquABuxGs_58gbKVv^}0|)UBgJ^PS6m1;CIZIsmX0u6R2v?{U z!7bGI0F!uzaeTxYexf#1?iE?XTO2{7&ohHj;#Hi&UDO6Huz(e8!>)+y6}I^5B#8`m z;5urj+o%&c^79qn8g`R^LM{A;^Vq;cjC1`2UZXbtf?Dqf=~Y4l-o;Wb8>TNyID-XT z#1iK49qZ^Y$7%e+OH44H1*~HeJ8hc8cCM`roZ>Q$JG_f~EL>Vjn8GX6Uu(UxVfylc RI>KM4I@@{^teVDz@eg1zI)wlL delta 566 zcmXZZKS%;`6u|M9Bu5vWcGXpy+COTrAhAHrp-`d*iJ&QmXb>8j!lk7fT8sXIL_>{1 z1c9Ro4N(L^Lj>W};1EO&1r6cQ_lI}}Ge8sVfE#$vYU0lN{)M$GfXR#MwP!;}*>OQMWgjywmsDKDCqG7jN0s$XmV lv0?h6)213>AXrQ^Uh3N7XlgyTl0W=3eD3fVPqAu5J^#;KLInT- diff --git a/credentials/conf/locale/ru/LC_MESSAGES/django.mo b/credentials/conf/locale/ru/LC_MESSAGES/django.mo index 511d1d19c980b86bc07256d8b78aeefeb2372cc0..881db8de2dcfb3d58b0f4abfbc7e6aad2d15b3c9 100644 GIT binary patch literal 14823 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 U`3}2zbk}VGskihR{wc-(0F|l;T>t<8 delta 1161 zcmYk)O>7fK6u|LGiQ9xi9UGE3K;jJslkeD3f@6cKK+FLt3Ly#HD7ellSlC{=S%p## zL6MMvB30-K4yYf8Ry{;Tiue#Hh*X>qH(N-E6CgNL>VeWjZ_xJtj1P=<_cyb<^JeDF z{4sZV^TM-W?G;0baBtxrtTCn^$3Eggsas=A6zlO*jNv-mjbYr6jhMrFT&RA(fPUIn zaRdH}TxNww9j@YM{B9mn(ZEk|J-)2^2DRhA=)+*``vF7PM0*$sU=H92et|l%pKv4o zh8_3-H{qYS7JUS3zy@q&eA7;4k{1c&A9E48%y-rH71YdJLksU{bq52sJw_+61^iESg2#LOnMhmQQqQt<^VSSzq5R&(|dz)6S`4-pR~ZlXli| zXRMCVsl%Oez~3VO_#I*8C=evvdMrXLVo!^bT~TQJL=k%KE3ITXQ!-zSbVrQ9_x+8t@zH=P@+3V zk-tKz=%iD~6s-v-&&+nu*mGGspLa%PnQ6W+>l8lksr0=$(ekiH{%b!j^O3FcHo8-W zJ5s?PO1I04R_Ru0xpcpDzx-|aoZRgCS&}_3B-wXNp7*s$T`VPE#?mqrkI2+Oi!2ZH q$m`A~xir`%+Y_hdMq*1~wY*sRy?mA^H%fQQi{(qwnQUV{;r{`8E5L~W diff --git a/credentials/conf/locale/ru/LC_MESSAGES/django.po b/credentials/conf/locale/ru/LC_MESSAGES/django.po index d98974cf0..515044018 100644 --- a/credentials/conf/locale/ru/LC_MESSAGES/django.po +++ b/credentials/conf/locale/ru/LC_MESSAGES/django.po @@ -13,6 +13,8 @@ # Maxim Alexeev , 2018 # Liubov Fomicheva , 2018 # Roman Polin , 2018 +# Валентина Пицик , 2019 +# Farhanah Sheets , 2019 # msgid "" msgstr "" @@ -20,7 +22,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Roman Polin , 2018\n" +"Last-Translator: Farhanah Sheets , 2019\n" "Language-Team: Russian (https://www.transifex.com/open-edx/teams/6205/ru/)\n" "Language: ru\n" "MIME-Version: 1.0\n" @@ -42,15 +44,17 @@ msgstr "Важные даты" #: apps/core/admin.py msgid "URLs" -msgstr "" +msgstr "URLs" #: apps/core/admin.py msgid "Social Sharing" -msgstr "" +msgstr "Социальный обмен" #: apps/core/forms.py msgid "A Facebook app ID is required to enable Facebook sharing." msgstr "" +"Для совместного использования Facebook требуется идентификатор приложения " +"Facebook." #: apps/core/models.py msgid "Platform Name" @@ -58,15 +62,15 @@ msgstr "Название платформы" #: apps/core/models.py msgid "Name of your Open edX platform" -msgstr "" +msgstr "Название вашей платформы Open EdX" #: apps/core/models.py msgid "Segment Key" -msgstr "" +msgstr "Ключ сегмента" #: apps/core/models.py msgid "Segment write/API key." -msgstr "" +msgstr "Ключ записи сегмента/API." #: apps/core/models.py msgid "Theme Name" @@ -76,10 +80,12 @@ msgstr "Название темы " msgid "" "Name of of the theme to use for this site. This value should be lower-cased." msgstr "" +"Название темы, которая будет использоваться для данного сайта. Это значение" +" должно быть в нижнем регистре." #: apps/core/models.py msgid "LMS base url for custom site" -msgstr "" +msgstr "Базовый URL LMS для пользовательского сайта" #: apps/core/models.py msgid "Root URL of this site's LMS (e.g. https://courses.stage.edx.org)" @@ -87,19 +93,20 @@ msgstr "Ссылка на главную страницу LMS (напр. https:/ #: apps/core/models.py msgid "Catalog API URL" -msgstr "" +msgstr "Каталог API URL" #: apps/core/models.py msgid "Root URL of the Catalog API (e.g. https://api.edx.org/catalog/v1/)" msgstr "" +"Корневой URL-адрес API каталога (например, https://api.edx.org/catalog/v1/)" #: apps/core/models.py msgid "Terms of Service URL" -msgstr "" +msgstr "URL-адрес страницы \"Условия предоставления услуг" #: apps/core/models.py msgid "Privacy Policy URL" -msgstr "" +msgstr "Политика конфиденциальности url" #: apps/core/models.py msgid "Homepage URL" @@ -111,39 +118,39 @@ msgstr "Название организации" #: apps/core/models.py msgid "Verified Certificate URL" -msgstr "" +msgstr "Подтверждённый сертификат url" #: apps/core/models.py msgid "Certificate Help URL" -msgstr "" +msgstr "URL-адрес справки по сертификату" #: apps/core/models.py msgid "URL of page for questions about certificates" -msgstr "" +msgstr "URL страницы для вопросов о сертификатах" #: apps/core/models.py msgid "Enable Learner Records" -msgstr "" +msgstr "Включить записи об учащемся" #: apps/core/models.py msgid "Enable the Records feature. The LMS has a similar setting." -msgstr "" +msgstr "Включите функцию Запись. LMS имеет схожие настройки" #: apps/core/models.py msgid "Learner Records Help URL" -msgstr "" +msgstr "Справочный URL-адрес справки по записям учащихся" #: apps/core/models.py msgid "URL of page for questions about Learner Records" -msgstr "" +msgstr "URL страницы для вопросов об учетных записях учащихся" #: apps/core/models.py msgid "Facebook App ID" -msgstr "" +msgstr "Facebook App ID" #: apps/core/models.py msgid "Facebook app ID used for sharing" -msgstr "" +msgstr "ID приложения Facebook, используемого для совместного использования" #: apps/core/models.py msgid "Twitter Username" @@ -152,30 +159,32 @@ msgstr "Имя пользователя в Twitter" #: apps/core/models.py msgid "Twitter username included in tweeted credentials. Do NOT include @." msgstr "" +"Имя пользователя Twitter, указанное в учетных данных в твиттере. НЕ " +"включайте @." #: apps/core/models.py msgid "Enable Facebook sharing" -msgstr "" +msgstr "Включить общий доступ к Facebook" #: apps/core/models.py msgid "Enable sharing via Facebook" -msgstr "" +msgstr "Разрешить общий доступ через Facebook" #: apps/core/models.py msgid "Enable LinkedIn sharing" -msgstr "" +msgstr "Включить общий доступ к LinkedIn" #: apps/core/models.py msgid "Enable sharing via LinkedIn" -msgstr "" +msgstr "Разрешить общий доступ через LinkedIn" #: apps/core/models.py msgid "Enable Twitter sharing" -msgstr "" +msgstr "Включить обмен сообщениями в Twitter" #: apps/core/models.py msgid "Enable sharing via Twitter" -msgstr "" +msgstr "Разрешить обмен через Twitter" #: apps/core/models.py msgid "Full Name" @@ -186,10 +195,12 @@ msgid "" "All authoring organizations of the program MUST have a certificate image " "defined!" msgstr "" +"Все авторские организации программы ДОЛЖНЫ иметь определенный образ " +"сертификата!" #: apps/credentials/models.py msgid "The image file size must be less than 250KB." -msgstr "" +msgstr "Размер файла изображения должен быть менее 250 КБ." #: apps/credentials/models.py msgid "Invalid course key." @@ -199,32 +210,38 @@ msgstr "Неверный код курса." msgid "" "Signatory organization name if its different from issuing organization." msgstr "" +"Название организации, подписавшей Кодекс, если оно отличается от названия " +"организации, выдавшей Кодекс." #: apps/credentials/models.py msgid "Image must be square PNG files. The file size should be under 250KB." msgstr "" +"Изображение должно быть квадратным файлом PNG. Размер файла не должен " +"превышать 250 КБ." #: apps/credentials/models.py msgid "awarded" -msgstr "" +msgstr "награждён " #: apps/credentials/models.py msgid "revoked" -msgstr "" +msgstr "аннулированный" #: apps/credentials/models.py msgid "URL at which the credential can be downloaded" -msgstr "" +msgstr "URL-адрес, по которому можно загрузить учетную запись" #: apps/credentials/models.py msgid "Program UUID" -msgstr "" +msgstr "Программа UUID" #: apps/credentials/models.py msgid "" "Display the associated organization's name (e.g. ACME University) instead of" " its short name (e.g. ACMEx)" msgstr "" +"Отображать название ассоциированной организации (например, Университет ACME)" +" вместо короткого названия (например, ACMEx)." #: apps/credentials/models.py msgid "Use organization name" @@ -234,7 +251,7 @@ msgstr "Использовать название организации" #, python-brace-format msgid "" "I completed a course at {platform_name}. Take a look at my certificate:" -msgstr "" +msgstr "Я закончил курс на {platform_name}. Посмотрите на мой сертификат." #: apps/credentials/views.py #, python-brace-format @@ -251,19 +268,21 @@ msgid "" "successfully completed all courses and received passing grades for a " "Professional Certificate in" msgstr "" +"успешно окончил все курсы и получил проходные баллы для получения " +"профессионального сертификата по следующим специальностям" #: apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html msgid "Professional Certificate" -msgstr "" +msgstr "Профессиональный сертификат" #: apps/edx_django_extensions/views.py msgid "Cache cleared." -msgstr "" +msgstr "Кэш очищен." #: apps/edx_django_extensions/views.py #, python-brace-format msgid "{action} is not a valid action." -msgstr "" +msgstr "{action} не является законным действием." #: apps/records/models.py msgid "sent" @@ -276,6 +295,8 @@ msgstr "другое" #: apps/records/models.py msgid "User credit pathways can only be connected to credit pathways." msgstr "" +"Пользовательские кредитные пути могут быть связаны только с кредитными " +"путями." #: apps/records/views.py msgid "N/A" @@ -285,23 +306,27 @@ msgstr "Нет данных" #. courses and grades. #: apps/records/views.py msgid "My Learner Records" -msgstr "" +msgstr "Мои записи о студентах" #: apps/records/views.py msgid "" "A program record is created once you have earned at least one course " "certificate in a program." msgstr "" +"Запись о прохождении курса создается после того, как вы получили хотя бы " +"один сертификат о прохождении курса в рамках программы." #: apps/records/views.py msgid "Program Listing View" -msgstr "" +msgstr "Просмотр списка программ" #: apps/records/views.py msgid "" "The following is a list of all active programs for which program records are" " being generated." msgstr "" +"Ниже приведен список всех активных программ, для которых создаются " +"программные записи." #: templates/404.html msgid "Page Not Found" @@ -317,19 +342,19 @@ msgstr "Напечатайте сертификат или поделитесь #: templates/_actions.html msgid "Share this certificate via Facebook" -msgstr "" +msgstr "Поделитесь этим сертификатом через Facebook" #: templates/_actions.html msgid "Tweet this certificate" -msgstr "" +msgstr "Отправьте этот сертификат в твиттер." #: templates/_actions.html msgid "Add to LinkedIn profile" -msgstr "" +msgstr "Добавить в профиль LinkedIn" #: templates/_actions.html msgid "Add this certificate to your LinkedIn profile" -msgstr "" +msgstr "Добавить этот сертификат в свой профиль LinkedIn" #: templates/_actions.html msgid "Print" @@ -341,11 +366,11 @@ msgstr "Распечатать этот сертификат" #: templates/_footer.html msgid "Legal Policies" -msgstr "" +msgstr "Правовая политика" #: templates/_footer.html msgid "Terms of Service & Honor Code" -msgstr "" +msgstr "Условия службы и Кодекс чести" #: templates/_footer.html msgid "Privacy Policy" @@ -356,6 +381,9 @@ msgid "" "All rights reserved except where noted. edX, Open edX and the edX and Open " "edX logos are registered trademarks or trademarks of edX Inc." msgstr "" +"Все права защищены, за исключением отмеченных случаев. edX, Open edX и " +"логотипы edX и Open edX являются зарегистрированными товарными знаками или " +"товарными знаками компании edX Inc." #: templates/_footer.html msgid "Powered by Open edX" @@ -377,6 +405,9 @@ msgid "" "with colleagues, friends, and family to get the word out about what you " "mastered in %(program_name)s." msgstr "" +"Вы усердно работали, чтобы заработать свой сертификат %(platform_name)s - " +"поделиться им с коллегами, друзьями и семьей, чтобы получить информацию о " +"том, чем вы овладел в %(program_name)s. " #: templates/credentials/programs/base.html msgid "Supported by the following organizations" @@ -394,6 +425,8 @@ msgid "" "{start_span}This is to certify that{end_span} {start_strong} {user_name} " "{end_strong}" msgstr "" +"{start_span}Это для сертификации{end_span} {start_strong} {user_name} " +"{end_strong}" #. Translators: organization_name is the display name for the provided #. organization e.g (e.g., Test Organization). @@ -414,15 +447,15 @@ msgstr "Запись сделана" #: templates/credentials/programs/base.html #, python-brace-format msgid "Issued {month} {year}" -msgstr "" +msgstr "Издано {month} {year}" #: templates/credentials/programs/base.html msgid "Valid Certificate ID" -msgstr "" +msgstr "Действительный ID сертификата" #: templates/credentials/programs/base.html msgid "Effort" -msgstr "" +msgstr "Усилие" #: templates/credentials/programs/base.html #, python-format @@ -439,10 +472,12 @@ msgid "" "For tips and tricks on printing your certificate, view the {start_anchor}Web" " Certificates help documentation{end_anchor}." msgstr "" +"Советы и подсказки по печати сертификата см. в справочной документации " +"{start_anchor}Сертификаты доступа к веб-сертификатам {end_anchor}." #: templates/management.html msgid "Management View" -msgstr "" +msgstr "Просмотр управления" #: templates/management.html msgid "Clear cache" @@ -451,7 +486,7 @@ msgstr "Очистить кэш" #: templates/programs.html #, python-brace-format msgid "{program_name} Record" -msgstr "" +msgstr "{program_name} Запись" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -459,6 +494,8 @@ msgstr "" msgid "" "%(user_full_name)s has sent an updated program record for %(program_name)s." msgstr "" +"%(user_full_name)s прислал обновленную запись о программе для " +"%(program_name)s. " #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -467,6 +504,8 @@ msgid "" "%(user_full_name)s has sent their completed program record for " "%(program_name)s." msgstr "" +"%(user_full_name)s прислали свои завершенные записи по программе для " +"%(program_name)s. " #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -475,6 +514,8 @@ msgid "" "%(user_full_name)s has sent their partially completed program record for " "%(program_name)s." msgstr "" +"%(user_full_name)sприслали свои частично завершенные записи по программе для" +" %(program_name)s. " #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -482,6 +523,8 @@ msgstr "" msgid "" "%(user_full_name)s would like to apply for credit in the %(pathway_name)s." msgstr "" +"%(user_full_name)s хотел бы подать заявку на получение кредита в системе " +"%(pathway_name)s. " #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -490,11 +533,13 @@ msgid "" "Please view or download %(user_full_name)s’s public program record to " "determine their credit eligibility status." msgstr "" +"Пожалуйста, просмотрите или загрузите %(user_full_name)s's информацию о " +"публичной программе для определения их кредитоспособности." #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt msgid "View Program Record" -msgstr "" +msgstr "Просмотреть запись программы" #: templates/records/edx_ace/programcreditrequest/email/body.html #: templates/records/edx_ace/programcreditrequest/email/body.txt @@ -510,13 +555,13 @@ msgstr "Команда %(platform_name)s" #: templates/records/edx_ace/programcreditrequest/email/subject.txt #, python-format msgid "%(program_name)s Updated Credit Request for %(user_full_name)s" -msgstr "" +msgstr "%(program_name)s Обновите кредитный запрос для %(user_full_name)s" #: templates/records/edx_ace/programcreditrequest/email/subject.txt #, python-format msgid "%(program_name)s Credit Request for %(user_full_name)s" -msgstr "" +msgstr "%(program_name)s Кредитный запрос для %(user_full_name)s" #: urls.py msgid "Credentials Administration" -msgstr "" +msgstr "Администрация учетных данных" diff --git a/credentials/conf/locale/ru/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/ru/LC_MESSAGES/djangojs.mo index 069d5c8ae17d8928a148c30c894d7dfe1144856a..03b07d52690d819b2f709b73901e26b99699eb0f 100644 GIT binary patch literal 9080 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 Y;&;#98xp@ol~nHPEvIivGT86^ALCcaO#lD@ delta 402 zcmez2_LaTGwdI z4T!%&=|4c)kBNbSnUR4(2*?g%W?)bS(y2gN8c25oX%Pm7dWPjd21vmkD19DEKLXM~ zy;K&&^EENG(fLu*m_7=;fv6Bo-t(q~|7P=IABo=i2Fj yP1&r^pT#(NlDN?1!$Rtle+kL(&%dzm!j=nLFYLas<6_grp2, 2018 # Weyedide , 2018 # Liubov Fomicheva , 2018 +# Валентина Пицик , 2019 +# Zimeng Chen, 2019 # msgid "" msgstr "" @@ -16,7 +18,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: Liubov Fomicheva , 2018\n" +"Last-Translator: Zimeng Chen, 2019\n" "Language-Team: Russian (https://www.transifex.com/open-edx/teams/6205/ru/)\n" "Language: ru\n" "MIME-Version: 1.0\n" @@ -26,7 +28,7 @@ msgstr "" #: static/components/MasqueradeBanner.jsx msgid "Change user" -msgstr "" +msgstr "Смена пользователя" #: static/components/MasqueradeBanner.jsx msgid "Submit" @@ -34,7 +36,7 @@ msgstr "Отправить" #: static/components/MasqueradeBanner.jsx msgid "View as: " -msgstr "" +msgstr "Смотреть как:" #: static/components/MasqueradeBanner.jsx msgid "Staff" @@ -42,37 +44,39 @@ msgstr "Сотрудник" #: static/components/MasqueradeBanner.jsx msgid "Specific Learner" -msgstr "" +msgstr "Конкретный обучающийся" #: static/components/MasqueradeBanner.jsx msgid "Username or email: " -msgstr "" +msgstr "Имя пользователя или адрес электронной почты:" #: static/components/MasqueradeBanner.jsx msgid "You are currently viewing as: {user}" -msgstr "" +msgstr "В настоящее время вы смотрите как: {user} " #: static/components/MasqueradeBanner.jsx msgid "Masquerading failed" -msgstr "" +msgstr "Маскировка не удалась " #: static/components/MasqueradeBanner.jsx msgid "" "You either do not have permission to masquerade as this user, or the user " "could not be found." msgstr "" +"У вас либо нет разрешения маскироваться под этого пользователя, либо его " +"невозможно найти." #: static/components/ProgramRecord.jsx msgid "Earned" -msgstr "" +msgstr "Заработанный" #: static/components/ProgramRecord.jsx msgid "Back to My Records" -msgstr "" +msgstr "Вернуться к моим записям" #: static/components/ProgramRecord.jsx msgid "Send Learner Record" -msgstr "" +msgstr "Отправить запись учащегося" #: static/components/ProgramRecord.jsx msgid "Share" @@ -80,39 +84,44 @@ msgstr "Поделиться" #: static/components/ProgramRecord.jsx msgid "Download Record" -msgstr "" +msgstr "Скачать запись " #: static/components/ProgramRecord.jsx msgid "We are sending your program record." -msgstr "" +msgstr "Мы посылаем вашу программу записи." #: static/components/ProgramRecord.jsx msgid "We were unable to send your program record." -msgstr "" +msgstr "Мы не смогли отправить вашу программу записи." #: static/components/ProgramRecord.jsx msgid "" "We were unable to send your record to {orgs}. You can attempt to send this " "record again. Contact support if this issue persists." msgstr "" +"Мы не смогли отправить вашу запись в {orgs}. Вы можете попытаться " +"отправить эту запись еще раз. Свяжитесь со службой поддержки, если эта " +"проблема не исчезнет." #: static/components/ProgramRecord.jsx msgid "You have successfully shared your Learner Record" -msgstr "" +msgstr "Вы успешно поделились своими записями с учениками." #: static/components/ProgramRecord.jsx msgid "" "You have sent your record to {orgs}. Next, ensure you understand their " "application process." msgstr "" +"Вы отправили свои записи в {orgs} . Затем убедитесь, что вы понимаете " +"процесс подачи заявления." #: static/components/ProgramRecord.jsx msgid "{name} Record" -msgstr "" +msgstr "{name} Запись" #: static/components/ProgramRecord.jsx msgid "{type} Program Record" -msgstr "" +msgstr "{type} записи программы " #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Completed" @@ -120,15 +129,15 @@ msgstr "Завершено" #: static/components/ProgramRecord.jsx msgid "Not Earned" -msgstr "" +msgstr "Не заработан" #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Partially Completed" -msgstr "" +msgstr "Частично Завершено" #: static/components/ProgramRecord.jsx msgid "Last Updated {date}" -msgstr "" +msgstr "Последнее обновление {date}" #: static/components/ProgramRecord.jsx msgid "Course Name" @@ -136,7 +145,7 @@ msgstr "Название курса" #: static/components/ProgramRecord.jsx msgid "School" -msgstr "" +msgstr "Школа" #: static/components/ProgramRecord.jsx msgid "Course ID" @@ -144,19 +153,19 @@ msgstr "Идентификатор курса" #: static/components/ProgramRecord.jsx msgid "Highest Grade Earned" -msgstr "" +msgstr "Самый высокий заработанный балл" #: static/components/ProgramRecord.jsx msgid "Letter Grade" -msgstr "" +msgstr "Письменная оценка" #: static/components/ProgramRecord.jsx msgid "Verified Attempts" -msgstr "" +msgstr "Подтвержденные попытки" #: static/components/ProgramRecord.jsx msgid "Date Earned" -msgstr "" +msgstr "Дата получения" #: static/components/ProgramRecord.jsx msgid "Status" @@ -164,71 +173,75 @@ msgstr "Состояние" #: static/components/ProgramRecord.jsx msgid "Course ID: {}" -msgstr "" +msgstr "Курс ID: {}" #: static/components/ProgramRecord.jsx msgid "Highest Grade Earned: {}" -msgstr "" +msgstr "Самый высокий заработанный балл: {}" #: static/components/ProgramRecord.jsx msgid "Letter Grade: {}" -msgstr "" +msgstr "Письменная оценка: {} " #: static/components/ProgramRecord.jsx msgid "Verified Attempts: {}" -msgstr "" +msgstr "Подтвержденные попытки: {} " #: static/components/ProgramRecord.jsx msgid "Date Earned: {}" -msgstr "" +msgstr "Дата получения : {} " #: static/components/ProgramRecord.jsx msgid "Status: {}" -msgstr "" +msgstr "Статус: {} " #: static/components/RecordsHelp.jsx msgid "Questions about Learner Records?" -msgstr "" +msgstr "Вопросы о записях учеников?" #: static/components/RecordsHelp.jsx msgid "" "To learn more about records you can {start_anchor}read more in our records " "help area.{end_anchor}" msgstr "" +"Чтобы узнать больше о записях, вы можете {start_anchor} прочитать больше в " +"нашей области справки по записям.{end_anchor}" #: static/components/RecordsList.jsx msgid "" "No records yet. Program records are created once you have earned at least " "one course certificate in a program." msgstr "" +"Пока никаких записей. Записи программы создаются после того, как вы " +"получили хотя бы один сертификат о прохождении курса в рамках программы." #: static/components/RecordsList.jsx msgid "Back to My Profile" -msgstr "" +msgstr "Вернуться в Мой профиль" #: static/components/RecordsList.jsx msgid "View Example" -msgstr "" +msgstr "Смотреть пример " #: static/components/RecordsList.jsx msgid "View Program Record" -msgstr "" +msgstr "Просмотреть запись программы" #: static/components/RecordsList.jsx msgid "Program Records" -msgstr "" +msgstr "Записи программы" #: static/components/SendLearnerRecordModal.jsx msgid "{name} - Sent" -msgstr "" +msgstr "{name} - Отправить" #: static/components/SendLearnerRecordModal.jsx msgid "{name} - Not Yet Available" -msgstr "" +msgstr "{name} - Пока не доступно " #: static/components/SendLearnerRecordModal.jsx msgid "Send to {platform} Credit Partner" -msgstr "" +msgstr "Отправить к {platform} Кредитный партнер" #: static/components/SendLearnerRecordModal.jsx msgid "" @@ -236,24 +249,29 @@ msgid "" "accept credit for this {type} Program. Once you send your record you cannot " "unsend it." msgstr "" +"Вы можете напрямую поделиться своей программной записью с партнерами " +"{platform} которые принимают кредиты за эту {type} программу. После того, " +"как вы отправите свою запись, вы не сможете ее вернуть. " #: static/components/SendLearnerRecordModal.jsx msgid "Not all credit partners are ready to receive records yet" -msgstr "" +msgstr "Пока что не все кредитные партнеры готовы к приему записей " #: static/components/SendLearnerRecordModal.jsx msgid "" "You can check back in the future or share your record link directly if you " "need to do so immediately." msgstr "" +"В будущем вы можете проверить данные обратно или поделиться своей ссылкой " +"напрямую, если это необходимо сделать немедленно." #: static/components/SendLearnerRecordModal.jsx msgid "Select organization(s) you wish to send this record to:" -msgstr "" +msgstr "Выберите организацию(ы), в которую вы хотите отправить эту запись:" #: static/components/SendLearnerRecordModal.jsx msgid "Send" -msgstr "" +msgstr "Отправить" #: static/components/ShareProgramRecordModal.jsx msgid "" @@ -261,22 +279,25 @@ msgid "" "program record to {platform} partners{end_anchor} for credit or application " "purposes." msgstr "" +"Вместо того, чтобы делиться ссылкой, вы также можете {start_anchor} напрямую" +" отправить запись вашей программы партнерам {platform}{end_anchor} в кредит " +"или для подачи заявки." #: static/components/ShareProgramRecordModal.jsx msgid "Share Link to Record" -msgstr "" +msgstr "Поделиться ссылкой на запись" #: static/components/ShareProgramRecordModal.jsx msgid "We were unable to create your record link." -msgstr "" +msgstr "Мы не смогли создать вашу ссылку на запись." #: static/components/ShareProgramRecordModal.jsx msgid "You can close this window and try again." -msgstr "" +msgstr "Вы можете закрыть это окно и повторить попытку." #: static/components/ShareProgramRecordModal.jsx msgid "Successfully copied program record link." -msgstr "" +msgstr "Успешно скопирована ссылка на запись программы." #: static/components/ShareProgramRecordModal.jsx msgid "" @@ -284,23 +305,26 @@ msgid "" "else of you choosing. Anyone you share this link with will have access to " "your record forever." msgstr "" +"Скопируйте эту ссылку, чтобы поделиться своими данными с университетом, " +"работодателем или кем-либо еще по вашему выбору. Любой, с кем вы поделитесь" +" этой ссылкой, будет иметь доступ к вашей записи навсегда." #: static/components/ShareProgramRecordModal.jsx msgid "Program Record URL" -msgstr "" +msgstr "URL-адрес записи программы" #: static/components/ShareProgramRecordModal.jsx msgid "Copy Link" -msgstr "" +msgstr "Копировать ссылку" #: static/components/ShareProgramRecordModal.jsx msgid "Loading record link..." -msgstr "" +msgstr "Загрузка ссылки записи... " #: static/components/Utils.jsx msgid "{first} and {second}" -msgstr "" +msgstr "{first} и {second}" #: static/components/Utils.jsx msgid "{firstItems}, and {lastItem}" -msgstr "" +msgstr "{firstItems}, и {lastItem}" diff --git a/credentials/conf/locale/sk/LC_MESSAGES/django.mo b/credentials/conf/locale/sk/LC_MESSAGES/django.mo index f13a99377bce6f45ebc109186ca8ddf7e2519be0..befbf1792e809d74a3fbdd57eb2de98d69c406c0 100644 GIT binary patch delta 16 YcmX@dvXo_l3e#)GiE5b}cT8gh05lQ?J^%m! delta 55 zcmZ3=a*kz!3X=%aM72z{5MAe@)Wnj^{5)Nk#FA7i1tSAP3taiWvbbu?4UI delta 54 zcmZo-Sl2j`NBLhPVT?12HV?zZ)Q!67gZ37_K I*i*&`0M2C(k^lez diff --git a/credentials/conf/locale/sl/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/sl/LC_MESSAGES/djangojs.mo index 9c42862e1ce01009773f605f6615a8a224b568e9..5420aa7ba3fd3da7487960db3ea3700cbca2e237 100644 GIT binary patch delta 15 WcmdnQ(#bMGh4K7E)vS#>su%$*y9LYu delta 54 zcmeBV*~BtIh4IHk)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12H149KvODjW5Z37_K I*i*|00MfP(u>b%7 diff --git a/credentials/conf/locale/sq/LC_MESSAGES/django.mo b/credentials/conf/locale/sq/LC_MESSAGES/django.mo index a691218c71349e56cc4c5b1cd9b3238c67d8e844..4b220246185f14c0d8e53a8a113ab99b049f73cd 100644 GIT binary patch delta 15 Xcmey)e2#g73geWCs#zO%*fRnEG2aE= delta 54 zcmX@d{GEA%3gh01s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+d IvB!xK0ONrVzyJUM diff --git a/credentials/conf/locale/sq/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/sq/LC_MESSAGES/djangojs.mo index cc7b9abc9ded68b702cfe8b3ec03cfb351e2ac33..8cdbe8e57ccd0c307d63c854aeb29b1a8cb20616 100644 GIT binary patch delta 15 Xcmey)e2#g73geWCs#zO%*fRnEG2aE= delta 54 zcmX@d{GEA%3gh01s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x&{`yM#c(;##V;r+6F+d IvB!xK0OPI?!Tb53e#=IiE5b}ceF7A05O6E{r~^~ delta 55 zcmbQrvX5ng3KI*{M72z{5MAe@)Wnj^{5)Nk#FA7i1tSAP3tat;j#;n?U2Lt9RtM1K%5W(%Gk!G(4&YD^2>$8A_sYD^WnI0kb#6@S9d@oea29MAkV z*5QlL(vik|!Mq-MPu{F#V7bwT73ia0+=msD)!?1cCLa7@M#STW|{gjOBO^ zKf?<+8vn*hyb*dAYgperWk7J|9cm~4p(3BkTl9iuI2PCA1l)}Z@DQ%XBd7(gqP}-C zeEt|WFn@!!*v#(qzBX*Yy;#fo<|qTD z_4#A?6<$JR;w>su?{O85W08Hh19_FXhI=`25*E+L0#s85lpVEYbnSEweHy)-PT7asTt-XiU(!`71)~h9ObRe>xp3f% zO+Tkbx^|=kwUDmH=8Hk8R70&f9H=Zh3u=q$Y_xbrnot{3HkDcSG2BXqM`nL9ZA0_r z43r*aMCnl~)%1@^jpI@Lg(&qyZC*H-j`eiab*R+^ZZsMxYwz>2SvMW*Dcu<eO8yrhk%l36#IE%cXfA3w99_j@PXl}h{H zB^}?Llduo5V<1nTb+v6!ny$QzDVjmvBNleEdxB?RsjmbqPuEp}0ZMdFxcgzvYp*?~6{w}(V z88wfoq|mW|ns5=dfj`)U&C8-6T*OS;6WET^xCwt_GUg;1lZJViisiTx>tnW~zCVIY zZTc~n_015KEIP(f6HK8KXK@|A#0FeIOQQB}@UDa322fzNOUenXwOgkvcZ)wmP);V~RThM3Q>-~Yl++8L~_ zHxWR(%mviO#*y1Hw@?wBNhkga#S=QRaSnAi-%)q_6BUUp25|`EMs1`PGw>j`;Yrj+ zX0Q_PBR^(|VmHP!SdXzC6@gyVdSe;H-%I5t9a`WmZp07RjqyB$0z8GfnlV&JFQZO) z8@1u*sPV6no3WFRQio8|H%gkT!mdS&Ox?dGQus@weRf$+$$PPPtsQH(|B{PJ9Jz$7 z#kY}lZTgE`QyC5Y57|Q2{VM5w5fu9!>F?j{a&=kitit7=NUq;OZN-`!yi zhK5~-L&5%YuDYR+)jxQ~{&4VkWkZj3)(Tl=eSvznJwxO{sy{9~kd_sxOq+B>-lumu S!p#|, 2017 # swaleh amin , 2018 # swalehe manture , 2018 -# Nasry Uronu , 2018 +# Benedict Mfoi , 2019 # msgid "" msgstr "" @@ -17,7 +17,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Nasry Uronu , 2018\n" +"Last-Translator: Benedict Mfoi , 2019\n" "Language-Team: Swahili (Kenya) (https://www.transifex.com/open-edx/teams/6205/sw_KE/)\n" "Language: sw_KE\n" "MIME-Version: 1.0\n" @@ -175,7 +175,7 @@ msgstr "Wezesha kupeana taarifa kupitia LinkedIn" #: apps/core/models.py msgid "Enable Twitter sharing" -msgstr "" +msgstr "Wezesha kushiriki taarifa kwa Twitter" #: apps/core/models.py msgid "Enable sharing via Twitter" @@ -227,7 +227,7 @@ msgstr "" #: apps/credentials/models.py msgid "Program UUID" -msgstr "" +msgstr "Programu ya UUID" #: apps/credentials/models.py msgid "" @@ -290,7 +290,7 @@ msgstr "" #: apps/records/views.py msgid "N/A" -msgstr "" +msgstr "Haihusiki" #. Translators: A 'record' here means something like a transcript -- a list of #. courses and grades. @@ -374,7 +374,7 @@ msgstr "Imewezesha na edX ya Wazi" #: templates/base.html templates/credentials/base.html msgid "Skip to main content" -msgstr "" +msgstr "Ruka mpaka kwenye maudhui kuu" #: templates/credentials/base.html #, python-format @@ -512,7 +512,7 @@ msgstr "" #: templates/records/edx_ace/programcreditrequest/email/body.txt #, python-format msgid "The %(platform_name)s Team" -msgstr "" +msgstr "Kikosi %(platform_name)s" #: templates/records/edx_ace/programcreditrequest/email/subject.txt #, python-format diff --git a/credentials/conf/locale/sw_KE/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/sw_KE/LC_MESSAGES/djangojs.mo index e53652070c62763d9388290f49ba06d8712bfd51..b45c81212a26bdd7e1085d71021ec569c0c19aa6 100644 GIT binary patch literal 2143 zcmbVMTaOe)6fOl7#w&OsBt{PUARFkJSqut;YYZ$XL@R8YWq^1t}2?H z4G;R@3p|(@6T=Vi(fDZm1O5RYjZZ%4FYwjx^z`gv;)`}t{q?Cn^__FRQ&qnmJ@}2n za}486j9)NLU_5#XFFc2yR_b}+Vc=ol>kXc0<`;mkV*VVk1zc(DP2jVzC%|Wb7WfJ< zH1?Ox|3?je4HTZoK#B7`@Ezcfz|+8AfnNX*9Z>31;6q>+_y_Ps;PHdV4frnbJLJ9u zJPP}F1eg5(0*c=OB=rLD2=Hy-TR@rr7vJHe)x*aA3MhI# z1`6*_z>~nAff4Xe;J3hIAX^9i0DKR4;Lt4Y2f*X7mw_U=#CQWk`Z0e%QEMwU%2lsn zyo4cnLY7)L$yMUZBRWX!@`&bApVam;#{7{Q5VF>$p1U&Pe5@%rU8*ZKD^l(C5MQBm znpUpPzix4+)lGJuwu&Lcg?0rVs9V<4GM70Pt#RoMr|Ayu6K%v3Yg4r`;Zm!OLdRxg zV!EkiMX5GCk4EzL!KsZf$c$H8$RtNJvL)$^P1+-M$LNaK;p?s@Fp<;w(BxyQ8&;S_Qnq?){~-T%Aqj8E3@+H&k3w23~kfnv^yz1Lac_qhGUxF$aF zm`x7fD>nBWdvc*DZ0X62sCF~X1x^C4)pDT#|BH8Fc*bCD$0?jPhKmYGqI{y1sJa2BiS1+CZWsa?_j!Jg%J3Rr86 z!i^8iZ5{h)Wfsl&}`fhB7(d969(WdQ@Ei|^r{m7S`yR=U5(%9N@ zs>h`bg=@z)Ypvam*7ZFjWYJaZtR9{1E}oCN=c4W+K6{H7PIvLNWDiE04U;WwL{f-`q&8ID6< z)TUcHdy!_JOQ&)=xkQT#7hCEw4^bO>O?@9S!`nu!WIEvtvk5C4=>gLY)2f{sb;IOL zX=C)En*7NsEVr~89Av^4pHw_Q_t)8MbAe_ioU`y|oSG>tN(?)L6CYBXc!@D()vw?+ z3i;Gf#+-&s-DTucnu^tREQIN1x$Y&BH8~Rp8gLmHM+VxdP4H!if)@;&ariOLUILkr zb7Cd&=1K@{u=|jNN>8x_A2sRRdn(@e@&CLjsc;KHkmhpO%DD-%&7~Ox#CtNe3DiO3 zb8geDSSSJ)NXWQ-*$m~sR7JQB&D2tAJR^WkHMiAPrk^CM>rA8@O>l?^eb3+`Nla4r z9LiuRTr%vigmH^iECUU&*}&A4BhKNCxg@v=x{(<4IvYSIyhO(*`c^@#{o6s*R=cy? MVSTRs{GJHvZ?5=UdH?_b delta 160 zcmcaF(9L3TPl#nI0}wC*u?!Ha05LNV>i{tbSOD=wprj>`2C0F8$u=zEY9YGLMX8A; znfZCTE{P?nRtiQ2h8DVp2D%1@3WjD@rsmoPKrnd%i$gZ(?z2s)9{P hQDR, 2018 -# YAHAYA MWAVURIZI , 2018 # Innocent Masue , 2018 +# YAHAYA MWAVURIZI , 2018 +# Benedict Mfoi , 2019 # msgid "" msgstr "" @@ -14,7 +15,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: Innocent Masue , 2018\n" +"Last-Translator: Benedict Mfoi , 2019\n" "Language-Team: Swahili (Kenya) (https://www.transifex.com/open-edx/teams/6205/sw_KE/)\n" "Language: sw_KE\n" "MIME-Version: 1.0\n" @@ -24,27 +25,27 @@ msgstr "" #: static/components/MasqueradeBanner.jsx msgid "Change user" -msgstr "" +msgstr "Badilisha Mtumiaji" #: static/components/MasqueradeBanner.jsx msgid "Submit" -msgstr "" +msgstr "Wasilisha" #: static/components/MasqueradeBanner.jsx msgid "View as: " -msgstr "" +msgstr "Tazama kama:" #: static/components/MasqueradeBanner.jsx msgid "Staff" -msgstr "" +msgstr "Waajiriwa" #: static/components/MasqueradeBanner.jsx msgid "Specific Learner" -msgstr "" +msgstr "Mwanafunzi maalum" #: static/components/MasqueradeBanner.jsx msgid "Username or email: " -msgstr "" +msgstr "Jina la mtumiaji au barua pepe:" #: static/components/MasqueradeBanner.jsx msgid "You are currently viewing as: {user}" @@ -74,7 +75,7 @@ msgstr "" #: static/components/ProgramRecord.jsx msgid "Share" -msgstr "" +msgstr "Sambaza" #: static/components/ProgramRecord.jsx msgid "Download Record" @@ -82,63 +83,67 @@ msgstr "" #: static/components/ProgramRecord.jsx msgid "We are sending your program record." -msgstr "" +msgstr "Tunatuma rekodi yako ya programu." #: static/components/ProgramRecord.jsx msgid "We were unable to send your program record." -msgstr "" +msgstr "Hatukuweza kukutumia rekodi zako za programu." #: static/components/ProgramRecord.jsx msgid "" "We were unable to send your record to {orgs}. You can attempt to send this " "record again. Contact support if this issue persists." msgstr "" +"Hatukuweza kukutumia rekodi zako za programu kwenda {orgs}. Unaweza kujaribu" +" kutuma rekodi hizi tena. Wasiliana kupata usaidizi kama tatizo litaendelea." #: static/components/ProgramRecord.jsx msgid "You have successfully shared your Learner Record" -msgstr "" +msgstr "Umefanikiwa kushiriki Kumbukumbu ya Mwanafunzi wako" #: static/components/ProgramRecord.jsx msgid "" "You have sent your record to {orgs}. Next, ensure you understand their " "application process." msgstr "" +"Umetuma rekodi yako kwa {orgs}. Halafu, hakikisha unaelewa mchakato wao wa " +"maombi." #: static/components/ProgramRecord.jsx msgid "{name} Record" -msgstr "" +msgstr "{name} Rekodi" #: static/components/ProgramRecord.jsx msgid "{type} Program Record" -msgstr "" +msgstr "{type} Rekodi ya Programu" #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Completed" -msgstr "" +msgstr "Imekamilika" #: static/components/ProgramRecord.jsx msgid "Not Earned" -msgstr "" +msgstr "Haijapatikana" #: static/components/ProgramRecord.jsx static/components/RecordsList.jsx msgid "Partially Completed" -msgstr "" +msgstr "Imemalizika kiasi" #: static/components/ProgramRecord.jsx msgid "Last Updated {date}" -msgstr "" +msgstr "Mara ya mwisho Kusahihishwa {date}" #: static/components/ProgramRecord.jsx msgid "Course Name" -msgstr "" +msgstr "Jina la Kozi" #: static/components/ProgramRecord.jsx msgid "School" -msgstr "" +msgstr "Shule" #: static/components/ProgramRecord.jsx msgid "Course ID" -msgstr "" +msgstr "Namba ya Kozi" #: static/components/ProgramRecord.jsx msgid "Highest Grade Earned" @@ -158,7 +163,7 @@ msgstr "" #: static/components/ProgramRecord.jsx msgid "Status" -msgstr "" +msgstr "Hali" #: static/components/ProgramRecord.jsx msgid "Course ID: {}" diff --git a/credentials/conf/locale/ta/LC_MESSAGES/django.mo b/credentials/conf/locale/ta/LC_MESSAGES/django.mo index 5fb616df88c3d14444b25c8b506867b7032990c4..6a98fa6ad4c40418c6c7895750f4a4708ae49383 100644 GIT binary patch delta 15 Xcmey$e1>^~3ge`Ss#zO%*fIhDF_Q(> delta 54 zcmX@Z{FQlv3ghmHs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y#)b-prdCE~+6F+d IvB!ZC0OCszxBvhE diff --git a/credentials/conf/locale/ta/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/ta/LC_MESSAGES/djangojs.mo index d10bd57fbe7f5fbbb08b77e6ca42f4754422b862..495ff6e03f33532c6f81cf7c0c995c0b7efbfb25 100644 GIT binary patch delta 15 Wcmcb}yn=ax3S-_x)vS#>BpCrLI|W7n delta 54 zcmZ3%e35yA3gh&Ns#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d Iu}6jx0L+sQD*ylh diff --git a/credentials/conf/locale/te/LC_MESSAGES/django.mo b/credentials/conf/locale/te/LC_MESSAGES/django.mo index defe0f7d40d99b2b465762ad2f65e6e93acfbaa1..4c0f48790cc3c9eec6aec4e801e8b54802f9f93a 100644 GIT binary patch delta 15 WcmeBYdB!|Jg>lV9)vS#>!WjWA{RO`O delta 54 zcmaFH+|M#Wh4Jb{)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12HV?zZ)Q!67gZ37_K I*b~JF0N6Yap#T5? diff --git a/credentials/conf/locale/te/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/te/LC_MESSAGES/djangojs.mo index af60682c101eccb931bdba13f143c301dcb7ada4..ced1d4ce9d0ad51d1971a9780237995ba4d114de 100644 GIT binary patch delta 15 Wcmcb_ypnl>3S<66)vS#>q!0L?BBF8}}l diff --git a/credentials/conf/locale/th/LC_MESSAGES/django.mo b/credentials/conf/locale/th/LC_MESSAGES/django.mo index d3eb246d18a456fb97e3131adf364bb2c7f2113d..4132613d36f15e0df64b470af96a374a71ec7acf 100644 GIT binary patch delta 58 zcmeywe29613S-Aa)vSp-%+&)*^NJIT@)T@JiW2jRa}rDPiyR6dVtR?inTd8f3PuKo KmXl=}uL1y%<`p{t delta 103 zcmX@a{E2yj3geE6s#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F){ zvByf!E3+uEvQ)u2BQX~UGZITm67v!jY;toG^NcDS(sL6tbM%t)bM17%MofHr6#$@$ BA=m%_ diff --git a/credentials/conf/locale/th/LC_MESSAGES/django.po b/credentials/conf/locale/th/LC_MESSAGES/django.po index 2a4f95e1c..d38af9579 100644 --- a/credentials/conf/locale/th/LC_MESSAGES/django.po +++ b/credentials/conf/locale/th/LC_MESSAGES/django.po @@ -10,6 +10,8 @@ # Sitdhibong Laokok , 2016 # Noppachai Eiamwasant , 2018 # Jirayu Chamamahattana , 2018 +# edx demo , 2019 +# Punsarn , 2019 # msgid "" msgstr "" @@ -17,7 +19,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Jirayu Chamamahattana , 2018\n" +"Last-Translator: Punsarn , 2019\n" "Language-Team: Thai (https://www.transifex.com/open-edx/teams/6205/th/)\n" "Language: th\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/th/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/th/LC_MESSAGES/djangojs.mo index 32e144790cd7eadb3adb7720c7b36112fe34248e..2034e89954156443289c7db677828232b240fb04 100644 GIT binary patch delta 15 Xcmey#e1Um_3gfhis#zO%I5GkNGAjk< delta 54 zcmcb>{F8Zt3giBXs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x&{`yM#c(;##V;r+6F+d IvB!lG0OaHk$^ZZW diff --git a/credentials/conf/locale/tr_TR/LC_MESSAGES/django.mo b/credentials/conf/locale/tr_TR/LC_MESSAGES/django.mo index 39b76307161b699e34f0d5b90317503bdbad962b..c14f529240f2e748a517f590e7837c0161ad7c0f 100644 GIT binary patch delta 1696 zcmXZcQD{?F9LMp0Qj^%ZsBLXrjcaeMo5eV%gSpjO?X)pUqnqhcZK2LgqsE5FQa09s z3loZhFUs9s22&}8!5ld4U zz=POWZZ;iH;SBs8XW>z^eu!$n6)UlTW%vyyaR6)aFPy4UKPAwM{~@_pIX8q>?b(1DcqwXC;;2fsqbkvh z{Mmjk8t@1zp&=w!JBO?B3btaH>@?0sEUANT0yX>$8}MsX1qM-THIBS%6WEB;XPUi^ z@1cHAqe{I87vTW18g?4h|2gE(u5o!A$58F6E2#ezg4Gq1B~73@>_N?-h?@B!JdMXt z?NdxsmDr5B@4;>ODXKD;P@DNaYN^zT0}&kbkvP7={jFKl|9t|ribE5w!V)gP3#d)^ z0NZf_2e6%K5!xtH6no&^KSL!P<|S3M9&2zh?!tAb-+w_(;BtvTnOyPw$1}q8=kt68 z>hsHZ9z?Z2iLH1YRj~-8EX5ce#~##I^Bk2(fc5FZO6i_7xjzNw)Ndfvq5Y`i1J0(G5&x5L zvpW{33#W+MIGZ`!-HKpUjf$s&QlFPcCsz%-$!!TneQCEV*x>uf-4|RH=;mqCy%;>; z>u}eHqP~yaY$)dI8a@)*Q9dJ?%@hmSqLa#Q-&@?#H+*eozu*0$YODX9CBrwW4o!2H yL?ey4eAdb3cjfXqr*CiZ^L(c0!|b{+*e^SGok}jq4R5Q<`(FUnsJ+tw delta 1658 zcmXZce@NVQ9LMqZ-MqW0)6%m{&AfB%Hs+4*#%@zry8N-mbe79)x|N&YrLN{c_*$r-#gRvV`J*~!mN*tql@!#&Of|nDID=};W!&Gi{Lrjj1yRgft6-8xE-T7fW
P652+v~?Ucy`rHA!I=6;oJ&bJ&gfIcAUGo9M?O)BwZyD2}-I=$ zSVsLnuKDy{%Kdt*#b#6h0~o>&@tCIiB?ZQ`Dxy#z2`$k5AwgT&Yw~QBcEKBo@1e{LDwC459`OqgEw>%G6d= zCJrD!>*JyU-$4a5jKpZ8*nk<_hKs0i>WDI>4t7$|i%DFKhfx{$2(?z%kv4V**WrCE z$F&bHKW|2*dOvQ!eq^2S1JPq%N>=YBHxIbaSMKhFQXb1 zGCgIW7p&)Due-jD+Rek*iYHNlO(UPY<&uB3D?;5zPO}4^V**qXItXdJ)y{B+la< z)aIQIu~Z2PvlMdiZ|uZH)Dr9^Eo9Z+LUs5F($+@8PR3UnR`zvhAL@9Pv*tl*IWc>k zd0$0dJ2g$5TlK^V`-3IBC@7KSpspGoL&AhPlt2JzuNP>Gvu%Lby3^m{OmvO z+3j=%$~-SR{edb^XZm#Dwd{1Fpwk=ah&J{eOddUShxd1!QAxM6+Pbn0hc1RqyPW_ diff --git a/credentials/conf/locale/tr_TR/LC_MESSAGES/django.po b/credentials/conf/locale/tr_TR/LC_MESSAGES/django.po index 56e3158ad..dc0b311d5 100644 --- a/credentials/conf/locale/tr_TR/LC_MESSAGES/django.po +++ b/credentials/conf/locale/tr_TR/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ # adem ÖZGÜR , 2016 # Erdoğan Şahin, 2017 # Emrah Emirtekin , 2018 -# Ali Işıngör , 2018 +# Ali Işıngör , 2020 # msgid "" msgstr "" @@ -17,7 +17,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Ali Işıngör , 2018\n" +"Last-Translator: Ali Işıngör , 2020\n" "Language-Team: Turkish (Turkey) (https://www.transifex.com/open-edx/teams/6205/tr_TR/)\n" "Language: tr_TR\n" "MIME-Version: 1.0\n" @@ -133,7 +133,7 @@ msgstr "Öğrenci Kayıtları Yardım URL'i" #: apps/core/models.py msgid "URL of page for questions about Learner Records" -msgstr "" +msgstr "Öğrenci Kayıtları ile ilgili sorular için sayfanın URL'i" #: apps/core/models.py msgid "Facebook App ID" diff --git a/credentials/conf/locale/tr_TR/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/tr_TR/LC_MESSAGES/djangojs.mo index f3fc6bcd9ed4b62d030e1c8fb43512b6312a0961..d1dff1acfc24261335edbccb9f89c520c9e56aee 100644 GIT binary patch delta 1330 zcmZA1Pe>GD7{~E<)%=rMX62S?jaH_WX8s{c58}m({Q z=z*bi((C9e9%BY@u#f|7qR5zP47ko>Iph0Sh7Vnzxh`WR*I%KYTgRRF&h<0yVf+L4 z8Iv-l+l<-Gg$As^Zmhz7tid7o{v;}qo2Y;bXyFqq#@BcT-ypG#hl`3^j15?e+%mnW z1p2Xx`ORhb#su;)VGi4I+8xhfGvj$wz!$g+^SBo`P>Fwc@Bczorn1DC!|281*n>xK z3>EJ#wllw3;Y6kXips2@lx)$9DrJXjA8Nu2_>^rL#T|@G$X1o9K~<~;Rr)^E^Mj~3 zBkuJm@-edXlF{T?wc;mHc<=B*%N` z{F1b#YO2sN=ggR+sETTS=k3(0_&qxN&LW(4f@1^yAiaRDO6Uz%)8B(is`o>C-9lGW zS^sY;IXmHOo$0rIK`Rll&L^!)u}F5HsH&iOXxiuV$Kzwup-|HDMWO-!s1=Px#$)!B z74!Qdu~91&2nX91hl{V34F~))mK{H5WwXUgp2fM+>z=dOPo*=3-NbXg610=~Ou`zm z^XZTsPUJK3e1_3l+K%Nj$&hUYBhjSIt!N6KNkeuRZjR!^o)_Cq^ SEVmj?L4NxY6mO0i4Nl6qKn_(^#mX8z0bXOm-n9cdEb2Pd7p25&ZTY} z;nVW8hmP484kbAem6TZy_r)H;Hu`6<6{lmb#5OR?{hO%w?%*1nj}5Vd{tMh-*03cm z)^Xz}X0WNrtQ~W>9(z&GtEfbdp$4p>imf>Y3ki&Mq zZ~a{8K?OPNAg5J09`{e62AaUNIE|fn6_s!hKfjN?^cQddmv9>! zA4NT%#D+4t$c2k@r~#j#)^-u2giteEMpeRLR3*@f>K9Qn+>a`K9kmowNbdFkH{oO4 zjW6T-zuTz)7H%Y{qXsGdmG3s1J@JD`5$mLi^!gC}D-(AEIgP6-roD z)p$xuORY&#Omq2uv>6~OVF61t{R6aX^-=A1m34ro*i1{(v;^7@+i40Vz9YI?IjJ%V z%~&zGLNsAbR&hY59@3GydN`H6=KKXK C8C+5T diff --git a/credentials/conf/locale/tr_TR/LC_MESSAGES/djangojs.po b/credentials/conf/locale/tr_TR/LC_MESSAGES/djangojs.po index c95c9f0c4..8b71c198b 100644 --- a/credentials/conf/locale/tr_TR/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/tr_TR/LC_MESSAGES/djangojs.po @@ -6,7 +6,7 @@ # Translators: # Gokhan Kaya , 2018 # adem ÖZGÜR , 2018 -# Ali Işıngör , 2018 +# Ali Işıngör , 2019 # msgid "" msgstr "" @@ -14,7 +14,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: Ali Işıngör , 2018\n" +"Last-Translator: Ali Işıngör , 2019\n" "Language-Team: Turkish (Turkey) (https://www.transifex.com/open-edx/teams/6205/tr_TR/)\n" "Language: tr_TR\n" "MIME-Version: 1.0\n" @@ -32,7 +32,7 @@ msgstr "Gönder" #: static/components/MasqueradeBanner.jsx msgid "View as: " -msgstr "" +msgstr "Şu kişiymiş gibi görüntüle: " #: static/components/MasqueradeBanner.jsx msgid "Staff" @@ -274,7 +274,7 @@ msgstr "Bu pencereyi kapatıp yeniden deneyebilirsiniz." #: static/components/ShareProgramRecordModal.jsx msgid "Successfully copied program record link." -msgstr "" +msgstr "Program kayıt bağlantısı başarıyla kopyalandı." #: static/components/ShareProgramRecordModal.jsx msgid "" diff --git a/credentials/conf/locale/uk/LC_MESSAGES/django.mo b/credentials/conf/locale/uk/LC_MESSAGES/django.mo index f8ea2669bb911faa8936260f11343c05abbf7b88..995d37b61ac76eb9671f9fc82b4ff4fcd30c4991 100644 GIT binary patch delta 93 zcmdnS)5|j<$G3=yf#EJA1A`m`1494{1A{D(&Ii(}K)M%5YXj-cK-vsQ-v`ppKw69y bA|3^#y@32SAZ-n#FK*mf#ke_!=@v5pcH$03 delta 132 zcmeC>*~T*=$9Fap1H)ZL1_n6>28K!&1_oImJp)Lq0_kl)S{q2;0n%nbnuC>r!5K)~ z18IWSIah2nAaJ delta 54 zcmZ3(dX;s83ghgFs#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d Iu}6Uk0MB+0I{*Lx diff --git a/credentials/conf/locale/ur/LC_MESSAGES/django.mo b/credentials/conf/locale/ur/LC_MESSAGES/django.mo index cac7707a88d6d4cd57019d52b5f14778e620fdfb..5a974ea659e9e777e1761b67ec5869733294ffab 100644 GIT binary patch delta 15 WcmZo;xyw93g>msj)vS#>{1^c&rv;k; delta 54 zcmcc1+{Q9Nh4IWp)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12HV?zZ)Q!67gZ37_K I*b~SI0Mf}1eEyqtN03S;g>)vS#>Bp3lK6$L^7 delta 54 zcmZ3^e1Um_3gfhis#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x(24Y28IfTmR5$A+6F+d Iu}7K_0L%CfCjbBd diff --git a/credentials/conf/locale/vi/LC_MESSAGES/django.mo b/credentials/conf/locale/vi/LC_MESSAGES/django.mo index 0b34a6c84b6537ca6283e1fcfb548d0fd919f4de..09cf07e69fe829f247f459e2452c4a0ce2893a52 100644 GIT binary patch delta 57 zcmey!e4KfL3S;j?)vSp-%+;I{^A$phO7rv56>O3-67vd66U&Sp(sL6tbM%t)bM15# Nj0_AdCyO!O1^{0M6Yu~4 delta 96 zcmX@k{E>Nr3gh;Ps#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F){ vvByf?FC#He!7shEGBr=ZCOJQ^xFjtxFWsRsF(W@;FF8NgP6w=a;+xw5xdR_v diff --git a/credentials/conf/locale/vi/LC_MESSAGES/django.po b/credentials/conf/locale/vi/LC_MESSAGES/django.po index 4a32a922f..12a03e3f4 100644 --- a/credentials/conf/locale/vi/LC_MESSAGES/django.po +++ b/credentials/conf/locale/vi/LC_MESSAGES/django.po @@ -11,6 +11,7 @@ # Theodora. Tôn Nữ Quý Thiện , 2018 # Hoà Lê Thanh , 2018 # Nhan Nguyen , 2018 +# Bao Truong , 2019 # msgid "" msgstr "" @@ -18,7 +19,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: Nhan Nguyen , 2018\n" +"Last-Translator: Bao Truong , 2019\n" "Language-Team: Vietnamese (https://www.transifex.com/open-edx/teams/6205/vi/)\n" "Language: vi\n" "MIME-Version: 1.0\n" diff --git a/credentials/conf/locale/vi/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/vi/LC_MESSAGES/djangojs.mo index 2b9216df2b84364fbf35fc54d439b0545ecfd578..9b9734224bb58fcf21b08df416d474209edab183 100644 GIT binary patch delta 15 Xcmeyye3p5F3ghI7s#zO%*f9bCF}VfW delta 54 zcmX@h{Ec~n3ge!Ms#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d IvB!}S0OFtzx&QzG diff --git a/credentials/conf/locale/zh_CN/LC_MESSAGES/django.mo b/credentials/conf/locale/zh_CN/LC_MESSAGES/django.mo index 65f4c96b857e8039856afdcf344814bf8fe3d9bf..12e366b63fbc5f6906cd049ec22c2236d967169e 100644 GIT binary patch delta 914 zcmXZaPe@cz6vy!+!ayZzmgY3tyfigLXDp$~Fbl-~Q9273wGeVd;>BQ#6r3QQZ33}H z!(b7$83@`*gAgJ8A!t+7Vro&)vfL_!kYrHbU+=Cy_ndd{J?Gy0*3z%jPqURlzQXKL zqgjKQCH9)7a0rj#O&rAMcozTQc{~*}JB|hH!!nNIjwZ7tUPce^;t-Z_9)DpC-fA|h z$9%H|{|8Sy|j1s@YJ^0AdL8vd?vAy#COj>s0}UYaPu9QCXS$Pyn#M^jPxeYkav)IFJ8bl;@`Lq*92T6 zsOP58!jl2VOwGpG!J!q+?AIHddBEyoasLwq_E&K%aoHA+y z71Tx-y!aPtBaYD}vYbT=>Rks=?<|A5@hNI!FHsMycySeVB7ad2PIkM`A4R4vJ^o6D}KZv_2nN6E!55Y_0DWujfXIar;s7YUF^Zv$QG%gj&KF1=$rVl55w4m zN#af5sE>1VsAzTg@>k9ZzOEii_9Md#4O67oSy1zT_t`COziEOHRL z!gH^D1I@uvD|I28F6Jf+*4}h6n=rS;?E%Y, 2018 # ifLab , 2018 # 嘉杰 李 , 2018 +# abby li , 2019 # msgid "" msgstr "" @@ -19,7 +20,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2016-09-14 14:52+0000\n" -"Last-Translator: 嘉杰 李 , 2018\n" +"Last-Translator: abby li , 2019\n" "Language-Team: Chinese (China) (https://www.transifex.com/open-edx/teams/6205/zh_CN/)\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" @@ -243,7 +244,7 @@ msgstr "{first_org} 及 {second_org}" #: apps/credentials/views.py #, python-brace-format msgid "{series_of_orgs}, and {last_org}" -msgstr "{series_of_orgs}, 及{last_org}" +msgstr "{series_of_orgs},及{last_org}" #: apps/credentials_theme_openedx/templates/openedx/credentials/programs/professional-certificate/certificate.html msgid "" diff --git a/credentials/conf/locale/zh_CN/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/zh_CN/LC_MESSAGES/djangojs.mo index 293f659b70b88f58a5a5ee8a9079c22d335e0d22..90acf7af5fccd732e328ed4d465a0ebeecdad812 100644 GIT binary patch delta 603 zcmXZYKS)AR6vy#XT9jpK_D6`!{tOi=T2v?zhC@UI4dDmY7i-Ph;)j`u(R$owo@-+ zD<(|6iC5G)Y(c9_#Ey2<^L@rqdvVt#dAVazWTP7cbTufBY{?3G zF^Sqg!Zo}`4gAGP{6hzZ-1T!Y)SLwB|5MnE1&m_R)X(mGeWDjOMrgd7hNY_>WJ3+| z83U+6QEbC`Q!nEn^(t=T0Wy<(pbk*OA$0Q=nm3KwkLOwFnQ}b7${8@gej&-kFEI*IumnWXw EKUteb3IG5A delta 654 zcmXZY%S#(k6vy#XZAGg_#3hrF_4+uVRt-Egg9&?9bKKI;n=Qk($Yv0;=e_5*dKxES+ z(jX#l>r2jJ4RsQ$Fm37$%u?sD5_`NNwK#x!e%$yDxy3d03TnUY-hDSr1#N_JQr zveArFbT#M$vL&Ce4ZorGf8qolqXzmKMY_?CbvTWBZV@#njrx8DEASUOcxdX|##Cvd z|7<*?;b|%z>_!c8P=j6@r%XSF)$A{s`XjbeuVM^$u?4+^=m5{rhhwODapX5hGR1;h z)_7>Z57a^anfe;PQdd7A1hY7eMbvXX{>LV!Z diff --git a/credentials/conf/locale/zh_CN/LC_MESSAGES/djangojs.po b/credentials/conf/locale/zh_CN/LC_MESSAGES/djangojs.po index 0e6d10612..fdaf92160 100644 --- a/credentials/conf/locale/zh_CN/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/zh_CN/LC_MESSAGES/djangojs.po @@ -8,9 +8,10 @@ # alisan617, 2018 # Natalia Berdnikov , 2018 # 百杰 陈 , 2018 -# Muhammad Ayub khan , 2018 # ifLab , 2018 # 嘉杰 李 , 2018 +# Muhammad Ayub khan , 2018 +# abby li , 2019 # msgid "" msgstr "" @@ -18,7 +19,7 @@ msgstr "" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" "POT-Creation-Date: 2018-10-01 16:57+0000\n" "PO-Revision-Date: 2018-05-01 20:19+0000\n" -"Last-Translator: 嘉杰 李 , 2018\n" +"Last-Translator: abby li , 2019\n" "Language-Team: Chinese (China) (https://www.transifex.com/open-edx/teams/6205/zh_CN/)\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" @@ -48,7 +49,7 @@ msgstr "特定学员" #: static/components/MasqueradeBanner.jsx msgid "Username or email: " -msgstr "用户名或电子邮件" +msgstr "用户名或邮箱" #: static/components/MasqueradeBanner.jsx msgid "You are currently viewing as: {user}" @@ -307,4 +308,4 @@ msgstr "{first} 及 {second}" #: static/components/Utils.jsx msgid "{firstItems}, and {lastItem}" -msgstr "{firstItems}, 及 {lastItem}" +msgstr "{firstItems},及 {lastItem}" diff --git a/credentials/conf/locale/zh_HK/LC_MESSAGES/django.mo b/credentials/conf/locale/zh_HK/LC_MESSAGES/django.mo index de0607bcd47c2387bf09ffbe315cf6ff473bbd7a..9bef4bc4c16e02c4af5c52e748a579be30a9c4ec 100644 GIT binary patch delta 15 WcmaFEe3*HH3S;L))vS#>Oc(($Jq5A= delta 54 zcmX@i{Dygg3gf1Us#$6wy3R$Zi6xo&dAcr%C8<^lMh1o!x`qb228IfTW>%)=+6F+d IvB#Vd0Nj!fk^lez diff --git a/credentials/conf/locale/zh_HK/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/zh_HK/LC_MESSAGES/djangojs.mo index 282c414cd1bffadb04a7fdac64f1764cb853b838..9d17213a0bf20b898a1d4fb194cdd04f4a95a438 100644 GIT binary patch delta 15 WcmZo-xyd|1g>n8w)vS#>ychv24+V<= delta 54 zcmcb~+{7|Lh4I8h)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT?12H149KvODjW5Z37_K I*yGCx0MKy{Z~y=R diff --git a/credentials/conf/locale/zh_TW/LC_MESSAGES/django.mo b/credentials/conf/locale/zh_TW/LC_MESSAGES/django.mo index e4ffa3375dc3bf66a43b6735be28757e5061d3bc..7def0c34760a7b2e68f36466e1fd3210dd37fdbc 100644 GIT binary patch delta 61 zcmcb@v6^FokM3kf28K02T*Sb@;K0nl;0vTT18IIB&B(&QAOxfZfixSCR@qqD$+%gY HNs|cxHJb>w delta 100 zcmZ3@afM@okM15u28K02T*Sb@kjc!z;0vVh0BL?8t;NE?AOxhXfHWJB_T5<7$*2~h t>s*wYSdy8ar|Xhfl4_-3WMF8aYiOWrV5nedW@T!wZ2$zD|1;_^0RXog6k-4X diff --git a/credentials/conf/locale/zh_TW/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/zh_TW/LC_MESSAGES/djangojs.mo index 0ca42ca6084e3c03189f6353336669ba094ec69c..9c17b322041606b2b89c3dae25102a06e3dbe7bc 100644 GIT binary patch delta 15 WcmZo>xy3v|g>k_|)vS#>ycq#3H3g3V delta 54 zcmcb`+{`jTh4JJ>)hx9TUFV|I#FEVXJYAQ>l2j`NBLhPVT|)z1149KvGb>YbZ37_K I*yG0t0MN1zZvX%Q diff --git a/credentials/settings/base.py b/credentials/settings/base.py index 4fae96e73..2c7af9de3 100644 --- a/credentials/settings/base.py +++ b/credentials/settings/base.py @@ -42,6 +42,7 @@ THIRD_PARTY_APPS = [ 'release_util', 'rest_framework', + 'rest_framework_jwt', 'social_django', 'sortedm2m', 'statici18n', @@ -55,7 +56,7 @@ 'rest_framework_swagger', 'hijack', 'compat', - 'xss_utils' + 'xss_utils', ] PROJECT_APPS = [ @@ -72,7 +73,7 @@ INSTALLED_APPS += THIRD_PARTY_APPS INSTALLED_APPS += PROJECT_APPS -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( 'credentials.apps.edx_credentials_extensions.edly_credentials_app.middleware.SettingsOverrideMiddleware', 'edx_django_utils.cache.middleware.RequestCacheMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -80,7 +81,6 @@ 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.sites.middleware.CurrentSiteMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', @@ -103,11 +103,13 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.', - 'NAME': '', - 'USER': '', - 'PASSWORD': '', - 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. + 'NAME': 'credentials', + 'USER': 'credentials001', + 'PASSWORD': 'password', + 'HOST': 'localhost', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. 'PORT': '', # Set to empty string for default. + 'ATOMIC_REQUESTS': False, + 'CONN_MAX_AGE': 60, } } @@ -306,14 +308,13 @@ AUTH_USER_MODEL = 'core.User' AUTHENTICATION_BACKENDS = ( - 'auth_backends.backends.EdXOpenIdConnect', + 'auth_backends.backends.EdXOAuth2', 'django.contrib.auth.backends.ModelBackend', ) ENABLE_AUTO_AUTH = False AUTO_AUTH_USERNAME_PREFIX = 'auto_auth_' -OAUTH2_PROVIDER_URL = None OAUTH_ID_TOKEN_EXPIRATION = 60 SOCIAL_AUTH_STRATEGY = 'auth_backends.strategies.EdxDjangoStrategy' @@ -337,12 +338,15 @@ 'credentials.apps.core.utils.update_full_name', ) -# Set these to the correct values for your OAuth2/OpenID Connect provider (e.g., devstack) -SOCIAL_AUTH_EDX_OIDC_KEY = 'replace-me' -SOCIAL_AUTH_EDX_OIDC_SECRET = 'replace-me' -SOCIAL_AUTH_EDX_OIDC_URL_ROOT = 'replace-me' -SOCIAL_AUTH_EDX_OIDC_LOGOUT_URL = 'replace-me' -SOCIAL_AUTH_EDX_OIDC_ID_TOKEN_DECRYPTION_KEY = SOCIAL_AUTH_EDX_OIDC_SECRET +# Set these to the correct values for your OAuth2 provider (e.g., devstack) +SOCIAL_AUTH_EDX_OAUTH2_KEY = 'credentials-sso-key' +SOCIAL_AUTH_EDX_OAUTH2_SECRET = 'credentials-sso-secret' +SOCIAL_AUTH_EDX_OAUTH2_ISSUER = 'http://127.0.0.1:8000' +SOCIAL_AUTH_EDX_OAUTH2_URL_ROOT = 'http://127.0.0.1:8000' +SOCIAL_AUTH_EDX_OAUTH2_LOGOUT_URL = 'http://127.0.0.1:8000/logout' +BACKEND_SERVICE_EDX_OAUTH2_KEY = 'credentials-backend-service-key' +BACKEND_SERVICE_EDX_OAUTH2_SECRET = 'credentials-backend-service-secret' +BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL = 'http://127.0.0.1:8000/logout' # Request the user's permissions in the ID token EXTRA_SCOPE = ['permissions'] @@ -362,12 +366,22 @@ CREDENTIALS_SERVICE_USER = 'credentials_service_user' JWT_AUTH = { - 'JWT_ISSUERS': [], + 'JWT_ISSUER': [ + { + 'AUDIENCE': 'SET-ME-PLEASE', + 'ISSUER': 'http://127.0.0.1:8000/oauth2', + 'SECRET_KEY': 'SET-ME-PLEASE' + } + ], 'JWT_ALGORITHM': 'HS256', 'JWT_VERIFY_EXPIRATION': True, 'JWT_PAYLOAD_GET_USERNAME_HANDLER': lambda d: d.get('preferred_username'), 'JWT_LEEWAY': 1, 'JWT_DECODE_HANDLER': 'edx_rest_framework_extensions.auth.jwt.decoder.jwt_decode_handler', + 'JWT_PUBLIC_SIGNING_JWK_SET': None, + 'JWT_AUTH_COOKIE_HEADER_PAYLOAD': 'edx-jwt-cookie-header-payload', + 'JWT_AUTH_COOKIE_SIGNATURE': 'edx-jwt-cookie-signature', + 'JWT_AUTH_HEADER_PREFIX': 'JWT', } # Email sending @@ -384,7 +398,6 @@ REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'credentials.apps.api.authentication.JwtAuthentication', - 'credentials.apps.api.authentication.BearerAuthentication', 'rest_framework.authentication.SessionAuthentication', ), 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',), @@ -414,7 +427,7 @@ 'debug_toolbar', ] - MIDDLEWARE_CLASSES += ( + MIDDLEWARE += ( 'debug_toolbar.middleware.DebugToolbarMiddleware', ) @@ -436,6 +449,29 @@ ] # END DJANGO DEBUG TOOLBAR CONFIGURATION +USERNAME_REPLACEMENT_WORKER = "replace with valid username" + +CSRF_COOKIE_SECURE = False +FILE_STORAGE_BACKEND = {} +EXTRA_APPS = [] +SESSION_EXPIRE_AT_BROWSER_CLOSE = False +STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + } +} +EDX_DRF_EXTENSIONS = { + "OAUTH2_USER_INFO_URL": "http://127.0.0.1:8000/oauth2/user_info" +} +API_ROOT = None +MEDIA_STORAGE_BACKEND = { + 'DEFAULT_FILE_STORAGE': 'django.core.files.storage.FileSystemStorage', + 'MEDIA_ROOT': MEDIA_ROOT, + 'MEDIA_URL': MEDIA_URL +} +SOCIAL_AUTH_REDIRECT_IS_HTTPS = False + # Edly user info cookie specific values EDLY_USER_INFO_COOKIE_NAME = 'edly-user-info' EDLY_COOKIE_SECRET_KEY = 'EDLY-COOKIE-SECRET-KEY' diff --git a/credentials/settings/devstack.py b/credentials/settings/devstack.py index 0a603e0da..400be9be7 100644 --- a/credentials/settings/devstack.py +++ b/credentials/settings/devstack.py @@ -45,20 +45,36 @@ STATICFILES_STORAGE = os.environ.get('STATICFILES_STORAGE', 'django.contrib.staticfiles.storage.StaticFilesStorage') STATIC_URL = os.environ.get('STATIC_URL', '/static/') -OAUTH2_PROVIDER_URL = 'http://edx.devstack.lms:18000/oauth2' -SOCIAL_AUTH_EDX_OIDC_KEY = os.environ.get('SOCIAL_AUTH_EDX_OIDC_KEY', 'credentials-key') -SOCIAL_AUTH_EDX_OIDC_SECRET = os.environ.get('SOCIAL_AUTH_EDX_OIDC_SECRET', 'credentials-secret') -SOCIAL_AUTH_EDX_OIDC_ISSUER = os.environ.get('SOCIAL_AUTH_EDX_OIDC_ISSUER', 'http://edx.devstack.lms:18000/oauth2') -SOCIAL_AUTH_EDX_OIDC_URL_ROOT = os.environ.get('SOCIAL_AUTH_EDX_OIDC_URL_ROOT', 'http://edx.devstack.lms:18000/oauth2') -SOCIAL_AUTH_EDX_OIDC_LOGOUT_URL = os.environ.get('SOCIAL_AUTH_EDX_OIDC_LOGOUT_URL', 'http://localhost:18000/logout') -SOCIAL_AUTH_EDX_OIDC_PUBLIC_URL_ROOT = os.environ.get('SOCIAL_AUTH_EDX_OIDC_PUBLIC_URL_ROOT', - 'http://localhost:18000/oauth2') -SOCIAL_AUTH_EDX_OIDC_ID_TOKEN_DECRYPTION_KEY = os.environ.get('SOCIAL_AUTH_EDX_OIDC_ID_TOKEN_DECRYPTION_KEY', - 'credentials-secret') +# OAuth2 variables specific to social-auth/SSO login use case. +SOCIAL_AUTH_EDX_OAUTH2_KEY = os.environ.get('SOCIAL_AUTH_EDX_OAUTH2_KEY', 'credentials-sso-key') +SOCIAL_AUTH_EDX_OAUTH2_SECRET = os.environ.get('SOCIAL_AUTH_EDX_OAUTH2_SECRET', 'credentials-sso-secret') +SOCIAL_AUTH_EDX_OAUTH2_ISSUER = os.environ.get('SOCIAL_AUTH_EDX_OAUTH2_ISSUER', 'http://localhost:18000') +SOCIAL_AUTH_EDX_OAUTH2_URL_ROOT = os.environ.get('SOCIAL_AUTH_EDX_OAUTH2_URL_ROOT', 'http://edx.devstack.lms:18000') +SOCIAL_AUTH_EDX_OAUTH2_LOGOUT_URL = os.environ.get('SOCIAL_AUTH_EDX_OAUTH2_LOGOUT_URL', 'http://localhost:18000/logout') +SOCIAL_AUTH_EDX_OAUTH2_PUBLIC_URL_ROOT = os.environ.get( + 'SOCIAL_AUTH_EDX_OAUTH2_PUBLIC_URL_ROOT', 'http://localhost:18000', +) + +# OAuth2 variables specific to backend service API calls. +BACKEND_SERVICE_EDX_OAUTH2_KEY = os.environ.get('BACKEND_SERVICE_EDX_OAUTH2_KEY', 'credentials-backend-service-key') +BACKEND_SERVICE_EDX_OAUTH2_SECRET = os.environ.get('BACKEND_SERVICE_EDX_OAUTH2_SECRET', 'credentials-backend-service-secret') +BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL = os.environ.get( + 'BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL', 'http://edx.devstack.lms:18000/oauth2', +) + SOCIAL_AUTH_REDIRECT_IS_HTTPS = str2bool(os.environ.get('SOCIAL_AUTH_REDIRECT_IS_HTTPS', False)) -JWT_AUTH['JWT_ISSUERS'].append({ - 'AUDIENCE': 'lms-key', - 'ISSUER': 'http://edx.devstack.lms:18000/oauth2', - 'SECRET_KEY': 'lms-secret', -}) +JWT_AUTH['JWT_ISSUERS'] = [ + { + 'AUDIENCE': 'lms-key', + 'ISSUER': 'http://127.0.0.1:8000/oauth2', + 'SECRET_KEY': 'lms-secret' + } + ] + +##################################################################### +# Lastly, see if the developer has any local overrides. +try: + from .private import * # pylint: disable=import-error +except ImportError: + pass diff --git a/credentials/settings/local.py b/credentials/settings/local.py index fa38003e9..9144510ec 100644 --- a/credentials/settings/local.py +++ b/credentials/settings/local.py @@ -37,12 +37,13 @@ # AUTHENTICATION SOCIAL_AUTH_REDIRECT_IS_HTTPS = False -# Set these to the correct values for your OAuth2/OpenID Connect provider (e.g., devstack) -OAUTH2_PROVIDER_URL = 'http://localhost:18000/oauth2' -SOCIAL_AUTH_EDX_OIDC_KEY = 'credentials-key' -SOCIAL_AUTH_EDX_OIDC_SECRET = 'credentials-secret' -SOCIAL_AUTH_EDX_OIDC_URL_ROOT = OAUTH2_PROVIDER_URL -SOCIAL_AUTH_EDX_OIDC_ID_TOKEN_DECRYPTION_KEY = SOCIAL_AUTH_EDX_OIDC_SECRET +# OAuth2 variables specific to social-auth/SSO login use case. +SOCIAL_AUTH_EDX_OAUTH2_KEY = 'credentials-sso-key' +SOCIAL_AUTH_EDX_OAUTH2_SECRET = 'credentials-sso-secret' + +# OAuth2 variables specific to backend service API calls. +BACKEND_SERVICE_EDX_OAUTH2_KEY = 'credentials-backend-service-key' +BACKEND_SERVICE_EDX_OAUTH2_SECRET = 'credentials-backend-service-secret' ENABLE_AUTO_AUTH = True @@ -65,7 +66,7 @@ # do this after private.py, ensuring this section picks up credential overrides. JWT_AUTH.update({ 'JWT_ALGORITHM': 'HS256', - 'JWT_SECRET_KEY': SOCIAL_AUTH_EDX_OIDC_SECRET, - 'JWT_ISSUER': OAUTH2_PROVIDER_URL, - 'JWT_AUDIENCE': SOCIAL_AUTH_EDX_OIDC_KEY, + 'JWT_SECRET_KEY': SOCIAL_AUTH_EDX_OAUTH2_SECRET, + 'JWT_ISSUER': SOCIAL_AUTH_EDX_OAUTH2_URL_ROOT, + 'JWT_AUDIENCE': SOCIAL_AUTH_EDX_OAUTH2_KEY, }) diff --git a/credentials/settings/private.py.example b/credentials/settings/private.py.example index 6134a6b42..ce382f6d2 100644 --- a/credentials/settings/private.py.example +++ b/credentials/settings/private.py.example @@ -2,10 +2,11 @@ from credentials.settings.base import INSTALLED_APPS INSTALLED_APPS += ['credentials.apps.edx_credentials_extensions'] -SOCIAL_AUTH_EDX_OIDC_KEY = 'credentials-key' -SOCIAL_AUTH_EDX_OIDC_SECRET = 'credentials-secret' -SOCIAL_AUTH_EDX_OIDC_URL_ROOT = 'http://127.0.0.1:8000/oauth2' -SOCIAL_AUTH_EDX_OIDC_ID_TOKEN_DECRYPTION_KEY = SOCIAL_AUTH_EDX_OIDC_SECRET +SOCIAL_AUTH_EDX_OAUTH2_KEY = 'credentials-sso-key' +SOCIAL_AUTH_EDX_OAUTH2_SECRET = 'credentials-sso-secret' +SOCIAL_AUTH_EDX_OAUTH2_URL_ROOT = 'http://127.0.0.1:8000/oauth2' +BACKEND_SERVICE_EDX_OAUTH2_KEY = 'credentials-backend-service-key' +BACKEND_SERVICE_EDX_OAUTH2_SECRET = 'credentials-backend-service-secret' # CATALOG API CONFIGURATION # Specified in seconds. Enable caching by setting this to a value greater than 0. diff --git a/credentials/settings/production.py b/credentials/settings/production.py index dde3118f3..0ad2fcb7b 100644 --- a/credentials/settings/production.py +++ b/credentials/settings/production.py @@ -29,7 +29,7 @@ CONFIG_FILE = get_env_setting('CREDENTIALS_CFG') with open(CONFIG_FILE, encoding='utf-8') as f: - config_from_yaml = yaml.load(f) + config_from_yaml = yaml.safe_load(f) # Remove the items that should be used to update dicts, and apply them separately rather # than pumping them into the local vars. diff --git a/credentials/settings/test.py b/credentials/settings/test.py index 98c97ed3b..c0649bbb9 100644 --- a/credentials/settings/test.py +++ b/credentials/settings/test.py @@ -1,11 +1,10 @@ import os -from path import Path as path +from pathlib import Path as path from credentials.settings.base import * from credentials.settings.utils import get_logger_config - INSTALLED_APPS += [ 'credentials.apps.edx_credentials_extensions', ] @@ -35,14 +34,15 @@ # Local Directories TEST_ROOT = path('test_root') DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' -MEDIA_ROOT = TEST_ROOT / 'uploads' +MEDIA_ROOT = str(TEST_ROOT / 'uploads') MEDIA_URL = '/static/uploads/' -OAUTH2_PROVIDER_URL = 'https://test-provider/oauth2' -SOCIAL_AUTH_EDX_OIDC_URL_ROOT = OAUTH2_PROVIDER_URL +SOCIAL_AUTH_EDX_OAUTH2_URL_ROOT = 'https://test-provider' +BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL = 'https://test-provider/oauth2' JWT_AUTH.update({ - 'JWT_SECRET_KEY': SOCIAL_AUTH_EDX_OIDC_SECRET, - 'JWT_ISSUER': OAUTH2_PROVIDER_URL, - 'JWT_AUDIENCE': SOCIAL_AUTH_EDX_OIDC_KEY, + 'JWT_SECRET_KEY': SOCIAL_AUTH_EDX_OAUTH2_SECRET, + 'JWT_ISSUER': 'https://test-provider/oauth2', + 'JWT_AUDIENCE': SOCIAL_AUTH_EDX_OAUTH2_KEY, }) +STATICFILES_STORAGE = None diff --git a/credentials/static/components/ProgramRecord.jsx b/credentials/static/components/ProgramRecord.jsx index f9e33c471..d82691d81 100644 --- a/credentials/static/components/ProgramRecord.jsx +++ b/credentials/static/components/ProgramRecord.jsx @@ -36,6 +36,8 @@ class ProgramRecord extends React.Component { this.showSendRecordButton = this.props.pathways.length > 0; this.state = { + focusActiveButton: false, + buttonDisabled: false, shareModelOpen: false, sendRecordModalOpen: false, isPublic: true, @@ -48,6 +50,13 @@ class ProgramRecord extends React.Component { }; } + componentDidUpdate() { + if (this.state.focusActiveButton) { + this.activeButton.focus(); + this.state.focusActiveButton = false; + } + } + setActiveButton(button) { this.activeButton = button; } @@ -56,6 +65,7 @@ class ProgramRecord extends React.Component { this.setState({ sendRecordModalOpen: true, shareModelOpen: false, + buttonDisabled: true, }); this.setActiveButton(event.target); trackEvent('edx.bi.credentials.program_record.send_started', { @@ -68,6 +78,7 @@ class ProgramRecord extends React.Component { this.setState({ sendRecordModalOpen: false, shareModelOpen: true, + buttonDisabled: true, }); this.setActiveButton(event.target); trackEvent('edx.bi.credentials.program_record.share_started', { @@ -79,15 +90,17 @@ class ProgramRecord extends React.Component { closeSendRecordModal() { this.setState({ sendRecordModalOpen: false, + buttonDisabled: false, + focusActiveButton: true, }); - this.activeButton.focus(); } closeShareModel() { this.setState({ shareModelOpen: false, + buttonDisabled: false, + focusActiveButton: true, }); - this.activeButton.focus(); } downloadRecord(uuid) { @@ -115,7 +128,7 @@ class ProgramRecord extends React.Component { return decimal; } - return parseInt(decimal * 100, 10).toString() + '%'; + return parseInt(Math.round(decimal * 100), 10).toString() + '%'; } formatGradeData() { @@ -268,6 +281,7 @@ class ProgramRecord extends React.Component { label={gettext('Send Learner Record')} className={['btn-primary']} onClick={this.loadSendRecordModal} + disabled={this.state.buttonDisabled} /> }