Code covered by the BSD License  

Highlights from
checkVersion - Check for a newer file version on the File Exchange

image thumbnail

checkVersion - Check for a newer file version on the File Exchange

by

 

24 Jan 2013 (Updated )

checkVersion implements an auto-update mechanism to check for a newer version of a utility on FEX

checkVersion(filename,fexId,mode)
% checkVersion Check for existence of a newer version of a file on Matlab's File Exchange
%
% Syntax:
%    [status,message] = checkVersion(filename,fexId,mode)
%
% Description:
%    checkVersion implements an auto-update background mechanism to check for
%    a newer version of a file on the Matlab File Exchange (FEX). This enables
%    FEX authors to easily embed an non-intrusive update mechanism in their
%    utilities, enabling its users to automatically update whenever a new
%    version is uploaded to FEX.
%
%    checkVersion(filename,fexId) checks on FILENAME's File Exchange webpage
%    whether any newer version of this utility has been uploaded. If so, a
%    popup notice is presented with the date and description of the latest
%    version. The popup enables users to download the newer version into the
%    current folder, or skip. There is also an option to skip the update and
%    not to remind ever again.
%
%    checkVersion is typically used after an applications has completed its
%    main task or has presented its GUI, in order to silently check for an
%    available update with minimal impact on the user. This could also be
%    done by using a background single-shot timer (see example below).
%
%    checkVersion(...,'silent') runs checkVersion in silent mode, without
%    prompting the user in case a newer version is detected. Newer file
%    versions will automatically be downloaded and installed.
%
%    [status,message] = checkVersion(...) returns a string with the update
%    check's status, along with an optional descriptive message
%       'ignored'    (message=empty)       - user requested not to be reminded
%       'unknown'    (message=webpage URL) - no newer version was ever uploaded to FEX, or FEX changed webpage format
%       'up-to-date' (message=upload date) - no newer version exists on FEX
%       'available'  (message=upload date) - newer version exists but not downloaded
%       'downloaded' (message=upload date) - newer version exists and downloaded
%       'error'      (message=error text)  - FEX download or parsing error
%
% Examples:
%    checkVersion('uiinspect',17935);
%    checkVersion('uiinspect',17935,'silent');
%    status = checkVersion('uiinspect',17935);
%    [status,message] = checkVersion('noSuchFile',1234);
%
%    % run checkVersion in a background single-shot timer
%    start(timer('TimerFcn',@(h,e)checkVersion('uiinspect',17935), 'StartDelay',5));
%
% Known issues/limitations:
%    This utility will silently fail if and when mathworks will ever modify the
%    File Exchange webpage format. In such case, download the latest version of
%    the utility, which hopefully solves the problem, or send me an email.
%
% Bugs and suggestions:
%    Please send to Yair Altman (altmany at gmail dot com)
%
% Change log:
%    2013-02-21: Ensure file is writable before updating (Thierry Dalon); increased FEX grace period 2=>3 days
%    2013-01-24: First version posted on <a href="http://www.mathworks.com/matlabcentral/fileexchange/authors/27420">MathWorks File Exchange</a>
%
% See also:
%    urlread; UIInspect & FindJObj (both on the File Exchange)

% License to use and modify this code is granted freely to all interested, as long as the original author is
% referenced and attributed as such. The original author maintains the right to be solely associated with this work.

% Programmed by Yair M. Altman: altmany(at)gmail.com
% $Revision: 1.02 $  $Date: 2013/02/21 21:87:12 $
function [status,message] = checkVersion(filename,fexId,mode)
    try
        % Initialization (not really needed - just in case I forget somewhere below...)
        statusStr  = ''; %#ok<NASGU>
        messageStr = ''; %#ok<NASGU>

        % Sanity checks
        error(nargchk(2,3,nargin,'struct')); %#ok<NCHKN>
        if nargin < 3
            silentMode = false;
        elseif ~ischar(mode) || ~strcmpi(mode,'silent')
            error('invalid mode specified - only ''silent'' is supported.')
        else
            silentMode = true;
        end

        % Normalize the specified filename (strip path, extension)
        [fpath,filename,fext] = fileparts(filename); %#ok<ASGLU,NASGU>
        
        % If the user has not indicated NOT to be informed
        if ~ispref(filename,'dontCheckNewerVersion')

            % Download the relevant FEX webpage
            baseUrl = 'http://www.mathworks.com/matlabcentral/fileexchange/';  % old URL format: '/loadFile.do?objectId='
            fexId = num2str(fexId);  % accepts both 123 and '123' formats
            webUrl = [baseUrl fexId];
            webPage = urlread(webUrl);

            % Check whether the specified filename is mentioned in the webpage
            if ~silentMode && isempty(strfind(lower(webPage),lower(filename)))
                msg = {['The term "' filename '" is not mentioned anywhere on ' webUrl], '', ...
                        'Are you sure you wish to continue checking?'};
                answer = questdlg(msg,[filename ' update'],'Yes','No','No');
                switch answer
                    case 'Yes'  % => Yes: continue normally
                        % ignore
                    otherwise   % => No or cancel
                        error('Mismatched filename, fexId were specified');
                end
            end

            % Get the latest version date from the File Exchange webpage
            modIdx = strfind(webPage,'>Updates<');
            if ~isempty(modIdx)
                webPage = webPage(modIdx:end);
                % Note: regexp hangs if substr not found, so use strfind instead...
                %latestWebVersion = regexprep(webPage,'.*?>(20[\d-]+)</td>.*','$1');
                dateIdx = strfind(webPage,'class="date">');
                if ~isempty(dateIdx)
                    latestDate = webPage(dateIdx(end)+13 : dateIdx(end)+23);
                    messageStr = latestDate;
                    try
                        startIdx = dateIdx(end)+27;
                        descStartIdx = startIdx + strfind(webPage(startIdx:startIdx+999),'<td>');
                        descEndIdx   = startIdx + strfind(webPage(startIdx:startIdx+999),'</td>');
                        descStr = webPage(descStartIdx(1)+3 : descEndIdx(1)-2);
                        descStr = regexprep(descStr,'</?[pP]>','');
                    catch
                        descStr = '';
                    end

                    % Get this file's latest date
                    thisFileName = which(filename);  %#ok
                    if isempty(thisFileName)
                        thisFileDatenum = 0;
                    else
                        try
                            % Try to get the file's date from the file-system
                            thisFileData = dir(thisFileName);
                            try
                                thisFileDatenum = thisFileData.datenum;
                            catch  % old ML versions...
                                thisFileDatenum = datenum(thisFileData.date);
                            end
                        catch
                            % Failed for some reason - check whether an internal change-log can be found in the file
                            thisFileText = evalc('type(thisFileName)');
                            thisFileLatestDate = regexprep(thisFileText,'.*Change log:[\s%]+([\d-]+).*','$1');
                            thisFileDatenum = datenum(thisFileLatestDate,'yyyy-mm-dd');
                        end
                    end

                    % If there's a newer version on the File Exchange webpage (allow 3 days grace period)
                    if (thisFileDatenum < datenum(latestDate,'dd mmm yyyy')-3)
                        if silentMode
                            % silent mode - download & install without prompting
                            answer = 'Yes';
                        else
                            % interactive (default) mode - prompt the user
                            % Ask the user whether to download the newer version (YES, no, no & don't ask again)
                            if isempty(thisFileName)
                                msg = {[filename ' is apparently not installed in your Matlab path.'], '', ...
                                        'Download & install this utility from the Matlab File Exchange in current folder?'};
                            else
                                msg = {['A newer version (' latestDate ') of ' filename ' is available on the MathWorks File Exchange:'], '', ...
                                       ['\color{blue}' descStr '\color{black}'], '', ...
                                        'Download & install the new version in current folder?'};
                            end
                            createStruct.Interpreter = 'tex';
                            createStruct.Default = 'Yes';
                            answer = questdlg(msg,[filename ' update'],'Yes','No','No & never ask again',createStruct);
                        end
                        switch answer
                            case 'Yes'  % => Yes: download & install newer file
                                try
                                    %fileUrl = [baseUrl '/download.do?objectId=' fexId '&fn=' filename '&fe=.m'];
                                    fileUrl = [baseUrl '/' fexId '?controller=file_infos&download=true'];
                                    %contents = urlread(fileUrl);
                                    %contents = regexprep(contents,[char(13),char(10)],'\n');  %convert to OS-dependent EOL
                                    %fid = fopen(thisFileName,'wt');
                                    %fprintf(fid,'%s',contents);
                                    %fclose(fid);
                                    if isempty(thisFileName)
                                        thisFileName = filename;
                                    end
                                    [fpath,fname,fext] = fileparts(thisFileName); %#ok<NASGU>
                                    try fileattrib(thisFileName,'+w'); catch, end  % make file writable (Thierry Dalon 21/2/2013)
                                    zipFileName = fullfile(fpath,[fname '.zip']);
                                    urlwrite(fileUrl,zipFileName);
                                    unzip(zipFileName,fpath);
                                    try delete(zipFileName); catch, end  %#ok delete zip file after installation
                                    rehash;  % make the downloaded file(s) visible to Matlab's path
                                    statusStr = 'downloaded';
                                catch
                                    % Error downloading: inform the user
                                    statusStr = 'error';
                                    messageStr = lasterr;  %#ok<LERR>
                                    msgbox(['Error in downloading: ' lasterr], filename, 'warn'); %#ok<LERR>
                                    web(webUrl);
                                end

                            case 'No & never ask again'   % => No & don't ask again
                                setpref(filename,'dontCheckNewerVersion',1);
                                statusStr = 'ignored';  % return the upload date in messageStr

                            otherwise  % => No or cancel
                                % ignore (this time only)...
                                statusStr = 'available';
                        end
                    else
                        statusStr = 'up-to-date';  % hurray!...
                    end
                else
                    % FEX webpage has probably changed its format once again...
                    statusStr = 'unknown';
                    messageStr = webUrl;
                end
            elseif ~isempty(strfind(lower(webPage),'</html>'))
                % Maybe webpage changed format or no updates were uploaded - bail out...
                statusStr = 'unknown';
                messageStr = webUrl;
            else
                % Maybe webpage not fully loaded - bail out...
                statusStr = 'error';
                messageStr = [webUrl ' was not fully loaded for some reason'];
            end
        else
            % user requested never to be reminded about updating this utility
            statusStr = 'ignored';
            messageStr = '';
        end
    catch
        % Never mind...
        statusStr = 'error';
        messageStr = lasterr;  %#ok<LERR>
    end

    % Return the status & message, if requested
    if nargout > 0,  status  = statusStr;   end
    if nargout > 1,  message = messageStr;  end

%end  % checkVersion

Contact us