Skip to content

Commit

Permalink
Merge branch 'learningequality:release-v0.16.x' into scroll-bookmark
Browse files Browse the repository at this point in the history
  • Loading branch information
nikkuAg authored Oct 24, 2023
2 parents 3e7f1ad + 7fb25aa commit 49e84c9
Show file tree
Hide file tree
Showing 136 changed files with 3,337 additions and 1,902 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_whl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
apt-get -y -qq update
apt-get install -y gettext sudo
- name: Use Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '16.x'
- name: Install Yarn
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/check_licenses.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '16.x'
- name: Cache Node.js modules
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
with:
python-version: '3.11'
- name: Use Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '16.x'
- name: Get yarn cache directory path
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/yarn.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '16.x'
- name: Get yarn cache directory path
Expand Down
24 changes: 14 additions & 10 deletions docker/base.dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ubuntu:bionic
FROM ubuntu:jammy

ENV NODE_VERSION=16.20.0

Expand All @@ -11,29 +11,33 @@ RUN apt-get update && \
git \
git-lfs \
psmisc \
python2.7 \
python-pip \
python-sphinx
python3 \
python3-pip \
python3-sphinx

# add yarn ppa
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

# install nodejs and yarn
RUN apt-get update && \
curl -sSO https://deb.nodesource.com/node_16.x/pool/main/n/nodejs/nodejs_$NODE_VERSION-1nodesource1_amd64.deb && \
dpkg -i ./nodejs_$NODE_VERSION-1nodesource1_amd64.deb && \
rm nodejs_$NODE_VERSION-1nodesource1_amd64.deb && \
ARCH=$(dpkg --print-architecture) && \
curl -sSO https://deb.nodesource.com/node_16.x/pool/main/n/nodejs/nodejs_$NODE_VERSION-1nodesource1_$ARCH.deb && \
dpkg -i ./nodejs_$NODE_VERSION-1nodesource1_$ARCH.deb && \
rm nodejs_$NODE_VERSION-1nodesource1_$ARCH.deb && \
apt-get install yarn

RUN git lfs install

# Check if symbolic links exist before creating them
RUN if [ ! -f /usr/bin/python ]; then ln -s /usr/bin/python3 /usr/bin/python; fi \
&& if [ ! -f /usr/bin/pip ]; then ln -s /usr/bin/pip3 /usr/bin/pip; fi

# copy Kolibri source code into image
COPY . /kolibri

# do the time-consuming base install commands
RUN cd /kolibri \
&& pip install -r requirements/dev.txt \
&& pip install -r requirements/build.txt \
&& pip install -r requirements/test.txt \
&& pip3 install -r requirements/dev.txt \
&& pip3 install -r requirements/test.txt \
&& yarn install --network-timeout 100000
1 change: 0 additions & 1 deletion docker/demoserver.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ FROM learningequality/kolibribase

ENV KOLIBRI_RUN_MODE=demoserver
ENV KOLIBRI_PROVISIONDEVICE_FACILITY="Kolibri Demo"
ENV WHICH_PYTHON=python2

COPY docker/entrypoint.py /docker/entrypoint.py

Expand Down
2 changes: 1 addition & 1 deletion docker/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
# - KOLIBRI_PROVISIONDEVICE_FACILITY if set, provision facility with this name
# - CHANNELS_TO_IMPORT if set, comma separated list of channel IDs to import
DEFAULT_ENVS = {
"WHICH_PYTHON": "python2", # or python3 if you prefer; Kolibri don't care
"WHICH_PYTHON": "python3", # or python3 if you prefer; Kolibri don't care
"KOLIBRI_HOME": "/kolibrihome",
"KOLIBRI_HTTP_PORT": "8080",
"KOLIBRI_LANG": "en",
Expand Down
7 changes: 3 additions & 4 deletions integration_testing/scripts/run_kolibri_app_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ def SERVING(self, port):
self.port = port

def RUN(self):
start_url = (
"http://127.0.0.1:{port}".format(port=self.port)
+ interface.get_initialize_url()
)
start_url = "http://127.0.0.1:{port}".format(
port=self.port
) + interface.get_initialize_url(auth_token="1234")
print("Kolibri running at: {start_url}".format(start_url=start_url))


Expand Down
3 changes: 3 additions & 0 deletions kolibri/core/assets/src/composables/useUserSyncStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const deviceStatus = ref(null);
const deviceStatusSentiment = ref(null);
const hasDownloads = ref(false);
const lastDownloadRemoved = ref(null);
const syncDownloadsInProgress = ref(false);
const timeoutInterval = computed(() => {
return get(status) === SyncStatus.QUEUED ? 1000 : 10000;
});
Expand Down Expand Up @@ -51,6 +52,7 @@ export function pollUserSyncStatusTask() {
lastDownloadRemoved.value = syncData[0].last_download_removed
? new Date(syncData[0].last_download_removed)
: null;
syncDownloadsInProgress.value = syncData[0].sync_downloads_in_progress;
} else {
status.value = SyncStatus.NOT_CONNECTED;
}
Expand Down Expand Up @@ -83,5 +85,6 @@ export default function useUserSyncStatus() {
deviceStatusSentiment,
hasDownloads,
lastDownloadRemoved,
syncDownloadsInProgress,
};
}
2 changes: 2 additions & 0 deletions kolibri/core/assets/src/core-app/apiSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import * as sync from '../views/sync/syncComponentSet';
import PageRoot from '../views/PageRoot';
import NotificationsRoot from '../views/NotificationsRoot';
import useMinimumKolibriVersion from '../composables/useMinimumKolibriVersion';
import useUserSyncStatus from '../composables/useUserSyncStatus';
import useUser from '../composables/useUser';

// webpack optimization
Expand Down Expand Up @@ -232,6 +233,7 @@ export default {
useKShow,
useMinimumKolibriVersion,
useUser,
useUserSyncStatus,
},
},
resources,
Expand Down
6 changes: 6 additions & 0 deletions kolibri/core/assets/src/utils/browserInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,9 @@ export const isMacWebView =
* All web views
*/
export const isEmbeddedWebView = isAndroidWebView || isMacWebView;

// Check for presence of the touch event in DOM or multi-touch capabilities
export const isTouchDevice =
'ontouchstart' in window ||
window.navigator?.maxTouchPoints > 0 ||
window.navigator?.msMaxTouchPoints > 0;
10 changes: 7 additions & 3 deletions kolibri/core/assets/src/views/AppBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<UiToolbar
:title="title"
:removeNavIcon="isAppContext && !windowIsLarge"
:removeNavIcon="isAppContext && isTouchDevice"
type="clear"
textColor="white"
class="app-bar"
Expand All @@ -19,7 +19,7 @@
:removeBrandDivider="true"
>
<template
v-if="windowIsLarge || !isAppContext"
v-if="windowIsLarge || !isAppContext || !isTouchDevice"
#icon
>
<KIconButton
Expand Down Expand Up @@ -104,7 +104,7 @@
<!-- Window size and app context. Changes may need to be made -->
<!-- in parallel in both files for non-breaking updates -->
<div
v-if="!windowIsLarge && !isAppContext"
v-if="!windowIsLarge && (!isAppContext || (isAppContext && !isTouchDevice))"
class="subpage-nav"
>
<slot name="sub-nav"></slot>
Expand All @@ -121,6 +121,7 @@
import UiToolbar from 'kolibri.coreVue.components.UiToolbar';
import KIconButton from 'kolibri-design-system/lib/buttons-and-links/KIconButton';
import themeConfig from 'kolibri.themeConfig';
import { isTouchDevice } from 'kolibri.utils.browserInfo';
import useKResponsiveWindow from 'kolibri.coreVue.composables.useKResponsiveWindow';
import navComponentsMixin from '../mixins/nav-components';
import SkipNavigationLink from './SkipNavigationLink';
Expand Down Expand Up @@ -160,6 +161,9 @@
usernameForDisplay() {
return !hashedValuePattern.test(this.username) ? this.username : this.fullName;
},
isTouchDevice() {
return isTouchDevice;
},
},
created() {
if (this.isLearner) {
Expand Down
9 changes: 8 additions & 1 deletion kolibri/core/assets/src/views/BottomNavigationBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,17 @@
<KIconButton
icon="menu"
:ariaLabel="coreString('menuLabel')"
size="small"
:color="navShown ? $themeTokens.primary : $themeTokens.annotation"
@click="$emit('toggleNav')"
/>
<p :style="{ color: $themeTokens.primary }">{{ coreString('menuLabel') }}</p>
<p
v-if="navShown"
class="nav-menu-label"
:style="{ color: $themeTokens.primary }"
>
{{ coreString('menuLabel') }}
</p>
</span>
</div>

Expand Down
12 changes: 8 additions & 4 deletions kolibri/core/assets/src/views/SideNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
</div>
</div>
<div
v-if="!isAppContext || windowIsLarge"
v-if="!isAppContext || !isTouchDevice || windowIsLarge"
class="side-nav-header"
:style="{
height: topBarHeight + 'px',
Expand Down Expand Up @@ -251,6 +251,7 @@
import Backdrop from 'kolibri.coreVue.components.Backdrop';
import LanguageSwitcherModal from 'kolibri.coreVue.components.LanguageSwitcherModal';
import TotalPoints from 'kolibri.coreVue.components.TotalPoints';
import { isTouchDevice } from 'kolibri.utils.browserInfo';
import navComponentsMixin from '../mixins/nav-components';
import useUser from '../composables/useUser';
import useUserSyncStatus from '../composables/useUserSyncStatus';
Expand Down Expand Up @@ -325,6 +326,9 @@
username: state => state.core.session.username,
fullName: state => state.core.session.full_name,
}),
isTouchDevice() {
return isTouchDevice;
},
width() {
return this.showAppNavView ? '100vw' : `${this.topBarHeight * 4.5}px`;
},
Expand All @@ -337,11 +341,11 @@
// Window size and app context. Changes may need to be made
// in parallel in both files for non-breaking updates
// The expected behavior is:
// In an app context, on small and medium screens,
// show the app Nav
// In an app context, on screens with touch capabilities,
// show the app Nav.
// In browser based contexts, and large screen app view
// use the "non-app" upper navigation bar
return this.isAppContext && !this.windowIsLarge;
return this.isAppContext && this.isTouchDevice;
},
footerMsg() {
return this.$tr('poweredBy', { version: __version });
Expand Down
7 changes: 2 additions & 5 deletions kolibri/core/assets/src/views/sortable/DragContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,17 @@
}
@keyframes bounce-in {
from {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
0% {
transform: scale3d(1.05, 1.05, 1.05);
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
50% {
transform: scale3d(0.98, 0.98, 0.98);
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
to {
100% {
transform: scale3d(1, 1, 1);
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,12 @@
methods: {
makeYearOptions(max, min) {
return range(max, min, -1).map(n => {
// Because of timezone, year could be mismatched when localized in any
// timezone that less than UTC. for ex- 2022 will be shown instead of 2023
const date = new Date();
date.setFullYear(n);
return {
label: this.$formatDate(String(n), { year: 'numeric' }),
label: this.$formatDate(String(date), { year: 'numeric' }),
value: String(n),
};
});
Expand Down
28 changes: 28 additions & 0 deletions kolibri/core/auth/migrations/0024_extend_username_length.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2023-10-17 17:51
from __future__ import unicode_literals

from django.db import migrations
from django.db import models

import kolibri.core.auth.models


class Migration(migrations.Migration):

dependencies = [
("kolibriauth", "0023_change_extra_fields_validator"),
]

operations = [
migrations.AlterField(
model_name="facilityuser",
name="username",
field=models.CharField(
help_text="Required. 254 characters or fewer.",
max_length=254,
validators=[kolibri.core.auth.models.validate_username],
verbose_name="username",
),
),
]
34 changes: 25 additions & 9 deletions kolibri/core/auth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
from .permissions.general import IsAdminForOwnFacility
from .permissions.general import IsOwn
from .permissions.general import IsSelf
from kolibri.core import error_constants
from kolibri.core.auth.constants.demographics import choices as GENDER_CHOICES
from kolibri.core.auth.constants.demographics import DEFERRED
from kolibri.core.auth.constants.demographics import NOT_SPECIFIED
Expand Down Expand Up @@ -344,6 +345,27 @@ def infer_dataset(self, *args, **kwargs):
)


validate_username_allowed_chars = validators.RegexValidator(
r'[\s`~!@#$%^&*()\-+={}\[\]\|\\\/:;"\'<>,\.\?]',
"Enter a valid username. This value can contain only letters, numbers, and underscores.",
code=error_constants.INVALID,
inverse_match=True,
)

validate_username_max_length = validators.MaxLengthValidator(
30, "Required. 30 characters or fewer. Letters and digits only"
)


def validate_username(value):
try:
validators.validate_email(value)
except ValidationError:
# for kolibri backwards compatibility, if the username is not an email:
validate_username_allowed_chars(value)
validate_username_max_length(value)


class KolibriAbstractBaseUser(AbstractBaseUser):
"""
Our custom user type, derived from ``AbstractBaseUser`` as described in the Django docs.
Expand All @@ -360,15 +382,9 @@ class Meta:

username = models.CharField(
"username",
max_length=30,
help_text="Required. 30 characters or fewer. Letters and digits only",
validators=[
validators.RegexValidator(
r'[\s`~!@#$%^&*()\-+={}\[\]\|\\\/:;"\'<>,\.\?]',
"Enter a valid username. This value can contain only letters, numbers, and underscores.",
inverse_match=True,
)
],
max_length=254,
help_text="Required. 254 characters or fewer.",
validators=[validate_username],
)
full_name = models.CharField("full name", max_length=120, blank=True)
date_joined = DateTimeTzField("date joined", default=local_now, editable=False)
Expand Down
Loading

0 comments on commit 49e84c9

Please sign in to comment.