Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f438d04c42 | ||
|
|
d798f6e3ef | ||
|
|
c96ca4e875 | ||
|
|
8a71fe39c5 | ||
|
|
e4ec5c38b7 | ||
|
|
b849d4c2ba | ||
|
|
db9c895da8 | ||
|
|
a938c386cf | ||
|
|
06c2ff0904 | ||
|
|
76e832c3a6 | ||
|
|
b7fd79aef0 | ||
|
|
28a9ab7131 | ||
|
|
8e0b1cc056 | ||
|
|
b424b34cab | ||
|
|
45a83da7be | ||
|
|
6e86292b99 | ||
|
|
89fe030fa3 | ||
|
|
c41c1e5293 |
No files matched your search
+6
-6
@@ -11,7 +11,7 @@ allow-deny@1.1.0
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
audit-argument-checks@1.0.7
|
||||
autoupdate@1.6.0
|
||||
babel-compiler@7.5.2
|
||||
babel-compiler@7.5.3
|
||||
babel-runtime@1.5.0
|
||||
base64@1.0.12
|
||||
binary-heap@1.0.11
|
||||
@@ -20,7 +20,7 @@ blaze-tools@1.0.10
|
||||
boilerplate-generator@1.7.0
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
caching-compiler@1.2.1
|
||||
caching-compiler@1.2.2
|
||||
caching-html-compiler@1.1.3
|
||||
callback-hook@1.3.0
|
||||
cfs:access-point@0.1.49
|
||||
@@ -55,9 +55,9 @@ ddp-rate-limiter@1.0.7
|
||||
ddp-server@2.3.1
|
||||
deps@1.0.12
|
||||
diff-sequence@1.1.1
|
||||
dynamic-import@0.5.1
|
||||
dynamic-import@0.5.2
|
||||
easylogic:summernote@0.8.8
|
||||
ecmascript@0.14.2
|
||||
ecmascript@0.14.3
|
||||
ecmascript-runtime@0.7.0
|
||||
ecmascript-runtime-client@0.10.0
|
||||
ecmascript-runtime-server@0.9.0
|
||||
@@ -110,7 +110,7 @@ modern-browsers@0.1.5
|
||||
modules@0.15.0
|
||||
modules-runtime@0.12.0
|
||||
momentjs:moment@2.24.0
|
||||
mongo@1.9.0
|
||||
mongo@1.9.1
|
||||
mongo-decimal@0.1.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.7
|
||||
@@ -186,7 +186,7 @@ useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.9.0
|
||||
webapp@1.9.1
|
||||
webapp-hashing@1.0.9
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-oidc@1.0.10
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v3.88.0",
|
||||
"version": "v3.90.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v3.88.0",
|
||||
"version": "v3.90.0",
|
||||
"description": "Open-Source kanban",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,3 +1,27 @@
|
||||
# v3.90 2020-04-06 Wekan release
|
||||
|
||||
This release makes the following updates:
|
||||
|
||||
- [Update dependencies](https://github.com/wekan/wekan/commit/d798f6e3ef09595ce4f1d1fbc053eec70fc91fb9).
|
||||
|
||||
and updates the following translations:
|
||||
|
||||
- [Update layouts.js for zh-TW language name](https://github.com/wekan/wekan/pull/2988).
|
||||
Thanks to doggy8088.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.89 2020-04-05 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Create subtasks in parenttask swimlane](https://github.com/wekan/wekan/issues/1953).
|
||||
Thanks to TOSCom-DanielEder.
|
||||
- [When searching cards in a board, also search from Custom Fields](https://github.com/wekan/wekan/pull/2985).
|
||||
Thanks to slvrpdr.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.88 2020-04-02 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v3.88.0"
|
||||
appVersion: "v3.90.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -20,7 +20,13 @@ BlazeComponent.extendComponent({
|
||||
const crtBoard = Boards.findOne(card.boardId);
|
||||
const targetBoard = crtBoard.getDefaultSubtasksBoard();
|
||||
const listId = targetBoard.getDefaultSubtasksListId();
|
||||
const swimlaneId = targetBoard.getDefaultSwimline()._id;
|
||||
|
||||
//Get the full swimlane data for the parent task.
|
||||
const parentSwimlane = Swimlanes.findOne({boardId: crtBoard._id, _id: card.swimlaneId});
|
||||
//find the swimlane of the same name in the target board.
|
||||
const targetSwimlane = Swimlanes.findOne({boardId: targetBoard._id, title: parentSwimlane.title});
|
||||
//If no swimlane with a matching title exists in the target board, fall back to the default swimlane.
|
||||
const swimlaneId = targetSwimlane === undefined ? targetBoard.getDefaultSwimline()._id : targetSwimlane._id;
|
||||
|
||||
if (title) {
|
||||
const _id = Cards.insert({
|
||||
|
||||
@@ -73,6 +73,8 @@ Template.userFormsLayout.helpers({
|
||||
name = 'Igbo';
|
||||
} else if (lang.name === 'oc') {
|
||||
name = 'Occitan';
|
||||
} else if (lang.name === 'zh-TW') {
|
||||
name = '繁體中文(台灣)';
|
||||
}
|
||||
return { tag, name };
|
||||
}).sort(function(a, b) {
|
||||
|
||||
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+7
-7
@@ -64,7 +64,7 @@
|
||||
"activity-unchecked-item": "hat %s in Checkliste %s von %s abgewählt",
|
||||
"activity-checklist-added": "hat eine Checkliste zu %s hinzugefügt",
|
||||
"activity-checklist-removed": "entfernte eine Checkliste von %s",
|
||||
"activity-checklist-completed": "completed checklist %s of %s",
|
||||
"activity-checklist-completed": "Abgeschlossene Checkliste",
|
||||
"activity-checklist-uncompleted": "unvervollständigte die Checkliste %s von %s",
|
||||
"activity-checklist-item-added": "hat ein Checklistenelement zu '%s' in %s hinzugefügt",
|
||||
"activity-checklist-item-removed": "hat ein Checklistenelement von '%s' in %s entfernt",
|
||||
@@ -440,7 +440,7 @@
|
||||
"save": "Speichern",
|
||||
"search": "Suchen",
|
||||
"rules": "Regeln",
|
||||
"search-cards": "Suche nach Karten-/Listentiteln und Beschreibungen auf diesem Board",
|
||||
"search-cards": "Suche nach Karten-/Listentiteln, Beschreibungen und personalisierten Feldern auf diesem Brett ",
|
||||
"search-example": "Suchbegriff",
|
||||
"select-color": "Farbe auswählen",
|
||||
"set-wip-limit-value": "Setzen Sie ein Limit für die maximale Anzahl von Aufgaben in dieser Liste",
|
||||
@@ -761,9 +761,9 @@
|
||||
"editUserPopup-title": "Benutzer ändern",
|
||||
"newUserPopup-title": "Neuer Benutzer",
|
||||
"notifications": "Benachrichtigungen",
|
||||
"view-all": "View All",
|
||||
"filter-by-unread": "Filter by Unread",
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"view-all": "Alle anzeigen",
|
||||
"filter-by-unread": "Nach Ungelesenen filtern",
|
||||
"mark-all-as-read": "Als ungelesen markieren",
|
||||
"allow-rename": "Umbenennen erlauben",
|
||||
"allowRenamePopup-title": "Umbenennen erlauben"
|
||||
}
|
||||
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -440,7 +440,7 @@
|
||||
"save": "Save",
|
||||
"search": "Search",
|
||||
"rules": "Rules",
|
||||
"search-cards": "Search from card/list titles and descriptions on this board",
|
||||
"search-cards": "Search from card/list titles, descriptions and custom fields on this board",
|
||||
"search-example": "Text to search for?",
|
||||
"select-color": "Select Color",
|
||||
"set-wip-limit-value": "Set a limit for the maximum number of tasks in this list",
|
||||
|
||||
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+2
-2
@@ -64,7 +64,7 @@
|
||||
"activity-unchecked-item": "desmarcado %s en lista %s de %s",
|
||||
"activity-checklist-added": "ha añadido una lista de verificación a %s",
|
||||
"activity-checklist-removed": "eliminada una lista de verificación desde %s",
|
||||
"activity-checklist-completed": "completed checklist %s of %s",
|
||||
"activity-checklist-completed": "lista de verificación completada %s de %s",
|
||||
"activity-checklist-uncompleted": "no completado la lista %s de %s",
|
||||
"activity-checklist-item-added": "ha añadido el elemento de la lista de verificación a '%s' en %s",
|
||||
"activity-checklist-item-removed": "eliminado un elemento de la lista de verificación desde '%s' en %s",
|
||||
@@ -440,7 +440,7 @@
|
||||
"save": "Añadir",
|
||||
"search": "Buscar",
|
||||
"rules": "Reglas",
|
||||
"search-cards": "Buscar entre los títulos y las descripciones de las tarjetas en este tablero.",
|
||||
"search-cards": "Buscar entre los títulos, las descripciones de las tarjetas/listas y los campos personalizados en este tablero. ",
|
||||
"search-example": "¿Texto a buscar?",
|
||||
"select-color": "Seleccionar el color",
|
||||
"set-wip-limit-value": "Cambiar el límite para el número máximo de tareas en esta lista.",
|
||||
|
||||
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+16
-16
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"accept": "Accetta",
|
||||
"accept": "Accept",
|
||||
"act-activity-notify": "Notifica attività",
|
||||
"act-addAttachment": "aggiunto allegato __attachment__ alla scheda __card__ della lista __list__ della corsia __swimlane__ nella bacheca __board__",
|
||||
"act-deleteAttachment": "eliminato allegato __attachment__ dalla scheda __card__ della lista __list__ della corsia __swimlane__ nella bacheca __board__",
|
||||
@@ -64,7 +64,7 @@
|
||||
"activity-unchecked-item": "disattivato %s nella checklist %s di %s",
|
||||
"activity-checklist-added": "aggiunta checklist a %s",
|
||||
"activity-checklist-removed": "È stata rimossa una checklist da%s",
|
||||
"activity-checklist-completed": "completed checklist %s of %s",
|
||||
"activity-checklist-completed": "%s di %s checklists completate",
|
||||
"activity-checklist-uncompleted": "La checklist non è stata completata",
|
||||
"activity-checklist-item-added": "Aggiunto l'elemento checklist a '%s' in %s",
|
||||
"activity-checklist-item-removed": "è stato rimosso un elemento della checklist da '%s' in %s",
|
||||
@@ -440,7 +440,7 @@
|
||||
"save": "Salva",
|
||||
"search": "Cerca",
|
||||
"rules": "Regole",
|
||||
"search-cards": "Ricerca per titolo e descrizione scheda/lista su questa bacheca",
|
||||
"search-cards": "Ricerca per titolo, descrizione scheda/lista e campi personalizzati su questa bacheca",
|
||||
"search-example": "Testo da ricercare?",
|
||||
"select-color": "Seleziona Colore",
|
||||
"set-wip-limit-value": "Seleziona un limite per il massimo numero di attività in questa lista",
|
||||
@@ -733,21 +733,21 @@
|
||||
"loading": "Loading, please wait.",
|
||||
"previous_as": "l'ultima volta è stata",
|
||||
"act-a-dueAt": "Scadenza modificata in __timeValue__\nData precedente: __timeOldValue__",
|
||||
"act-a-endAt": "modified ending time to __timeValue__ from (__timeOldValue__)",
|
||||
"act-a-startAt": "modified starting time to __timeValue__ from (__timeOldValue__)",
|
||||
"act-a-receivedAt": "modified received time to __timeValue__ from (__timeOldValue__)",
|
||||
"a-dueAt": "modified due time to be",
|
||||
"a-endAt": "modified ending time to be",
|
||||
"a-startAt": "modified starting time to be",
|
||||
"a-receivedAt": "modified received time to be",
|
||||
"act-a-endAt": "orario finale modificato in __timeValue__ (precedentemente: __timeOldValue__)",
|
||||
"act-a-startAt": "orario iniziale modificato in __timeValue__ (precedentemente: __timeOldValue__)",
|
||||
"act-a-receivedAt": "orario di ricezione modificato in __timeValue__ (precedentemente: __timeOldValue__)",
|
||||
"a-dueAt": "scadenza modificata in",
|
||||
"a-endAt": "orario finale modificato in",
|
||||
"a-startAt": "orario iniziale modificato in",
|
||||
"a-receivedAt": "orario di ricezione modificato in",
|
||||
"almostdue": "la data di scadenza attuale %s si sta avvicinando",
|
||||
"pastdue": "la data di scadenza attuale %s è scaduta",
|
||||
"duenow": "la data di scadenza attuale %s è oggi",
|
||||
"act-newDue": "__list__/__card__ has 1st due reminder [__board__]",
|
||||
"act-withDue": "__list__/__card__ due reminders [__board__]",
|
||||
"act-almostdue": "was reminding the current due (__timeValue__) of __card__ is approaching",
|
||||
"act-pastdue": "was reminding the current due (__timeValue__) of __card__ is past",
|
||||
"act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
|
||||
"act-newDue": "__list__/__card__ ha un 1° sollecito [__board__]",
|
||||
"act-withDue": "sollecito relativo a __list__/__card__ [__board__]",
|
||||
"act-almostdue": "sollecito inviato: la scadenza (__timeValue__) di __card__ è vicina",
|
||||
"act-pastdue": "sollecito inviato: la scadenza (__timeValue__) di __card__ è già passata",
|
||||
"act-duenow": "sollecito inviato: la scadenza (__timeValue__) di __card__ è adesso",
|
||||
"act-atUserComment": "Sei stato menzionato in [__board__] __list__/__card__",
|
||||
"delete-user-confirm-popup": "Sei sicuro di voler cancellare questo profilo? Non sarà possibile ripristinarlo.",
|
||||
"accounts-allowUserDelete": "Permetti agli utenti di cancellare il loro profilo",
|
||||
@@ -762,7 +762,7 @@
|
||||
"newUserPopup-title": "Nuovo utente",
|
||||
"notifications": "Notifiche",
|
||||
"view-all": "Mostra Tutto",
|
||||
"filter-by-unread": "Filtra per non Letto",
|
||||
"filter-by-unread": "Filtra per non letto",
|
||||
"mark-all-as-read": "Segna come letto",
|
||||
"allow-rename": "Consenti Rinomina",
|
||||
"allowRenamePopup-title": "Consenti Rinomina"
|
||||
|
||||
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+8
-8
@@ -64,7 +64,7 @@
|
||||
"activity-unchecked-item": "%s uitgevinkt in checklist %s van %s",
|
||||
"activity-checklist-added": "checklist toegevoegd aan %s",
|
||||
"activity-checklist-removed": "checklist verwijderd van %s",
|
||||
"activity-checklist-completed": "completed checklist %s of %s",
|
||||
"activity-checklist-completed": "checklist %s afgewerkt van %s",
|
||||
"activity-checklist-uncompleted": "checklist %s onafgewerkt van %s",
|
||||
"activity-checklist-item-added": "checklist item toegevoegd aan '%s' in '%s'",
|
||||
"activity-checklist-item-removed": "checklist item verwijderd van '%s' in %s",
|
||||
@@ -440,7 +440,7 @@
|
||||
"save": "Opslaan",
|
||||
"search": "Zoek",
|
||||
"rules": "Regels",
|
||||
"search-cards": "Zoek op kaarten/lijsten en omschrijvingen op dit bord",
|
||||
"search-cards": "Zoek in kaart/lijst, titels, beschrijvingen en maatwerkvelden op dit bord",
|
||||
"search-example": "Tekst om naar te zoeken?",
|
||||
"select-color": "Selecteer kleur",
|
||||
"set-wip-limit-value": "Zet een limiet voor het maximaal aantal taken in deze lijst",
|
||||
@@ -760,10 +760,10 @@
|
||||
"new": "Nieuw",
|
||||
"editUserPopup-title": "Wijzig gebruiker",
|
||||
"newUserPopup-title": "Nieuwe gebruiker",
|
||||
"notifications": "Notifications",
|
||||
"view-all": "View All",
|
||||
"filter-by-unread": "Filter by Unread",
|
||||
"mark-all-as-read": "Mark all as read",
|
||||
"allow-rename": "Allow Rename",
|
||||
"allowRenamePopup-title": "Allow Rename"
|
||||
"notifications": "Meldingen",
|
||||
"view-all": "Bekijk alles",
|
||||
"filter-by-unread": "Filter op Ongelezen",
|
||||
"mark-all-as-read": "Markeer alles als gelezen",
|
||||
"allow-rename": "Sta Hernoemen toe",
|
||||
"allowRenamePopup-title": "Sta Hernoemen toe"
|
||||
}
|
||||
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+768
-768
File diff suppressed because it is too large.
Load diff
+5
-1
@@ -806,7 +806,11 @@ Boards.helpers({
|
||||
if (term) {
|
||||
const regex = new RegExp(term, 'i');
|
||||
|
||||
query.$or = [{ title: regex }, { description: regex }];
|
||||
query.$or = [
|
||||
{ title: regex },
|
||||
{ description: regex },
|
||||
{ customFields: { $elemMatch: { value: regex } } },
|
||||
];
|
||||
}
|
||||
|
||||
return Cards.find(query, projection);
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v3.88.0",
|
||||
"version": "v3.90.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v3.88.0",
|
||||
"version": "v3.90.0",
|
||||
"description": "Open-Source kanban",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1524,7 +1524,7 @@ var n=this.pipeline.run(e.tokenizer(t)),r=new e.Vector,i=[],o=this._fields.reduc
|
||||
<ul class="toc-list-h1">
|
||||
|
||||
<li>
|
||||
<a href="#wekan-rest-api" class="toc-h1 toc-link" data-title="Wekan REST API v3.88">Wekan REST API v3.88</a>
|
||||
<a href="#wekan-rest-api" class="toc-h1 toc-link" data-title="Wekan REST API v3.90">Wekan REST API v3.90</a>
|
||||
|
||||
</li>
|
||||
|
||||
@@ -2017,7 +2017,7 @@ var n=this.pipeline.run(e.tokenizer(t)),r=new e.Vector,i=[],o=this._fields.reduc
|
||||
<div class="page-wrapper">
|
||||
<div class="dark-box"></div>
|
||||
<div class="content">
|
||||
<h1 id="wekan-rest-api">Wekan REST API v3.88</h1>
|
||||
<h1 id="wekan-rest-api">Wekan REST API v3.90</h1>
|
||||
<blockquote>
|
||||
<p>Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.</p>
|
||||
</blockquote>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: Wekan REST API
|
||||
version: v3.88
|
||||
version: v3.90
|
||||
description: |
|
||||
The REST API allows you to control and extend Wekan with ease.
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ const pkgdef :Spk.PackageDefinition = (
|
||||
appTitle = (defaultText = "Wekan"),
|
||||
# The name of the app as it is displayed to the user.
|
||||
|
||||
appVersion = 388,
|
||||
appVersion = 390,
|
||||
# Increment this for every release.
|
||||
|
||||
appMarketingVersion = (defaultText = "3.88.0~2020-04-02"),
|
||||
appMarketingVersion = (defaultText = "3.90.0~2020-04-06"),
|
||||
# Human-readable presentation of the app version.
|
||||
|
||||
minUpgradableAppVersion = 0,
|
||||
|
||||
Reference in new issue
Block a user