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.

421 lines
12 KiB

  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.on('input', _.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. };
  36. function updateAvatar (hidedefault) {
  37. var $headerdiv = $('#header .avatardiv');
  38. var $displaydiv = $('#displayavatar .avatardiv');
  39. //Bump avatar avatarversion
  40. oc_userconfig.avatar.version = -(Math.floor(Math.random() * 1000));
  41. if (hidedefault) {
  42. $headerdiv.hide();
  43. $('#header .avatardiv').removeClass('avatardiv-shown');
  44. } else {
  45. $headerdiv.css({'background-color': ''});
  46. $headerdiv.avatar(OC.currentUser, 32, true);
  47. $('#header .avatardiv').addClass('avatardiv-shown');
  48. }
  49. $displaydiv.css({'background-color': ''});
  50. $displaydiv.avatar(OC.currentUser, 145, true, null, function() {
  51. $displaydiv.removeClass('loading');
  52. $('#displayavatar img').show();
  53. if($('#displayavatar img').length === 0) {
  54. $('#removeavatar').removeClass('inlineblock').addClass('hidden');
  55. } else {
  56. $('#removeavatar').removeClass('hidden').addClass('inlineblock');
  57. }
  58. });
  59. }
  60. function showAvatarCropper () {
  61. var $cropper = $('#cropper');
  62. var $cropperImage = $('<img/>');
  63. $cropperImage.css('opacity', 0); // prevent showing the unresized image
  64. $cropper.children('.inner-container').prepend($cropperImage);
  65. $cropperImage.attr('src',
  66. OC.generateUrl('/avatar/tmp') + '?requesttoken=' + encodeURIComponent(oc_requesttoken) + '#' + Math.floor(Math.random() * 1000));
  67. $cropperImage.load(function () {
  68. var img = $cropperImage.get()[0];
  69. var selectSize = Math.min(img.width, img.height);
  70. var offsetX = (img.width - selectSize) / 2;
  71. var offsetY = (img.height - selectSize) / 2;
  72. $cropperImage.Jcrop({
  73. onChange: saveCoords,
  74. onSelect: saveCoords,
  75. aspectRatio: 1,
  76. boxHeight: Math.min(500, $('#app-content').height() -100),
  77. boxWidth: Math.min(500, $('#app-content').width()),
  78. setSelect: [offsetX, offsetY, selectSize, selectSize]
  79. }, function() {
  80. $cropper.show();
  81. });
  82. });
  83. }
  84. function sendCropData () {
  85. cleanCropper();
  86. var cropperData = $('#cropper').data();
  87. var data = {
  88. x: cropperData.x,
  89. y: cropperData.y,
  90. w: cropperData.w,
  91. h: cropperData.h
  92. };
  93. $.post(OC.generateUrl('/avatar/cropped'), {crop: data}, avatarResponseHandler);
  94. }
  95. function saveCoords (c) {
  96. $('#cropper').data(c);
  97. }
  98. function cleanCropper () {
  99. var $cropper = $('#cropper');
  100. $('#displayavatar').show();
  101. $cropper.hide();
  102. $('.jcrop-holder').remove();
  103. $('#cropper img').removeData('Jcrop').removeAttr('style').removeAttr('src');
  104. $('#cropper img').remove();
  105. }
  106. function avatarResponseHandler (data) {
  107. if (typeof data === 'string') {
  108. data = JSON.parse(data);
  109. }
  110. var $warning = $('#avatarform .warning');
  111. $warning.hide();
  112. if (data.status === "success") {
  113. updateAvatar();
  114. } else if (data.data === "notsquare") {
  115. showAvatarCropper();
  116. } else {
  117. $warning.show();
  118. $warning.text(data.data.message);
  119. }
  120. }
  121. $(document).ready(function () {
  122. if($('#pass2').length) {
  123. $('#pass2').showPassword().keyup();
  124. }
  125. var removeloader = function () {
  126. setTimeout(function(){
  127. if ($('.password-state').length > 0) {
  128. $('.password-state').remove();
  129. }
  130. }, 5000)
  131. };
  132. $("#passwordbutton").click(function () {
  133. var isIE8or9 = $('html').hasClass('lte9');
  134. // FIXME - TODO - once support for IE8 and IE9 is dropped
  135. // for IE8 and IE9 this will check additionally if the typed in password
  136. // is different from the placeholder, because in IE8/9 the placeholder
  137. // is simply set as the value to look like a placeholder
  138. if ($('#pass1').val() !== '' && $('#pass2').val() !== ''
  139. && !(isIE8or9 && $('#pass2').val() === $('#pass2').attr('placeholder'))) {
  140. // Serialize the data
  141. var post = $("#passwordform").serialize();
  142. $('#passwordchanged').hide();
  143. $('#passworderror').hide();
  144. $("#passwordbutton").attr('disabled', 'disabled');
  145. $("#passwordbutton").after("<span class='password-loading icon icon-loading-small-dark password-state'></span>");
  146. $(".personal-show-label").hide();
  147. // Ajax foo
  148. $.post(OC.generateUrl('/settings/personal/changepassword'), post, function (data) {
  149. if (data.status === "success") {
  150. $("#passwordbutton").after("<span class='checkmark icon icon-checkmark password-state'></span>");
  151. removeloader();
  152. $(".personal-show-label").show();
  153. $('#pass1').val('');
  154. $('#pass2').val('').change();
  155. }
  156. if (typeof(data.data) !== "undefined") {
  157. OC.msg.finishedSaving('#password-error-msg', data);
  158. } else {
  159. OC.msg.finishedSaving('#password-error-msg',
  160. {
  161. 'status' : 'error',
  162. 'data' : {
  163. 'message' : t('core', 'Unable to change password')
  164. }
  165. }
  166. );
  167. }
  168. $(".password-loading").remove();
  169. $("#passwordbutton").removeAttr('disabled');
  170. });
  171. return false;
  172. } else {
  173. OC.msg.finishedSaving('#password-error-msg',
  174. {
  175. 'status' : 'error',
  176. 'data' : {
  177. 'message' : t('core', 'Unable to change password')
  178. }
  179. }
  180. );
  181. return false;
  182. }
  183. });
  184. var showVerifyDialog = function(dialog, howToVerify, verificationCode) {
  185. var dialogContent = dialog.children('.verification-dialog-content');
  186. dialogContent.children(".explainVerification").text(howToVerify);
  187. dialogContent.children(".verificationCode").text(verificationCode);
  188. dialog.css('display', 'block');
  189. };
  190. $(".verify-action").click(function () {
  191. var account = $(this);
  192. var accountId = $(this).attr('id');
  193. $.ajax(
  194. OC.generateUrl('/settings/users/{account}/verify', {account: accountId}),
  195. {method: 'GET'}
  196. ).done(function(data) {
  197. var dialog = account.closest('.verify').children('.verification-dialog');
  198. showVerifyDialog(dialog, data.msg, data.code);
  199. account.attr('title', t('core', 'Verifying …'));
  200. account.attr('src', OC.imagePath('core', 'actions/verifying.svg'));
  201. account.removeClass('verify-action');
  202. });
  203. });
  204. // When the user clicks anywhere outside of the verification dialog we close it
  205. $(document).click(function(event){
  206. var element = event.target;
  207. var isDialog = $(element).hasClass('verificationCode')
  208. || $(element).hasClass('explainVerification')
  209. || $(element).hasClass('verification-dialog-content')
  210. || $(element).hasClass('verification-dialog');
  211. if (!isDialog) {
  212. $(document).find('.verification-dialog').css('display', 'none');
  213. }
  214. });
  215. var federationSettingsView = new OC.Settings.FederationSettingsView({
  216. el: '#personal-settings'
  217. });
  218. federationSettingsView.render();
  219. $("#languageinput").change(function () {
  220. // Serialize the data
  221. var post = $("#languageinput").serialize();
  222. // Ajax foo
  223. $.ajax(
  224. 'ajax/setlanguage.php',
  225. {
  226. method: 'POST',
  227. data: post
  228. }
  229. ).done(function() {
  230. location.reload();
  231. }).fail(function(jqXHR) {
  232. $('#passworderror').text(jqXHR.responseJSON.message);
  233. });
  234. return false;
  235. });
  236. var uploadparms = {
  237. pasteZone: null,
  238. done: function (e, data) {
  239. var response = data;
  240. if (typeof data.result === 'string') {
  241. response = JSON.parse(data.result);
  242. } else if (data.result && data.result.length) {
  243. // fetch response from iframe
  244. response = JSON.parse(data.result[0].body.innerText);
  245. } else {
  246. response = data.result;
  247. }
  248. avatarResponseHandler(response);
  249. },
  250. submit: function(e, data) {
  251. $('#displayavatar img').hide();
  252. $('#displayavatar .avatardiv').addClass('loading');
  253. data.formData = _.extend(data.formData || {}, {
  254. requesttoken: OC.requestToken
  255. });
  256. },
  257. fail: function (e, data){
  258. var msg = data.jqXHR.statusText + ' (' + data.jqXHR.status + ')';
  259. if (!_.isUndefined(data.jqXHR.responseJSON) &&
  260. !_.isUndefined(data.jqXHR.responseJSON.data) &&
  261. !_.isUndefined(data.jqXHR.responseJSON.data.message)
  262. ) {
  263. msg = data.jqXHR.responseJSON.data.message;
  264. }
  265. avatarResponseHandler({
  266. data: {
  267. message: msg
  268. }
  269. });
  270. }
  271. };
  272. $('#uploadavatar').fileupload(uploadparms);
  273. $('#selectavatar').click(function () {
  274. OC.dialogs.filepicker(
  275. t('settings', "Select a profile picture"),
  276. function (path) {
  277. $('#displayavatar img').hide();
  278. $('#displayavatar .avatardiv').addClass('loading');
  279. $.ajax({
  280. type: "POST",
  281. url: OC.generateUrl('/avatar/'),
  282. data: { path: path }
  283. }).done(avatarResponseHandler)
  284. .fail(function(jqXHR) {
  285. var msg = jqXHR.statusText + ' (' + jqXHR.status + ')';
  286. if (!_.isUndefined(jqXHR.responseJSON) &&
  287. !_.isUndefined(jqXHR.responseJSON.data) &&
  288. !_.isUndefined(jqXHR.responseJSON.data.message)
  289. ) {
  290. msg = jqXHR.responseJSON.data.message;
  291. }
  292. avatarResponseHandler({
  293. data: {
  294. message: msg
  295. }
  296. });
  297. });
  298. },
  299. false,
  300. ["image/png", "image/jpeg"]
  301. );
  302. });
  303. $('#removeavatar').click(function () {
  304. $.ajax({
  305. type: 'DELETE',
  306. url: OC.generateUrl('/avatar/'),
  307. success: function () {
  308. updateAvatar(true);
  309. }
  310. });
  311. });
  312. $('#abortcropperbutton').click(function () {
  313. $('#displayavatar .avatardiv').removeClass('loading');
  314. $('#displayavatar img').show();
  315. cleanCropper();
  316. });
  317. $('#sendcropperbutton').click(function () {
  318. sendCropData();
  319. });
  320. $('#pass2').strengthify({
  321. zxcvbn: OC.linkTo('core','vendor/zxcvbn/dist/zxcvbn.js'),
  322. titles: [
  323. t('core', 'Very weak password'),
  324. t('core', 'Weak password'),
  325. t('core', 'So-so password'),
  326. t('core', 'Good password'),
  327. t('core', 'Strong password')
  328. ],
  329. drawTitles: true,
  330. });
  331. // Load the big avatar
  332. $('#avatarform .avatardiv').avatar(OC.currentUser, 145, true, null, function() {
  333. if($('#displayavatar img').length === 0) {
  334. $('#removeavatar').removeClass('inlineblock').addClass('hidden');
  335. } else {
  336. $('#removeavatar').removeClass('hidden').addClass('inlineblock');
  337. }
  338. });
  339. // Show token views
  340. var collection = new OC.Settings.AuthTokenCollection();
  341. var view = new OC.Settings.AuthTokenView({
  342. collection: collection
  343. });
  344. view.reload();
  345. // 'redirect' to anchor sections
  346. // anchors are lost on redirects (e.g. while solving the 2fa challenge) otherwise
  347. // example: /settings/person?section=devices will result in /settings/person?#devices
  348. if (!window.location.hash) {
  349. var query = OC.parseQueryString(location.search);
  350. if (query && query.section) {
  351. OC.Util.History.replaceState({});
  352. window.location.hash = query.section;
  353. }
  354. }
  355. });
  356. if (!OC.Encryption) {
  357. OC.Encryption = {};
  358. }
  359. OC.Encryption.msg = {
  360. start: function (selector, msg) {
  361. var spinner = '<img src="' + OC.imagePath('core', 'loading-small.gif') + '">';
  362. $(selector)
  363. .html(msg + ' ' + spinner)
  364. .removeClass('success')
  365. .removeClass('error')
  366. .stop(true, true)
  367. .show();
  368. },
  369. finished: function (selector, data) {
  370. if (data.status === "success") {
  371. $(selector).html(data.data.message)
  372. .addClass('success')
  373. .stop(true, true)
  374. .delay(3000);
  375. } else {
  376. $(selector).html(data.data.message).addClass('error');
  377. }
  378. }
  379. };
  380. OC.Settings.updateAvatar = updateAvatar;