Commit f7994e6c authored by fengjie's avatar fengjie

初始化

parents
Pipeline #644 failed with stages

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

{
"plugins": [
"babel-plugin-styled-components",
"transform-flow-strip-types",
"add-react-displayname",
"transform-decorators-legacy",
["transform-builtin-extend", {
"globals": ["Error", "Array"]
}],
"syntax-trailing-function-commas"
],
"presets": ["es2015", "stage-0", "react"],
"env": {
"development": {
"presets": []
},
"extract": {
"plugins": [
["ttag", {
"extract": {
"output": "locales/metabase-frontend.pot"
},
"discover": ["t", "jt"],
"numberedExpressions": true
}]
]
}
}
}
This diff is collapsed.
#!/usr/bin/env bash
# Determines whether we should skip tests for a driver, usage:
#
# ./.circleci/skip-driver-tests.sh oracle
#
# Returns false if the commit message contains [ci all], [ci drivers], or [ci <driver-name>],
# or if the current branch is master or a release branch.
set -eu
COMMIT_MESSAGE=`cat commit.txt`
! [[ "$CIRCLE_BRANCH" =~ ^master|release-.+$ ]] &&
! [[ "$COMMIT_MESSAGE" == *"[ci all]"* ]] &&
! [[ "$COMMIT_MESSAGE" == *"[ci drivers]"* ]] &&
! [[ "$COMMIT_MESSAGE" == *"[ci $1]"* ]] &&
echo "Skipping driver tests: $1"
((nil . ((indent-tabs-mode . nil) ; always use spaces for tabs
(require-final-newline . t))) ; add final newline on save
(js2-mode . ((js2-mode-show-parse-errors . nil) ; these settings will let flycheck do everything through eslint,
(js2-mode-show-strict-warnings . nil))) ; because js2-mode can't handle flowtype
(clojure-mode . ((eval . (progn
;; Specify which arg is the docstring for certain macros
;; (Add more as needed)
(put 'defendpoint 'clojure-doc-string-elt 3)
(put 'defendpoint-async 'clojure-doc-string-elt 3)
(put 'api/defendpoint 'clojure-doc-string-elt 3)
(put 'api/defendpoint-async 'clojure-doc-string-elt 3)
(put 'defsetting 'clojure-doc-string-elt 2)
(put 'setting/defsetting 'clojure-doc-string-elt 2)
(put 's/defn 'clojure-doc-string-elt 2)
(put 'p.types/defprotocol+ 'clojure-doc-string-elt 2)
;; Define custom indentation for functions inside metabase.
;; This list isn't complete; add more forms as we come across them.
(define-clojure-indent
(let-404 1)
(match 1)
(merge-with 1)
(p.types/defprotocol+ '(1 (:defn)))
(p.types/def-abstract-type '(1 (:defn)))
(p.types/deftype+ '(2 nil nil (:defn)))
(p/def-map-type '(2 nil nil (:defn)))
(p.types/defrecord+ '(2 nil nil (:defn))))))
;; if you're using clj-refactor (highly recommended!), prefer prefix notation when cleaning the ns form
(cljr-favor-prefix-notation . t)
;; prefer keeping source width about ~118, GitHub seems to cut off stuff at either 119 or 120 and
;; it's nicer to look at code in GH when you don't have to scroll back and forth
(fill-column . 118)
(clojure-docstring-fill-column . 118))))
.babel_cache/*
docs/*
OSX/*
target/*
**node_modules
**metabase.jar
*.db
.dockerignore
Dockerfile
# Metabase EditorConfig settings
# http://editorconfig.org/
# use the top most editor config file
root = true
[*]
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
end_of_line = lf
[Makefile]
indent_style = tab
[*.clj]
indent_size = 2
max_line_length = 120
[*.css]
indent_size = 2
[*.html]
indent_size = 2
[*.mustache]
indent_size = 2
[*.js]
indent_size = 2
[*.jsx]
indent_size = 2
[*.json]
indent_size = 2
{
"rules": {
"strict": [2, "never"],
"no-undef": 2,
"no-var": 1,
"no-unused-vars": [1, { "vars": "all", "args": "none" }],
"no-empty": [1, { "allowEmptyCatch": true }],
"curly": [1, "all"],
"eqeqeq": [1, "smart"],
"import/no-commonjs": 1,
"no-console": 0,
"react/no-is-mounted": 2,
"react/prefer-es6-class": 2,
"react/display-name": 1,
"react/prop-types": 0,
"react/no-did-mount-set-state": 0,
"react/no-did-update-set-state": 0,
"react/no-find-dom-node": 0,
"flowtype/define-flow-type": 1,
"flowtype/use-flow-type": 1,
"prefer-const": [1, { "destructuring": "all" }]
},
"globals": {
"pending": false,
"cy": true,
"Cypress": true
},
"env": {
"browser": true,
"es6": true,
"commonjs": true,
"jest": true
},
"parser": "babel-eslint",
"plugins": ["react", "flowtype"],
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:import/errors",
"plugin:import/warnings"
],
"settings": {
"import/resolver": "webpack",
"import/ignore": ["\\.css$"]
}
}
[ignore]
.*/node_modules/react/node_modules/.*
.*/node_modules/postcss-import/node_modules/.*
.*/node_modules/react-resizable/.*
.*/node_modules/documentation/.*
.*/node_modules/.*/\(lib\|test\).*\.json$
.*/node_modules/react-element-to-jsx-string/.*
.*/node_modules/react-element-to-jsx-string/.*
.*/node_modules/resize-observer-polyfill/.*
.*/node_modules/react-virtualized/.*
.*/node_modules/styled-components/.*
.*/node_modules/grid-styled/.*
.*/node_modules/update-notifier/.*
.*/node_modules/boxen/.*
.*/node_modules/libnpx/.*
.*/node_modules/@babel/standalone/.*
.*/node_modules/@testing-library/jest-dom/.*
.*/node_modules/.*/node_modules/chalk/.*
.*/node_modules/react-draggable/lib/.*
[include]
.*/frontend/.*
[libs]
./frontend/interfaces
[options]
module.system.node.resolve_dirname=node_modules
module.system.node.resolve_dirname=frontend/src
esproposal.decorators=ignore
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe
module.name_mapper='.*\(\.css\)' -> 'CSSModule'
module.name_mapper='^ace/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/ace-builds/src-min-noconflict/\1'
module.name_mapper='^assets\/\(.*\)$' -> '<PROJECT_ROOT>/resources/frontend_client/app/assets/\1'
module.name_mapper='^metabase-types\/\(.*\)$' -> '<PROJECT_ROOT>/frontend/src/metabase-types/\1'
module.system=haste
strip_root=true
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.css
Please use the issue chooser, when creating a new issue:
https://github.com/metabase/metabase/issues/new/choose
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ".Needs Triage, Type:Bug"
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Logs**
Please include javascript console and server logs around the time this bug occurred. For information about how to get these, consult our [bug troubleshooting guide](https://metabase.com/docs/latest/troubleshooting-guide/bugs.html)
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Information about your Metabase Installation:**
You can get this information by going to Admin -> Troubleshooting.
- Your browser and the version: (e.x. Chrome 52.1, Firefox 48.0, Safari 11.1, …)
- Your operating system: (e.x. OS X 10.10, Windows 10.1809, Ubuntu 16.04, …)
- Your databases: (e.x. MySQL, Postgres, MongoDB, …)
- Metabase version: (e.x. 0.19.3)
- Metabase hosting environment: (e.x. Mac app, Elastic Beanstalk, Docker, Heroku, Jar-file on Ubuntu, …)
- Metabase internal database: (e.x. H2 (default), Postgres or MySQL)
**Severity**
How severe an issue is this bug to you? Is this annoying, blocking some users, blocking an upgrade or blocking your usage of Metabase entirely?
Note: the more honest and specific you are here the more we will take you seriously.
**Additional context**
Add any other context about the problem here.
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: "Type:New Feature, .Needs Triage"
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**How important is this feature to you?**
Note: the more honest and specific you are here the more we will take you seriously.
**Additional context**
Add any other context or screenshots about the feature request here.
---
name: Questions and help
about: If you think you need help with something related to Metabase
title: ''
labels: "Type:Question, .Needs Triage"
assignees: ''
---
For help with installation and setup, information on how specific features work, or general questions about Metabase, please use our discussion forum:
https://discourse.metabase.com
The Github issue tracker is intended to collect bug reports and feature requests.
Any issues open for help requests will be closed to keep from clogging up the issue tracker.
---
name: Security Issue
about: If you think you found a security vulnerability with Metabase
title: ''
labels: "Security, .Needs Triage"
assignees: ''
---
Please email security@metabase.com directly, and do not open an issue.
###### Before submitting the PR, please make sure you do the following
- [ ] If you're attempting to fix a translation issue, please submit your changes to our [POEditor project](https://poeditor.com/join/project/ynjQmwSsGh) instead of opening a PR.
### Tests
- [ ] Run the frontend and integration tests with `yarn lint && yarn flow && yarn test`)
- [ ] If there are changes to the backend codebase, run the backend tests with `lein test && lein lint && ./bin/reflection-linter`
- [ ] Sign the [Contributor License Agreement](https://docs.google.com/a/metabase.com/forms/d/1oV38o7b9ONFSwuzwmERRMi9SYrhYeOrkbmNaq9pOJ_E/viewform)
(unless it's a tiny documentation change).
# For now we want to only check for stale pull requests
only: pulls
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Override the default Label of "wontfix"
staleLabel: ""
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue was automatically marked as stale due to a lack of recent activity.
This isn't a criticism of the PR, just an attempt to keep the PR queue in a happier state.
If you'd like to bring this PR back to the attention of the folks on the Metabase team, just comment
to prevent it from being closed.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
*.class
*.iml
*.jar
*.log
*.po~
*.sqlite
*.trace.db
*.xcworkspacedata
.DS_Store
.\#*
.eastwood
.idea/
.nrepl-port
.vscode
/*.h2.db
/*.lock.db
/*.mv.db
/*.trace.db
/.babel_cache
/.env
/.lein-env
/.lein-failures
/.lein-plugins
/.lein-repl-history
/.sqlite.db
/backend-checksums.txt
/build.xml
/checkouts
/classes
/coverage
/crate-*
/cypress
/deploy/artifacts/*
/docs/uberdoc.html
/frontend/test/snapshots/*
/lein-plugins/*/target
/local/src
/local/src
/locales/metabase-*.pot
/modules/drivers/*/resources/namespaces.edn
/modules/drivers/*/target
/node_modules/
/osx-artifacts
/plugins
/process.yml
/reset-password-artifacts
/resources/frontend_client/app/dist/
/resources/frontend_client/app/locales
/resources/frontend_client/app/locales
/resources/frontend_client/embed.html
/resources/frontend_client/index.html
/resources/frontend_client/public.html
/resources/namespaces.edn
/resources/sample-dataset.db.trace.db
/resources/version.properties
/screenshots
/sqlite.db
/stats.json
/target
/test-report-*
OSX/.cpcache
OSX/Metabase/jre
OSX/Resources/metabase.jar
OSX/build
OSX/dsa_priv.pem
\#*\#
bin/node_modules/
bin/release/aws-eb/metabase-aws-eb.zip
coverage-summary.json
pom.xml
pom.xml.asc
profiles.clj
target/checksum.txt
xcshareddata
xcuserdata
{
"trailingComma": "all"
}
network-timeout 600000
###################
# STAGE 1: builder
###################
# Build currently doesn't work on > Java 11 (i18n utils are busted) so build on 8 until we fix this
FROM adoptopenjdk/openjdk8:alpine as builder
WORKDIR /app/source
ENV FC_LANG en-US
ENV LC_CTYPE en_US.UTF-8
# bash: various shell scripts
# wget: installing lein
# git: ./bin/version
# yarn: frontend building
# make: backend building
# gettext: translations
# java-cacerts: installs updated cacerts to /etc/ssl/certs/java/cacerts
RUN apk add --update coreutils bash yarn git wget make gettext java-cacerts
# lein: backend dependencies and building
ADD https://raw.github.com/technomancy/leiningen/stable/bin/lein /usr/local/bin/lein
RUN chmod 744 /usr/local/bin/lein
RUN lein upgrade
# install dependencies before adding the rest of the source to maximize caching
# backend dependencies
ADD project.clj .
RUN lein deps
# frontend dependencies
ADD yarn.lock package.json .yarnrc ./
RUN yarn
# add the rest of the source
ADD . .
# build the app
RUN bin/build
# import AWS RDS cert into /etc/ssl/certs/java/cacerts
ADD https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem .
RUN keytool -noprompt -import -trustcacerts -alias aws-rds \
-file rds-combined-ca-bundle.pem \
-keystore /etc/ssl/certs/java/cacerts \
-keypass changeit -storepass changeit
# ###################
# # STAGE 2: runner
# ###################
FROM adoptopenjdk/openjdk11:alpine-jre as runner
WORKDIR /app
ENV FC_LANG en-US
ENV LC_CTYPE en_US.UTF-8
# dependencies
RUN apk add --update bash ttf-dejavu fontconfig
# add fixed cacerts
COPY --from=builder /etc/ssl/certs/java/cacerts /opt/java/openjdk/lib/security/cacerts
# add Metabase script and uberjar
RUN mkdir -p bin target/uberjar
COPY --from=builder /app/source/target/uberjar/metabase.jar /app/target/uberjar/
COPY --from=builder /app/source/bin/start /app/bin/
# create the plugins directory, with writable permissions
RUN mkdir -p /plugins
RUN chmod a+rwx /plugins
# expose our default runtime port
EXPOSE 3000
# run it
ENTRYPOINT ["/app/bin/start"]
METABASE APP-EMBED.JS
SOFTWARE LICENSE AGREEMENT
PLEASE READ THE FOLLOWING TERMS AND CONDITIONS CAREFULLY BEFORE DOWNLOADING, INSTALLING OR USING THE APP-EMBED.JS SOFTWARE OR ANY ACCOMPANYING DOCUMENTATION (COLLECTIVELY, THE “SOFTWARE”).
THE TERMS AND CONDITIONS OF THIS SOFTWARE LICENSE AGREEMENT (“AGREEMENT”) GOVERN USE OF THE SOFTWARE UNLESS YOU AND METABASE HAVE EXECUTED A SEPARATE AGREEMENT GOVERNING USE OF THE SOFTWARE.
Metabase is willing to license the Software to you only upon the condition that you accept all the terms contained in this Agreement. By downloading, installing or using the Software, you have indicated that you understand this Agreement and accept all of its terms. If you are accepting the terms of this Agreement on behalf of a company or other legal entity, you represent and warrant that you have the authority to bind that company or other legal entity to the terms of this Agreement, and, in such event, “you” and “your” will refer to that company or other legal entity. If you do not accept all the terms of this Agreement, then Metabase is unwilling to license the Software to you, you are not permitted to use the Software, and you must destroy all copies of the Software.
1. **Grant of License**. Conditioned upon your compliance with the terms and conditions of this Agreement, Metabase grants you a non-exclusive and non-transferable license to Execute (as defined herein) the executable form of the Software, free and clear of any Affero GPL License requirements applicable to the Metabase application available for download by the Metabase at its website https://metabase.com. Metabase reserves all rights in the Software not expressly granted to you in this Agreement. For purposes of this Agreement, “Execute” and “Execution” means to load, install, and run the Software in order to benefit from its functionality as designed by Metabase.
2. **Restrictions**. Except as expressly specified in this Agreement or otherwise permitted by law notwithstanding this Agreement, you agree not to do any of the following, : (a) copy (except in the course of loading or installing) or modify the Software, including but not limited to modifying or blocking, overlaying, or otherwise obstructing the contents of the embedded element that is embedded in your application via the Software, adding new features, or otherwise making adaptations that alter the functioning of the Software; or (b) transfer, sublicense, lease, lend, rent or otherwise distribute the Software to any third party. You acknowledge and agree that portions of the Software, including but not limited to the source code and the specific design and structure of individual modules or programs, constitute or contain trade secrets of Metabase and its licensors.
3. **Ownership**. The copy of the Software is licensed, not sold. You own the media on which the Software is recorded, but Metabase retains ownership of the copy of the Software itself, including all intellectual property rights therein. The Software is protected by United States copyright law and international treaties. You will not delete or in any manner alter the copyright, trademark, and other proprietary rights notices or markings appearing on the Software as delivered to you. This includes the removal of the Metabase’s name or logo in the iframe that is embedded in your application.
4. **Term**. The license granted under this Agreement remains in effect unless terminated in accordance with this Agreement. You may terminate the license at any time by destroying all copies of the Software in your possession or control. The license granted under this Agreement will automatically terminate, with or without notice from Metabase, if you breach any term of this Agreement. Upon termination, you must promptly destroy all copies of the Software in your possession or control.
5. **No Warranty**. “THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND. METABASE DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT, AND ANY WARRANTIES AND CONDITIONS ARISING OUT OF COURSE OF DEALING OR USAGE OF TRADE. NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED FROM METABASE OR ELSEWHERE WILL CREATE ANY WARRANTY OR CONDITION NOT EXPRESSLY STATED IN THIS AGREEMENT.”
6. **Limitation of Liability**. METABASE’S TOTAL LIABILITY TO YOU FROM ALL CAUSES OF ACTION AND UNDER ALL THEORIES OF LIABILITY WILL BE LIMITED TO THE AMOUNTS PAID OR PAYABLE TO METABASE BY YOU FOR THE SOFTWARE OR, IN THE EVENT THAT METABASE HAS MADE THE SOFTWARE AVAILABLE TO YOU WITHOUT CHARGE, METABASE’S TOTAL LIABILITY WILL BE LIMITED TO $[500]. IN NO EVENT WILL METABASE BE LIABLE TO YOU FOR ANY SPECIAL, INCIDENTAL, EXEMPLARY, PUNITIVE OR CONSEQUENTIAL DAMAGES (INCLUDING LOSS OF DATA, BUSINESS, PROFITS OR ABILITY TO EXECUTE) OR FOR THE COST OF PROCURING SUBSTITUTE PRODUCTS ARISING OUT OF OR IN CONNECTION WITH THIS AGREEMENT OR THE EXECUTION OR PERFORMANCE OF THE SOFTWARE, WHETHER SUCH LIABILITY ARISES FROM ANY CLAIM BASED UPON CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, AND WHETHER OR NOT METABASE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE. THE FOREGOING LIMITATIONS WILL SURVIVE AND APPLY EVEN IF ANY LIMITED REMEDY SPECIFIED IN THIS AGREEMENT IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE.
8. **U.S. Government End Users**. The Software and Documentation are “commercial items” as that term is defined in FAR 2.101, consisting of “commercial computer software” and “commercial computer software documentation,” respectively, as such terms are used in FAR 12.212 and DFARS 227.7202. If the Software and Documentation are being acquired by or on behalf of the U.S. Government, then, as provided in FAR 12.212 and DFARS 227.7202-1 through 227.7202-4, as applicable, the U.S. Government’s rights in the Software and Documentation will be only those specified in this Agreement.
9. **Export Law**. You agree to comply fully with all U.S. export laws and regulations to ensure that neither the Software nor any technical data related thereto nor any direct product thereof are exported or re-exported directly or indirectly in violation of, or used for any purposes prohibited by, such laws and regulations.
10. **General**. This Agreement will be governed by and construed in accordance with the laws of the State of California, without regard to or application of conflict of laws rules or principles. The United Nations Convention on Contracts for the International Sale of Goods will not apply. You may not assign or transfer this Agreement or any rights granted hereunder, by operation of law or otherwise, without Metabase’s prior written consent, and any attempt by you to do so, without such consent, will be void. Except as expressly set forth in this Agreement, the exercise by either party of any of its remedies under this Agreement will be without prejudice to its other remedies under this Agreement or otherwise. All notices or approvals required or permitted under this Agreement will be in writing and delivered by confirmed facsimile transmission, by overnight delivery service, or by certified mail, and in each instance will be deemed given upon receipt. All notices or approvals will be sent to the addresses specified by either party to the other in accordance with this section. The failure by either party to enforce any provision of this Agreement will not constitute a waiver of future enforcement of that or any other provision. Any waiver, modification or amendment of any provision of this Agreement will be effective upon Metabase posting the modified terms of this Agreement to the Metabase website. If any provision of this Agreement is held to be unenforceable or invalid, that provision will be enforced to the maximum extent possible, and the other provisions will remain in full force and effect. This Agreement is the complete and exclusive understanding and agreement between the parties regarding its subject matter, and supersedes all proposals, understandings or communications between the parties, oral or written, regarding its subject matter, unless you and Metabase have executed a separate agreement. Any terms or conditions contained in your purchase order, if any, or other ordering document that are inconsistent with or in addition to the terms and conditions of this Agreement are hereby rejected by Metabase and will be deemed null.
11. **Contact Information**. If you have any questions regarding this Agreement, you may contact Metabase by email at legal@metabase.com, phone by (415) 767-0490 or by mail at 660 4th Street #557, San Francisco, CA 94107.
This diff is collapsed.
This diff is collapsed.
//
// AppDelegate.h
// Metabase
//
// Created by Cam Saul on 9/21/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
@import Cocoa;
@interface AppDelegate : NSObject <NSApplicationDelegate>
+ (AppDelegate *)instance;
@property (readonly) NSUInteger port;
- (void)stopMetabaseTask;
- (void)startMetabaseTask;
@end
//
// AppDelegate.m
// Metabase
//
// Created by Cam Saul on 9/21/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
#import <Sparkle/Sparkle.h>
#import "AppDelegate.h"
#import "MainViewController.h"
#import "MetabaseTask.h"
#import "TaskHealthChecker.h"
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@property (strong, nonatomic) MetabaseTask *task;
@property (strong, nonatomic) TaskHealthChecker *healthChecker;
@end
static AppDelegate *sInstance = nil;
@implementation AppDelegate
+ (AppDelegate *)instance {
return sInstance;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
sInstance = self;
[[SUUpdater sharedUpdater] checkForUpdatesInBackground];
[self startMetabaseTask];
[self.healthChecker start];
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
// re-start the health checker if it's not checking like it should be : the HEALTH CHECKER HEALTH CHECKER
[self.healthChecker start];
}
- (void)applicationWillTerminate:(NSNotification *)notification {
[self stopMetabaseTask];
}
#pragma mark - Static Methods
- (void)startMetabaseTask {
self.task = [MetabaseTask task];
self.healthChecker.port = self.task.port;
}
- (void)stopMetabaseTask {
[self.task disableTerminationAlert];
self.task = nil;
}
#pragma mark - Getters / Setters
- (TaskHealthChecker *)healthChecker {
if (!_healthChecker) {
_healthChecker = [[TaskHealthChecker alloc] init];
}
return _healthChecker;
}
- (void)setTask:(MetabaseTask *)task {
[_task terminate];
_task = task;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[task launch];
});
}
- (NSUInteger)port {
return self.task.port;
}
@end
//
// JavaTask.h
// Metabase
//
// Created by Cam Saul on 11/24/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
NSString *JREPath();
NSString *UberjarPath();
NSString *DBPath(); ///< Path to the H2 DB file
NSString *PluginsDirPath(); ///< Path for plugins
/// Base class for running JRE-based NSTasks
@interface JavaTask : NSObject
@property (strong, nonatomic) NSTask *task;
/// Called when a new data is written to the task's stdin / stdout. Default implementation does nothing. This is called on a background thread!
- (void)readHandleDidRead:(NSString *)message;
/// Terminate the associated NSTask.
- (void)terminate;
@end
//
// JavaTask.m
// Metabase
//
// Created by Cam Saul on 11/24/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
#import "JavaTask.h"
NSString *JREPath() {
return [[NSBundle mainBundle] pathForResource:@"java" ofType:nil inDirectory:@"jre/bin"];
}
NSString *UberjarPath() {
return [[NSBundle mainBundle] pathForResource:@"metabase" ofType:@"jar"];
}
NSString *ApplicationSupportDirPath() {
NSString *applicationSupportDir = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"Metabase"];
if (![[NSFileManager defaultManager] fileExistsAtPath:applicationSupportDir]) {
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:applicationSupportDir withIntermediateDirectories:YES attributes:nil error:&error];
if (error) {
NSLog(@"Error creating %@: %@", applicationSupportDir, error.localizedDescription);
}
}
return applicationSupportDir;
}
NSString *DBPath() {
return [ApplicationSupportDirPath() stringByAppendingPathComponent:@"metabase.db"];
}
NSString *PluginsDirPath() {
return [ApplicationSupportDirPath() stringByAppendingPathComponent:@"Plugins"];
}
@interface JavaTask ()
@property (nonatomic, strong) NSPipe *pipe;
@property (nonatomic, strong) NSFileHandle *readHandle;
@end
@implementation JavaTask
- (void)dealloc {
[self terminate];
}
#pragma mark - Local Methods
- (void)readHandleDidReadData:(NSData *)data {
NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (!message.length) return;
[self readHandleDidRead:message];
}
- (void)readHandleDidRead:(NSString *)message {}
- (void)terminate {
if (!self.task) return; // already dead
NSLog(@"Killing %@ @ 0x%zx...", [self class], (size_t)self);
self.task = nil;
}
#pragma mark - Getters / Setters
- (void)setTask:(NSTask *)task {
self.pipe = nil;
[_task terminate];
_task = task;
if (task) {
self.pipe = [NSPipe pipe];
task.standardOutput = self.pipe;
task.standardError = self.pipe;
}
}
- (void)setPipe:(NSPipe *)pipe {
self.readHandle = nil;
_pipe = pipe;
if (pipe) {
self.readHandle = pipe.fileHandleForReading;
}
}
- (void)setReadHandle:(NSFileHandle *)readHandle {
[_readHandle closeFile];
_readHandle = readHandle;
if (readHandle) {
__weak JavaTask *weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
while (readHandle == weakSelf.readHandle) {
NSData *data = readHandle.availableData;
if (!data.length) return;
[weakSelf readHandleDidReadData:data];
}
});
}
}
@end
//
// MetabaseTask.h
// Metabase
//
// Created by Cam Saul on 10/9/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
#import "JavaTask.h"
/// Task for running the MetabaseServer
@interface MetabaseTask : JavaTask
/// Create (and launch) a task to run the Metabase backend server.
+ (MetabaseTask *)task;
- (void)launch;
/// Remove the task termination handler that pops up the 'Task died unexpectedly' alert.
/// For cases when we want to kill the Metabase task without freaking the user out, e.g. for Reset Password
- (void)disableTerminationAlert;
- (NSUInteger)port;
@end
//
// MetabaseTask.m
// Metabase
//
// Created by Cam Saul on 10/9/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
#import "MetabaseTask.h"
@interface MetabaseTask ()
@property (nonatomic) NSUInteger port;
@property (nonatomic, strong) NSMutableArray *lastMessages; ///< 10 most recently logged messages.
@end
@implementation MetabaseTask
+ (MetabaseTask *)task {
return [[MetabaseTask alloc] init];
}
#pragma mark - Local Methods
- (void)readHandleDidRead:(NSString *)message {
// skip calls to health endpoint
if ([message rangeOfString:@"GET /api/health"].location != NSNotFound) return;
// strip off the timestamp that comes back from the backend so we don't get double-timestamps when NSLog adds its own
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^[\\d-:,\\s]+(.*$)" options:NSRegularExpressionAnchorsMatchLines|NSRegularExpressionAllowCommentsAndWhitespace error:nil];
message = [regex stringByReplacingMatchesInString:message options:0 range:NSMakeRange(0, message.length) withTemplate:@"$1"];
// remove control codes used to color output
regex = [NSRegularExpression regularExpressionWithPattern:@"\\[\\d+m" options:0 error:nil];
message = [regex stringByReplacingMatchesInString:message options:0 range:NSMakeRange(0, message.length) withTemplate:@""];
// add the message to the recently logged messages. If we now have more than 5 remove the oldest
[self.lastMessages addObject:message];
if (self.lastMessages.count > 10) [self.lastMessages removeObjectAtIndex:0];
// log the message the normal way as well
NSLog(@"%@", message);
}
- (void)deleteOldDBLockFilesIfNeeded {
NSString *lockFile = [DBPath() stringByAppendingString:@".lock.db"];
NSString *traceFile = [DBPath() stringByAppendingString:@".trace.db"];
for (NSString *file in @[lockFile, traceFile]) {
if ([[NSFileManager defaultManager] fileExistsAtPath:file]) {
NSLog(@"Deleting %@...", file);
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:file error:&error];
if (error) {
NSLog(@"Error deleting %@: %@", file, error.localizedDescription);
}
}
}
}
- (void)launch {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self deleteOldDBLockFilesIfNeeded];
NSLog(@"Starting MetabaseTask @ 0x%zx...", (size_t)self);
self.task = [[NSTask alloc] init];
self.task.launchPath = JREPath();
self.task.environment = @{@"MB_DB_FILE": DBPath(),
@"MB_PLUGINS_DIR": PluginsDirPath(),
@"MB_JETTY_PORT": @(self.port),
@"MB_CLIENT": @"OSX"};
self.task.arguments = @[@"-Djava.awt.headless=true", // this prevents the extra java icon from popping up in the dock when running
@"-client", // make sure we're running in -client mode, which has a faster lanuch time
@"-Xverify:none", // disable bytecode verification for faster launch speed, not really needed here since JAR is packaged as part of signed .app
@"-jar", UberjarPath()];
__weak MetabaseTask *weakSelf = self;
self.task.terminationHandler = ^(NSTask *task){
NSLog(@"\n\n!!!!! Task terminated with exit code %d !!!!!\n\n", task.terminationStatus);
dispatch_async(dispatch_get_main_queue(), ^{
if ([[NSAlert alertWithMessageText:@"Fatal Error"
defaultButton:@"Restart"
alternateButton:@"Quit"
otherButton:nil
informativeTextWithFormat:@"The Metabase server terminated unexpectedly.\n\nMessages:\n%@", [weakSelf.lastMessages componentsJoinedByString:@""]] // components should already have newline at end
runModal] == NSAlertDefaultReturn) {
[weakSelf launch];
} else {
exit(task.terminationStatus);
}
});
};
NSLog(@"Launching MetabaseTask\nMB_DB_FILE='%@'\nMB_PLUGINS_DIR='%@'\nMB_JETTY_PORT=%lu\n%@ -jar %@", DBPath(), PluginsDirPath(), self.port, JREPath(), UberjarPath());
[self.task launch];
});
}
- (void)terminate {
[super terminate];
_port = 0;
}
- (void)disableTerminationAlert {
self.task.terminationHandler = nil;
}
#pragma mark - Getters / Setters
- (NSUInteger)port {
if (!_port) {
srand((unsigned)time(NULL));
_port = (rand() % 1000) + 13000;
NSLog(@"Using port %lu", _port);
}
return _port;
}
- (NSMutableArray *)lastMessages {
if (!_lastMessages) _lastMessages = [NSMutableArray array];
return _lastMessages;
}
@end
//
// ResetPasswordTask.h
// Metabase
//
// Created by Cam Saul on 11/24/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
#import "JavaTask.h"
@interface ResetPasswordTask : JavaTask
/// blocks are ran on main thread <3
- (void)resetPasswordForEmailAddress:(NSString *)emailAddress success:(void(^)(NSString *resetToken))successBlock error:(void(^)(NSString *errorMessage))errorBlock;
@end
//
// ResetPasswordTask.m
// Metabase
//
// Created by Cam Saul on 11/24/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
#import "AppDelegate.h"
#import "ResetPasswordTask.h"
@interface ResetPasswordTask ()
@property (copy) NSString *output;
@end
@implementation ResetPasswordTask
#pragma mark - Local Methods
- (void)resetPasswordForEmailAddress:(NSString *)emailAddress success:(void (^)(NSString *))successBlock error:(void (^)(NSString *))errorBlock {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// first, we need to stop the main Metabase task so we can access the DB
NSLog(@"Stopping Metabase task in order to reset password...");
[[AppDelegate instance] stopMetabaseTask];
self.task = [[NSTask alloc] init];
// time travelers from the future: this is hardcoded since I'm the only one who works on this. I give you permission to fix it - Cam
#define DEBUG_RUN_LEIN_TASK 0
#if DEBUG_RUN_LEIN_TASK
self.task.environment = @{@"MB_DB_FILE": DBPath()};
self.task.currentDirectoryPath = @"/Users/cam/metabase";
self.task.launchPath = @"/usr/local/bin/lein";
self.task.arguments = @[@"run", @"reset-password", emailAddress];
NSLog(@"Launching ResetPasswordTask\nMB_DB_FILE='%@' lein run reset-password %@", DBPath(), emailAddress);
#else
self.task.environment = @{@"MB_DB_FILE": DBPath()};
self.task.launchPath = JREPath();
self.task.arguments = @[@"-Djava.awt.headless=true", // this prevents the extra java icon from popping up in the dock when running
@"-Xverify:none", // disable bytecode verification for faster launch speed, not really needed here since JAR is packaged as part of signed .app
@"-jar", UberjarPath(),
@"reset-password", emailAddress];
NSLog(@"Launching ResetPasswordTask\nMB_DB_FILE='%@' %@ -jar %@ reset-password %@", DBPath(), JREPath(), UberjarPath(), emailAddress);
#endif
__weak ResetPasswordTask *weakSelf = self;
self.task.terminationHandler = ^(NSTask *task) {
NSLog(@"ResetPasswordTask terminated with status: %d", task.terminationStatus);
[weakSelf terminate];
dispatch_async(dispatch_get_main_queue(), ^{
if (!task.terminationStatus && weakSelf.output.length >= 38) { // should be of format <user-id>_<36-char-uuid>, e.g. "1_b20466b9-1f5b-488d-8ab6-5039107482f8"
successBlock(weakSelf.output);
} else {
errorBlock(weakSelf.output.length ? weakSelf.output : @"An unknown error has occured.");
}
// now restart the main Metabase task
NSLog(@"Reset password complete, restarting Metabase task...");
[[AppDelegate instance] startMetabaseTask];
});
};
[self.task launch];
});
}
- (void)readHandleDidRead:(NSString *)message {
NSLog(@"[PasswordResetTask] %@", message);
/// output comes back like "STATUS [[[message]]]"
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^(?:(?:OK)||(?:FAIL))\\s+\\[\\[\\[(.+)\\]\\]\\]\\s*$" options:NSRegularExpressionAnchorsMatchLines|NSRegularExpressionAllowCommentsAndWhitespace error:NULL];
if (![regex numberOfMatchesInString:message options:0 range:NSMakeRange(0, message.length)]) return;
NSString *result = [regex stringByReplacingMatchesInString:message options:0 range:NSMakeRange(0, message.length) withTemplate:@"$1"];
if (result) {
self.output = result;
NSLog(@"[PasswordResetTask] task output is '%@'", self.output);
}
}
@end
//
// SettingsManager.h
// Metabase
//
// Created by Cam Saul on 9/22/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
NSString *LocalHostBaseURL();
@interface SettingsManager : NSObject
+ (instancetype)instance;
@property (copy) NSString *baseURL;
@end
//
// SettingsManager.m
// Metabase
//
// Created by Cam Saul on 9/22/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
#import "AppDelegate.h"
#import "SettingsManager.h"
static SettingsManager *sSettingsManager = nil;
static NSString * const BaseURLUserDefaultsKey = @"com.metabase.baseURL";
NSString *LocalHostBaseURL() {
return [NSString stringWithFormat:@"http://localhost:%lu", [AppDelegate instance].port];
}
@implementation SettingsManager
+ (instancetype)instance {
@synchronized(self) {
if (!sSettingsManager) sSettingsManager = [[SettingsManager alloc] init];
}
return sSettingsManager;
}
#pragma mark - Getters / Setters
- (NSString *)baseURL {
return [[NSUserDefaults standardUserDefaults] objectForKey:BaseURLUserDefaultsKey];
}
- (void)setBaseURL:(NSString *)baseURL {
[[NSUserDefaults standardUserDefaults] setObject:[baseURL copy] forKey:BaseURLUserDefaultsKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
@end
//
// TaskHealthChecker.h
// Metabase
//
// Created by Cam Saul on 10/9/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
static NSString * const MetabaseTaskBecameHealthyNotification = @"MetabaseTaskBecameHealthyNotification";
static NSString * const MetabaseTaskBecameUnhealthyNotification = @"MetabaseTaskBecameUnhealthyNotification";
/// Manages the MetabaseTask (server) and restarts it if it gets unresponsive
@interface TaskHealthChecker : NSObject
@property () NSUInteger port;
/// (re)start the health checker
- (void)start;
- (void)stop;
@end
//
// TaskHealthChecker.m
// Metabase
//
// Created by Cam Saul on 10/9/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
#import "TaskHealthChecker.h"
/// Check out health every this many seconds
static const CGFloat HealthCheckIntervalSeconds = 1.0f;
/// This number should be lower than HealthCheckIntervalSeconds so requests don't end up piling up
static const CGFloat HealthCheckRequestTimeout = 1.75f;
@interface TaskHealthChecker ()
@property (strong, nonatomic) NSOperationQueue *healthCheckOperationQueue;
@property (strong, nonatomic) NSTimer *healthCheckTimer;
@property (nonatomic) BOOL healthy;
/// Set this to YES after server has started successfully one time
/// we'll hold of on the whole killing the Metabase server until it launches one time, I guess
@property (nonatomic) BOOL hasEverBeenHealthy;
@end
@implementation TaskHealthChecker
- (void)dealloc {
[self stop];
}
#pragma mark - Local Methods
- (void)start {
NSLog(@"(re)starting health checker @ 0x%zx...", (size_t)self);
self.healthCheckOperationQueue = [[NSOperationQueue alloc] init];
self.healthCheckOperationQueue.maxConcurrentOperationCount = 1;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
self.healthCheckTimer = [NSTimer timerWithTimeInterval:HealthCheckIntervalSeconds target:self selector:@selector(checkHealth) userInfo:nil repeats:YES];
self.healthCheckTimer.tolerance = HealthCheckIntervalSeconds / 2.0f;
[[NSRunLoop mainRunLoop] addTimer:self.healthCheckTimer forMode:NSRunLoopCommonModes];
});
}
- (void)stop {
self.healthCheckTimer = nil;
self.healthCheckOperationQueue = nil;
}
- (void)checkHealth:(void(^)(BOOL healthy))completion {
/// Cancel any pending checks so they don't pile up indefinitely
[self.healthCheckOperationQueue cancelAllOperations];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:%lu/api/health", self.port]] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:HealthCheckRequestTimeout];
[NSURLConnection sendAsynchronousRequest:request queue:self.healthCheckOperationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
completion(NO);
return;
}
NSError *jsonError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
if (jsonError) {
completion(NO);
return;
}
completion([json[@"status"] isEqualToString:@"ok"]);
}];
}
- (void)checkHealth {
// run the health check on the high-priorty GCD queue because it's imperative that it complete so we can get an accurate picture of Mac App health
__weak TaskHealthChecker *weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[weakSelf checkHealth:^(BOOL healthy) {
if (!healthy) NSLog(@"😷");
if (healthy && !weakSelf.healthy) NSLog(@"✅");
weakSelf.healthy = healthy;
}];
});
}
#pragma mark - Getters / Setters
- (void)setHealthCheckTimer:(NSTimer *)healthCheckTimer {
[_healthCheckTimer invalidate];
_healthCheckTimer = healthCheckTimer;
}
- (void)setHealthCheckOperationQueue:(NSOperationQueue *)healthCheckOperationQueue {
[_healthCheckOperationQueue cancelAllOperations];
_healthCheckOperationQueue = healthCheckOperationQueue;
}
- (void)setHealthy:(BOOL)healthy {
if (_healthy == healthy) return;
_healthy = healthy;
NSLog(@"Server is now %@", healthy ? @"HEALTHY" : @"UNHEALTHY");
__weak TaskHealthChecker *weakSelf = self;
NSString *notification = healthy ? MetabaseTaskBecameHealthyNotification : MetabaseTaskBecameUnhealthyNotification;
[[NSNotificationCenter defaultCenter] postNotificationName:notification object:weakSelf];
}
@end
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "Logo_16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "Logo_32.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "Logo_32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "Logo_64.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "Logo_128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "Logo_256.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "Logo_256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "Logo_512.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "Logo_512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "Logo_1024.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.34.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.34.3</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2015 Metabase. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>SUEnableAutomaticChecks</key>
<true/>
<key>SUFeedURL</key>
<string>https://downloads.metabase.com/appcast.xml</string>
<key>SUPublicDSAKeyFile</key>
<string>dsa_pub.pem</string>
</dict>
</plist>
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#ifdef __OBJC__
@import Cocoa;
#endif
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
//
// LoadingView.h
// Metabase
//
// Created by Cam Saul on 10/14/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
@import Cocoa;
@interface LoadingView : NSView
@property (nonatomic) BOOL animate;
@end
//
// LoadingView.m
// Metabase
//
// Created by Cam Saul on 10/14/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
@import QuartzCore;
#import "LoadingView.h"
@interface LoadingView ()
@property (weak) IBOutlet NSImageView *outerImageView;
@end
@implementation LoadingView
#pragma mark - Lifecycle
- (instancetype)init {
NSArray *objects;
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil topLevelObjects:&objects];
for (id obj in objects) {
if ([obj isMemberOfClass:[self class]]) return obj;
}
return nil;
}
- (void)awakeFromNib {
self.outerImageView.wantsLayer = YES;
}
#pragma mark - Getters / Setters
- (void)setAnimate:(BOOL)animate {
if (_animate == animate) return;
_animate = animate;
if (!animate) {
[self.outerImageView.layer removeAllAnimations];
} else {
self.outerImageView.layer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
self.outerImageView.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
CABasicAnimation *rotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotation.fromValue = @(M_PI * 2.0f);
rotation.toValue = @0.0f;
rotation.duration = 2.0f;
rotation.repeatCount = HUGE_VALF;
[self.outerImageView.layer addAnimation:rotation forKey:@"rotation"];
}
}
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5053" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5053"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="c22-O7-iKe" customClass="LoadingView">
<rect key="frame" x="0.0" y="0.0" width="200" height="200"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView canDrawConcurrently="YES" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="cZb-9N-gBv" userLabel="outerImageView">
<rect key="frame" x="6" y="6" width="188" height="188"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" image="Loading_View_Outer" id="gdw-zk-O24"/>
</imageView>
<imageView canDrawConcurrently="YES" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ppb-yU-gW5" userLabel="innerImageView">
<rect key="frame" x="42" y="42" width="116" height="116"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" image="Loading_View_Inner" id="umY-yg-KA6"/>
</imageView>
</subviews>
<constraints>
<constraint firstAttribute="centerY" secondItem="Ppb-yU-gW5" secondAttribute="centerY" id="DZe-EF-QxD"/>
<constraint firstAttribute="centerX" secondItem="Ppb-yU-gW5" secondAttribute="centerX" id="Xtt-Ah-8QM"/>
<constraint firstAttribute="centerY" secondItem="cZb-9N-gBv" secondAttribute="centerY" id="YLx-iu-oop"/>
<constraint firstAttribute="centerX" secondItem="cZb-9N-gBv" secondAttribute="centerX" id="aJ4-b5-5M8"/>
</constraints>
<connections>
<outlet property="outerImageView" destination="cZb-9N-gBv" id="K4s-1M-P8D"/>
</connections>
</customView>
</objects>
<resources>
<image name="Loading_View_Inner" width="116" height="116"/>
<image name="Loading_View_Outer" width="188" height="188"/>
</resources>
</document>
This diff is collapsed.
//
// MainViewController.h
// Metabase
//
// Created by Cam Saul on 9/21/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
@import Cocoa;
@interface MainViewController : NSViewController
@end
This diff is collapsed.
//
// ResetPasswordWindowController.h
// Metabase
//
// Created by Cam Saul on 11/24/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
@import Cocoa;
@class ResetPasswordWindowController;
@protocol ResetPasswordWindowControllerDelegate <NSObject>
- (void)resetPasswordWindowController:(ResetPasswordWindowController *)resetPasswordWindowController didFinishWithResetToken:(NSString *)resetToken;
@end
@interface ResetPasswordWindowController : NSWindowController
@property (weak) id<ResetPasswordWindowControllerDelegate> delegate;
@end
//
// ResetPasswordWindowController.m
// Metabase
//
// Created by Cam Saul on 11/24/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
#import <objc/runtime.h>
#import "ResetPasswordTask.h"
#import "ResetPasswordWindowController.h"
@interface ResetPasswordWindowController () <NSTextFieldDelegate>
@property (weak) IBOutlet NSButton *resetPasswordButton;
@property (weak) IBOutlet NSTextField *emailAddressTextField;
@property (nonatomic, strong) ResetPasswordTask *resetPasswordTask;
@end
@implementation ResetPasswordWindowController
#pragma mark - Lifecycle
- (NSString *)windowNibName {
return NSStringFromClass(self.class);
}
- (void)windowDidLoad {
[super windowDidLoad];
}
#pragma mark - Actions
- (IBAction)resetPasswordButtonPressed:(NSButton *)sender {
self.resetPasswordButton.enabled = NO;
self.resetPasswordButton.title = @"One moment...";
self.emailAddressTextField.enabled = NO;
self.resetPasswordTask = [[ResetPasswordTask alloc] init];
[self.resetPasswordTask resetPasswordForEmailAddress:self.emailAddressTextField.stringValue success:^(NSString *resetToken) {
self.emailAddressTextField.enabled = YES;
self.resetPasswordButton.title = @"Success!";
NSLog(@"Got reset token: '%@'", resetToken);
[self.delegate resetPasswordWindowController:self didFinishWithResetToken:resetToken];
} error:^(NSString *errorMessage) {
self.emailAddressTextField.enabled = YES;
self.resetPasswordButton.enabled = YES;
self.resetPasswordButton.title = @"Reset Password";
[[NSAlert alertWithMessageText:@"Password Reset Failed" defaultButton:@"Done" alternateButton:nil otherButton:nil informativeTextWithFormat:@"%@", errorMessage] runModal];
}];
}
- (IBAction)emailAddressTextFieldDidReturn:(id)sender {
if (self.resetPasswordButton.isEnabled) [self resetPasswordButtonPressed:self.resetPasswordButton];
}
#pragma mark - NSTextFieldDelegate
- (void)controlTextDidChange:(NSNotification *)obj {
self.resetPasswordButton.title = @"Reset Password";
self.resetPasswordButton.enabled = self.emailAddressTextField.stringValue.length > 5;
}
@end
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5053" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5053"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ResetPasswordWindowController">
<connections>
<outlet property="emailAddressTextField" destination="SGZ-eW-nZQ" id="aMf-FE-ey1"/>
<outlet property="resetPasswordButton" destination="1wF-h5-c8o" id="N1J-e1-a4y"/>
<outlet property="window" destination="FU9-gs-izu" id="SSm-mw-LIb"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<window title="Reset Password" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="FU9-gs-izu">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="283" y="305" width="492" height="229"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
<view key="contentView" id="3rs-Tz-giP">
<rect key="frame" x="0.0" y="0.0" width="492" height="229"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="249" verticalHuggingPriority="249" horizontalCompressionResistancePriority="240" verticalCompressionResistancePriority="800" translatesAutoresizingMaskIntoConstraints="NO" id="oEx-8w-YG4">
<rect key="frame" x="14" y="175" width="464" height="34"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="460" id="qwI-wW-wTd"/>
</constraints>
<textFieldCell key="cell" enabled="NO" refusesFirstResponder="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="Enter the email address you used to create your Metabase account on this computer and we'll get started changing your password." id="Lgy-GU-YOj">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1wF-h5-c8o">
<rect key="frame" x="178" y="13" width="136" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Reset Password" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="YII-IA-Kyg">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="resetPasswordButtonPressed:" target="-2" id="Y6X-98-Hsn"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8ft-6m-5T0">
<rect key="frame" x="200" y="126" width="92" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" enabled="NO" refusesFirstResponder="YES" sendsActionOnEndEditing="YES" title="Email Address" id="ETj-8f-dsZ">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SGZ-eW-nZQ">
<rect key="frame" x="118" y="96" width="256" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="width" constant="256" id="8Fr-91-TXf"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" alignment="center" placeholderString="cam@toucans4lyfe.org" drawsBackground="YES" id="mP8-DI-PfW">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<action selector="emailAddressTextFieldDidReturn:" target="-2" id="JLo-r8-R9z"/>
<outlet property="delegate" destination="-2" id="h0P-Zb-hJJ"/>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="oEx-8w-YG4" firstAttribute="leading" secondItem="3rs-Tz-giP" secondAttribute="leading" constant="16" id="056-xy-j5r"/>
<constraint firstItem="8ft-6m-5T0" firstAttribute="top" relation="greaterThanOrEqual" secondItem="oEx-8w-YG4" secondAttribute="bottom" constant="32" id="2Jb-ua-CCI"/>
<constraint firstAttribute="centerX" secondItem="SGZ-eW-nZQ" secondAttribute="centerX" id="90I-fl-PKI"/>
<constraint firstItem="1wF-h5-c8o" firstAttribute="top" relation="greaterThanOrEqual" secondItem="SGZ-eW-nZQ" secondAttribute="bottom" constant="32" id="CM1-Nh-mnk"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="SGZ-eW-nZQ" secondAttribute="trailing" constant="16" id="NHO-1C-XOJ"/>
<constraint firstItem="SGZ-eW-nZQ" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="3rs-Tz-giP" secondAttribute="leading" constant="16" id="On6-0w-5PF"/>
<constraint firstAttribute="bottom" secondItem="1wF-h5-c8o" secondAttribute="bottom" constant="20" id="VRO-Qm-4vb"/>
<constraint firstItem="oEx-8w-YG4" firstAttribute="top" secondItem="3rs-Tz-giP" secondAttribute="top" constant="20" id="bzN-6L-86m"/>
<constraint firstItem="SGZ-eW-nZQ" firstAttribute="top" secondItem="8ft-6m-5T0" secondAttribute="bottom" constant="8" id="e7T-c5-OYs"/>
<constraint firstAttribute="centerY" secondItem="SGZ-eW-nZQ" secondAttribute="centerY" constant="-8" id="mTu-yO-Smv"/>
<constraint firstAttribute="trailing" secondItem="oEx-8w-YG4" secondAttribute="trailing" constant="16" id="shZ-a7-r8y"/>
<constraint firstAttribute="centerX" secondItem="1wF-h5-c8o" secondAttribute="centerX" id="vH7-Xk-LcO"/>
<constraint firstAttribute="centerX" secondItem="8ft-6m-5T0" secondAttribute="centerX" id="vh8-pN-ohL"/>
</constraints>
</view>
</window>
</objects>
</document>
{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;}
{\colortbl;\red255\green255\blue255;}
\paperw9840\paperh8400
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\b\fs24 \cf0 Engineering:
\b0 \
Some people\
\
\b Human Interface Design:
\b0 \
Some other people\
\
\b Testing:
\b0 \
Hopefully not nobody\
\
\b Documentation:
\b0 \
Whoever\
\
\b With special thanks to:
\b0 \
Mom\
}
/* Localized versions of Info.plist keys */
//
// main.m
// Metabase
//
// Created by Cam Saul on 9/21/15.
// Copyright (c) 2015 Metabase. All rights reserved.
//
@import Cocoa;
int main(int argc, const char * argv[])
{
return NSApplicationMain(argc, argv);
}
-----BEGIN PUBLIC KEY-----
MIIGOjCCBC0GByqGSM44BAEwggQgAoICAQC7RwleUWvm0dR07ENWdXslkpZEs1e6
B32itoDF1sm414IMzm2OoHspBqfGf3nQrDcZCW5cngtdLg5eS3x00Ssfdz8j+zHz
7sH5sMOipdUzxY0LS2qfOW0E5HKeSstLi5qpyE3IDOoQs57KRMeIlk0jPYml8rA7
z1CTgEe18eSVcNgG2oceamNT9+dE5KleaOmuyV4RuPQF9n76kssMxN4qvP38AMI0
MjEMKzwF8HsPS4ALPyf4S27ACNJU3Ws/clqDD9UWzsbB7gYtZYkqUjIJKbPqH1sm
ldGXQV5safL0f/kT4r7r+pSae669BeYEHG2k7JrOccQYfmL0arfhq5ucTYX3nd0v
XoEFjxmue4ofMpjgFBu/GstVbQl37oxihAwG7jxGbk9uzihGek6HfnKpbkVUAUiH
LhOvZXUJ6cO+FH4+KCBHsl9HwU0leiBLJ40xnNmWs42moG6LE4Hhs8Uco/9Vpj6R
0FWtoONiUtvXiAUY3EWQBRpb5uEnqD/r3HYTSpqwYWJkauOR0/DkvvIwmAi0NaO4
bViiOz+luvn5p/6Le21qOdPopdArRXvQ3bSiopV7P0G6uwnQVtwS+A+rUfDZXSza
kNZBhK8lOILvk04kp+H4jjdeDGqOe/6HTBrkiVeQsQKla3h0V4ltRkVyBtfbNj6w
4asW5IsAKiCD2wIVAPy6gMCR+CERLTMkp4qoUYkpMiiXAoICABrhiisnOCqBSqR+
m+4RRv2aE0pRSJ8XDfA7Kbzvssb31DP8p2AaQH9DZRVlaIEj/PUwO3HHAg6FeV3j
zLbDBkUHQKAlUb8JsonTthK6i7bC4ZfDge2JndLLvhi345P0dBL7gEZ/91jSYv7p
QF/bBZQ6FDp4Fpm5cqbg5FXaXJPneaqDd+VqgqZclgfihu92j+HvL6UhWtFdT/DB
JZTezjIJ4ox299vcEfCfhXXZYiNH6jv+guUt6reAVUobiQOk0QhQhNGXlSdUNs4f
NQ6ABE4RiB+iRy1PkoAZW4oZtBdBdmEKE8eC0skSqwoLf/XCljv/VySvO0ZO6bBg
FmZ3yhDgMLEwAzCnjGuaarBl5KxoN7Y7FBQjIugbYlNnr5nxDR5JcDqli+q/NEZT
qc6c3wf9rqEB9JxMxtkWWcqD7jJBIUiapUG3byieoDaaHi/cvxgnGTGI4nyDPge9
hpPoP2WBU840wLDjZGxNyNQ78QwUHFIW5v0lPCwdIndZPQJq20z1YZ22lH7azOmB
NssDVVWSeqn2+DwG275nfK1Qv+V8ww/x0+8wYqOMjaGypMeS0g0MpBBJHZjVQ5Ku
NH+p9/QjvElk43tLq33cJYnFU/EPVqypE94kbH4bvHpOuysNtrbnvptXLEASO1V3
sl0OAITKHVDZvvuDsXFPdhXQ9pUVA4ICBQACggIAQ7wfn+Fi2j1Ka5YPgTDf4LLA
4Z6vvdKcp3+gWnG0oX/X6n5fw//mu7tp2POMCEUm7ANsdTFUqKBfJvcYL15qZCUr
9D50prWTr4l7A+/NBv5qv/9STDEjccT7Vr5q0pJf6NOkNr/ydGB+0WZEBt/Cpnba
dAcAKm2BgSrfbb7gIXvOMm7/N+gfYqhYavWKeXv65k8bTfN9UX4odGnz4x0YfUe3
OOank9ciKwqgDxPCY6mlqe6SuWrfboMimBrZzGUZKMqsAq8CNjH7wwv5XvGyLTMK
kqysS7fljvhmMf6A1adSxcn3vyckoXP0gHYE/pnn+lrQIpnRzC0x3pbxBYIrV7Qi
C93YtZAkH5UBIY2q3GL4YmNEIFI6jsJSWLVAxzxqaKbRe3M7FUtkCA2ae3WD+n8e
XNTCgZmQ0/4syz/3AH8aEaE1ujVE7K9J0Gl4GB8flrXOoKpmMC29zbOg5yuPG+N2
w5uYmDmmRcOE3dYtSORVzNGObFg9bhvqa3H5zdasC6xCX9/XNFk6Cg693erYpDx3
KKR6FgQN5qjSrVBBSn/+5JYFntGCs28CIuS3Fd+/Kp9/ZKExBT8AOfU1dXlNNnV3
Ho77JDPPvJ/mCZwMDERrirU7K794qZ6EdSxzEaaj1VsEkG/nxOEo/3QmSGhr+sJU
2Rp2O05XdAhgjiObhhg=
-----END PUBLIC KEY-----
Versions/Current/Headers
\ No newline at end of file
Versions/Current/Modules
\ No newline at end of file
Versions/Current/PrivateHeaders
\ No newline at end of file
Versions/Current/Resources
\ No newline at end of file
Versions/Current/Sparkle
\ No newline at end of file
//
// SUAppcast.h
// Sparkle
//
// Created by Andy Matuschak on 3/12/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#ifndef SUAPPCAST_H
#define SUAPPCAST_H
#import <Foundation/Foundation.h>
#import "SUExport.h"
@class SUAppcastItem;
SU_EXPORT @interface SUAppcast : NSObject<NSURLDownloadDelegate>
@property (copy) NSString *userAgentString;
@property (copy) NSDictionary *httpHeaders;
- (void)fetchAppcastFromURL:(NSURL *)url completionBlock:(void (^)(NSError *))err;
@property (readonly, copy) NSArray *items;
@end
#endif
//
// SUAppcastItem.h
// Sparkle
//
// Created by Andy Matuschak on 3/12/06.
// Copyright 2006 Andy Matuschak. All rights reserved.
//
#ifndef SUAPPCASTITEM_H
#define SUAPPCASTITEM_H
#import <Foundation/Foundation.h>
#import "SUExport.h"
SU_EXPORT @interface SUAppcastItem : NSObject
@property (copy, readonly) NSString *title;
@property (copy, readonly) NSDate *date;
@property (copy, readonly) NSString *itemDescription;
@property (strong, readonly) NSURL *releaseNotesURL;
@property (copy, readonly) NSString *DSASignature;
@property (copy, readonly) NSString *minimumSystemVersion;
@property (copy, readonly) NSString *maximumSystemVersion;
@property (strong, readonly) NSURL *fileURL;
@property (copy, readonly) NSString *versionString;
@property (copy, readonly) NSString *displayVersionString;
@property (copy, readonly) NSDictionary *deltaUpdates;
@property (strong, readonly) NSURL *infoURL;
// Initializes with data from a dictionary provided by the RSS class.
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString **)error;
@property (getter=isDeltaUpdate, readonly) BOOL deltaUpdate;
@property (getter=isCriticalUpdate, readonly) BOOL criticalUpdate;
// Returns the dictionary provided in initWithDictionary; this might be useful later for extensions.
@property (readonly, copy) NSDictionary *propertiesDictionary;
- (NSURL *)infoURL;
@end
#endif
//
// SUErrors.h
// Sparkle
//
// Created by C.W. Betts on 10/13/14.
// Copyright (c) 2014 Sparkle Project. All rights reserved.
//
#ifndef SUERRORS_H
#define SUERRORS_H
#import <Foundation/Foundation.h>
#import "SUExport.h"
/**
* Error domain used by Sparkle
*/
SU_EXPORT extern NSString *const SUSparkleErrorDomain;
typedef NS_ENUM(OSStatus, SUError) {
// Appcast phase errors.
SUAppcastParseError = 1000,
SUNoUpdateError = 1001,
SUAppcastError = 1002,
SURunningFromDiskImageError = 1003,
// Downlaod phase errors.
SUTemporaryDirectoryError = 2000,
// Extraction phase errors.
SUUnarchivingError = 3000,
SUSignatureError = 3001,
// Installation phase errors.
SUFileCopyFailure = 4000,
SUAuthenticationFailure = 4001,
SUMissingUpdateError = 4002,
SUMissingInstallerToolError = 4003,
SURelaunchError = 4004,
SUInstallationError = 4005,
SUDowngradeError = 4006
};
#endif
//
// SUExport.h
// Sparkle
//
// Created by Jake Petroules on 2014-08-23.
// Copyright (c) 2014 Sparkle Project. All rights reserved.
//
#ifndef SUEXPORT_H
#define SUEXPORT_H
#ifdef BUILDING_SPARKLE
#define SU_EXPORT __attribute__((visibility("default")))
#else
#define SU_EXPORT
#endif
#endif
//
// SUVersionComparisonProtocol.h
// Sparkle
//
// Created by Andy Matuschak on 12/21/07.
// Copyright 2007 Andy Matuschak. All rights reserved.
//
#ifndef SUVERSIONCOMPARISONPROTOCOL_H
#define SUVERSIONCOMPARISONPROTOCOL_H
#import <Cocoa/Cocoa.h>
#import "SUExport.h"
/*!
Provides version comparison facilities for Sparkle.
*/
@protocol SUVersionComparison
/*!
An abstract method to compare two version strings.
Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a,
and NSOrderedSame if they are equivalent.
*/
- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; // *** MAY BE CALLED ON NON-MAIN THREAD!
@end
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment