Skip to content

Instantly share code, notes, and snippets.

@jdorrance
Last active December 27, 2015 17:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jdorrance/7361535 to your computer and use it in GitHub Desktop.
Save jdorrance/7361535 to your computer and use it in GitHub Desktop.
sublimetext cq5 integration --> Will 'PUT' to the corresponding path in CQ5. Uses coffeescript if the file is *.coffee, and compiles to JS
import sublime_plugin
class cq5Save(sublime_plugin.WindowCommand):
def run(self, **args):
view = self.window.active_view()
file_name = view.file_name()
if CQ5Poster.JCRROOT in file_name:
CQ5Poster(args).send_file(file_name)
else: print("file doesn't live beneath a jcr_root")
import base64, codecs, os, io, mimetypes, shlex, subprocess, sys, urllib.request, uuid
from xml.dom.minidom import parse, parseString
class CQ5Poster():
# Replace these variables in the arg
host = "http://localhost:4502/"
username = 'admin'
password = 'admin'
COFFEE_SUFFIX = ".coffee"
JS_SUFFIX = ".js"
XML_SUFFIX = ".xml"
COFFEXEC = 'coffee.cmd'
JCRROOT = "jcr_root\\"
ENC = 'utf-8'
DIALOG = "dialog.xml"
DESIGN_DIALOG = "design_dialog.xml"
CLIENTLIB = "clientlibs"
TIMEOUT = 2
def __init__(self, args=[]):
"""
args --> i.e. ({"host": "http://yourservername:4502/", "username" : "your_user_name", "password" : "your_password"})
"""
for i in args: self.__dict__[i] = args[i]
def __getpw__(self):
return 'Basic %s' %(str(base64.b64encode(bytes(self.username + ":" + self.password,self.ENC)),self.ENC))
def __run_command__(self,command):
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return iter(p.stdout.readline, b'')
def __get_content__(self, file_name):
bits = b''
if self.COFFEE_SUFFIX in file_name[-self.COFFEE_SUFFIX.__len__():]:
command = '%s -b -p -c "%s"' %(self.COFFEXEC,file_name)
for line in self.__run_command__(command):
bits += line
else:
with open(file_name, "rb") as f:
bits = f.read(10000000)
if self.DESIGN_DIALOG in file_name[-self.DESIGN_DIALOG.__len__():]: bits = bytes(str(bits,self.ENC).replace("<jcr:root","<design_dialog").replace("</jcr:root","</design_dialog"),self.ENC)
elif self.DIALOG in file_name[-self.DIALOG.__len__():]: bits = bytes(str(bits,self.ENC).replace("<jcr:root","<dialog").replace("</jcr:root","</dialog"),self.ENC)
return bits
def send_file(self, file_name):
"""
file_name --> the fully qualified path to the file that you wish to PUT/POST. Must have jcr_root in the path
Will log the status code of the send operation the std out
"""
basepath = file_name[file_name.rfind(self.JCRROOT) + self.JCRROOT.__len__():].replace("\\","/")
url = (self.host + basepath).replace("\\","/")
content = self.__get_content__(file_name)
opener = urllib.request.build_opener()
opener.addheaders = [('Content-Length', os.path.getsize(file_name)),('Authorization', self.__getpw__())]
content_type = "application/x-html"
get_method = lambda : 'PUT'
if self.COFFEE_SUFFIX in url[-self.COFFEE_SUFFIX.__len__():]: url = url[:-self.COFFEE_SUFFIX.__len__()] + self.JS_SUFFIX
if self.CLIENTLIB in url:
cliburl = url[:url.rfind(self.CLIENTLIB) + self.CLIENTLIB.__len__()] +".js"
cliburl2 = self.host + "var/clientlibs/" + cliburl[cliburl.rfind(self.host) + self.host.__len__():]
r0 = urllib.request.Request(cliburl2)
r0.get_method = lambda :'DELETE'
try:
print("Deleting the clientlib first @ %s, SC: %s" %(cliburl2, opener.open(r0,timeout = self.TIMEOUT).status))
except Exception:
print ("most likely doesnt exist" )
if self.XML_SUFFIX in file_name[-self.XML_SUFFIX.__len__():]: content_type = "application/xml"
if self.DIALOG in file_name[-self.DIALOG.__len__():]:
# First - we delete the existing tab, because CQ doesnt properly overwrite for some reason
r1 = urllib.request.Request(url.replace(url[-self.XML_SUFFIX.__len__():],""))
r1.get_method = lambda :'DELETE'
try:
print("Deleting the dialog first @ %s, SC: %s" %(url, opener.open(r1,timeout = self.TIMEOUT).status))
except Exception:
print ("most likely doesnt exist")
# Set up the POST operation for either the design dialog or the (regular) dialog.xml
get_method = lambda : 'POST'
# See http://sling.apache.org/documentation/bundles/manipulating-content-the-slingpostservlet-servlets-post.html#importing-content-structures
fields = [(':operation', 'import'), (':contentType', 'jcr.xml'), (':replaceProperties', 'true'),('replace','true')]
files = []
_fn = self.DESIGN_DIALOG if self.DESIGN_DIALOG in file_name[-self.DESIGN_DIALOG.__len__():] else self.DIALOG
fields.extend([(':nameHint',_fn)])
files.extend([(':content', _fn, content)])
url = url.replace(url[-_fn.__len__():],"")
#Now we encode all this information
content_type, content = MultipartFormdataEncoder().encode(fields, files)
#Time to finish up building the request
r = urllib.request.Request(url, data=content)
r.add_header('Content-Type', content_type)
r.get_method = get_method
try:
print("sending file to %s content type is %s, SC:%s" %(url, content_type,opener.open(r,timeout = self.TIMEOUT).status))
except Exception:
print("problem sending to %s" %url)
class MultipartFormdataEncoder(object):
def __init__(self):
self.boundary = uuid.uuid4().hex
self.content_type = 'multipart/form-data; boundary={}'.format(self.boundary)
@classmethod
def u(cls, s):
if sys.hexversion < 0x03000000 and isinstance(s, str): s = s.decode('utf-8')
if sys.hexversion >= 0x03000000 and isinstance(s, bytes): s = s.decode('utf-8')
return s
def iter(self, fields, files):
"""
fields is a sequence of (name, value) elements for regular form fields.
files is a sequence of (name, filename, file-type) elements for data to be uploaded as files
Yield body's chunk as bytes
"""
encoder = codecs.getencoder('utf-8')
for (key, value) in fields:
key = self.u(key)
yield encoder('--{}\r\n'.format(self.boundary))
yield encoder(self.u('Content-Disposition: form-data; name="{}"\r\n').format(key))
yield encoder('\r\n')
if isinstance(value, int) or isinstance(value, float):
value = str(value)
yield encoder(self.u(value))
yield encoder('\r\n')
for (key, filename, fd) in files:
key = self.u(key)
filename = self.u(filename)
yield encoder('--{}\r\n'.format(self.boundary))
yield encoder(self.u('Content-Disposition: form-data; name="{}"; filename="{}"\r\n').format(key, filename))
yield encoder('Content-Type: {}\r\n'.format(mimetypes.guess_type(filename)[0] or 'application/octet-stream'))
yield encoder('\r\n')
yield (fd, len(fd))
yield encoder('\r\n')
yield encoder('--{}--\r\b'.format(self.boundary))
def encode(self, fields, files):
body = io.BytesIO()
for chunk, chunk_len in self.iter(fields, files): body.write(chunk)
return self.content_type, body.getvalue()
[
{
"caption": "Save to CQ5",
"command": "cq5_save"
}
]
[
{ "keys": ["ctrl+shift+s"], "command": "cq5_save",
"args": {"host": "http://localhost:4502/", "username" : "admin", "password" : "admin"}
}
]
@rajeshp
Copy link

rajeshp commented May 15, 2014

can you provide some documentation of what exactly this plugin does? Does it save the scripts directly into cq5?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment