mirror of
https://github.com/vale981/Vulcan
synced 2025-03-07 02:21:43 -05:00
trackEvent > Events.track; add files to telescope:core
This commit is contained in:
parent
0fae65e718
commit
93a3861265
24 changed files with 201 additions and 192 deletions
|
@ -12,7 +12,7 @@ var editComment = function(instance) {
|
|||
}
|
||||
});
|
||||
|
||||
trackEvent("edit comment", {'postId': comment.postId, 'commentId': comment._id});
|
||||
Events.track("edit comment", {'postId': comment.postId, 'commentId': comment._id});
|
||||
Router.go('post_page_comment', {_id: comment.postId, commentId: comment._id});
|
||||
};
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ var submitComment = function(instance) {
|
|||
console.log(error);
|
||||
Messages.flash(error.reason, "error");
|
||||
}else{
|
||||
trackEvent("newComment", newComment);
|
||||
Events.track("newComment", newComment);
|
||||
$commentForm.val('');
|
||||
}
|
||||
});
|
||||
|
|
|
@ -112,7 +112,7 @@ var handleVoteClick = function (meteorMethodName, eventName, e, instance) {
|
|||
Messages.flash(i18n.t('please_log_in_first'), 'info');
|
||||
} else {
|
||||
Meteor.call(meteorMethodName, this, function(error, result){
|
||||
trackEvent(eventName, {
|
||||
Events.track(eventName, {
|
||||
'commentId': instance.data._id,
|
||||
'postId': instance.data.post,
|
||||
'authorId': instance.data.userId
|
||||
|
|
|
@ -15,7 +15,7 @@ Template.postUpvote.events({
|
|||
Messages.flash(i18n.t("please_log_in_first"), "info");
|
||||
}
|
||||
Meteor.call('upvotePost', post, function(error, result){
|
||||
trackEvent("post upvoted", {'_id': post._id});
|
||||
Events.track("post upvoted", {'_id': post._id});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ AutoForm.hooks({
|
|||
},
|
||||
|
||||
onSuccess: function(operation, post) {
|
||||
trackEvent("edit post", {'postId': post._id});
|
||||
Events.track("edit post", {'postId': post._id});
|
||||
Router.go('post_page', {_id: post._id});
|
||||
},
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ AutoForm.hooks({
|
|||
|
||||
onSuccess: function(operation, post) {
|
||||
this.template.$('button[type=submit]').removeClass('loading');
|
||||
trackEvent("new post", {'postId': post._id});
|
||||
Events.track("new post", {'postId': post._id});
|
||||
Router.go('post_page', {_id: post._id});
|
||||
if (post.status === Posts.config.STATUS_PENDING) {
|
||||
Messages.flash(i18n.t('thanks_your_post_is_awaiting_approval'), 'success');
|
||||
|
|
|
@ -31,7 +31,7 @@ Template.user_email.events({
|
|||
} else {
|
||||
Messages.flash(i18n.t('thanks_for_signing_up'), "success");
|
||||
// Meteor.call('addCurrentUserToMailChimpList');
|
||||
trackEvent("new sign-up", {'userId': user._id, 'auth':'twitter'});
|
||||
Events.track("new sign-up", {'userId': user._id, 'auth':'twitter'});
|
||||
Router.go('/');
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
// SimpleSchema.debug = true;
|
|
@ -1,4 +0,0 @@
|
|||
clog = function (s) {
|
||||
if(Settings.get('debug', false))
|
||||
console.log(s);
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
trackEvent = function(event, properties){
|
||||
// console.log('trackevent: ', event, properties);
|
||||
var properties= (typeof properties === 'undefined') ? {} : properties;
|
||||
//TODO
|
||||
// add event to an Events collection for logging and buffering purposes
|
||||
if(Meteor.isClient){
|
||||
if(typeof mixpanel !== 'undefined' && typeof mixpanel.track !== 'undefined'){
|
||||
mixpanel.track(event, properties);
|
||||
}
|
||||
if(typeof GoSquared !== 'undefined' && typeof GoSquared.DefaultTracker !== 'undefined'){
|
||||
GoSquared.DefaultTracker.TrackEvent(event, JSON.stringify(properties));
|
||||
}
|
||||
}
|
||||
};
|
304
lib/users.js
304
lib/users.js
|
@ -1,161 +1,161 @@
|
|||
isAdminById = function (userId) {
|
||||
var user = Meteor.users.findOne(userId);
|
||||
return !!(user && isAdmin(user));
|
||||
};
|
||||
isAdmin = function (user) {
|
||||
user = (typeof user === 'undefined') ? Meteor.user() : user;
|
||||
return !!user && !!user.isAdmin;
|
||||
};
|
||||
updateAdmin = function (userId, admin) {
|
||||
Meteor.users.update(userId, {$set: {isAdmin: admin}});
|
||||
};
|
||||
isInvited = function (user) {
|
||||
if(!user || typeof user === 'undefined')
|
||||
return false;
|
||||
return isAdmin(user) || !!user.isInvited;
|
||||
};
|
||||
adminUsers = function(){
|
||||
return Meteor.users.find({isAdmin : true}).fetch();
|
||||
};
|
||||
// isAdminById = function (userId) {
|
||||
// var user = Meteor.users.findOne(userId);
|
||||
// return !!(user && isAdmin(user));
|
||||
// };
|
||||
// isAdmin = function (user) {
|
||||
// user = (typeof user === 'undefined') ? Meteor.user() : user;
|
||||
// return !!user && !!user.isAdmin;
|
||||
// };
|
||||
// updateAdmin = function (userId, admin) {
|
||||
// Meteor.users.update(userId, {$set: {isAdmin: admin}});
|
||||
// };
|
||||
// isInvited = function (user) {
|
||||
// if(!user || typeof user === 'undefined')
|
||||
// return false;
|
||||
// return isAdmin(user) || !!user.isInvited;
|
||||
// };
|
||||
// adminUsers = function(){
|
||||
// return Meteor.users.find({isAdmin : true}).fetch();
|
||||
// };
|
||||
|
||||
adminMongoQuery = {isAdmin: true};
|
||||
notAdminMongoQuery = {isAdmin: false};
|
||||
// adminMongoQuery = {isAdmin: true};
|
||||
// notAdminMongoQuery = {isAdmin: false};
|
||||
|
||||
getUserName = function (user) {
|
||||
try{
|
||||
if (user.username)
|
||||
return user.username;
|
||||
if (user && user.services && user.services.twitter && user.services.twitter.screenName)
|
||||
return user.services.twitter.screenName
|
||||
}
|
||||
catch (error){
|
||||
console.log(error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
getDisplayName = function (user) {
|
||||
return (user.profile && user.profile.username) ? user.profile.username : getUserName(user);
|
||||
};
|
||||
getDisplayNameById = function (userId) {
|
||||
return getDisplayName(Meteor.users.findOne(userId));
|
||||
};
|
||||
getProfileUrl = function (user) {
|
||||
return getProfileUrlBySlugOrId(user.slug);
|
||||
};
|
||||
getProfileUrlBySlugOrId = function (slugOrId) {
|
||||
return Telescope.utils.getRouteUrl('user_profile', {_idOrSlug: slugOrId});
|
||||
};
|
||||
hasPassword = function (user) {
|
||||
return !!user.services.password;
|
||||
};
|
||||
getTwitterName = function (user) {
|
||||
// return twitter name provided by user, or else the one used for twitter login
|
||||
// getUserName = function (user) {
|
||||
// try{
|
||||
// if (user.username)
|
||||
// return user.username;
|
||||
// if (user && user.services && user.services.twitter && user.services.twitter.screenName)
|
||||
// return user.services.twitter.screenName
|
||||
// }
|
||||
// catch (error){
|
||||
// console.log(error);
|
||||
// return null;
|
||||
// }
|
||||
// };
|
||||
// getDisplayName = function (user) {
|
||||
// return (user.profile && user.profile.username) ? user.profile.username : getUserName(user);
|
||||
// };
|
||||
// getDisplayNameById = function (userId) {
|
||||
// return getDisplayName(Meteor.users.findOne(userId));
|
||||
// };
|
||||
// getProfileUrl = function (user) {
|
||||
// return getProfileUrlBySlugOrId(user.slug);
|
||||
// };
|
||||
// getProfileUrlBySlugOrId = function (slugOrId) {
|
||||
// return Telescope.utils.getRouteUrl('user_profile', {_idOrSlug: slugOrId});
|
||||
// };
|
||||
// hasPassword = function (user) {
|
||||
// return !!user.services.password;
|
||||
// };
|
||||
// getTwitterName = function (user) {
|
||||
// // return twitter name provided by user, or else the one used for twitter login
|
||||
|
||||
if(checkNested(user, 'profile', 'twitter')){
|
||||
return user.profile.twitter;
|
||||
}else if(checkNested(user, 'services', 'twitter', 'screenName')){
|
||||
return user.services.twitter.screenName;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
getGitHubName = function (user) {
|
||||
// return twitter name provided by user, or else the one used for twitter login
|
||||
if(checkNested(user, 'profile', 'github')){
|
||||
return user.profile.github;
|
||||
}else if(checkNested(user, 'services', 'github', 'screenName')){ // TODO: double-check this with GitHub login
|
||||
return user.services.github.screenName;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
getTwitterNameById = function (userId) {
|
||||
var user = Meteor.users.findOne(userId);
|
||||
if (user)
|
||||
return getTwitterName(user);
|
||||
};
|
||||
getEmail = function (user) {
|
||||
if(user.profile && user.profile.email){
|
||||
return user.profile.email;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
};
|
||||
getEmailHash = function (user) {
|
||||
// has to be this way to work with Gravatar
|
||||
return Gravatar.hash(getEmail(user));
|
||||
};
|
||||
getAvatarUrl = function (user) {
|
||||
console.warn('FUNCTION getAvatarUrl() IS DEPRECATED -- package bengott:avatar is used instead.')
|
||||
return Avatar.getUrl(user);
|
||||
};
|
||||
getCurrentUserEmail = function () {
|
||||
return Meteor.user() ? getEmail(Meteor.user()) : '';
|
||||
};
|
||||
userProfileComplete = function (user) {
|
||||
for (var i = 0; i < userProfileCompleteChecks.length; i++) {
|
||||
if (!userProfileCompleteChecks[i](user)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
// if(checkNested(user, 'profile', 'twitter')){
|
||||
// return user.profile.twitter;
|
||||
// }else if(checkNested(user, 'services', 'twitter', 'screenName')){
|
||||
// return user.services.twitter.screenName;
|
||||
// }
|
||||
// return null;
|
||||
// };
|
||||
// getGitHubName = function (user) {
|
||||
// // return twitter name provided by user, or else the one used for twitter login
|
||||
// if(checkNested(user, 'profile', 'github')){
|
||||
// return user.profile.github;
|
||||
// }else if(checkNested(user, 'services', 'github', 'screenName')){ // TODO: double-check this with GitHub login
|
||||
// return user.services.github.screenName;
|
||||
// }
|
||||
// return null;
|
||||
// };
|
||||
// getTwitterNameById = function (userId) {
|
||||
// var user = Meteor.users.findOne(userId);
|
||||
// if (user)
|
||||
// return getTwitterName(user);
|
||||
// };
|
||||
// getEmail = function (user) {
|
||||
// if(user.profile && user.profile.email){
|
||||
// return user.profile.email;
|
||||
// }else{
|
||||
// return null;
|
||||
// }
|
||||
// };
|
||||
// getEmailHash = function (user) {
|
||||
// // has to be this way to work with Gravatar
|
||||
// return Gravatar.hash(getEmail(user));
|
||||
// };
|
||||
// getAvatarUrl = function (user) {
|
||||
// console.warn('FUNCTION getAvatarUrl() IS DEPRECATED -- package bengott:avatar is used instead.')
|
||||
// return Avatar.getUrl(user);
|
||||
// };
|
||||
// getCurrentUserEmail = function () {
|
||||
// return Meteor.user() ? getEmail(Meteor.user()) : '';
|
||||
// };
|
||||
// userProfileComplete = function (user) {
|
||||
// for (var i = 0; i < userProfileCompleteChecks.length; i++) {
|
||||
// if (!userProfileCompleteChecks[i](user)) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// };
|
||||
|
||||
findLast = function(user, collection) {
|
||||
return collection.findOne({userId: user._id}, {sort: {createdAt: -1}});
|
||||
};
|
||||
timeSinceLast = function(user, collection) {
|
||||
var now = new Date().getTime();
|
||||
var last = findLast(user, collection);
|
||||
if(!last)
|
||||
return 999; // if this is the user's first post or comment ever, stop here
|
||||
return Math.abs(Math.floor((now-last.createdAt)/1000));
|
||||
};
|
||||
numberOfItemsInPast24Hours = function (user, collection) {
|
||||
var mNow = moment();
|
||||
var items = collection.find({
|
||||
userId: user._id,
|
||||
createdAt: {
|
||||
$gte: mNow.subtract(24, 'hours').toDate()
|
||||
}
|
||||
});
|
||||
return items.count();
|
||||
};
|
||||
getUserSetting = function(setting, defaultValue, user){
|
||||
var user = (typeof user == 'undefined') ? Meteor.user() : user;
|
||||
var defaultValue = (typeof defaultValue == "undefined") ? null: defaultValue;
|
||||
var settingValue = getProperty(user.profile, setting);
|
||||
return (settingValue == null) ? defaultValue : settingValue;
|
||||
};
|
||||
setUserSetting = function (setting, value, userArgument) {
|
||||
// note: for some very weird reason, doesn't work when called from Accounts.onCreateUser
|
||||
// findLast = function(user, collection) {
|
||||
// return collection.findOne({userId: user._id}, {sort: {createdAt: -1}});
|
||||
// };
|
||||
// timeSinceLast = function(user, collection) {
|
||||
// var now = new Date().getTime();
|
||||
// var last = findLast(user, collection);
|
||||
// if(!last)
|
||||
// return 999; // if this is the user's first post or comment ever, stop here
|
||||
// return Math.abs(Math.floor((now-last.createdAt)/1000));
|
||||
// };
|
||||
// numberOfItemsInPast24Hours = function (user, collection) {
|
||||
// var mNow = moment();
|
||||
// var items = collection.find({
|
||||
// userId: user._id,
|
||||
// createdAt: {
|
||||
// $gte: mNow.subtract(24, 'hours').toDate()
|
||||
// }
|
||||
// });
|
||||
// return items.count();
|
||||
// };
|
||||
// getUserSetting = function(setting, defaultValue, user){
|
||||
// var user = (typeof user == 'undefined') ? Meteor.user() : user;
|
||||
// var defaultValue = (typeof defaultValue == "undefined") ? null: defaultValue;
|
||||
// var settingValue = getProperty(user.profile, setting);
|
||||
// return (settingValue == null) ? defaultValue : settingValue;
|
||||
// };
|
||||
// setUserSetting = function (setting, value, userArgument) {
|
||||
// // note: for some very weird reason, doesn't work when called from Accounts.onCreateUser
|
||||
|
||||
var user;
|
||||
// var user;
|
||||
|
||||
if(Meteor.isClient){
|
||||
user = Meteor.user(); // on client, default to current user
|
||||
}else if (Meteor.isServer){
|
||||
user = userArgument; // on server, use argument
|
||||
}
|
||||
// if(Meteor.isClient){
|
||||
// user = Meteor.user(); // on client, default to current user
|
||||
// }else if (Meteor.isServer){
|
||||
// user = userArgument; // on server, use argument
|
||||
// }
|
||||
|
||||
if(!user)
|
||||
throw new Meteor.Error(500, 'User not defined');
|
||||
// if(!user)
|
||||
// throw new Meteor.Error(500, 'User not defined');
|
||||
|
||||
console.log('Setting user setting "'+setting+'" to "'+value+'" for '+getUserName(user));
|
||||
var find = {_id: user._id};
|
||||
var field = {};
|
||||
field['profile.'+setting] = value;
|
||||
var options = {$set: field};
|
||||
var result = Meteor.users.update(find, options, {validate: false});
|
||||
};
|
||||
// console.log('Setting user setting "'+setting+'" to "'+value+'" for '+getUserName(user));
|
||||
// var find = {_id: user._id};
|
||||
// var field = {};
|
||||
// field['profile.'+setting] = value;
|
||||
// var options = {$set: field};
|
||||
// var result = Meteor.users.update(find, options, {validate: false});
|
||||
// };
|
||||
|
||||
getProperty = function(object, property){
|
||||
// recursive function to get nested properties
|
||||
var array = property.split('.');
|
||||
if(array.length > 1){
|
||||
var parent = array.shift();
|
||||
// if our property is not at this level, call function again one level deeper if we can go deeper, else return null
|
||||
return (typeof object[parent] == "undefined") ? null : getProperty(object[parent], array.join('.'));
|
||||
}else{
|
||||
// else return property
|
||||
return object[array[0]];
|
||||
}
|
||||
};
|
||||
// getProperty = function(object, property){
|
||||
// // recursive function to get nested properties
|
||||
// var array = property.split('.');
|
||||
// if(array.length > 1){
|
||||
// var parent = array.shift();
|
||||
// // if our property is not at this level, call function again one level deeper if we can go deeper, else return null
|
||||
// return (typeof object[parent] == "undefined") ? null : getProperty(object[parent], array.join('.'));
|
||||
// }else{
|
||||
// // else return property
|
||||
// return object[array[0]];
|
||||
// }
|
||||
// };
|
||||
|
|
|
@ -214,8 +214,8 @@ Meteor.startup( function (){
|
|||
// After Hooks
|
||||
|
||||
// Router.onAfterAction(filters.resetScroll, {except:['posts_top', 'posts_new', 'posts_best', 'posts_pending', 'posts_category', 'all-users']});
|
||||
Router.onAfterAction(analyticsInit); // will only run once thanks to _.once()
|
||||
Router.onAfterAction(analyticsRequest); // log this request with mixpanel, etc
|
||||
Router.onAfterAction(Events.analyticsInit); // will only run once thanks to _.once()
|
||||
Router.onAfterAction(Events.analyticsRequest); // log this request with mixpanel, etc
|
||||
Router.onAfterAction(filters.setSEOProperties);
|
||||
Router.onAfterAction(filters.setCanonical, {only: ["post_page", "post_page_with_slug"]});
|
||||
|
||||
|
|
|
@ -24,18 +24,22 @@ Package.onUse(function(api) {
|
|||
'meteorhacks:subs-manager@1.3.0',
|
||||
'telescope:events@0.1.0',
|
||||
'telescope:settings@0.1.0',
|
||||
'percolatestudio:synced-cron@1.1.0'
|
||||
'telescope:events@0.1.0',
|
||||
'percolatestudio:synced-cron@1.1.0',
|
||||
'useraccounts:unstyled@1.8.1'
|
||||
]);
|
||||
|
||||
api.addFiles([
|
||||
'lib/router/config.js',
|
||||
'lib/router/filters.js',
|
||||
'lib/router/admin.js',
|
||||
'lib/router/server.js'
|
||||
'lib/router/server.js',
|
||||
'lib/vote.js',
|
||||
'lib/config.js'
|
||||
], ['client', 'server']);
|
||||
|
||||
api.addFiles([
|
||||
// 'lib/client/templates/page.html',
|
||||
'lib/client/handlebars.js',
|
||||
], 'client');
|
||||
|
||||
api.addFiles([
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
analyticsInit = _.once(function() {
|
||||
Events.analyticsInit = _.once(function() {
|
||||
|
||||
// Mixpanel
|
||||
if (mixpanelId=Settings.get("mixpanelId")){
|
||||
|
@ -71,7 +71,7 @@ analyticsInit = _.once(function() {
|
|||
|
||||
});
|
||||
|
||||
analyticsRequest = function() {
|
||||
Events.analyticsRequest = function() {
|
||||
|
||||
// Google Analytics
|
||||
if (typeof window.ga !== 'undefined'){
|
|
@ -41,4 +41,19 @@ if (Meteor.isServer) {
|
|||
Events.insert(event);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Events.track = function(event, properties){
|
||||
// console.log('trackevent: ', event, properties);
|
||||
var properties= (typeof properties === 'undefined') ? {} : properties;
|
||||
//TODO
|
||||
// add event to an Events collection for logging and buffering purposes
|
||||
if(Meteor.isClient){
|
||||
if(typeof mixpanel !== 'undefined' && typeof mixpanel.track !== 'undefined'){
|
||||
mixpanel.track(event, properties);
|
||||
}
|
||||
if(typeof GoSquared !== 'undefined' && typeof GoSquared.DefaultTracker !== 'undefined'){
|
||||
GoSquared.DefaultTracker.TrackEvent(event, JSON.stringify(properties));
|
||||
}
|
||||
}
|
||||
};
|
|
@ -19,6 +19,10 @@ Package.onUse(function(api) {
|
|||
'lib/events.js'
|
||||
], ['client', 'server']);
|
||||
|
||||
api.addFiles([
|
||||
'lib/client/analytics.js'
|
||||
], ['client']);
|
||||
|
||||
api.export([
|
||||
'Events'
|
||||
]);
|
||||
|
|
|
@ -213,3 +213,8 @@ Telescope.utils.checkNested = function(obj /*, level1, level2, ... levelN*/) {
|
|||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
clog = function (s) {
|
||||
if(Settings.get('debug', false))
|
||||
console.log(s);
|
||||
};
|
|
@ -7,7 +7,7 @@ Template.unsubscribe.created = function(){
|
|||
Session.set('unsubscribedMessage', __('user_not_found'));
|
||||
}
|
||||
});
|
||||
trackEvent('notificationsUnsubcribe', {hash: hash});
|
||||
Events.track('notificationsUnsubcribe', {hash: hash});
|
||||
};
|
||||
|
||||
Template.unsubscribe.helpers({
|
||||
|
|
|
@ -26,7 +26,7 @@ Template.postSubscribe.events({
|
|||
|
||||
Meteor.call('subscribePost', post._id, function(error, result) {
|
||||
if (result)
|
||||
trackEvent("post subscribed", {'_id': post._id});
|
||||
Events.track("post subscribed", {'_id': post._id});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -41,7 +41,7 @@ Template.postSubscribe.events({
|
|||
|
||||
Meteor.call('unsubscribePost', post._id, function(error, result) {
|
||||
if (result)
|
||||
trackEvent("post unsubscribed", {'_id': post._id});
|
||||
Events.track("post unsubscribed", {'_id': post._id});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ Users.getUserName = function (user) {
|
|||
};
|
||||
|
||||
Users.getDisplayName = function (user) {
|
||||
return (user.profile && user.profile.username) ? user.profile.username : getUserName(user);
|
||||
return (user.profile && user.profile.username) ? user.profile.username : Users.getUserName(user);
|
||||
};
|
||||
|
||||
Users.getDisplayNameById = function (userId) {
|
||||
|
@ -166,7 +166,7 @@ Users.setUserSetting = function (setting, value, userArgument) {
|
|||
if(!user)
|
||||
throw new Meteor.Error(500, 'User not defined');
|
||||
|
||||
console.log('Setting user setting "'+setting+'" to "'+value+'" for '+getUserName(user));
|
||||
console.log('Setting user setting "' + setting + '" to "' + value + '" for ' + Users.getUserName(user));
|
||||
var find = {_id: user._id};
|
||||
var field = {};
|
||||
field['profile.'+setting] = value;
|
||||
|
|
|
@ -50,7 +50,7 @@ Accounts.onCreateUser(function(options, user){
|
|||
|
||||
// ------------------------------ Analytics ------------------------------ //
|
||||
|
||||
trackEvent('new user', {username: user.username, email: user.profile.email});
|
||||
Events.track('new user', {username: user.username, email: user.profile.email});
|
||||
|
||||
return user;
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue