diff --git a/pype/ftrack/actions/action_application_loader.py b/pype/ftrack/actions/action_application_loader.py
index 67a158db14..bc126fc691 100644
--- a/pype/ftrack/actions/action_application_loader.py
+++ b/pype/ftrack/actions/action_application_loader.py
@@ -37,6 +37,9 @@ def registerApp(app, session):
description = apptoml.get('description', None)
preactions = apptoml.get('preactions', [])
+ if icon:
+ icon = icon.format(os.environ.get('PYPE_STATICS_SERVER', ''))
+
# register action
AppAction(
session, label, name, executable, variant,
diff --git a/pype/ftrack/actions/action_component_open.py b/pype/ftrack/actions/action_component_open.py
index c40a04b2fd..d3213c555a 100644
--- a/pype/ftrack/actions/action_component_open.py
+++ b/pype/ftrack/actions/action_component_open.py
@@ -1,8 +1,8 @@
+import os
import sys
import argparse
import logging
import subprocess
-import os
from pype.vendor import ftrack_api
from pype.ftrack import BaseAction
@@ -15,9 +15,8 @@ class ComponentOpen(BaseAction):
# Action label
label = 'Open File'
# Action icon
- icon = (
- 'https://cdn4.iconfinder.com/data/icons/rcons-application/32/'
- 'application_go_run-256.png'
+ icon = '{}/ftrack/action_icons/ComponentOpen.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
def discover(self, session, entities, event):
diff --git a/pype/ftrack/actions/action_create_cust_attrs.py b/pype/ftrack/actions/action_create_cust_attrs.py
index 9f2564406a..7dd8335ecc 100644
--- a/pype/ftrack/actions/action_create_cust_attrs.py
+++ b/pype/ftrack/actions/action_create_cust_attrs.py
@@ -114,10 +114,8 @@ class CustomAttributes(BaseAction):
description = 'Creates Avalon/Mongo ID for double check'
#: roles that are allowed to register this action
role_list = ['Pypeclub', 'Administrator']
- icon = (
- 'https://cdn4.iconfinder.com/data/icons/'
- 'ios-web-user-interface-multi-circle-flat-vol-4/512/'
- 'Bullet_list_menu_lines_points_items_options-512.png'
+ icon = '{}/ftrack/action_icons/CustomAttributes.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
def __init__(self, session):
diff --git a/pype/ftrack/actions/action_create_folders.py b/pype/ftrack/actions/action_create_folders.py
index 9c8f576e3b..2a777911b4 100644
--- a/pype/ftrack/actions/action_create_folders.py
+++ b/pype/ftrack/actions/action_create_folders.py
@@ -22,10 +22,10 @@ class CreateFolders(BaseAction):
label = 'Create Folders'
#: Action Icon.
- icon = (
- 'https://cdn1.iconfinder.com/data/icons/hawcons/32/'
- '698620-icon-105-folder-add-512.png'
+ icon = '{}/ftrack/action_icons/CreateFolders.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
+
db = DbConnector()
def discover(self, session, entities, event):
diff --git a/pype/ftrack/actions/action_create_project_folders.py b/pype/ftrack/actions/action_create_project_folders.py
index dba2a88f02..3ccdb08714 100644
--- a/pype/ftrack/actions/action_create_project_folders.py
+++ b/pype/ftrack/actions/action_create_project_folders.py
@@ -20,9 +20,8 @@ class CreateProjectFolders(BaseAction):
description = 'Creates folder structure'
#: roles that are allowed to register this action
role_list = ['Pypeclub', 'Administrator']
- icon = (
- 'https://cdn2.iconfinder.com/data/icons/'
- 'buttons-9/512/Button_Add-01.png'
+ icon = '{}/ftrack/action_icons/CreateProjectFolders.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
pattern_array = re.compile('\[.*\]')
diff --git a/pype/ftrack/actions/action_delete_asset.py b/pype/ftrack/actions/action_delete_asset.py
index 838a77570f..eabadecee6 100644
--- a/pype/ftrack/actions/action_delete_asset.py
+++ b/pype/ftrack/actions/action_delete_asset.py
@@ -1,3 +1,4 @@
+import os
import sys
import logging
from bson.objectid import ObjectId
@@ -16,10 +17,8 @@ class DeleteAsset(BaseAction):
label = 'Delete Asset/Subsets'
#: Action description.
description = 'Removes from Avalon with all childs and asset from Ftrack'
- icon = (
- 'https://cdn4.iconfinder.com/data/icons/'
- 'ios-web-user-interface-multi-circle-flat-vol-5/512/'
- 'Delete_dustbin_empty_recycle_recycling_remove_trash-512.png'
+ icon = '{}/ftrack/action_icons/DeleteAsset.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
#: roles that are allowed to register this action
role_list = ['Pypeclub', 'Administrator']
diff --git a/pype/ftrack/actions/action_delete_asset_byname.py b/pype/ftrack/actions/action_delete_asset_byname.py
index 9da60ce763..fa966096a8 100644
--- a/pype/ftrack/actions/action_delete_asset_byname.py
+++ b/pype/ftrack/actions/action_delete_asset_byname.py
@@ -1,3 +1,4 @@
+import os
import sys
import logging
import argparse
@@ -17,10 +18,8 @@ class AssetsRemover(BaseAction):
description = 'Removes assets from Ftrack and Avalon db with all childs'
#: roles that are allowed to register this action
role_list = ['Pypeclub', 'Administrator']
- icon = (
- 'https://cdn4.iconfinder.com/data/icons/'
- 'ios-web-user-interface-multi-circle-flat-vol-5/512/'
- 'Clipboard_copy_delete_minus_paste_remove-512.png'
+ icon = '{}/ftrack/action_icons/AssetsRemover.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
#: Db
db = DbConnector()
diff --git a/pype/ftrack/actions/action_djvview.py b/pype/ftrack/actions/action_djvview.py
index 29d4604968..e0c0334e5f 100644
--- a/pype/ftrack/actions/action_djvview.py
+++ b/pype/ftrack/actions/action_djvview.py
@@ -16,7 +16,9 @@ class DJVViewAction(BaseAction):
identifier = "djvview-launch-action"
label = "DJV View"
description = "DJV View Launcher"
- icon = "http://a.fsdn.com/allura/p/djv/icon"
+ icon = '{}/app_icons/djvView.png'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
+ )
type = 'Application'
def __init__(self, session):
diff --git a/pype/ftrack/actions/action_job_killer.py b/pype/ftrack/actions/action_job_killer.py
index 8cbddecb1c..44acb24d55 100644
--- a/pype/ftrack/actions/action_job_killer.py
+++ b/pype/ftrack/actions/action_job_killer.py
@@ -1,3 +1,4 @@
+import os
import sys
import argparse
import logging
@@ -18,9 +19,8 @@ class JobKiller(BaseAction):
description = 'Killing selected running jobs'
#: roles that are allowed to register this action
role_list = ['Pypeclub', 'Administrator']
- icon = (
- 'https://cdn2.iconfinder.com/data/icons/new-year-resolutions/64/'
- 'resolutions-23-512.png'
+ icon = '{}/ftrack/action_icons/JobKiller.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
def discover(self, session, entities, event):
diff --git a/pype/ftrack/actions/action_multiple_notes.py b/pype/ftrack/actions/action_multiple_notes.py
index 2d93f64242..338083fe47 100644
--- a/pype/ftrack/actions/action_multiple_notes.py
+++ b/pype/ftrack/actions/action_multiple_notes.py
@@ -1,3 +1,4 @@
+import os
import sys
import argparse
import logging
@@ -15,9 +16,8 @@ class MultipleNotes(BaseAction):
label = 'Multiple Notes'
#: Action description.
description = 'Add same note to multiple Asset Versions'
- icon = (
- 'https://cdn2.iconfinder.com/data/icons/'
- 'mixed-rounded-flat-icon/512/note_1-512.png'
+ icon = '{}/ftrack/action_icons/MultipleNotes.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
def discover(self, session, entities, event):
diff --git a/pype/ftrack/actions/action_rv.py b/pype/ftrack/actions/action_rv.py
index f07d339e5c..3832dffae4 100644
--- a/pype/ftrack/actions/action_rv.py
+++ b/pype/ftrack/actions/action_rv.py
@@ -17,7 +17,9 @@ class RVAction(BaseAction):
identifier = "rv.launch.action"
label = "rv"
description = "rv Launcher"
- icon = "https://img.icons8.com/color/48/000000/circled-play.png"
+ icon = '{}/ftrack/action_icons/RV.png'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
+ )
type = 'Application'
def __init__(self, session):
diff --git a/pype/ftrack/actions/action_sync_to_avalon_local.py b/pype/ftrack/actions/action_sync_to_avalon_local.py
index 1056b5ee55..54fd0b47f8 100644
--- a/pype/ftrack/actions/action_sync_to_avalon_local.py
+++ b/pype/ftrack/actions/action_sync_to_avalon_local.py
@@ -50,9 +50,8 @@ class SyncToAvalon(BaseAction):
#: Action description.
description = 'Send data from Ftrack to Avalon'
#: Action icon.
- icon = (
- 'https://cdn1.iconfinder.com/data/icons/hawcons/32/'
- '699650-icon-92-inbox-download-512.png'
+ icon = '{}/ftrack/action_icons/SyncToAvalon-local.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
#: roles that are allowed to register this action
role_list = ['Pypeclub']
diff --git a/pype/ftrack/actions/action_test.py b/pype/ftrack/actions/action_test.py
index 36adb99074..dcb9dd32d0 100644
--- a/pype/ftrack/actions/action_test.py
+++ b/pype/ftrack/actions/action_test.py
@@ -1,8 +1,8 @@
+import os
import sys
import argparse
import logging
import collections
-import os
import json
import re
@@ -27,9 +27,8 @@ class TestAction(BaseAction):
priority = 10000
#: roles that are allowed to register this action
role_list = ['Pypeclub']
- icon = (
- 'https://cdn4.iconfinder.com/data/icons/hospital-19/512/'
- '8_hospital-512.png'
+ icon = '{}/ftrack/action_icons/TestAction.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
def discover(self, session, entities, event):
diff --git a/pype/ftrack/actions/action_thumbToChildern.py b/pype/ftrack/actions/action_thumbToChildern.py
index 5b63ec264f..4e7f1298f5 100644
--- a/pype/ftrack/actions/action_thumbToChildern.py
+++ b/pype/ftrack/actions/action_thumbToChildern.py
@@ -1,3 +1,4 @@
+import os
import sys
import argparse
import logging
@@ -15,9 +16,8 @@ class ThumbToChildren(BaseAction):
# Action label
label = 'Thumbnail to Children'
# Action icon
- icon = (
- 'https://cdn3.iconfinder.com/data/icons/transfers/100/'
- '239322-download_transfer-128.png'
+ icon = '{}/ftrack/action_icons/thumbToChildren.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
def discover(self, session, entities, event):
diff --git a/pype/ftrack/actions/action_thumbToParent.py b/pype/ftrack/actions/action_thumbToParent.py
index eb5623328e..632d2a50b2 100644
--- a/pype/ftrack/actions/action_thumbToParent.py
+++ b/pype/ftrack/actions/action_thumbToParent.py
@@ -1,3 +1,4 @@
+import os
import sys
import argparse
import logging
@@ -14,9 +15,8 @@ class ThumbToParent(BaseAction):
# Action label
label = 'Thumbnail to Parent'
# Action icon
- icon = (
- "https://cdn3.iconfinder.com/data/icons/transfers/100/"
- "239419-upload_transfer-512.png"
+ icon = '{}/ftrack/action_icons/thumbToParent.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
def discover(self, session, entities, event):
diff --git a/pype/ftrack/events/action_sync_to_avalon.py b/pype/ftrack/events/action_sync_to_avalon.py
index 22358cd775..dd9534a764 100644
--- a/pype/ftrack/events/action_sync_to_avalon.py
+++ b/pype/ftrack/events/action_sync_to_avalon.py
@@ -49,9 +49,8 @@ class Sync_To_Avalon(BaseAction):
#: Action description.
description = 'Send data from Ftrack to Avalon'
#: Action icon.
- icon = (
- 'https://cdn1.iconfinder.com/data/icons/hawcons/32/'
- '699650-icon-92-inbox-download-512.png'
+ icon = '{}/ftrack/action_icons/SyncToAvalon.svg'.format(
+ os.environ.get('PYPE_STATICS_SERVER', '')
)
def register(self):
diff --git a/pype/services/statics_server/__init__.py b/pype/services/statics_server/__init__.py
new file mode 100644
index 0000000000..4b2721b18b
--- /dev/null
+++ b/pype/services/statics_server/__init__.py
@@ -0,0 +1,5 @@
+from .statics_server import StaticsServer
+
+
+def tray_init(tray_widget, main_widget):
+ return StaticsServer()
diff --git a/pype/services/statics_server/statics_server.py b/pype/services/statics_server/statics_server.py
new file mode 100644
index 0000000000..90048d2ee2
--- /dev/null
+++ b/pype/services/statics_server/statics_server.py
@@ -0,0 +1,190 @@
+import os
+import socket
+import http.server
+import urllib
+import posixpath
+import socketserver
+
+from Qt import QtCore
+from pypeapp import config, Logger
+
+
+DIRECTORY = os.path.sep.join([os.environ['PYPE_MODULE_ROOT'], 'res'])
+
+
+class Handler(http.server.SimpleHTTPRequestHandler):
+ directory=DIRECTORY
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def send_head(self):
+ """Common code for GET and HEAD commands.
+
+ This sends the response code and MIME headers.
+
+ Return value is either a file object (which has to be copied
+ to the outputfile by the caller unless the command was HEAD,
+ and must be closed by the caller under all circumstances), or
+ None, in which case the caller has nothing further to do.
+
+ """
+ path = self.translate_path(self.path)
+ f = None
+ if os.path.isdir(path):
+ parts = urllib.parse.urlsplit(self.path)
+ if not parts.path.endswith('/'):
+ # redirect browser - doing basically what apache does
+ self.send_response(HTTPStatus.MOVED_PERMANENTLY)
+ new_parts = (parts[0], parts[1], parts[2] + '/',
+ parts[3], parts[4])
+ new_url = urllib.parse.urlunsplit(new_parts)
+ self.send_header("Location", new_url)
+ self.end_headers()
+ return None
+ for index in "index.html", "index.htm":
+ index = os.path.join(path, index)
+ if os.path.exists(index):
+ path = index
+ break
+ else:
+ return self.list_directory(path)
+ ctype = self.guess_type(path)
+ try:
+ f = open(path, 'rb')
+ except OSError:
+ self.send_error(HTTPStatus.NOT_FOUND, "File not found")
+ return None
+
+ try:
+ fs = os.fstat(f.fileno())
+ # Use browser cache if possible
+ if ("If-Modified-Since" in self.headers
+ and "If-None-Match" not in self.headers):
+ # compare If-Modified-Since and time of last file modification
+ try:
+ ims = email.utils.parsedate_to_datetime(
+ self.headers["If-Modified-Since"])
+ except (TypeError, IndexError, OverflowError, ValueError):
+ # ignore ill-formed values
+ pass
+ else:
+ if ims.tzinfo is None:
+ # obsolete format with no timezone, cf.
+ # https://tools.ietf.org/html/rfc7231#section-7.1.1.1
+ ims = ims.replace(tzinfo=datetime.timezone.utc)
+ if ims.tzinfo is datetime.timezone.utc:
+ # compare to UTC datetime of last modification
+ last_modif = datetime.datetime.fromtimestamp(
+ fs.st_mtime, datetime.timezone.utc)
+ # remove microseconds, like in If-Modified-Since
+ last_modif = last_modif.replace(microsecond=0)
+
+ if last_modif <= ims:
+ self.send_response(HTTPStatus.NOT_MODIFIED)
+ self.end_headers()
+ f.close()
+ return None
+
+ self.send_response(HTTPStatus.OK)
+ self.send_header("Content-type", ctype)
+ self.send_header("Content-Length", str(fs[6]))
+ self.send_header("Last-Modified",
+ self.date_time_string(fs.st_mtime))
+ self.end_headers()
+ return f
+ except:
+ f.close()
+ raise
+
+ def translate_path(self, path):
+ """Translate a /-separated PATH to the local filename syntax.
+
+ Components that mean special things to the local file system
+ (e.g. drive or directory names) are ignored. (XXX They should
+ probably be diagnosed.)
+
+ """
+ # abandon query parameters
+ path = path.split('?',1)[0]
+ path = path.split('#',1)[0]
+ # Don't forget explicit trailing slash when normalizing. Issue17324
+ trailing_slash = path.rstrip().endswith('/')
+ try:
+ path = urllib.parse.unquote(path, errors='surrogatepass')
+ except UnicodeDecodeError:
+ path = urllib.parse.unquote(path)
+ path = posixpath.normpath(path)
+ words = path.split('/')
+ words = filter(None, words)
+ path = self.directory
+ for word in words:
+ if os.path.dirname(word) or word in (os.curdir, os.pardir):
+ # Ignore components that are not a simple file/directory name
+ continue
+ path = os.path.join(path, word)
+ if trailing_slash:
+ path += '/'
+ return path
+
+
+class StaticsServer(QtCore.QThread):
+ """ Measure user's idle time in seconds.
+ Idle time resets on keyboard/mouse input.
+ Is able to emit signals at specific time idle.
+ """
+
+ def __init__(self):
+ super(StaticsServer, self).__init__()
+ self._is_running = False
+ self._failed = False
+ self.log = Logger().get_logger(self.__class__.__name__)
+ try:
+ self.presets = config.get_presets().get(
+ 'services', {}).get('statics_server')
+ except Exception:
+ self.presets = {'default_port': 8010, 'exclude_ports': []}
+
+ self.port = self.find_port()
+
+ def tray_start(self):
+ self.start()
+
+ @property
+ def is_running(self):
+ return self._is_running
+
+ @property
+ def failed(self):
+ return self._failed
+
+ def stop(self):
+ self._is_running = False
+
+ def run(self):
+ self._is_running = True
+ try:
+ with socketserver.TCPServer(("", self.port), Handler) as httpd:
+ while self._is_running:
+ httpd.handle_request()
+ except Exception:
+ self._failed = True
+ self._is_running = False
+
+ def find_port(self):
+ start_port = self.presets['default_port']
+ exclude_ports = self.presets['exclude_ports']
+ found_port = None
+ # port check takes time so it's lowered to 100 ports
+ for port in range(start_port, start_port+100):
+ if port in exclude_ports:
+ continue
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
+ result = sock.connect_ex(('localhost', port))
+ if result != 0:
+ found_port = port
+ if found_port is not None:
+ break
+ if found_port is None:
+ return None
+ os.environ['PYPE_STATICS_SERVER'] = 'http://localhost:{}'.format(found_port)
+ return found_port
diff --git a/res/app_icons/Aport.png b/res/app_icons/Aport.png
new file mode 100644
index 0000000000..0a6816513a
Binary files /dev/null and b/res/app_icons/Aport.png differ
diff --git a/res/app_icons/clockify-white.png b/res/app_icons/clockify-white.png
new file mode 100644
index 0000000000..2803049fbe
Binary files /dev/null and b/res/app_icons/clockify-white.png differ
diff --git a/res/app_icons/clockify.png b/res/app_icons/clockify.png
new file mode 100644
index 0000000000..ac4c44c763
Binary files /dev/null and b/res/app_icons/clockify.png differ
diff --git a/res/app_icons/djvView.png b/res/app_icons/djvView.png
new file mode 100644
index 0000000000..854604d57f
Binary files /dev/null and b/res/app_icons/djvView.png differ
diff --git a/res/app_icons/houdini.png b/res/app_icons/houdini.png
new file mode 100644
index 0000000000..11cfa46dce
Binary files /dev/null and b/res/app_icons/houdini.png differ
diff --git a/res/app_icons/maya.png b/res/app_icons/maya.png
new file mode 100644
index 0000000000..e84a6a3742
Binary files /dev/null and b/res/app_icons/maya.png differ
diff --git a/res/app_icons/nuke.png b/res/app_icons/nuke.png
new file mode 100644
index 0000000000..4234454096
Binary files /dev/null and b/res/app_icons/nuke.png differ
diff --git a/res/app_icons/premiere.png b/res/app_icons/premiere.png
new file mode 100644
index 0000000000..eb5b3d1ba2
Binary files /dev/null and b/res/app_icons/premiere.png differ
diff --git a/res/app_icons/python.png b/res/app_icons/python.png
new file mode 100644
index 0000000000..b3b5b2220a
Binary files /dev/null and b/res/app_icons/python.png differ
diff --git a/res/ftrack/action_icons/AssetsRemover.svg b/res/ftrack/action_icons/AssetsRemover.svg
new file mode 100644
index 0000000000..e838ee9f28
--- /dev/null
+++ b/res/ftrack/action_icons/AssetsRemover.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/res/ftrack/action_icons/ComponentOpen.svg b/res/ftrack/action_icons/ComponentOpen.svg
new file mode 100644
index 0000000000..6d4eba6839
--- /dev/null
+++ b/res/ftrack/action_icons/ComponentOpen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/res/ftrack/action_icons/CreateFolders.svg b/res/ftrack/action_icons/CreateFolders.svg
new file mode 100644
index 0000000000..c07e474e5c
--- /dev/null
+++ b/res/ftrack/action_icons/CreateFolders.svg
@@ -0,0 +1,51 @@
+
+
+
+
+
diff --git a/res/ftrack/action_icons/CreateProjectFolders.svg b/res/ftrack/action_icons/CreateProjectFolders.svg
new file mode 100644
index 0000000000..5fa653361e
--- /dev/null
+++ b/res/ftrack/action_icons/CreateProjectFolders.svg
@@ -0,0 +1,51 @@
+
+
+
+
+
diff --git a/res/ftrack/action_icons/CustomAttributes.svg b/res/ftrack/action_icons/CustomAttributes.svg
new file mode 100644
index 0000000000..6d73746ed0
--- /dev/null
+++ b/res/ftrack/action_icons/CustomAttributes.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/res/ftrack/action_icons/DeleteAsset.svg b/res/ftrack/action_icons/DeleteAsset.svg
new file mode 100644
index 0000000000..a41ae31d12
--- /dev/null
+++ b/res/ftrack/action_icons/DeleteAsset.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/res/ftrack/action_icons/JobKiller.svg b/res/ftrack/action_icons/JobKiller.svg
new file mode 100644
index 0000000000..595c780a9b
--- /dev/null
+++ b/res/ftrack/action_icons/JobKiller.svg
@@ -0,0 +1,374 @@
+
+
diff --git a/res/ftrack/action_icons/MultipleNotes.svg b/res/ftrack/action_icons/MultipleNotes.svg
new file mode 100644
index 0000000000..6ed916f1aa
--- /dev/null
+++ b/res/ftrack/action_icons/MultipleNotes.svg
@@ -0,0 +1,15 @@
+
+
diff --git a/res/ftrack/action_icons/RV.png b/res/ftrack/action_icons/RV.png
new file mode 100644
index 0000000000..741e7a9772
Binary files /dev/null and b/res/ftrack/action_icons/RV.png differ
diff --git a/res/ftrack/action_icons/SyncToAvalon-local.svg b/res/ftrack/action_icons/SyncToAvalon-local.svg
new file mode 100644
index 0000000000..bf4708e8a5
--- /dev/null
+++ b/res/ftrack/action_icons/SyncToAvalon-local.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/res/ftrack/action_icons/SyncToAvalon.svg b/res/ftrack/action_icons/SyncToAvalon.svg
new file mode 100644
index 0000000000..48071b2430
--- /dev/null
+++ b/res/ftrack/action_icons/SyncToAvalon.svg
@@ -0,0 +1,67 @@
+
+
+
+
diff --git a/res/ftrack/action_icons/TestAction.svg b/res/ftrack/action_icons/TestAction.svg
new file mode 100644
index 0000000000..771644340e
--- /dev/null
+++ b/res/ftrack/action_icons/TestAction.svg
@@ -0,0 +1,84 @@
+
+
diff --git a/res/ftrack/action_icons/thumbToChildren.svg b/res/ftrack/action_icons/thumbToChildren.svg
new file mode 100644
index 0000000000..30b146803e
--- /dev/null
+++ b/res/ftrack/action_icons/thumbToChildren.svg
@@ -0,0 +1,88 @@
+
+
+
+
diff --git a/res/ftrack/action_icons/thumbToParent.svg b/res/ftrack/action_icons/thumbToParent.svg
new file mode 100644
index 0000000000..254b650306
--- /dev/null
+++ b/res/ftrack/action_icons/thumbToParent.svg
@@ -0,0 +1,95 @@
+
+
+
+