User friends: Fix CRUD, UserRelUserListener.php added

pull/3924/head
Julio Montoya 5 years ago
parent 77c8083101
commit 318bf3ce3b
  1. 10
      assets/vue/router/personalfile.js
  2. 22
      assets/vue/store/modules/crud.js
  3. 2
      assets/vue/utils/fetch.js
  4. 62
      assets/vue/views/personalfile/Create.vue
  5. 72
      assets/vue/views/userreluser/Add.vue
  6. 176
      assets/vue/views/userreluser/List.vue
  7. 1
      src/CoreBundle/Entity/Listener/UserListener.php
  8. 53
      src/CoreBundle/Entity/Listener/UserRelUserListener.php
  9. 5
      src/CoreBundle/Entity/UserRelUser.php

@ -15,11 +15,11 @@ export default {
path: '',
component: () => import('../views/personalfile/List.vue')
},
{
name: 'PersonalFileCreate',
path: 'new',
component: () => import('../views/personalfile/Create.vue')
},
// {
// name: 'PersonalFileCreate',
// path: 'new',
// component: () => import('../views/personalfile/Create.vue')
// },
{
name: 'PersonalFileUploadFile',
path: 'upload',

@ -139,6 +139,23 @@ export default function makeCrudModule({
commit(ACTIONS.TOGGLE_LOADING);
}
},
findAll: ({ commit, state }, params) => {
if (!service) throw new Error('No service specified!');
console.log('crud.js findAll');
//commit(ACTIONS.TOGGLE_LOADING);
return service
.findAll({params})
.then(response => response.json())
.then(retrieved => {
console.log('result of retrieved');
//commit(ACTIONS.TOGGLE_LOADING);
return retrieved['hydra:member'];
})
.catch(e => handleError(commit, e));
},
fetchAll: ({ commit, state }, params) => {
if (!service) throw new Error('No service specified!');
@ -235,16 +252,13 @@ export default function makeCrudModule({
findResourceNode: ({ commit }, params) => {
const id = params['id'];
delete params['id'];
console.log('findResourceNode');
console.log(id);
console.log(params);
console.log('findResourceNode', id);
if (!service) throw new Error('No service specified!');
commit(ACTIONS.TOGGLE_LOADING);
return service
.find(id, params)
//.then(response => service.checkResponse(response))
.then(response => {
if (200 === response.status) {
return response.json();

@ -43,8 +43,6 @@ export default function(id, options = {}) {
const entryPoint = ENTRYPOINT + (ENTRYPOINT.endsWith('/') ? '' : '/');
console.log('entryPoint', entryPoint);
if ('PUT' === options.method) {
const payload = options.body && JSON.parse(options.body);
if (isObject(payload) && payload['@id']) {

@ -1,62 +0,0 @@
<template>
<div>
<Toolbar
:handle-submit="onSendFormData"
:handle-reset="resetForm"
/>
<DocumentsForm
ref="createForm"
:values="item"
:errors="violations"
/>
<Loading :visible="isLoading" />
</div>
</template>
<script>
import { mapActions } from 'vuex';
import { createHelpers } from 'vuex-map-fields';
import DocumentsForm from '../../components/personalfile/Form.vue';
import Loading from '../../components/Loading.vue';
import Toolbar from '../../components/Toolbar.vue';
import CreateMixin from '../../mixins/CreateMixin';
const servicePrefix = 'PersonalFile';
const { mapFields } = createHelpers({
getterType: 'personalfile/getField',
mutationType: 'personalfile/updateField'
});
export default {
name: 'PersonalFileCreate',
servicePrefix,
components: {
Loading,
Toolbar,
DocumentsForm
},
mixins: [CreateMixin],
data() {
return {
item: {},
type: 'folder'
};
},
computed: {
...mapFields(['error', 'isLoading', 'created', 'violations'])
},
created() {
this.item.parentResourceNodeId = this.$route.params.node;
this.item.resourceLinkList = JSON.stringify([{
gid: this.$route.query.gid,
sid: this.$route.query.sid,
cid: this.$route.query.cid,
visibility: 2, // visible by default
}]);
},
methods: {
...mapActions('personalfile', ['createWithFormData', 'reset'])
}
};
</script>

@ -4,8 +4,8 @@
<v-btn
tile
icon
:loading="isLoading"
@click="reloadHandler">
:to="{ name: 'UserRelUserList' }"
>
<v-icon icon="mdi-arrow-left" />
</v-btn>
</template>
@ -17,14 +17,12 @@
<VueMultiselect
placeholder="Add"
:loading="isLoadingSelect"
:options="users"
:multiple="true"
:searchable="true"
:internal-search="false"
@search-change="asyncFind"
@select="addFriend"
limit-text="3"
limit="3"
@ -42,7 +40,6 @@
<script>
import { mapActions, mapGetters } from 'vuex';
import { mapFields } from 'vuex-map-fields';
import ListMixin from '../../mixins/ListMixin';
import Toolbar from '../../components/Toolbar.vue';
import VueMultiselect from 'vue-multiselect'
@ -59,7 +56,6 @@ export default {
Toolbar,
VueMultiselect
},
mixins: [ListMixin],
setup() {
const users = ref([]);
const isLoadingSelect = ref(false);
@ -104,12 +100,6 @@ export default {
},
data() {
return {
selected: [],
isBusy: false,
options: {
sortBy: 'sendDate',
sortDesc: 'asc',
},
selectedItems: [],
// prime vue
itemDialog: false,
@ -119,72 +109,14 @@ export default {
submitted: false,
};
},
mounted() {
this.onUpdateOptions(this.options);
},
computed: {
// From crud.js list function
...mapGetters('resourcenode', {
resourceNode: 'getResourceNode'
}),
...mapGetters({
'isAuthenticated': 'security/isAuthenticated',
'isAdmin': 'security/isAdmin',
'currentUser': 'security/getUser',
}),
...mapGetters('message', {
items: 'list',
}),
//...getters
// From ListMixin
...mapFields('message', {
deletedItem: 'deleted',
error: 'error',
isLoading: 'isLoading',
resetList: 'resetList',
totalItems: 'totalItems',
view: 'view'
}),
},
methods: {
deleteItemButton() {
console.log('deleteItem');
this.deleteItem(this.item);
//this.items = this.items.filter(val => val.iid !== this.item.iid);
this.deleteItemDialog = false;
this.item = {};
this.onUpdateOptions(this.options);
},
onRowSelected(items) {
this.selected = items
},
selectAllRows() {
this.$refs.selectableTable.selectAllRows()
},
clearSelected() {
this.$refs.selectableTable.clearSelected()
},
async deleteSelected() {
this.deleteMultipleAction(this.selected);
this.onRequest({
pagination: this.pagination,
});
},
//...actions,
// From ListMixin
...mapActions('userreluser', {
getPage: 'fetchAll',
create: 'create',
update: 'update',
deleteItem: 'del',
deleteMultipleAction: 'delMultiple'
}),
...mapActions('resourcenode', {
findResourceNode: 'findResourceNode',
}),
}
};
</script>

@ -33,21 +33,55 @@
</template>
</Toolbar>
<div v-if="friendRequests.length">
<div class="text-h4 q-mb-md">
Requests
</div>
<v-card
v-for="request in friendRequests"
>
<q-avatar size="40px">
<img :src="request.user.illustrationUrl + '?w=80&h=80&fit=crop'" />
</q-avatar>
{{ request.user.username }}
<v-btn
tile
icon
@click="addFriend(request)" >
<v-icon icon="mdi-plus" />
</v-btn>
</v-card>
</div>
<v-card
v-for="request in waitingRequests"
>
<q-avatar size="40px">
<img :src="request.friend.illustrationUrl + '?w=80&h=80&fit=crop'" />
</q-avatar>
{{ request.friend.username }}
<v-chip>Waiting</v-chip>
</v-card>
<div class="flex flex-row pt-2">
<div class="w-full">
<div class="text-h4 q-mb-md">Friends</div>
<!-- :loading="isLoading"-->
<DataTable
class="p-datatable-sm"
:value="items"
v-model:selection="selectedItems"
dataKey="id"
v-model:filters="filters"
v-model:filters="friendFilter"
sortBy="sendDate"
sortOrder="asc"
:lazy="true"
:paginator="false"
:totalRecords="totalItems"
:loading="isLoading"
@page="onPage($event)"
@sort="sortingChanged($event)"
paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown"
@ -62,7 +96,7 @@
<template #body="slotProps">
<q-avatar size="40px">
<img :src="slotProps.data.friend.illustrationUrl + '?w=80&h=80&fit=crop'" />
</q-avatar>
</q-avatar>
{{ slotProps.data.friend.username }}
</template>
</Column>
@ -74,28 +108,15 @@
</Column>
<Column :exportable="false">
<template #body="slotProps">
<!-- class="flex flex-row gap-2"-->
<v-icon v-if="slotProps.data.relationType == 3" icon="mdi-check" />
<v-btn
v-if="slotProps.data.relationType == 10"
tile
icon
@click="addFriend(slotProps.data)" >
<v-icon icon="mdi-plus" />
</v-btn>
<v-btn
tile
icon
@click="confirmDeleteItem(slotProps.data)" >
<v-icon icon="mdi-delete" />
</v-btn>
<!-- <v-icon v-if="slotProps.data.relationType == 3" icon="mdi-check" />-->
<v-btn
tile
icon
@click="confirmDeleteItem(slotProps.data)" >
<v-icon icon="mdi-delete" />
</v-btn>
</template>
</Column>
@ -130,7 +151,7 @@
</div>
<template #footer>
<Button label="No" icon="pi pi-times" class="p-button-text" @click="deleteItemDialog = false"/>
<Button label="Yes" icon="pi pi-check" class="p-button-text" @click="deleteItemButton" />
<Button label="Yes" icon="pi pi-check" class="p-button-text" @click="deleteItemButton(item)" />
</template>
</Dialog>
@ -172,29 +193,89 @@ export default {
setup() {
const store = useStore();
const user = store.getters["security/getUser"];
const filters = ref([]);
const isLoadingSelect = ref(false);
const deleteItemDialog = ref(false);
const item = ref([]);
const friendRequests = ref([]);
const waitingRequests = ref([]);
//const friendFilter = ref([]);
const friendRequestFilter = {
friend: user.id,
relationType: 10
};
const waitingFilter = {
user: user.id,
relationType: 10
};
filters.value = {
friend: user.id
const friendFilter = {
user: user.id,
relationType: 3,
};
/*store.dispatch('userreluser/findAll', friendRequestFilter).then(response => {
friendRequests.value = response;
});
store.dispatch('userreluser/findAll', waitingFilter).then(response => {
waitingRequests.value = response;
});
// Handles the table
store.dispatch('userreluser/fetchAll', friendFilter);*/
function addFriend(friend) {
axios.put(friend['@id'], {
// Create new friend connection
axios.post(ENTRYPOINT + 'user_rel_users', {
user: user['@id'],
friend: friend.user['@id'],
relationType: 3,
}).then(response => {
console.log(response);
isLoadingSelect.value = false;
store.dispatch('userreluser/resetList');
store.dispatch('userreluser/fetchAll', filters.value);
// Update other relation from invitation to friend.
axios.put(friend['@id'], {
relationType: 3,
}).then(response => {
console.log(response);
reloadHandler();
}).catch(function (error) {
console.log(error);
});
}).catch(function (error) {
isLoadingSelect.value = false;
console.log(error);
});
}
function reloadHandler() {
store.dispatch('userreluser/resetList');
store.dispatch('userreluser/fetchAll', friendFilter);
store.dispatch('userreluser/findAll', friendRequestFilter).then(response => {
friendRequests.value = response;
});
store.dispatch('userreluser/findAll', waitingFilter).then(response => {
waitingRequests.value = response;
});
}
function deleteItemButton(item) {
store.dispatch('userreluser/del', item);
deleteItemDialog.value = false;
reloadHandler();
}
reloadHandler();
return {
addFriend,
reloadHandler,
deleteItemButton,
item,
friendRequests,
waitingRequests,
friendFilter,
deleteItemDialog
}
},
data() {
@ -206,23 +287,13 @@ export default {
],
pageOptions: [10, 20, 50, this.$i18n.t('All')],
selected: [],
isBusy: false,
selectedItems: [],
// prime vue
itemDialog: false,
deleteItemDialog: false,
deleteMultipleDialog: false,
item: {},
submitted: false,
};
},
mounted() {
console.log('mounted');
this.filters = {
friend: this.currentUser.id
};
this.onUpdateOptions(this.options);
},
computed: {
// From crud.js list function
...mapGetters('resourcenode', {
@ -257,7 +328,10 @@ export default {
this.options.page = event.page + 1;
this.options.sortBy = event.sortField;
this.options.sortDesc = event.sortOrder === -1;
this.filters = {
user: this.currentUser.id,
relationType: 3
};
this.onUpdateOptions(this.options);
},
sortingChanged(event) {
@ -270,11 +344,6 @@ export default {
// ctx.sortBy ==> Field key for sorting by (or null for no sorting)
// ctx.sortDesc ==> true if sorting descending, false otherwise
},
openNew() {
this.item = {};
this.submitted = false;
this.itemDialog = true;
},
hideDialog() {
this.itemDialog = false;
this.submitted = false;
@ -314,12 +383,7 @@ export default {
confirmDeleteMultiple() {
this.deleteMultipleDialog = true;
},
reloadHandler() {
this.onUpdateOptions(this.options);
},
deleteMultipleItems() {
console.log('deleteMultipleItems');
console.log(this.selectedItems);
this.deleteMultipleAction(this.selectedItems);
this.onRequest({
pagination: this.pagination,
@ -328,14 +392,6 @@ export default {
this.selectedItems = null;
//this.onUpdateOptions(this.options);
},
deleteItemButton() {
console.log('deleteItem');
this.deleteItem(this.item);
//this.items = this.items.filter(val => val.iid !== this.item.iid);
this.deleteItemDialog = false;
this.item = {};
this.onUpdateOptions(this.options);
},
onRowSelected(items) {
this.selected = items
},

@ -30,7 +30,6 @@ class UserListener
public function prePersist(User $user, LifecycleEventArgs $args): void
{
//error_log('User listener prePersist');
if ($user) {
$this->userRepository->updateCanonicalFields($user);
$this->userRepository->updatePassword($user);

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Entity\Listener;
use Chamilo\CoreBundle\Entity\ResourceNode;
use Chamilo\CoreBundle\Entity\User;
use Chamilo\CoreBundle\Entity\UserRelUser;
use Chamilo\CoreBundle\Repository\Node\UserRepository;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\Security;
class UserRelUserListener
{
private Security $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function prePersist(UserRelUser $userRelUser, LifecycleEventArgs $args): void
{
$currentUser = $this->security->getUser();
// User cannot be connected to himself
if ($userRelUser->getFriend()->getUsername() === $currentUser->getUserIdentifier()) {
throw new \Exception('Invalid relation UserRelUser');
}
}
public function postRemove(UserRelUser $userRelUser, LifecycleEventArgs $args)
{
// Deletes the other connection
$em = $args->getEntityManager();
$repo = $em->getRepository(UserRelUser::class);
$connection = $repo->findOneBy(
[
'user' => $userRelUser->getFriend(),
'friend' => $userRelUser->getUser(),
'relationType' => $userRelUser->getRelationType(),
]
);
if (null !== $connection) {
$em->remove($connection);
$em->flush();
}
}
}

@ -33,6 +33,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* }
* )
* @ORM\Entity
* @ORM\EntityListeners({"Chamilo\CoreBundle\Entity\Listener\UserRelUserListener"})
*/
#[ApiResource(
collectionOperations: [
@ -51,7 +52,7 @@ use Symfony\Component\Validator\Constraints as Assert;
//'security' => "is_granted('ROLE_ADMIN') or object.user == user",
],
'delete' => [
//'security' => "is_granted('ROLE_ADMIN') or object.user == user",
//'security' => "object.user == user",
],
],
attributes: [
@ -96,7 +97,7 @@ class UserRelUser
* @ORM\Id
* @ORM\GeneratedValue
*/
protected int $id;
protected ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\User", inversedBy="friends")

Loading…
Cancel
Save