YaWK  24.1
Yet another WebKit
update-helper.js
Go to the documentation of this file.
1 /**
2  * @brief Update Helper
3  * @details This script is used to check for updates and display update info, as well as call xhr functions to update yawk
4  * @file update-helper.js
5  */
6 $(document).ready(function() { // wait until document is ready
7  // get button and nodes
8  var updateBtn = $("#checkForUpdatesBtn");
9  var readFilebaseNode = $("#readFilebaseNode");
10  let installedVersion = $('#installedVersion').text(); // the current installed version of YAWK
11  var currentVersion = ''; // current version of YAWK (filled with api call from update.yawk.io)
12  var updateVersion = ''; // latest version of YAWK (filled with api call from update.yawk.io)
13  var statusBarNode = $("#statusBarNode"); // status bar node
14  var extendedInfoNode = $("#extendedInfoNode"); // displays more info about update
15  var statusBarMessage = ''; // message to display in status bar
16  var successMsg = ''; // holds success message, if update is available
17  var errorMsg = ''; // holds error message, if no update is available
18  let lang = $('#checkForUpdatesBtn');
19  let updateCheck = lang.attr('data-UPDATE_CHECK');
20 
21  function fastForwardVersionNumber(fastForwardBtn) {
22 
23  // if user click on fast-forward button
24  $(fastForwardBtn).click(function ()
25  {
26  var updateVersion = $("#updateVersion").text();
27  console.log('Fast forward button clicked');
28  // ajax to fast-forward to the latest version number
29  $.ajax({ // ajax call
30  url: 'js/update-fastForward.php',
31  type: 'POST',
32  data: {fastForward: true, updateVersion: updateVersion },
33  success: function (data) {
34  console.log('Fast forward to latest version successful');
35  console.log(data);
36  $(fastForwardBtn).removeClass().addClass('btn btn-success disabled animated fadeIn slow').html("<i class=\"fa fa-refresh fa-spin\"></i> &nbsp; fast-forwarding...");
37 
38  // reload page
39  setTimeout(function () {
40  location.reload();
41  }, 2200);
42  },
43  error: function (data) {
44  console.log('Fast forward to latest version failed');
45  console.log(data);
46  }
47  });
48  });
49  }
50 
51  /* UPDATE BTN CLICK */
52  // if update button is clicked
53  $(updateBtn).click(function() {
54  // update button with spinner icon
55  $(updateBtn).html("<i class=\"fa fa-refresh fa-spin\"></i> &nbsp;" + updateCheck);
56 
57  // Call compareVersions() to check if there is an update available
58  checkVersion(function(error, updateVersion) {
59 
60  let lang = $('#checkForUpdatesBtn');
61  let updateAvailable = lang.attr('data-UPDATE_AVAILABLE');
62  let updateAvailableSubtext = lang.attr('data-UPDATE_AVAILABLE_SUBTEXT');
63  let updateNotAvailable = lang.attr('data-UPDATE_NOT_AVAILABLE');
64  let updateNotAvailableSubtext = lang.attr('data-UPDATE_NOT_AVAILABLE_SUBTEXT');
65  let updateInstall = lang.attr('data-UPDATE_INSTALL');
66  let updateUpToDate = lang.attr('data-UPDATE_UP_TO_DATE');
67  let updateCurrentInstalledVersion = lang.attr('data-UPDATE_CURRENT_INSTALLED_VERSION');
68  let updateNoUpdate = lang.attr('data-UPDATE_NO_UPDATE');
69  let verifyFiles = lang.attr('data-UPDATE_VERIFY_FILES');
70  let verifyingFiles = lang.attr('data-UPDATE_VERIFYING_FILES');
71  let latestAvailableVersion = lang.attr('data-UPDATE_LATEST_AVAILABLE_VERSION');
72  let updateChanges = lang.attr('data-UPDATE_CHANGES');
73  let released = lang.attr('data-RELEASED');
74  let githubReference = lang.attr('data-GITHUB_REFERENCE');
75  let githubMilestoneText = lang.attr('data-GITHUB_MILESTONE');
76 
77  if (error) {
78  console.error(error);
79  } else {
80  // check, if version is higher than installed version (if so, update is available)
81  // UPDATE AVAILABLE: call api and get update config, update markup and display update message
82  if (compareVersions(installedVersion, updateVersion) < 0)
83  {
84  // This method will call the api and return the update config object
85  getUpdateConfig(function(error, data) {
86  if (error) {
87  console.error(error);
88  }
89  else // retrieved update config successfully
90  { console.log('Update config:', data);
91 
92  // check, if all required properties are set
93  if (data.updateConfig && data.updateConfig.UPDATE
94  && data.updateConfig.UPDATE.buildMessage
95  && data.updateConfig.UPDATE.buildTime
96  && data.updateConfig.UPDATE.updateVersion
97  && data.updateConfig.UPDATE.updateFilebase)
98  {
99  // ok, set vars from update config
100  let buildMessage = data.updateConfig.UPDATE.buildMessage;
101  let buildTime = data.updateConfig.UPDATE.buildTime;
102  let updateVersion = data.updateConfig.UPDATE.updateVersion;
103  // let updateFilebase = data.updateConfig.UPDATE.updateFilebase;
104 
105  if (data.updateConfig.UPDATE.githubIssues){
106  var githubIssues = data.updateConfig.UPDATE.githubIssues;
107  }
108  else { githubIssues = false; }
109 
110  if (data.updateConfig.UPDATE.githubMilestone){
111  var githubMilestone = data.updateConfig.UPDATE.githubMilestone;
112  }
113  else { githubMilestone = false; }
114 
115  // log vars
116  // console.log('Build message:', buildMessage);
117  // console.log('Build time:', buildTime);
118  // console.log('Build version:', updateVersion);
119  // console.log('Build filebase:', updateFilebase);
120 
121  if (githubIssues){
122  // Use a regular expression to match issue numbers (# followed by digits)
123  const regex = /#(\d+)/g;
124  // Extract the issue numbers
125  const issueNumbers = [];
126  let match;
127 
128  while ((match = regex.exec(githubIssues)) !== null) {
129  issueNumbers.push(match[1]);
130  }
131  // Replace issue numbers with GitHub links
132  const repoUrl = 'https://github.com/YaWK/yawk.io/issues/';
133  var issuesWithLinks = githubIssues.replace(regex, (match, issueNumber) => {
134  return `<a href="${repoUrl}${issueNumber}" target="_blank">${match}</a>`;
135  });
136  // console.log('Issue numbers:', issueNumbers);
137  // console.log('String with links:', stringWithLinks);
138 
139  // create GitHub info string
140  var githubRelatedIssues = '<li>'+githubReference+': <b>'+issuesWithLinks+'</b></li>';
141  }
142  else // no GitHub issues found
143  { // leave GitHub info empty
144  githubRelatedIssues = '';
145  }
146 
147  // check, if GitHub milestone is set
148  if (githubMilestone){
149  // Use a regular expression to match issue numbers (# followed by digits)
150  const regex = /#(\d+)/g;
151  // Extract the milestone numbers
152  const milestoneNumbers = [];
153  let match;
154 
155  while ((match = regex.exec(githubMilestone)) !== null) {
156  milestoneNumbers.push(match[1]);
157  }
158 
159  // Replace milestone numbers with GitHub links
160  const repoUrl = 'https://github.com/YaWK/yawk.io/milestone/';
161  var milestoneWithLinks = githubMilestone.replace(regex, (match, milestoneNumber) => {
162  return `<a href="${repoUrl}${milestoneNumber}" target="_blank">${match}</a>`;
163  });
164 
165  // create GitHub info string
166  var githubRelatedMilestone = '<li>'+githubMilestoneText+': <b>'+milestoneWithLinks+'</b></li>';
167  }
168  else // no GitHub issues found
169  { // leave GitHub info empty
170  githubRelatedMilestone = '';
171  }
172 
173  // update available msg
174  statusBarMessage = updateAvailable;
175  successMsg = '<h3 class="text-primary animated fadeIn"><b><i class="fa fa-globe animated bounce slow"></i></b> &nbsp;' + updateAvailable + '<br><small>'+updateAvailableSubtext+'</small></h3>';
176  statusBarNode.html(successMsg).fadeIn(1000);
177 
178  let extendedInfo = '<ul class="animated fadeIn slow delay-1s"><li><span class="text-primary"><b>' + latestAvailableVersion + '</b> build <b>' + '<span id="updateVersion">'+updateVersion+'</span></b></span></li><li>' + updateCurrentInstalledVersion + ' build <b class="text-muted">' + '<span id="currentVersion">'+installedVersion + '</span></b></li>' + '<li>'+updateChanges+': <b>'+ buildMessage + '</b></li>'+githubRelatedIssues+githubRelatedMilestone+'<li>'+released+': ' + buildTime + '</li></ul>';
179  extendedInfoNode.html(extendedInfo).fadeIn(1000);
180  console.log(statusBarMessage);
181 
182  // START BTN CREATION
183  // switch styling of update button to "install update" process
184  // to achieve this, we have to remove the old button and create a new one
185  $("#checkForUpdatesBtn").remove();
186 
187  // Create the new startUpdateBtn element with the given attributes
188  var newBtn = $('<a>', {
189  'href': '#startUpdateBtn',
190  'id': 'getFilebaseBtn',
191  'class': 'btn btn-primary pull-right animated fadeIn slow',
192  'html': '<i class="fa fa-search"></i> &nbsp;&nbsp;' + verifyFiles
193  });
194  // Append the new startUpdateBtn to a container element on the page
195  $('#updateBtnNode').append(newBtn);
196  // END BTN CREATION
197 
198  // get new startUpdateBtn, check if it is clicked and call generateLocalFileBase()
199  var getFilebaseBtn = $("#getFilebaseBtn");
200  $(getFilebaseBtn).click(function() {
201  console.log('install update button clicked, read local filebase and store to ini file');
202  getFilebaseBtn.removeClass().addClass('btn btn-primary pull-right disabled').html("<i class=\"fa fa-refresh fa-spin\"></i> &nbsp;&nbsp;" + verifyingFiles);
203  // this function will read the local filebase from your installation and store it to a filebase.ini file
204  // Call both functions and wait for them to complete
205  Promise.all([generateLocalFileBase(updateInstall)])
206  .then(([localFilebase]) => {
207  // local filebase generated successfully, now read update filebase
208  readUpdateFileBase();
209  })
210  .catch((error) => {
211  console.log(error);
212  });
213  });
214  } // end if all required properties are set
215  else // update config not found or properties not set
216  { // throw error to console
217  console.error('updateConfig, UPDATE, or properties not found in xhr data');
218  }
219  }
220  });
221  }
222  else // no update available
223  { // update status bar message and updateBtn text
224  statusBarMessage = updateNotAvailable + ' (' + installedVersion + ') ' + updateNotAvailableSubtext;
225  errorMsg = '<h3 class="text-success animated fadeIn slow"><i class="fa fa-check-circle-o"></i> &nbsp;' + statusBarMessage + '</h3>';
226  statusBarNode.html(errorMsg).fadeIn(1000);
227  $(updateBtn).removeClass().addClass('btn btn-default pull-right disabled animated fadeIn slow').html("<i class=\"fa fa-check-circle-o\"></i> &nbsp;" + updateNoUpdate);
228  console.log(statusBarMessage);
229  }
230  }
231  });
232  }); // end update button click processing
233 
234  /**
235  * @brief read update filebase from remote update server
236  * @details Read the filebase from local installation and generate a filebase.ini file to compare with the latest update filebase
237  * @param updateInstall string language tag to display on update button
238  */
239  // Wrap the AJAX request in a Promise
240  function generateLocalFileBase(updateInstall) {
241  var updateBtnText = updateInstall;
242  return new Promise((resolve, reject) => {
243  $.ajax({
244  type: 'POST',
245  url: 'js/update-generateLocalFilebase.php',
246  success: function (response) {
247  resolve(response);
248  $(readFilebaseNode).html(response).fadeIn(1000);
249  // this function will read the filebase.ini from remote update server https://update.yawk.io
250  // add start update button
251  // Create the new startUpdateBtn element with the given attributes
252  var newBtn = $('<a>', {
253  'href': '#startUpdateBtn',
254  'id': 'startUpdateBtn',
255  'class': 'btn btn-success pull-right animated flipInX',
256  'html': '<i class="fa fa-download"></i> &nbsp;' + updateBtnText
257  });
258  // Append the new startUpdateBtn to a container element on the page
259  delay(function(){
260  $("#getFilebaseBtn").remove();
261  $('#updateBtnNode').append(newBtn);
262 
263  // get new startUpdateBtn, check if it is clicked and call startUpdate()
264  var startUpdateBtn = $("#startUpdateBtn");
265  $(startUpdateBtn).click(function() {
266  console.log('start update button clicked, start update process');
267  // start the update process
268  runMigrations(); // if migrations are required, they will be run first
269  });
270  }, 5000 ); // end delay
271 
272  },
273  error: function (response) {
274  reject('generateLocalFileBase() ERROR: ' + response);
275  },
276  });
277  });
278  }
279  var delay = ( function() {
280  var timer = 0;
281  return function(callback, ms) {
282  clearTimeout (timer);
283  timer = setTimeout(callback, ms);
284  };
285  })();
286 
287  /**
288  * @brief run migrations
289  * @details check and run all migrations between current installed version and latest available version
290  */
291  function runMigrations(){
292  console.log('called runMigrations()');
293  var runMigrationsNode = $("#runMigrationsNode");
294  var currentVersion = $("#currentVersion").text();
295  var updateVersion = $("#updateVersion").text();
296 
297  $.ajax({
298  type: 'POST',
299  url: 'js/update-runMigrations.php',
300  data: {
301  updateVersion: updateVersion,
302  currentVersion: currentVersion
303  },
304  success: function (response)
305  { // update view with response
306  $(runMigrationsNode).html(response).fadeIn(1000);
307  console.log("runMigrations() response: ", response);
308 
309  },
310  error: function (response)
311  { // on error..
312  $(runMigrationsNode).html(response).fadeIn(1000);
313  console.error('runMigrations() ERROR: ', response);
314  },
315  });
316 
317  // ok, done with migrations, now fetch files
318  fetchFiles();
319  }
320 
321 
322  /**
323  * @brief start update process
324  * @details This function will start the update process by calling the fetchUpdate() method from update.php class
325  */
326  function fetchFiles()
327  { var fetchUpdateNode = $("#fetchUpdateNode");
328  var currentVersion = $("#currentVersion").text();
329  var updateVersion = $("#updateVersion").text();
330  // check via ajax, if there are updates available
331  $.ajax({ // create a new AJAX call
332  type: 'POST', // GET or POST
333  url: 'js/update-fetchFiles.php', // the file to call
334  data: {
335  updateVersion: updateVersion,
336  currentVersion: currentVersion
337  },
338  success: function (response) { // fileBase checked successfully
339  // update view with response
340  console.log("fetchUpdate() response: " + response);
341  $(fetchUpdateNode).append(response).fadeIn(1000);
342 
343  var fastForwardBtn = $("#fastForwardUpdateBtn");
344  if (fastForwardBtn){
345  fastForwardVersionNumber(fastForwardBtn);
346  }
347  },
348  error: function (response) { // on error..
349  console.log('fetchUpdate() ERROR: ' +response);
350  }
351  });
352  }
353 
354  /**
355  * @brief read update filebase from remote update server
356  * @details Read the filebase from local installation and generate a filebase.ini file to compare with the latest update filebase
357  */
358  function readUpdateFileBase(){
359  console.log('readUpdateFileBase() called');
360  // check via ajax, if there are updates available
361  $.ajax({ // create a new AJAX call
362  type: 'POST', // GET or POST
363  url: 'js/update-readUpdateFilebase.php', // the file to call
364  success: function (response) { // fileBase checked successfully
365  // update view with response
366  // console.log("readUpdateFileBase() response: " + response);
367  $(readUpdateFilebaseNode).html(response).fadeIn(1000);
368  },
369  error: function (response) { // on error..
370  console.log('readUpdateFileBase() ERROR: ' +response);
371  }
372  });
373  }
374 
375  /**
376  * @brief Return the latest update version from update.yawk.io
377  * @details This function will connect to update.yawk.io and fetch the latest version number
378  * @return {string} version
379  */
380  function checkVersion(callback) {
381  fetch('https://update.yawk.io/?action=version')
382  .then(response => {
383  if (response.ok) {
384  return response.json();
385  } else {
386  throw new Error('API error: ' + response.status);
387  }
388  })
389  .then(data => {
390  if (data && data.yawkversion) {
391  callback(null, data.yawkversion);
392  } else {
393  callback('Error fetching version: ' + JSON.stringify(data));
394  }
395  })
396  .catch(error => {
397  callback('Error: ' + error);
398  });
399  }
400 
401  /**
402  * @brief Return the update config from update.yawk.io
403  * @details This function will connect to update.yawk.io and fetch the update config
404  * @return {string} update config as json
405  */
406  function getUpdateConfig(callback)
407  { // get update config from update.yawk.io
408  fetch('https://update.yawk.io/?action=getUpdateConfig')
409  .then(response => {
410  if (response.ok) {
411  return response.json();
412  } else {
413  throw new Error('API error: ' + response.status);
414  }
415  })
416  .then(data => {
417  if (data) {
418  callback(null, data);
419  } else {
420  callback('Error fetching data: ' + JSON.stringify(data));
421  }
422  })
423  .catch(error => {
424  callback('Error: ' + error);
425  });
426  }
427 
428 
429  /**
430  * @brief Compare two version numbers
431  * @param {string} v1
432  * @param {string} v2
433  * @return {number} 1 if v1 > v2, -1 if v1 < v2, 0 if v1 === v2
434  * @details This function will compare two version numbers and return 1 if v1 > v2, -1 if v1 < v2, 0 if v1 === v2
435  * compareVersions('1.0.0', '1.0.1'); // -1
436  * compareVersions('1.0.1', '1.0.0'); // 1
437  * compareVersions('1.0.0', '1.0.0'); // 0
438  */
439  function compareVersions(v1, v2) {
440  const v1Parts = v1.split('.').map(Number);
441  const v2Parts = v2.split('.').map(Number);
442  const maxLength = Math.max(v1Parts.length, v2Parts.length);
443 
444  for (let i = 0; i < maxLength; i++) {
445  const v1Part = v1Parts[i] || 0;
446  const v2Part = v2Parts[i] || 0;
447 
448  if (v1Part > v2Part) {
449  return 1;
450  }
451  if (v1Part < v2Part) {
452  return -1;
453  }
454  }
455  return 0;
456  }
457 
458 }); // END document ready
if(!isset( $lang))
Definition: dashboard.php:12
function a
Definition: browser.js:14
c jPlayer errorMsg
c jPlayer error
type
Definition: menu-new.php:35
function i(e, t)
Definition: plyr.js:1
function g(e, t, n, r)
Definition: plyr.js:1
function d(e, n, r)
Definition: plyr.js:1
document ready(function() { $('a[data-confirm]').click(function(ev) { modal='#dataConfirmModal';var href=$(this).attr('href');var title=$(this).attr('title');var icon=$(this).attr('data-icon');if(!icon) { icon='fa fa-trash-o';} if(!$(modal).length) { $('body').append('< div id="dataConfirmModal" class="modal fade" role="dialog" aria-labelledby="dataConfirmLabel" aria-hidden="true">< div class="modal-dialog">< div class="modal-content">< div class="modal-header">< button type="button" class="close" data-dismiss="modal" aria-hidden="true">< i class="fa fa-times"></i ></button >< br >< div class="col-md-1">< h3 class="modal-title">< i class="'+icon+'"></i ></h3 ></div >< div class="col-md-11">< h3 class="modal-title" id="dataConfirmLabel">'+title+'</h3 ></div ></h3 ></div >< div class="modal-body"></div >< div class="modal-footer">< button type="button" class="btn btn-default" data-dismiss="modal" aria-hidden="true">Abbrechen</button >< a type="button" class="btn btn-danger" id="dataConfirmOK">< i class="'+icon+'"></i > L &ouml;schen</a ></div ></div ></div ></div >');} $(modal).find('.modal-body').text($(this).attr('data-confirm'));$('#dataConfirmOK').attr('href', href);$(modal).modal({show:true});return false;});$('#terminateUser').click(function() { var terminate=window.confirm("ACHTUNG!\nDas wird Deinen Account permanent deaktivieren.\n"+"Bist Du Dir sicher, dass Du das tun willst?");if(terminate===true) { var terminateUser=window.confirm("Bist Du Dir wirklich ganz sicher?\n"+"Diese Aktion kann nicht rueckgaengig gemacht werden.");if(terminateUser===true) { $.get('system/templates/YaWK-bootstrap3/js/terminate-user.php', function(data) { if(data==="true") { setTimeout("window.location='logout.html'", 0);} else { alert("Fehler: "+data);} });} } });function dismissNotifications() { $.ajax({ url:'js/dismiss-notifications.php', type:'POST', success:function(data) { if(!data) { alert('Something went wrong!');return false;} } });$("#bell-label").fadeOut();$('#notification-header').html('You have 0 notifications');$('#notification-menu').fadeOut();} $("#dismiss").click(function() { dismissNotifications();});function disableButtons(delay) { $('#loginButton').removeClass().addClass('btn btn-success disabled').attr('id', 'LOGIN_FORBIDDEN');$('#resetPasswordButton').removeClass().addClass('btn btn-danger disabled');setTimeout(function() { $('#LOGIN_FORBIDDEN').attr('id', 'loginButton').removeClass().addClass('btn btn-success');$('#resetPasswordButton').removeClass().addClass('btn btn-danger');}, delay);} $("#loginButton").click(function(){ if($('#loginButton').length > 0) { if($('#loginButton').hasClass('btn') &&$('#loginButton').hasClass('btn-success') &&$('#loginButton').hasClass('disabled')) { } else { $("#loginForm").submit();disableButtons(10000);} } else if($('#LOGIN_FORBIDDEN').length > 0) { if($('#LOGIN_FORBIDDEN').hasClass('btn') &&$('#LOGIN_FORBIDDEN').hasClass('btn-success') &&$('#LOGIN_FORBIDDEN').hasClass('disabled')) { } else { } } });$("#blockedBtn").hover(function() { $("#blockedBtn").hide();$("#askBtn").fadeIn(820);});})