1""" 2Based on "TinyMCE Compressor PHP" from MoxieCode. 3 4http://tinymce.moxiecode.com/ 5 6Copyright (c) 2008 Jason Davies 7Licensed under the terms of the MIT License (see LICENSE.txt) 8""" 9 10from datetime import datetime 11import json 12import logging 13import os 14import re 15 16from django.contrib.staticfiles import finders 17from django.core.cache import cache 18from django.http import HttpResponse 19from django.template.loader import render_to_string 20from django.utils.cache import patch_response_headers, patch_vary_headers 21from django.utils.http import http_date 22from django.utils.text import compress_string 23 24import tinymce.settings 25 26logger = logging.getLogger(__name__) 27 28safe_filename_re = re.compile("^[a-zA-Z][a-zA-Z0-9_/-]*$") 29 30 31def get_file_contents(filename, source=False): 32 33 file_path = finders.find(os.path.join("tinymce", f"{filename}.js")) 34 if not file_path: 35 file_path = finders.find(os.path.join("tinymce", f"{filename}.min.js")) 36 37 try: 38 with open(file_path) as fh: 39 return fh.read() 40 except (IOError, TypeError): 41 logger.error(f"Couldn't load file: {file_path} for {filename}") 42 return "" 43 44 45def split_commas(str): 46 if str == "": 47 return [] 48 return str.split(",") 49 50 51def gzip_compressor(request): 52 plugins = split_commas(request.GET.get("plugins", "")) 53 languages = split_commas(request.GET.get("languages", "")) 54 themes = split_commas(request.GET.get("themes", "")) 55 files = split_commas(request.GET.get("files", "")) 56 source = request.GET.get("src", "") == "true" 57 isJS = request.GET.get("js", "") == "true" 58 compress = request.GET.get("compress", "true") == "true" 59 content = [] 60 61 response = HttpResponse() 62 response["Content-Type"] = "text/javascript" 63 64 if not isJS: 65 response.write( 66 render_to_string( 67 "tinymce/tiny_mce_gzip.js", {"base_url": tinymce.settings.JS_BASE_URL} 68 ) 69 ) 70 return response 71 72 patch_vary_headers(response, ["Accept-Encoding"]) 73 74 now = datetime.utcnow() 75 response["Date"] = now.strftime("%a, %d %b %Y %H:%M:%S GMT") 76 77 cacheKey = "|".join(plugins + languages + themes) 78 cacheData = cache.get(cacheKey) 79 80 if cacheData is not None: 81 if "ETag" in cacheData: 82 if_none_match = request.META.get("HTTP_IF_NONE_MATCH") 83 if if_none_match == cacheData["ETag"]: 84 response.status_code = 304 85 response.content = "" 86 response["Content-Length"] = "0" 87 return response 88 89 if "Last-Modified" in cacheData: 90 if_modified_since = request.META.get("HTTP_IF_MODIFIED_SINCE") 91 if if_modified_since == cacheData["Last-Modified"]: 92 response.status_code = 304 93 response.content = "" 94 response["Content-Length"] = "0" 95 return response 96 97 tinyMCEPreInit = { 98 "base": tinymce.settings.JS_BASE_URL, 99 "suffix": "", 100 } 101 content.append(f"var tinyMCEPreInit={json.dumps(tinyMCEPreInit)};") 102 103 # Add core 104 files = ["tinymce"] 105 106 # Add core languages 107 for lang in languages: 108 files.append(f"langs/{lang}") 109 110 # Add plugins 111 for plugin in plugins: 112 files.append(f"plugins/{plugin}/plugin") 113 114 for lang in languages: 115 files.append(f"plugins/{plugin}/langs/{lang}") 116 117 # Add themes 118 for theme in themes: 119 files.append(f"themes/{theme}/theme") 120 121 for lang in languages: 122 files.append(f"themes/{theme}/langs/{lang}") 123 124 for f in files: 125 # Check for unsafe characters 126 if not safe_filename_re.match(f): 127 continue 128 content.append(get_file_contents(f, source=source)) 129 130 # Restore loading functions 131 content.append( 132 'tinymce.each("{}".split(",")'.format(",".join(files)) 133 + ', function(f){tinymce.ScriptLoader.markDone(tinyMCE.baseURL+"/"+f+".js");});' 134 ) 135 136 # Compress 137 if compress: 138 content = compress_string(b"".join([c.encode("utf-8") for c in content])) 139 response["Content-Encoding"] = "gzip" 140 response["Content-Length"] = str(len(content)) 141 142 response.write(content) 143 timeout = 3600 * 24 * 10 144 patch_response_headers(response, timeout) 145 if not response.has_header("Last-Modified"): 146 response["Last-Modified"] = http_date() 147 cache.set( 148 cacheKey, 149 {"Last-Modified": response["Last-Modified"], "ETag": response.get("ETag", "")}, 150 ) 151 return response 152