personal.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /* global OC */
  2. /**
  3. * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
  4. * 2013, Morris Jobke <morris.jobke@gmail.com>
  5. * 2016, Christoph Wurst <christoph@owncloud.com>
  6. * This file is licensed under the Affero General Public License version 3 or later.
  7. * See the COPYING-README file.
  8. */
  9. OC.Settings = OC.Settings || {};
  10. /**
  11. * The callback will be fired as soon as enter is pressed by the
  12. * user or 1 second after the last data entry
  13. *
  14. * @param callback
  15. * @param allowEmptyValue if this is set to true the callback is also called when the value is empty
  16. */
  17. jQuery.fn.keyUpDelayedOrEnter = function (callback, allowEmptyValue) {
  18. var cb = callback;
  19. var that = this;
  20. this.keyup(_.debounce(function (event) {
  21. // enter is already handled in keypress
  22. if (event.keyCode === 13) {
  23. return;
  24. }
  25. if (allowEmptyValue || that.val() !== '') {
  26. cb(event);
  27. }
  28. }, 1000));
  29. this.keypress(function (event) {
  30. if (event.keyCode === 13 && (allowEmptyValue || that.val() !== '')) {
  31. event.preventDefault();
  32. cb(event);
  33. }
  34. });
  35. this.bind('paste', null, function (event) {
  36. if(!event.keyCode){
  37. if (allowEmptyValue || that.val() !== '') {
  38. cb(event);
  39. }
  40. }
  41. });
  42. };
  43. /**
  44. * Post the email address change to the server.
  45. */
  46. function changeEmailAddress () {
  47. var emailInfo = $('#email');
  48. if (emailInfo.val() === emailInfo.defaultValue) {
  49. return;
  50. }
  51. emailInfo.defaultValue = emailInfo.val();
  52. OC.msg.startSaving('#lostpassword .msg');
  53. var post = $("#lostpassword").serializeArray();
  54. $.ajax({
  55. type: 'PUT',
  56. url: OC.generateUrl('/settings/users/{id}/mailAddress', {id: OC.currentUser}),
  57. data: {
  58. mailAddress: post[0].value
  59. }
  60. }).done(function(result){
  61. // I know the following 4 lines look weird, but that is how it works
  62. // in jQuery - for success the first parameter is the result
  63. // for failure the first parameter is the result object
  64. OC.msg.finishedSaving('#lostpassword .msg', result);
  65. }).fail(function(result){
  66. OC.msg.finishedError('#lostpassword .msg', result.responseJSON.message);
  67. });
  68. }
  69. /**
  70. * Post the display name change to the server.
  71. */
  72. function changeDisplayName () {
  73. if ($('#displayName').val() !== '') {
  74. OC.msg.startSaving('#displaynameform .msg');
  75. // Serialize the data
  76. var post = $("#displaynameform").serialize();
  77. // Ajax foo
  78. $.post(OC.generateUrl('/settings/users/{id}/displayName', {id: OC.currentUser}), post, function (data) {
  79. if (data.status === "success") {
  80. $('#oldDisplayName').val($('#displayName').val());
  81. // update displayName on the top right expand button
  82. $('#expandDisplayName').text($('#displayName').val());
  83. // update avatar if avatar is available
  84. if(!$('#removeavatar').hasClass('hidden')) {
  85. updateAvatar();
  86. }
  87. }
  88. else {
  89. $('#newdisplayname').val(data.data.displayName);
  90. }
  91. OC.msg.finishedSaving('#displaynameform .msg', data);
  92. });
  93. }
  94. }
  95. function updateAvatar (hidedefault) {
  96. var $headerdiv = $('#header .avatardiv');
  97. var $displaydiv = $('#displayavatar .avatardiv');
  98. //Bump avatar avatarversion
  99. oc_userconfig.avatar.version = -(Math.floor(Math.random() * 1000));
  100. if (hidedefault) {
  101. $headerdiv.hide();
  102. $('#header .avatardiv').removeClass('avatardiv-shown');
  103. } else {
  104. $headerdiv.css({'background-color': ''});
  105. $headerdiv.avatar(OC.currentUser, 32, true);
  106. $('#header .avatardiv').addClass('avatardiv-shown');
  107. }
  108. $displaydiv.css({'background-color': ''});
  109. $displaydiv.avatar(OC.currentUser, 145, true, null, function() {
  110. $displaydiv.removeClass('loading');
  111. $('#displayavatar img').show();
  112. });
  113. $.get(OC.generateUrl(
  114. '/avatar/{user}/{size}',
  115. {
  116. user: OC.currentUser,
  117. size: 1
  118. }
  119. ), function (result) {
  120. if (typeof(result) === 'string') {
  121. // Show the delete button when the avatar is custom
  122. $('#removeavatar').removeClass('hidden').addClass('inlineblock');
  123. }
  124. });
  125. }
  126. function showAvatarCropper () {
  127. var $cropper = $('#cropper');
  128. var $cropperImage = $('<img/>');
  129. $cropperImage.css('opacity', 0); // prevent showing the unresized image
  130. $cropper.children('.inner-container').prepend($cropperImage);
  131. $cropperImage.attr('src',
  132. OC.generateUrl('/avatar/tmp') + '?requesttoken=' + encodeURIComponent(oc_requesttoken) + '#' + Math.floor(Math.random() * 1000));
  133. $cropperImage.load(function () {
  134. var img = $cropperImage.get()[0];
  135. var selectSize = Math.min(img.width, img.height);
  136. var offsetX = (img.width - selectSize) / 2;
  137. var offsetY = (img.height - selectSize) / 2;
  138. $cropperImage.Jcrop({
  139. onChange: saveCoords,
  140. onSelect: saveCoords,
  141. aspectRatio: 1,
  142. boxHeight: Math.min(500, $('#app-content').height() -100),
  143. boxWidth: Math.min(500, $('#app-content').width()),
  144. setSelect: [offsetX, offsetY, selectSize, selectSize]
  145. }, function() {
  146. $cropper.show();
  147. });
  148. });
  149. }
  150. function sendCropData () {
  151. cleanCropper();
  152. var cropperData = $('#cropper').data();
  153. var data = {
  154. x: cropperData.x,
  155. y: cropperData.y,
  156. w: cropperData.w,
  157. h: cropperData.h
  158. };
  159. $.post(OC.generateUrl('/avatar/cropped'), {crop: data}, avatarResponseHandler);
  160. }
  161. function saveCoords (c) {
  162. $('#cropper').data(c);
  163. }
  164. function cleanCropper () {
  165. var $cropper = $('#cropper');
  166. $('#displayavatar').show();
  167. $cropper.hide();
  168. $('.jcrop-holder').remove();
  169. $('#cropper img').removeData('Jcrop').removeAttr('style').removeAttr('src');
  170. $('#cropper img').remove();
  171. }
  172. function avatarResponseHandler (data) {
  173. if (typeof data === 'string') {
  174. data = JSON.parse(data);
  175. }
  176. var $warning = $('#avatarform .warning');
  177. $warning.hide();
  178. if (data.status === "success") {
  179. updateAvatar();
  180. } else if (data.data === "notsquare") {
  181. showAvatarCropper();
  182. } else {
  183. $warning.show();
  184. $warning.text(data.data.message);
  185. }
  186. }
  187. $(document).ready(function () {
  188. if($('#pass2').length) {
  189. $('#pass2').showPassword().keyup();
  190. }
  191. var removeloader = function () {
  192. setTimeout(function(){
  193. if ($('.password-state').length > 0) {
  194. $('.password-state').remove();
  195. }
  196. }, 5000)
  197. };
  198. $("#passwordbutton").click(function () {
  199. var isIE8or9 = $('html').hasClass('lte9');
  200. // FIXME - TODO - once support for IE8 and IE9 is dropped
  201. // for IE8 and IE9 this will check additionally if the typed in password
  202. // is different from the placeholder, because in IE8/9 the placeholder
  203. // is simply set as the value to look like a placeholder
  204. if ($('#pass1').val() !== '' && $('#pass2').val() !== ''
  205. && !(isIE8or9 && $('#pass2').val() === $('#pass2').attr('placeholder'))) {
  206. // Serialize the data
  207. var post = $("#passwordform").serialize();
  208. $('#passwordchanged').hide();
  209. $('#passworderror').hide();
  210. $("#passwordbutton").attr('disabled', 'disabled');
  211. $("#passwordbutton").after("<span class='password-loading icon icon-loading-small-dark password-state'></span>");
  212. $(".personal-show-label").hide();
  213. // Ajax foo
  214. $.post(OC.generateUrl('/settings/personal/changepassword'), post, function (data) {
  215. if (data.status === "success") {
  216. $("#passwordbutton").after("<span class='checkmark icon icon-checkmark password-state'></span>");
  217. removeloader();
  218. $(".personal-show-label").show();
  219. $('#pass1').val('');
  220. $('#pass2').val('').change();
  221. }
  222. if (typeof(data.data) !== "undefined") {
  223. OC.msg.finishedSaving('#password-error-msg', data);
  224. } else {
  225. OC.msg.finishedSaving('#password-error-msg',
  226. {
  227. 'status' : 'error',
  228. 'data' : {
  229. 'message' : t('core', 'Unable to change password')
  230. }
  231. }
  232. );
  233. }
  234. $(".password-loading").remove();
  235. $("#passwordbutton").removeAttr('disabled');
  236. });
  237. return false;
  238. } else {
  239. OC.msg.finishedSaving('#password-error-msg',
  240. {
  241. 'status' : 'error',
  242. 'data' : {
  243. 'message' : t('core', 'Unable to change password')
  244. }
  245. }
  246. );
  247. return false;
  248. }
  249. });
  250. var federationSettingsView = new OC.Settings.FederationSettingsView({
  251. el: '#personal-settings'
  252. });
  253. federationSettingsView.render();
  254. $("#languageinput").change(function () {
  255. // Serialize the data
  256. var post = $("#languageinput").serialize();
  257. // Ajax foo
  258. $.ajax(
  259. 'ajax/setlanguage.php',
  260. {
  261. method: 'POST',
  262. data: post
  263. }
  264. ).done(function() {
  265. location.reload();
  266. }).fail(function(jqXHR) {
  267. $('#passworderror').text(jqXHR.responseJSON.message);
  268. });
  269. return false;
  270. });
  271. var uploadparms = {
  272. pasteZone: null,
  273. done: function (e, data) {
  274. var response = data;
  275. if (typeof data.result === 'string') {
  276. response = JSON.parse(data.result);
  277. } else if (data.result && data.result.length) {
  278. // fetch response from iframe
  279. response = JSON.parse(data.result[0].body.innerText);
  280. } else {
  281. response = data.result;
  282. }
  283. avatarResponseHandler(response);
  284. },
  285. submit: function(e, data) {
  286. $('#displayavatar img').hide();
  287. $('#displayavatar .avatardiv').addClass('loading');
  288. data.formData = _.extend(data.formData || {}, {
  289. requesttoken: OC.requestToken
  290. });
  291. },
  292. fail: function (e, data){
  293. var msg = data.jqXHR.statusText + ' (' + data.jqXHR.status + ')';
  294. if (!_.isUndefined(data.jqXHR.responseJSON) &&
  295. !_.isUndefined(data.jqXHR.responseJSON.data) &&
  296. !_.isUndefined(data.jqXHR.responseJSON.data.message)
  297. ) {
  298. msg = data.jqXHR.responseJSON.data.message;
  299. }
  300. avatarResponseHandler({
  301. data: {
  302. message: msg
  303. }
  304. });
  305. }
  306. };
  307. $('#uploadavatar').fileupload(uploadparms);
  308. $('#selectavatar').click(function () {
  309. OC.dialogs.filepicker(
  310. t('settings', "Select a profile picture"),
  311. function (path) {
  312. $('#displayavatar img').hide();
  313. $('#displayavatar .avatardiv').addClass('loading');
  314. $.ajax({
  315. type: "POST",
  316. url: OC.generateUrl('/avatar/'),
  317. data: { path: path }
  318. }).done(avatarResponseHandler)
  319. .fail(function(jqXHR) {
  320. var msg = jqXHR.statusText + ' (' + jqXHR.status + ')';
  321. if (!_.isUndefined(jqXHR.responseJSON) &&
  322. !_.isUndefined(jqXHR.responseJSON.data) &&
  323. !_.isUndefined(jqXHR.responseJSON.data.message)
  324. ) {
  325. msg = jqXHR.responseJSON.data.message;
  326. }
  327. avatarResponseHandler({
  328. data: {
  329. message: msg
  330. }
  331. });
  332. });
  333. },
  334. false,
  335. ["image/png", "image/jpeg"]
  336. );
  337. });
  338. $('#removeavatar').click(function () {
  339. $.ajax({
  340. type: 'DELETE',
  341. url: OC.generateUrl('/avatar/'),
  342. success: function () {
  343. updateAvatar(true);
  344. $('#removeavatar').addClass('hidden').removeClass('inlineblock');
  345. }
  346. });
  347. });
  348. $('#abortcropperbutton').click(function () {
  349. $('#displayavatar .avatardiv').removeClass('loading');
  350. $('#displayavatar img').show();
  351. cleanCropper();
  352. });
  353. $('#sendcropperbutton').click(function () {
  354. sendCropData();
  355. });
  356. $('#pass2').strengthify({
  357. zxcvbn: OC.linkTo('core','vendor/zxcvbn/dist/zxcvbn.js'),
  358. titles: [
  359. t('core', 'Very weak password'),
  360. t('core', 'Weak password'),
  361. t('core', 'So-so password'),
  362. t('core', 'Good password'),
  363. t('core', 'Strong password')
  364. ],
  365. drawTitles: true,
  366. });
  367. // does the user have a custom avatar? if he does show #removeavatar
  368. $.get(OC.generateUrl(
  369. '/avatar/{user}/{size}',
  370. {
  371. user: OC.currentUser,
  372. size: 1
  373. }
  374. ), function (result) {
  375. if (typeof(result) === 'string') {
  376. // Show the delete button when the avatar is custom
  377. $('#removeavatar').removeClass('hidden').addClass('inlineblock');
  378. }
  379. });
  380. // Load the big avatar
  381. if (oc_config.enable_avatars) {
  382. $('#avatarform .avatardiv').avatar(OC.currentUser, 145);
  383. }
  384. // Show token views
  385. var collection = new OC.Settings.AuthTokenCollection();
  386. var view = new OC.Settings.AuthTokenView({
  387. collection: collection
  388. });
  389. view.reload();
  390. // 'redirect' to anchor sections
  391. // anchors are lost on redirects (e.g. while solving the 2fa challenge) otherwise
  392. // example: /settings/person?section=devices will result in /settings/person?#devices
  393. if (!window.location.hash) {
  394. var query = OC.parseQueryString(location.search);
  395. if (query && query.section) {
  396. OC.Util.History.replaceState({});
  397. window.location.hash = query.section;
  398. }
  399. }
  400. });
  401. if (!OC.Encryption) {
  402. OC.Encryption = {};
  403. }
  404. OC.Encryption.msg = {
  405. start: function (selector, msg) {
  406. var spinner = '<img src="' + OC.imagePath('core', 'loading-small.gif') + '">';
  407. $(selector)
  408. .html(msg + ' ' + spinner)
  409. .removeClass('success')
  410. .removeClass('error')
  411. .stop(true, true)
  412. .show();
  413. },
  414. finished: function (selector, data) {
  415. if (data.status === "success") {
  416. $(selector).html(data.data.message)
  417. .addClass('success')
  418. .stop(true, true)
  419. .delay(3000);
  420. } else {
  421. $(selector).html(data.data.message).addClass('error');
  422. }
  423. }
  424. };
  425. OC.Settings.updateAvatar = updateAvatar;