You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

personal.js 11KB

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