Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ask permission for automatically downloading and installing new updates #2285

Merged
merged 10 commits into from
Dec 3, 2022
58 changes: 48 additions & 10 deletions Sparkle/Base.lproj/SUUpdatePermissionPrompt.xib
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<customObject id="-2" userLabel="File's Owner" customClass="SUUpdatePermissionPrompt">
<connections>
<outlet property="anonymousInfoDisclosureButton" destination="JW6-0z-Ud3" id="8Ac-kJ-T5e"/>
<outlet property="automaticallyDownloadUpdatesView" destination="37T-Ef-DtX" id="7VP-FF-3iP"/>
<outlet property="cancelButton" destination="cFC-wV-H3j" id="L2P-Ux-4Vn"/>
<outlet property="checkButton" destination="sMh-ha-r7R" id="2O6-G4-etk"/>
<outlet property="infoChoiceView" destination="ov1-yV-Uol" id="A1X-Y9-Hnh"/>
Expand Down Expand Up @@ -275,17 +276,16 @@ Gw
<point key="canvasLocation" x="-291" y="554"/>
</customView>
<customView id="ov1-yV-Uol" userLabel="Anonymous Info View">
<rect key="frame" x="0.0" y="0.0" width="437" height="27"/>
<rect key="frame" x="0.0" y="0.0" width="437" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="a0k-dW-Jhx" userLabel="View">
<rect key="frame" x="0.0" y="-26" width="437" height="66"/>
<rect key="frame" x="0.0" y="-26" width="437" height="64"/>
<subviews>
<button focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="a90-Iq-FgS" userLabel="IncludeInfoButton">
<rect key="frame" x="104" y="25" width="313" height="16"/>
<rect key="frame" x="104" y="25" width="200" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="MvK-K5-kxt"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="14" id="STF-87-eYy"/>
<constraint firstAttribute="height" constant="14" id="STF-87-eYy"/>
</constraints>
<buttonCell key="cell" type="check" title="Include anonymous system profile" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" state="on" focusRingType="none" inset="2" id="gz7-LM-gNf">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
Expand Down Expand Up @@ -321,12 +321,12 @@ Gw
</button>
</subviews>
<constraints>
<constraint firstItem="JW6-0z-Ud3" firstAttribute="leading" secondItem="a0k-dW-Jhx" secondAttribute="leading" constant="85" id="6Y1-s7-g6r"/>
<constraint firstItem="a90-Iq-FgS" firstAttribute="centerY" secondItem="JW6-0z-Ud3" secondAttribute="centerY" id="BNJ-xV-faQ"/>
<constraint firstItem="a90-Iq-FgS" firstAttribute="leading" secondItem="a0k-dW-Jhx" secondAttribute="leading" constant="105" id="ILg-Qu-R6M"/>
<constraint firstItem="a90-Iq-FgS" firstAttribute="top" secondItem="a0k-dW-Jhx" secondAttribute="top" constant="26" id="Mh6-5L-xqM"/>
<constraint firstAttribute="trailing" secondItem="a90-Iq-FgS" secondAttribute="trailing" constant="20" id="U4G-Eh-HPu"/>
<constraint firstItem="JW6-0z-Ud3" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="a0k-dW-Jhx" secondAttribute="leading" constant="20" symbolic="YES" id="V3P-iz-aJ9"/>
<constraint firstItem="a90-Iq-FgS" firstAttribute="leading" secondItem="JW6-0z-Ud3" secondAttribute="trailing" constant="4" id="axA-s0-uGg"/>
<constraint firstItem="a90-Iq-FgS" firstAttribute="top" secondItem="a0k-dW-Jhx" secondAttribute="top" constant="24" id="Mh6-5L-xqM"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="a90-Iq-FgS" secondAttribute="trailing" constant="20" id="U4G-Eh-HPu"/>
<constraint firstItem="a90-Iq-FgS" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="JW6-0z-Ud3" secondAttribute="trailing" constant="4" id="axA-s0-uGg"/>
<constraint firstAttribute="width" constant="437" id="iUc-Zs-h9S"/>
<constraint firstAttribute="bottom" secondItem="a90-Iq-FgS" secondAttribute="bottom" constant="26" id="qij-uH-6Yl"/>
</constraints>
Expand All @@ -338,7 +338,45 @@ Gw
<constraint firstAttribute="trailing" secondItem="a0k-dW-Jhx" secondAttribute="trailing" id="rAn-bJ-Mp6"/>
<constraint firstAttribute="bottom" secondItem="a0k-dW-Jhx" secondAttribute="bottom" constant="-26" id="xw5-bj-VOd"/>
</constraints>
<point key="canvasLocation" x="-113" y="145.5"/>
<point key="canvasLocation" x="-135.5" y="242.5"/>
</customView>
<customView id="37T-Ef-DtX" userLabel="Automatic Download View">
<rect key="frame" x="0.0" y="0.0" width="437" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="MEW-2n-kJE" userLabel="View">
<rect key="frame" x="0.0" y="-26" width="437" height="64"/>
<subviews>
<button focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="BId-kb-eYW" userLabel="automaticallyDownloadUpdatesButton">
<rect key="frame" x="104" y="25" width="248" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="kJy-g7-0Un"/>
</constraints>
<buttonCell key="cell" type="check" title="Automatically download and install updates" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" focusRingType="none" inset="2" id="AUc-33-qGN">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<connections>
<binding destination="-2" name="value" keyPath="self.automaticallyDownloadUpdates" id="V01-B1-yEx"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="BId-kb-eYW" firstAttribute="leading" secondItem="MEW-2n-kJE" secondAttribute="leading" constant="105" id="MnW-AM-QzP"/>
<constraint firstAttribute="bottom" secondItem="BId-kb-eYW" secondAttribute="bottom" constant="26" id="QXK-90-kUJ"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="BId-kb-eYW" secondAttribute="trailing" constant="20" id="Vk3-CH-cXd"/>
<constraint firstItem="BId-kb-eYW" firstAttribute="top" secondItem="MEW-2n-kJE" secondAttribute="top" constant="24" id="YrX-aL-LAq"/>
<constraint firstAttribute="width" constant="437" id="n4W-Z4-w9i"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="MEW-2n-kJE" secondAttribute="bottom" constant="-26" id="4iq-ZN-n9x"/>
<constraint firstItem="MEW-2n-kJE" firstAttribute="top" secondItem="37T-Ef-DtX" secondAttribute="top" constant="-13" id="FCe-KY-rHW"/>
<constraint firstItem="MEW-2n-kJE" firstAttribute="leading" secondItem="37T-Ef-DtX" secondAttribute="leading" id="dsv-fP-ffg"/>
<constraint firstAttribute="trailing" secondItem="MEW-2n-kJE" secondAttribute="trailing" id="puT-Df-xgH"/>
</constraints>
<point key="canvasLocation" x="-136" y="181"/>
</customView>
<customView hidden="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AGr-V0-0al" userLabel="Placeholder View">
<rect key="frame" x="0.0" y="0.0" width="437" height="205"/>
Expand Down
4 changes: 4 additions & 0 deletions Sparkle/SPUUpdater.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ SU_EXPORT @interface SPUUpdater : NSObject

By default, updates are not automatically downloaded.

By default starting from Sparkle 2.4, users are provided an option to opt in to automatically downloading and installing updates when they are asked if they want automatic update checks enabled.
The default value for this option is based on what the developer sets `SUAutomaticallyUpdate` in their Info.plist.
This is not done if `SUEnableAutomaticChecks` is set in the Info.plist however. Please check `automaticallyChecksForUpdates` property for more details.

Note that the developer can disallow automatic downloading of updates from being enabled (via `SUAllowsAutomaticUpdates` Info.plist key).
In this case, this property will return NO regardless of how this property is set.

Expand Down
5 changes: 5 additions & 0 deletions Sparkle/SPUUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,11 @@ - (void)updatePermissionRequestFinishedWithResponse:(SUUpdatePermissionResponse
{
[self setSendsSystemProfile:response.sendSystemProfile];
[self setAutomaticallyChecksForUpdates:response.automaticUpdateChecks];

NSNumber *automaticUpdateDownloading = response.automaticUpdateDownloading;
if (automaticUpdateDownloading != nil) {
[self setAutomaticallyDownloadsUpdates:automaticUpdateDownloading.boolValue];
}
}

- (NSDate *)lastUpdateCheckDate
Expand Down
24 changes: 23 additions & 1 deletion Sparkle/SUUpdatePermissionPrompt.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
@interface SUUpdatePermissionPrompt () <NSTouchBarDelegate>

@property (nonatomic) BOOL shouldSendProfile;
@property (nonatomic) BOOL automaticallyDownloadUpdates;

@property (nonatomic) SUHost *host;
@property (nonatomic) NSArray *systemProfileInformationArray;
Expand All @@ -33,6 +34,7 @@ @interface SUUpdatePermissionPrompt () <NSTouchBarDelegate>
@property (nonatomic) IBOutlet NSView *placeholderView;
@property (nonatomic) IBOutlet NSView *responseView;
@property (nonatomic) IBOutlet NSView *infoChoiceView;
@property (nonatomic) IBOutlet NSView *automaticallyDownloadUpdatesView;

@property (nonatomic) IBOutlet NSButton *cancelButton;
@property (nonatomic) IBOutlet NSButton *checkButton;
Expand All @@ -48,6 +50,7 @@ @implementation SUUpdatePermissionPrompt

@synthesize reply = _reply;
@synthesize shouldSendProfile = _shouldSendProfile;
@synthesize automaticallyDownloadUpdates = _automaticallyDownloadUpdates;
@synthesize host = _host;
@synthesize systemProfileInformationArray = _systemProfileInformationArray;
@synthesize stackView = _stackView;
Expand All @@ -56,6 +59,7 @@ @implementation SUUpdatePermissionPrompt
@synthesize placeholderView = _placeholderView;
@synthesize responseView = _responseView;
@synthesize infoChoiceView = _infoChoiceView;
@synthesize automaticallyDownloadUpdatesView = _automaticallyDownloadUpdatesView;
@synthesize cancelButton = _cancelButton;
@synthesize checkButton = _checkButton;
@synthesize anonymousInfoDisclosureButton = _anonymousInfoDisclosureButton;
Expand All @@ -70,6 +74,7 @@ - (instancetype)initPromptWithHost:(SUHost *)theHost request:(SPUUpdatePermissio
_host = theHost;
_shouldSendProfile = [self shouldAskAboutProfile];
_systemProfileInformationArray = request.systemProfile;
_automaticallyDownloadUpdates = [theHost boolForKey:SUAutomaticallyUpdateKey];
[self setShouldCascadeWindows:NO];
} else {
assert(false);
Expand All @@ -82,15 +87,23 @@ - (BOOL)shouldAskAboutProfile
return [(NSNumber *)[self.host objectForInfoDictionaryKey:SUEnableSystemProfilingKey] boolValue];
}

- (BOOL)allowsAutomaticUpdates
{
NSNumber *allowsAutomaticUpdates = [self.host objectForInfoDictionaryKey:SUAllowsAutomaticUpdatesKey];
return (allowsAutomaticUpdates == nil || allowsAutomaticUpdates.boolValue);
}

- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [self.host bundlePath]]; }

- (void)windowDidLoad
{
[self.window center];

self.infoChoiceView.hidden = ![self shouldAskAboutProfile];
self.automaticallyDownloadUpdatesView.hidden = ![self allowsAutomaticUpdates];

[self.stackView addArrangedSubview:self.promptView];
[self.stackView addArrangedSubview:self.automaticallyDownloadUpdatesView];
[self.stackView addArrangedSubview:self.infoChoiceView];
[self.stackView addArrangedSubview:self.placeholderView];
[self.stackView addArrangedSubview:self.moreInfoView];
Expand Down Expand Up @@ -148,7 +161,16 @@ - (IBAction)toggleMoreInfo:(id)__unused sender

- (IBAction)finishPrompt:(NSButton *)sender
{
SUUpdatePermissionResponse *response = [[SUUpdatePermissionResponse alloc] initWithAutomaticUpdateChecks:([sender tag] == 1) sendSystemProfile:self.shouldSendProfile];
BOOL automaticUpdateChecksEnabled = ([sender tag] == 1);

NSNumber *automaticUpdateDownloading;
if ([self allowsAutomaticUpdates]) {
automaticUpdateDownloading = @(automaticUpdateChecksEnabled && self.automaticallyDownloadUpdates);
} else {
automaticUpdateDownloading = nil;
}

SUUpdatePermissionResponse *response = [[SUUpdatePermissionResponse alloc] initWithAutomaticUpdateChecks:automaticUpdateChecksEnabled automaticUpdateDownloading:automaticUpdateDownloading sendSystemProfile:self.shouldSendProfile];
self.reply(response);

[self close];
Expand Down
28 changes: 26 additions & 2 deletions Sparkle/SUUpdatePermissionResponse.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#import <Foundation/Foundation.h>
#import <Sparkle/SUExport.h>

NS_ASSUME_NONNULL_BEGIN

/**
This class represents a response for permission to check updates.
*/
Expand All @@ -17,24 +19,46 @@ SU_EXPORT @interface SUUpdatePermissionResponse : NSObject<NSSecureCoding>
/**
Initializes a new update permission response instance.

@param automaticUpdateChecks Flag for whether to allow automatic update checks.
@param automaticUpdateChecks Flag to enable automatic update checks.
@param sendSystemProfile Flag for if system profile information should be sent to the server hosting the appcast.
*/
- (instancetype)initWithAutomaticUpdateChecks:(BOOL)automaticUpdateChecks sendSystemProfile:(BOOL)sendSystemProfile;

/**
Initializes a new update permission response instance.

@param automaticUpdateChecks Flag to enable automatic update checks.
@param automaticUpdateDownloading Flag to enable automatic downloading and installing of updates. If this is nil, this option will be ignored.
@param sendSystemProfile Flag for if system profile information should be sent to the server hosting the appcast.
*/
- (instancetype)initWithAutomaticUpdateChecks:(BOOL)automaticUpdateChecks automaticUpdateDownloading:(NSNumber * _Nullable)automaticUpdateDownloading sendSystemProfile:(BOOL)sendSystemProfile;

/*
Use -initWithAutomaticUpdateChecks:sendSystemProfile: instead.
*/
- (instancetype)init NS_UNAVAILABLE;

/**
A read-only property indicating whether automatic update checks are allowed or not.
A read-only property indicating if update checks should be done automatically.
*/
@property (nonatomic, readonly) BOOL automaticUpdateChecks;

/**
A read-only property indicating if updates should be automatically downloaded and installed.

If this property is `nil`, then no user choice was made for this option.

If `automaticUpdateChecks` is `NO` then this property should not be `@(YES)`.
Set it to `NO` if the user was given the choice of automatically downloading and installing updates,
otherwise set it to `nil`.
*/
@property (nonatomic, readonly, nullable) NSNumber *automaticUpdateDownloading;

/**
A read-only property indicating if system profile should be sent or not.
*/
@property (nonatomic, readonly) BOOL sendSystemProfile;

@end

NS_ASSUME_NONNULL_END
18 changes: 16 additions & 2 deletions Sparkle/SUUpdatePermissionResponse.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
#include "AppKitPrevention.h"

static NSString *SUUpdatePermissionAutomaticUpdateChecksKey = @"SUUpdatePermissionAutomaticUpdateChecks";
static NSString *SUUpdatePermissionAutomaticUpdateDownloadingKey = @"SUUpdatePermissionAutomaticUpdateDownloading";
static NSString *SUUpdatePermissionSendSystemProfileKey = @"SUUpdatePermissionSendSystemProfile";

@implementation SUUpdatePermissionResponse

@synthesize automaticUpdateChecks = _automaticUpdateChecks;
@synthesize sendSystemProfile = _sendSystemProfile;
@synthesize automaticUpdateDownloading = _automaticUpdateDownloading;

+ (BOOL)supportsSecureCoding
{
Expand All @@ -27,24 +29,36 @@ + (BOOL)supportsSecureCoding
- (instancetype)initWithCoder:(NSCoder *)decoder
{
BOOL automaticUpdateChecks = [decoder decodeBoolForKey:SUUpdatePermissionAutomaticUpdateChecksKey];
NSNumber *automaticUpdateDownloading = [decoder decodeObjectOfClass:[NSNumber class] forKey:SUUpdatePermissionAutomaticUpdateDownloadingKey];
BOOL sendSystemProfile = [decoder decodeBoolForKey:SUUpdatePermissionSendSystemProfileKey];
return [self initWithAutomaticUpdateChecks:automaticUpdateChecks sendSystemProfile:sendSystemProfile];
return [self initWithAutomaticUpdateChecks:automaticUpdateChecks automaticUpdateDownloading:automaticUpdateDownloading sendSystemProfile:sendSystemProfile];
}

- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeBool:self.automaticUpdateChecks forKey:SUUpdatePermissionAutomaticUpdateChecksKey];

if (self.automaticUpdateDownloading != nil) {
[encoder encodeObject:self.automaticUpdateDownloading forKey:SUUpdatePermissionAutomaticUpdateDownloadingKey];
}

[encoder encodeBool:self.sendSystemProfile forKey:SUUpdatePermissionSendSystemProfileKey];
}

- (instancetype)initWithAutomaticUpdateChecks:(BOOL)automaticUpdateChecks sendSystemProfile:(BOOL)sendSystemProfile
- (instancetype)initWithAutomaticUpdateChecks:(BOOL)automaticUpdateChecks automaticUpdateDownloading:(NSNumber * _Nullable)automaticUpdateDownloading sendSystemProfile:(BOOL)sendSystemProfile
{
self = [super init];
if (self != nil) {
_automaticUpdateChecks = automaticUpdateChecks;
_automaticUpdateDownloading = automaticUpdateDownloading;
_sendSystemProfile = sendSystemProfile;
}
return self;
}

- (instancetype)initWithAutomaticUpdateChecks:(BOOL)automaticUpdateChecks sendSystemProfile:(BOOL)sendSystemProfile
{
return [self initWithAutomaticUpdateChecks:automaticUpdateChecks automaticUpdateDownloading:nil sendSystemProfile:sendSystemProfile];
}

@end
3 changes: 3 additions & 0 deletions Sparkle/ar.lproj/SUUpdatePermissionPrompt.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@
/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */
"gz7-LM-gNf.title" = "تضمين تقرير عن النظام دون ذكر معلومات عن المستخدم";

/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */
"AUc-33-qGN.title" = "تنزيل التحديثات وتثبيتها تلقائيًا في المست";

/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */
"OhZ-1K-DmA.title" = "التحقق تلقائيًا";
3 changes: 3 additions & 0 deletions Sparkle/cs.lproj/SUUpdatePermissionPrompt.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@
/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */
"gz7-LM-gNf.title" = "Odeslat anonymní systémový profil";

/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */
"AUc-33-qGN.title" = "Stahovat a instalovat aktualizace automaticky";

/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */
"OhZ-1K-DmA.title" = "Automaticky vyhledávat";
3 changes: 3 additions & 0 deletions Sparkle/da.lproj/SUUpdatePermissionPrompt.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@
/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */
"gz7-LM-gNf.title" = "Vedhæft anonym systemprofil";

/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */
"AUc-33-qGN.title" = "Hent og installer opdateringer automatisk";

/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */
"OhZ-1K-DmA.title" = "Søg automatisk";
Loading