<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8526508655697471701</id><updated>2026-10-15T01:59:59.135-04:00</updated><category term="Programming"/><category term="Scripting"/><category term="Extensions"/><category term="Linux"/><category term=".NET"/><category term="OSX"/><category term="Javascript"/><category term="Umbraco"/><category term="FreeBSD"/><category term="Firefox"/><category term="C#"/><category term="Flex"/><category term="jQuery"/><category term="PHP"/><category term="Perl"/><category term="Photography"/><category term="Ubiquity"/><category term="IIS"/><category term="PDF"/><category term="Photoshop"/><category term="Printing"/><category term="VirtualBox"/><category term="3D Printing"/><category term="Apache"/><category term="Astaro"/><category term="Automator"/><category term="Citrix"/><category term="Fixes"/><category term="Gmail"/><category term="Google"/><category term="HTML"/><category term="MediaWiki"/><category term="MsSQL"/><category term="MySQL"/><category term="OTRS"/><category term="Raspberry Pi"/><category term="Smarty"/><category term="Sophos UTM"/><category term="Themes"/><category term="Varnish"/><category term="Xbian"/><category term="iPad"/><category term="Apple"/><category term="Blacklist"/><category term="Board Games"/><category term="Boot Camp"/><category term="Chartist"/><category term="Computer Games"/><category term="Configureation"/><category term="Css"/><category term="Digital signature"/><category term="Framework"/><category term="Google Chrome"/><category term="Java"/><category term="LINQ"/><category term="Netflix"/><category term="Network"/><category term="OCR"/><category term="OSX Dock"/><category term="OpenSSL"/><category term="Regex"/><category term="RescueTime"/><category term="Review"/><category term="SSL"/><category term="Slack"/><category term="Steam"/><category term="Trakt.tv"/><category term="Umbraco Courier"/><category term="VMware"/><category term="Wordpress"/><category term="iContact"/><category term="macOS"/><title type='text'>Jeremy Pyne</title><subtitle type='html'>This is my personal blog and mainly contains technical solutions, open source code, and other projects I have worked on.  Much of the technical solutions are very niche so your milage may vary.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>111</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-558830895937759716</id><published>2018-11-01T17:06:00.003-04:00</published><updated>2018-11-01T17:09:25.398-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="3D Printing"/><title type='text'>Adjusting for X/Y Skew in your Slicer</title><content type='html'>I have had a minor annoyance with my 3D printer that it skews the print in the X/Y place and will print a cube as a bit of a parallelogram instead. This is not that the X or Y are misaligned, the sizing is consistent, there is just a skew. After some research I found a solution in the slicer instead of manual tweaking the frame or flashing the firmware.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;To be sure I could have flashed the firmware with a recent &lt;a href=&quot;http://marlinfw.org/&quot;&gt;Marlin firmware&lt;/a&gt;, but that required extensive knowledge of the parts and configuration values, testing, and in my case an Arduino as well witch I don&#39;t have. I tried messing with the frame and rods a bit but couldn&#39;t find any noticeable defects and everything mounted quite securely without much adjustability.&lt;br /&gt;
&lt;br /&gt;
Then I stumbled upon this &lt;a href=&quot;https://www.thingiverse.com/thing:3050059&quot;&gt;Cura plugin&lt;/a&gt; that clams to make the corrections in a Post Processing script. I was a bit skeptical but I tried it out and two tests latter I had it printing perfect cubes.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Determine if you have a skew:&lt;/h3&gt;
Print t&lt;a href=&quot;https://www.thingiverse.com/thing:2563185&quot;&gt;his calibration model&lt;/a&gt;. You can safely print it at &amp;nbsp;high speed with .2mm layers and still get good adjustment values. I also scaled the Z down to speed it up, but print the full 100mm X/Y.&amp;nbsp;

&lt;br /&gt;
&lt;br /&gt;
When done first measure the X at multiple points and the Y at multiple points to ensure that all measurements are 100mm and you don&#39;t have stepper, rod alignment, or other issues. Those issues are not covered here but would need to be corrected first.

&lt;br /&gt;
&lt;br /&gt;
Finally measure the diagonal form A to C and compare it to B to D. Record the numbers and decide if you need skew correction. In my case on a 100mm test by AC and DB were off by around 1.8mm which was noticeable on compartments and trays I printed for board games.

&lt;br /&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
To correct the skew:&lt;/h3&gt;
First download the &lt;code&gt;&lt;a href=&quot;https://www.thingiverse.com/thing:3050059/files&quot;&gt;SkewCompensation.py&lt;/a&gt;&lt;/code&gt; script for Cura from this project by&amp;nbsp;&lt;a href=&quot;https://www.thingiverse.com/joochung&quot;&gt;joochung&lt;/a&gt;. &amp;nbsp;You will need to install it in Cura&#39;s scripts folder. Open Cura and go to &lt;code&gt;Help -&amp;gt; Show Configuration Folder&lt;/code&gt; and drop it in the &lt;code&gt;scripts&lt;/code&gt; folder there. You will then need to restart Cura once.&lt;br /&gt;
&lt;br /&gt;
Now calculate the skew angle but using the Excel file that is included on the project. The formula is rather complex so just open the &lt;code&gt;3D_Printer_Axis_Calibration.xlsx&lt;/code&gt; file and fill in your AC and BD measurements in cells &lt;code&gt;C13&lt;/code&gt; and &lt;code&gt;C14&lt;/code&gt;. Cell &lt;code&gt;C18&lt;/code&gt; should then have a decimal result less then .05. Record this number as it&#39;s your Skew Adjustment and what you will enter in Cura. If your skew is more then that I&#39;d look into physical issues and adjustments.&lt;br /&gt;
&lt;br /&gt;
Now, back in Cura click on &lt;code&gt;Extensions -&amp;gt; Post Processing -&amp;gt; Modify G-Code&lt;/code&gt;. In the dialog select &lt;code&gt;Add a script -&amp;gt; Skew Compensation&lt;/code&gt;. On the right side change &lt;code&gt;XY Type&lt;/code&gt; to Tangent and enter the skew adjustment you calculated in the &lt;code&gt;XY Tangent&lt;/code&gt; field.&lt;br /&gt;
&lt;br /&gt;
Close this dialog and print the calibration model again and re-measure. If you swap the AC and BD measurements that the box will be more skewed not less. I was able to get a perfect box on the first try.</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/558830895937759716/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/558830895937759716?isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/558830895937759716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/558830895937759716'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2018/11/adjusting-for-xy-skew-in-your-slicer.html' title='Adjusting for X/Y Skew in your Slicer'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3700265210963361824</id><published>2018-05-02T15:15:00.000-04:00</published><updated>2018-10-04T11:07:02.140-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="3D Printing"/><title type='text'>Monoprice Select V2 3D Printer Customizations</title><content type='html'>I have a Monoprice Select V2 3D printer. This is just a few notes and recommendations for the modifications and part&#39;s I have customized it with to get improved print speed, reliability, and quality.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Printer: &lt;a href=&quot;https://www.monoprice.com/product?p_id=13860&quot;&gt;Monoprice Select 3D Printer v2.1&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;One of the best value printers in the price range. The all metal frame makes a huge difference for print quality.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Filament: &lt;a href=&quot;http://thefoxsmart.com/shop/pla-filament-2/&quot;&gt;FoxSmart&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Very good and inexpensive PLA filament. &amp;nbsp;I have tried others twice the cost and still feel this is much better.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Replacement Y Carriage: &lt;a href=&quot;https://www.amazon.com/gp/product/B01MUAMRN7/ref=oh_aui_detailpage_o01_s00?ie=UTF8&amp;amp;psc=1&quot;&gt;RepRap Champion Upgrade&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;The y Carriage that the print bed rides on is to flimsy and flexes. This means you will hav issues keeping the build plate level and have to re-level it all the time. I use this replacement and never have to re-level any more unless I move the printer.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Z-Brace Frame Support Kit: &lt;a href=&quot;https://www.amazon.com/Gulfcoast-Robotics-Z-Brace-Duplicator-Printers/dp/B07B3X1XH8/ref=sr_1_3?ie=UTF8&amp;amp;qid=1525287563&amp;amp;sr=8-3&amp;amp;keywords=Z-Brace++Wanhao+Duplicator+i3&quot;&gt;Gulf Coast Robotics&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;This is another critical upgrade as it removed all the wobble in the Z tower from the print bed moving in and out along the Y axis.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Glass Print Bed: &lt;a href=&quot;https://www.amazon.com/gp/product/B00CW9QUME/ref=oh_aui_detailpage_o00_s00?ie=UTF8&amp;amp;psc=1&quot;&gt;Geeetech Borosilicate Glass&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Another important upgrade. I really liked the Buildtek print surface that the printer came with, but had many issues with it not being level, having bows and crowns, and flexing when the bed was heated. Using a pice of gas on top of it solves basically all these problems. You can also use regular window glass, but this is more durable and won&#39;t break under the high temperatures.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.thingiverse.com/thing:2303045&quot;&gt;Clips&lt;/a&gt; for attaching glass to print bed.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;These are the best option as they don&#39;t apply any downward pressure yet still keep the glass from wiggling around. You may need to use small pieces of paper as shims. Also they won&#39;t last for ever, so print a set or two.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Sewing Machine Oil&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Best choies for oiling parts as it won&#39;t damage them.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.thingiverse.com/thing:1857904&quot;&gt;Spool Holder&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Some spools may not fit on the provided one.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.thingiverse.com/thing:1025471&quot;&gt;Improved Fan Duct&lt;/a&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Another must have. This proves the airflow from the cooling fan to more evenly cool from all sides. This will remove defects and issues that would appear on the back side due to insufficient cooling while printing.&lt;/li&gt;
&lt;li&gt;You need the &lt;code&gt;DiiiCooler_Rev3-21-V2-1_Beta3.STL&lt;/code&gt; file and the &lt;code&gt;DiiiCooler_Adapter_30mm.stl&lt;/code&gt; file.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3700265210963361824/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/3700265210963361824?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3700265210963361824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3700265210963361824'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2018/05/monoprice-select-v2-3d-printer.html' title='Monoprice Select V2 3D Printer Customizations'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-9118618765031146121</id><published>2018-01-08T13:16:00.001-05:00</published><updated>2018-01-09T16:17:41.652-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="FreeBSD"/><category scheme="http://www.blogger.com/atom/ns#" term="Google"/><category scheme="http://www.blogger.com/atom/ns#" term="Scripting"/><title type='text'>Creating FreeBSD images in Google Compute</title><content type='html'>I found &lt;a href=&quot;https://www.cyberciti.biz/faq/howto-deploying-freebsd11-unix-on-google-cloud/&quot;&gt;various&lt;/a&gt; &lt;a href=&quot;https://forums.freebsd.org/threads/56664/&quot;&gt;instructions&lt;/a&gt; to create a FreeBSD image in Google Compute but non of those instructions where complete of functional. &amp;nbsp;Here are the correct instructions.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Install the cloud SDK&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;There are instructions &lt;a href=&quot;https://cloud.google.com/sdk/downloads&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I use OS X and &lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt;&amp;nbsp;so I just ran the following instead.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;brew tap caskroom/cask&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;brew cask install google-cloud-sdk&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Then run the following commands to setup and log in.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud init&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gcloud auth list&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;li&gt;List the versions available&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Run this to list all the releases available You can delete the last grep to see betas and release candidates as well.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud compute images list --project freebsd-org-cloud-dev --no-standard-images | grep -i freebsd- | grep -release&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;li&gt;Now to create a new instance&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Update the following command as needed and run it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gcloud compute instances create &quot;InstanceName&quot; --machine-type &quot;g1-small&quot; --network &quot;default&quot; --subnet &quot;default&quot; --maintenance-policy &quot;MIGRATE&quot; --image &quot;freebsd-11-1-release-amd64&quot; --image-project=freebsd-org-cloud-dev --boot-disk-size &quot;30&quot; --boot-disk-type &quot;pd-standard&quot;&lt;/code&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt; InstanceName &lt;/code&gt; should be changed to a useful host name.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--machine-type&lt;/code&gt; can just be g1-small for now, or another option from tis list.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud compute machine-types list&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;code&gt;gcloud compute machine-types lis&lt;br /&gt;t&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--network&lt;/code&gt; and &lt;code&gt;--subnet&lt;/code&gt; will probably need to be changed to your custom network. You can list the available options with these commands.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud compute networks list&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud compute networks subnets list&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;code&gt;--image&lt;/code&gt; can be changed another version from the earlier list.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--boot-disk-size&lt;/code&gt; should be at least 30GB. You can add additional drives later.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--boot-disk-type&lt;/code&gt; can be &lt;code&gt;pd-standard&lt;/code&gt; or &lt;code&gt;pd-ssd&lt;/code&gt;&amp;nbsp;depending if you want SSD or not.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;This should create and power on the VM and you will now be able to see it in the website and make adjustments to the configuration as needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;To gain SSH access to this server you will need to do the following steps.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Ensure you can ping the servers internal or external IP address.&lt;/li&gt;
&lt;li&gt;Optionally go into the Google Cloud -&amp;gt; VPC network -&amp;gt; Firewall rules and add a rule for &lt;code&gt;tcp:22&lt;/code&gt; traffic route to the network or server.&lt;/li&gt;
&lt;li&gt;Create a public key and load it onto the server. This step is required and was one of my major hurdles.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Check that you have a public key on the computer to authenticate&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cat ~/.ssh/id_rsa.pub&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;If you don&#39;t have this file its empty runt he following to create one.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ssh-keygen -t rsa -f ~/.ssh/id_rsa&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Add this public key to the instances you can access it.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud compute instances add-metadata InstanceName --metadata-from-file sshKeys=~/.ssh/id_rsa.pub&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Or you can add it to the entire project.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud compute project-info add-metadata --metadata-from-file sshKeys=~/.ssh/id_rsa.pub&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;li&gt;Now you will be able to login with the following.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud compute ssh freebsd-recovery&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
If you get errors like &lt;code&gt;Operation timed out&lt;/code&gt; they would indicate &amp;nbsp;issues with your networking, firewall rules, or ip address configuration. &amp;nbsp;If you get a &lt;code&gt;Permission denied (publickey)&lt;/code&gt; error instead it means the public key was not loaded properly form the &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt; file.  You can also open the file in a text editor and manually enter it in the VM instance under Custom metadata with a key of &lt;code&gt;sshKeys&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Note that this authentication is per device, but once you authenticate you can enable password login the ssh config and add other users if you want.</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/9118618765031146121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/9118618765031146121?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/9118618765031146121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/9118618765031146121'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2018/01/creating-freebsd-images-in-google.html' title='Creating FreeBSD images in Google Compute'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2645863838916974541</id><published>2017-11-08T11:54:00.002-05:00</published><updated>2017-12-19T14:56:30.966-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="OSX"/><category scheme="http://www.blogger.com/atom/ns#" term="PDF"/><title type='text'>Shrink PDF Files in OS X and Control Image Quality</title><content type='html'>This guide is using information from &lt;a href=&quot;https://computers.tutsplus.com/tutorials/how-to-reduce-the-size-of-your-pdf-without-compromising-image-quality--cms-21698&quot;&gt;here&lt;/a&gt; and providing clearer instructions and a Automator script to compress many at once.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Go &lt;a href=&quot;https://github.com/joshcarr/Apple-Quartz-Filters&quot;&gt;here&lt;/a&gt;&amp;nbsp;and click the green Clone or download button, then select Download ZIP.&lt;/li&gt;
&lt;li&gt;Go to Downloads on your computer and rename the downloaded folder to&amp;nbsp;Filters.&lt;/li&gt;
&lt;li&gt;Make a new Finder window (Command+n) and then hold Option while clicking Go -&amp;gt; Library in the menu.&lt;/li&gt;
&lt;li&gt;Drag the Filters folder with he files you downloaded into this new Library folder.&lt;/li&gt;
&lt;li&gt;Download &lt;a href=&quot;http://68.40.119.10/Shared/Shrink%20PDF%20(150%20DPI%20@%20High).zip&quot;&gt;this&lt;/a&gt; program to your Applications folder or Desktop.&lt;/li&gt;
&lt;li&gt;Now you can just all your PDF&#39;s files at once and drop them onto the application and it will compress them and save the compressed versions to your desktop.&lt;/li&gt;
&lt;/ul&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2645863838916974541/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/2645863838916974541?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2645863838916974541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2645863838916974541'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2017/11/string-pdf-files-in-os-x-and-control.html' title='Shrink PDF Files in OS X and Control Image Quality'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-7675633949606581631</id><published>2017-08-17T17:04:00.000-04:00</published><updated>2017-09-08T11:25:04.382-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="MsSQL"/><category scheme="http://www.blogger.com/atom/ns#" term="MySQL"/><title type='text'>Connecting to MySQL Server from a Microsoft SQL Server</title><content type='html'>This seams to me that connecting MySQL and Microsoft SQL databases is something that should be simple to do. Unfortunately looking around online there was no real clear directions or even indications that this was possible without buying some additional drivers. &amp;nbsp;While fear not because this does work out of the box without needing to buy any additional software on pretty much any versions of the two databases. &lt;br /&gt;
&lt;br /&gt;
In short what we will do is add a MySQL ODBC driver, configure a System DSN using that driver, Link MsSQL to that DSN connection, and to do a few test queries and updates. &amp;nbsp;This will allow full insert, update, and delete commands.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
You will need administrative access to the Microsoft SQL Server along with access to the database. In addition you will need to know the connection information for the MySQL database, it&#39;s IP Address, a list of the databases you want to connect, and any MySQL login&#39;s and passwords you will be using.&lt;br /&gt;
&lt;h2&gt;
Setup&lt;/h2&gt;
First off you will need to identify if you have the 32-bit or 64-bit version of Microsoft SQL Server. &amp;nbsp;You can determine this by connecting to the server in &lt;code&gt;Management Studio&lt;/code&gt;, Right Clicking on the connected server, selecting &lt;code&gt;Properties&lt;/code&gt;, and checking the Product field.&lt;br /&gt;
&lt;br /&gt;
Once you have determined the proper edition go &lt;a href=&quot;https://dev.mysql.com/downloads/connector/odbc/5.3.html&quot;&gt;here&lt;/a&gt; and download the matching format of the MySQL ODBC driver. You do not need to create and account as there is a link to skip that step. Be sure to install this package.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
ODBC Configuration&lt;/h2&gt;
We need to create one or more ODBC connections using this new driver called DSN&#39;s. Navigate to the &lt;code&gt;Administrative Tools&lt;/code&gt; folder in your start menu or control panel and launch the matching 32-bit or 64-bit version of &lt;code&gt;ODBC Data Source&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
As for how to set up the connections you have a few options. &amp;nbsp;You can create individual DSN&#39;s for each database using separate user logins for each, you can create one DSN with permission to all the tables, or you can create one DSN and map MsSQL users to MySQL users and control what database&#39;s each can see based on that mapping. Decide on this and then adjust the &lt;b&gt;Server Name&lt;/b&gt; and &lt;b&gt;Database Name&lt;/b&gt; fields below as you configure things.&lt;br /&gt;
&lt;br /&gt;
If you are using one login for everything or mapping users across then do the following.
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Open the &lt;code&gt;ODBC Data Source Administration&lt;/code&gt; tool.&lt;/li&gt;
&lt;li&gt;Go to the &lt;code&gt;System DSN&lt;/code&gt; tab.&lt;/li&gt;
&lt;li&gt;Click Add to create a new ODBC connection.&lt;/li&gt;
&lt;li&gt;Select &lt;i&gt;MySQL ODBC 5.3 ANSI Driver&lt;/i&gt; or&amp;nbsp;&amp;nbsp;&lt;i&gt;MySQL ODBC 5.3 Unicode Driver&lt;/i&gt; as your needs dictate. Unicode supports a larger character set.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Finish&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On the new screen fill in the following fields:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Data Source Name&lt;/code&gt;: &lt;b&gt;ServerName&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TCP/IP&lt;/code&gt;: MySQL Server &lt;b&gt;IP Address&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;User/Password&lt;/code&gt;: MySQL Admin username/password.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Database&lt;/code&gt;: Leave this blank.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Click &lt;code&gt;Test&lt;/code&gt; to make sure it is connection and then press &lt;code&gt;Ok&lt;/code&gt; to save.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
Or if you are creating separate ODBC connections for each database or only have one database but use one login per database then do this instead for each.
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Open the &lt;code&gt;ODBC Data Source Administration&lt;/code&gt; tool.&lt;/li&gt;
&lt;li&gt;Go to the &lt;code&gt;System DSN&lt;/code&gt; tab.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Add&lt;/code&gt; to create a new ODBC connection.&lt;/li&gt;
&lt;li&gt;Select &lt;i&gt;MySQL ODBC 5.3 ANSI Driver&lt;/i&gt; or&amp;nbsp;&amp;nbsp;&lt;i&gt;MySQL ODBC 5.3 Unicode Driver&lt;/i&gt; as your needs dictate. Unicode supports a larger character set.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Finish&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On the new screen fill in the following fields:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Data Source Name&lt;/code&gt;:&amp;nbsp;&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TCP/IP&lt;/code&gt;: MySQL Server &lt;b&gt;IP Address&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;User/Password&lt;/code&gt;: MySQL login/password with access to the specific database.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Database&lt;/code&gt;: Select the proper database.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Click &lt;code&gt;Test&lt;/code&gt; to make sure it is connection and then press &lt;code&gt;Ok&lt;/code&gt; to save.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Microsoft SQL Server Configuration&lt;/h2&gt;
Now for the final step open up the Microsoft SQL Server Management Studio and connect to the database server. &amp;nbsp;Agains he steps will vary depending if you have one single connection are are making connections for each database in question.

If you are using one login for everything to grand full access to any MsSQL users to all MySQL data do the following.

&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to &lt;code&gt;Server Objects&lt;/code&gt; -&amp;gt; &lt;code&gt;Linked Servers&lt;/code&gt; then Right Click -&amp;gt; &lt;code&gt;New Linked Server&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Fill in the following on the &lt;code&gt;General Tab&lt;/code&gt;:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Linked Server&lt;/code&gt;: &lt;b&gt;ServerName&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Provider&lt;/code&gt;: &lt;i&gt;Microsoft OLE DB Provider for ODBC Drivers&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Data Source&lt;/code&gt;:&amp;nbsp;&lt;b&gt;ServerName&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;On the &lt;code&gt;Security Tab&lt;/code&gt;:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Select &lt;code&gt;Be made using the security context&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Reneter the same MySQL Admin username/password used above.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Click &lt;code&gt;Ok&lt;/code&gt;.&amp;nbsp;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;If there are any issues select &lt;code&gt;No&lt;/code&gt; and make adjustments. &amp;nbsp;Once you click &lt;code&gt;Yes&lt;/code&gt; you can&#39;t edit the General settings and have to remove and re-add the connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
On the other hand if you want to map MsSQL Users to MySQL users so they have access to different tables based on the login then so the following.
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to &lt;code&gt;Server Objects&lt;/code&gt; -&amp;gt;&lt;code&gt; Linked Servers&lt;/code&gt; then Right Click -&amp;gt; &lt;code&gt;New Linked Server&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Fill in the following on the &lt;code&gt;General Tab&lt;/code&gt;:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Linked Server&lt;/code&gt;:&amp;nbsp;&lt;b&gt;ServerName&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Provider&lt;/code&gt;: &lt;i&gt;Microsoft OLE DB Provider for ODBC Drivers&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Data Source&lt;/code&gt;:&amp;nbsp;&lt;b&gt;ServerName&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;On the &lt;code&gt;Security Tab&lt;/code&gt;:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Add a row for each user that should be mapped and select the MySQL Local Login Name.&lt;/li&gt;
&lt;li&gt;Enter the matching MySQL usernames and passwords or shared account details that each login should map to.&lt;/li&gt;
&lt;li&gt;Leave &lt;code&gt;Impersonate&lt;/code&gt; unchecked.&lt;/li&gt;
&lt;li&gt;You can change the radio to &lt;code&gt;Not be made&lt;/code&gt; so that unmapped users don&#39;t see anything or specify a general login for unmapped users.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Click &lt;code&gt;Ok&lt;/code&gt;.&amp;nbsp;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;If there are any issues select &lt;code&gt;No&lt;/code&gt; and make adjustments. &amp;nbsp;Once you click &lt;code&gt;Yes&lt;/code&gt; you can&#39;t edit the General settings and have to remove and re-add the connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
Or if you create per database DSN&#39;s then to the this for each.

&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to Server Objects -&amp;gt; Linked Servers then Right Click -&amp;gt; New Linked Server.&lt;/li&gt;
&lt;li&gt;Fill in the following on the &lt;code&gt;General Tab&lt;/code&gt;:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Linked Server&lt;/code&gt;:&amp;nbsp;&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Provider&lt;/code&gt;: &lt;i&gt;Microsoft OLE DB Provider for ODBC Drivers&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Data Source&lt;/code&gt;:&amp;nbsp;&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;On the &lt;code&gt;Security Tab&lt;/code&gt;:&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Select &lt;code&gt;Be made using the security context&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Reneter the table specific MySQL username/password for this table.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Click &lt;code&gt;Ok&lt;/code&gt;.&amp;nbsp;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;If there are any issues select &lt;code&gt;No&lt;/code&gt; and make adjustments. &amp;nbsp;Once you click &lt;code&gt;Yes&lt;/code&gt; you can&#39;t edit the General settings and have to remove and re-add the connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;h2&gt;
Querying and Updating Data&lt;/h2&gt;
Now that this is connected you can browse the structure in Management Studio by going back to &lt;code&gt;Server Objects&lt;/code&gt; -&amp;gt; &lt;code&gt;Linked Servers&lt;/code&gt; -&amp;gt; &lt;code&gt;Server Name&lt;/code&gt;. &amp;nbsp;To test some sample operations lets assume we have the following table in MySQL.

&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;test01&lt;/code&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt;, int, pk, auto_increment&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Name&lt;/code&gt;, nvarchar(20) not null&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Items&lt;/code&gt;, smallint, not null&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
Then we can do all of the following:
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;SELECT count(*) as &#39;Count&#39;
FROM OPENQUERY([&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;], &#39;SELECT * FROM test01&#39;)
GO

INSERT OPENQUERY([&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;], &#39;SELECT Name, Items FROM test01 &#39;)
VALUES
(&#39;Shirts&#39;, 47),
(&#39;Pants&#39;, 55),
(&#39;Shorts&#39;, 2),
(&#39;Shoes&#39;, 43)
GO

SELECT *
FROM OPENQUERY([&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;], &#39;SELECT * FROM test01&#39;)
GO

UPDATE OPENQUERY([&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;], &#39;SELECT * FROM test01 WHERE Name = &#39;&#39;Shoes&#39;&#39; &#39;)
SET Items = 55
GO

SELECT *
FROM OPENQUERY([&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;], &#39;SELECT * FROM test01  WHERE Name = &#39;&#39;Shoes&#39;&#39;&#39;)
GO

DELETE OPENQUERY ([&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;], &#39;SELECT id FROM test01 WHERE Items &amp;gt; 50&#39;)
GO

SELECT *
FROM OPENQUERY([&lt;b&gt;ServerName/DatabaseName&lt;/b&gt;], &#39;SELECT * FROM test01&#39;)
GO&lt;/code&gt;&lt;/pre&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/7675633949606581631/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/7675633949606581631?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7675633949606581631'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7675633949606581631'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2017/08/connecting-to-mysql-server-from.html' title='Connecting to MySQL Server from a Microsoft SQL Server'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-6638500452840500989</id><published>2017-07-21T14:31:00.003-04:00</published><updated>2018-03-05T16:18:25.361-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Netflix"/><category scheme="http://www.blogger.com/atom/ns#" term="Programming"/><category scheme="http://www.blogger.com/atom/ns#" term="Trakt.tv"/><title type='text'>Netflix to Trakt.tv Sync</title><content type='html'>&lt;i&gt;Update Details Below. Note that if the dialog shows an older version you may need to recreate your bookmarklet.&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;br /&gt;&lt;/i&gt;
&lt;i&gt;Also note that this will&amp;nbsp;probably only work in English, though you may be able to&amp;nbsp;temporally change your language on Netflix to get the sync to work.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
There used to be a nice &lt;a href=&quot;https://chrome.google.com/webstore/detail/traktflix-netflix-and-tra/bmoemkaigjgcgjjnpmdgkifndiidkeji?hl=en&quot; target=&quot;_blank&quot;&gt;Chrome plugin&lt;/a&gt;&amp;nbsp;to synchronize by Netflix history with &lt;a href=&quot;https://trakt.tv/&quot;&gt;Trakt.tv&lt;/a&gt;&amp;nbsp;but it had been unreliable and doesn&#39;t seam to be working now. &amp;nbsp;I wrote this code to solve my own need but it does the same thing and I am posting it here for others to use. &lt;br /&gt;
&lt;br /&gt;
Note that this is not 100% perfect as some show names aren&#39;t exactly the same and some Netflix show&#39;s don&#39;t use episode names at all. This code it not using any Netflix API calls but rather just scraping your Viewing Activity page and then making API calls to your Trakt.tv account to post the item&#39;s it finds.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
You may need to manually select some episodes or shows on the Tract.tv side if they aren&#39;t matched and it is possible that duplicates could get synced if the names cause issues though I tried to prevent this. In short I&#39;m not out to make this 100% perfect but it should get you 90% of the way and it shows all the changes it will post for confirmation before posting.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h2&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;
Usage&lt;/span&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Drag this link,&amp;nbsp;&lt;a href=&quot;javascript:function load(filename,type){ if(type==&#39;js&#39;){ var fileref=document.createElement(&#39;script&#39;); fileref.setAttribute(&#39;type&#39;,&#39;text/javascript&#39;); fileref.setAttribute(&#39;src&#39;,filename); } else if (type==&#39;css&#39;){ var fileref=document.createElement(&#39;link&#39;); fileref.setAttribute(&#39;rel&#39;,&#39;stylesheet&#39;); fileref.setAttribute(&#39;type&#39;,&#39;text/css&#39;); fileref.setAttribute(&#39;href&#39;,filename); } document.getElementsByTagName(&#39;head&#39;)[0].appendChild(fileref); } load(&#39;https://www.inkonit.com/netflix/netflix-sync.js?v1.6.5&#39;,&#39;js&#39;);%20load(&#39;https://www.inkonit.com/netflix/netflix-sync.css?v1.6.5&#39;,&#39;css&#39;);&quot;&gt;Sync to Trakt.tv&lt;/a&gt;, to your bookmark bar or bookmerks menu.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;This is a bit of Javascript that will inject the Sync tool into your Netflix activity page.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Open Netflix in your browser and log in.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;This is tested and working in Safari and Chrome.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Go here&amp;nbsp;&lt;a href=&quot;https://www.netflix.com/viewingactivity&quot; target=&quot;netflix&quot;&gt;https://www.netflix.com/viewingactivity&lt;/a&gt;&amp;nbsp;to see your history or click on your name in the top right, then on Account, and finally on Viewing Activity.&lt;/li&gt;
&lt;li&gt;Netflix will not load your entire history so you will need to scroll down to get it to load more. &amp;nbsp;Keep doing this until all the history you want to sync has been loaded.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;You don&#39;t have to do everything, if you are doing this regularly then the first screen or two will be sufficient.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Now for the magic click the bookmarklet you added while looking at the history page.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Note:&lt;/strong&gt;&amp;nbsp;The first tim you do this you will need to log in to Trakt.tv and click the bookmark again.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;The script will scrape the Netflix page, they check each item in Trakt.tv and build a list of already synced items, items not found, and items scheduled to sync.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;If re-running the tool past plays will be greyed out, new plays to by synced will be green, &amp;nbsp;items that could not be cached will remain white, and duplicate plays that are being ignored will be orange.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;When you are satisfied just click Sync Now to post the changes. You can then check Trakt.tv or reload the page and run the script again to see the results.&lt;/li&gt;
&lt;li&gt;You may have issues the first time the history is pulled from Trakt.tv. &amp;nbsp;In some cases you may need to load the page and run the script a few times before it successfully reads the history.&lt;/li&gt;
&lt;li&gt;If you have and hard locks or shows that won&#39;t identify properly please leave me a note with as much detail about the problem items as possible. &amp;nbsp;You can get this by adding #debug to the URL, reloading the page, and running the script again. &amp;nbsp;If you are able to identify the offending items i can make corrections. You can contact me at&amp;nbsp;&amp;nbsp;jeremy.pyne at gmail.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;
How It Works&lt;/span&gt;&lt;/h2&gt;
This solution is a simple bit of javascript that scans your Netflix&amp;nbsp;&lt;a href=&quot;https://www.netflix.com/viewingactivity&quot;&gt;history page&lt;/a&gt;&amp;nbsp;and sync&#39;s plays over to Trakt.tv. It also needs to read your list of past plays in Trakt.tv along with the history it reads from the Netflix page.&lt;br /&gt;
&lt;br /&gt;
None of this data is saved or sent to any other servers in any way. As the script runs a listing of all the changes that will be posted to Trakt.tv is show with a confirmation option. No passwords or logins are prompted or recorded in any way. You are logging in yourself to Netflix and Trakt.tv, though you do have to grant this application API access to your account. The only bit that is saved is the Trakt.tv access token so that you don&#39;t have to log in on every use of the tool. You can even remove this by clearing the cookies for the www.netflix.com domain.&lt;br /&gt;
&lt;br /&gt;
You can view the entire script&amp;nbsp;&lt;a href=&quot;https://www.inkonit.com/netflix/netflix-sync.js&quot;&gt;here&lt;/a&gt;&amp;nbsp;first if you wish. The bookmarklet code is an easy way to inject this code into your netflix page, you could also run this code manually in your browser console.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;function load(filename, type){
 if(type == &#39;js&#39;){
  var fileref=document.createElement(&#39;script&#39;);
  fileref.setAttribute(&#39;type&#39;,&#39;text/javascript&#39;);
  fileref.setAttribute(&#39;src&#39;,filename);
 }
 else if (type == &#39;css&#39;){
  var fileref=document.createElement(&#39;link&#39;);
  fileref.setAttribute(&#39;rel&#39;,&#39;stylesheet&#39;);
  fileref.setAttribute(&#39;type&#39;,&#39;text/css&#39;);
  fileref.setAttribute(&#39;href&#39;,filename);
 }
 document.getElementsByTagName(&#39;head&#39;)[0].appendChild(fileref);
}
load(&#39;https://www.inkonit.com/netflix/netflix-sync.js?v1.6.5&#39;,&#39;js&#39;);
load(&#39;https://www.inkonit.com/netflix/netflix-sync.css?v1.6.5&#39;,&#39;css&#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h2&gt;
Alternate Date Format&lt;/h2&gt;
Your milage in other language may vary but in short if the show and episode titles still match properly between Netflix and Trackt it should still work. The biggest issue is with different date format. Trakt seams to be happy to accept everything in en-US format but Netflix changes formats as you change languages. &amp;nbsp;I added a flag that will tell the code to read Netflix dates in &#39;dd/MM/yyyy&#39; format witch should help.&amp;nbsp;

To make this logic execute run the following after changing the language in Netflix. You will need to go to the Netflix site, open up the console in your browser, and paste and execute this line of code. &amp;nbsp;Then refresh and run the tool again.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;document.cookie = &quot;date_format=dd/MM/yyyy; expires=&quot; + new Date(Date.now() + 1000 * 60 * 60 * 24 * 365).toUTCString()&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h2&gt;
Working On/Planned
&lt;/h2&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;Ability to manually re-query a episode/movie with a custom name.&lt;/li&gt;
&lt;li&gt;Ability to provide episode numbers &lt;strike&gt;or auto-detect them based on episode watch order&lt;/strike&gt;.&lt;/li&gt;
&lt;li&gt;Ability to tie track items to unknown items so they don&#39;t sync duplicates.&lt;/li&gt;
&lt;li&gt;Ability to post mapping corrections back to the server so the corrections persist.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h2&gt;
Updates&lt;/h2&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Updated 8/22/2017, v1.1&lt;/b&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Added version number to script.&lt;/li&gt;
&lt;li&gt;Fixes for many situations where a show, movie, or episode not being found would cause the entire script to never complete processing.&lt;/li&gt;
&lt;li&gt;Manual fixes added for: &lt;code&gt;MSK3k The Return&lt;/code&gt;, &lt;code&gt;Marvel&#39;s The Defenders&lt;/code&gt;, items using &lt;code&gt;Series&lt;/code&gt; and &lt;code&gt;Collection&lt;/code&gt; nomenclature, items with &lt;code&gt; (U.S)&lt;/code&gt; suffix.&lt;/li&gt;
&lt;li&gt;Removed unneeded jquery-ui script.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;b&gt;Updated 8/23/2017, v1.2&lt;/b&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Added error detection for cases where episode titles can&#39;t be determined.&lt;/li&gt;
&lt;li&gt;Added checkbox option to prevent specific items form syncing in odd cases where they match incorrectly.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;b&gt;Update 8/24/2017, v1.3&lt;/b&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Added another fix hard error when show name wasnt found.&lt;/li&gt;
&lt;li&gt;Added support for shows and movies with Unicode charecters in names.&lt;/li&gt;
&lt;li&gt;Fix for past plays in the format &lt;code&gt;Title: Seaseon ##: Episode &lt;/code&gt; wouldnt match properly in history.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;b&gt;Update 8/28/2017, v1.4&lt;/b&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Fix a typo breaking movies detection.&lt;/li&gt;
&lt;li&gt;Added version number to console output.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;b&gt;Update 9/8/2017, v1.5&lt;/b&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Added logic to guess episode numbers in Netflix based on play order. This will not work as expected if episodes are played out or order or if only part of the season is visible in the Netflix history. This is also only done as a last chance if no name matching finds a match. If there are incorrect items you can uncheck them to prevent errors or add #no-guess to your page url before running the script to disable this option.&lt;/li&gt;
&lt;li&gt;Fixes for shows with numbers in the title. This was originally working but some unicode fixes I made broke it.&lt;/li&gt;
&lt;li&gt;Fixes for shows with &lt;code&gt;Part #&lt;/code&gt; in roman numerals.&lt;/li&gt;
&lt;li&gt;Fixes for &lt;code&gt;MythBusters&lt;/code&gt;, &lt;code&gt;Stranger Things&lt;/code&gt;, and &lt;code&gt;Wet Hot American Summer&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;b&gt;Update 10/25/2017, v1.6&lt;/b&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Major rewrite of code for maintainability and reliability.&lt;/li&gt;
&lt;li&gt;Replaced custom process counting with more reliable &lt;code&gt;promise-queue&lt;/code&gt; module.&lt;/li&gt;
&lt;li&gt;UI improvements.&lt;/li&gt;
&lt;li&gt;More logging with &lt;code&gt;#debug&lt;/code&gt; flag.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;b&gt;Updated 12/14/2017, v1.6.4&lt;/b&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Fixed Trakt authentication bug.&lt;/li&gt;
&lt;li&gt;Fix for &lt;code&gt;Stranger Things&lt;/code&gt;, &lt;code&gt;Marvel&#39;s The Defenders&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;b&gt;Updated 3/5/2017, v1.6.5&lt;/b&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Added a flag to support &#39;dd/MM/yyyy&#39; date encodings when changing the language on Netflix. See Alternate Date Format above for me details.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/6638500452840500989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/6638500452840500989?isPopup=true' title='55 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6638500452840500989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/6638500452840500989'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2017/07/netflix-to-trakttv-sync.html' title='Netflix to Trakt.tv Sync'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>55</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-9106519390891936790</id><published>2017-06-19T15:12:00.001-04:00</published><updated>2017-09-08T11:24:25.711-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PHP"/><category scheme="http://www.blogger.com/atom/ns#" term="Scripting"/><category scheme="http://www.blogger.com/atom/ns#" term="Varnish"/><title type='text'>Restricting Access at in Varnish Server Wide to Admin Areas</title><content type='html'>There are lots of ways to implement access controls and additional security on your hosting. I recently implemented a very restrictive but easy to manage implementation for our FreeBSD server that runs everything through &lt;a href=&quot;https://www.varnish-cache.org/&quot; target=&quot;_blank&quot;&gt;Varnish&lt;/a&gt;. I could have placed this restriction in Apache via .HTACCESS files or global rules but placing them in Varnish they execute earlier and have less server overhead and ensure there are no caching related flaws.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
To set this up I added the following to the top of my &lt;code&gt;/usr/local/etc/varnish.vcl&lt;/code&gt; file.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;# Whitelist of IP Address&#39;s or Ranges that are allowd to access restricted administration pages.
acl admin {
    &quot;127.0.0.1&quot;;
    &quot;localhost&quot;;
    &quot;10.1.0.0&quot;/16; # Local Network Class B
    &quot;192.168.1.0&quot;/16; # Local Network Class C
    &quot;1.1.1.1&quot;; # Sample
    # INSERT IPS #
}&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Next inside the vcl_recv sub, or add one if needed include this before other rules. The bold text is doing URL matching to determine what scripts or directories to require approval for. &amp;nbsp;This could also be applied to specific domains but in this case it is server wide.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;# Optional feature to only allow access to matched pages if client is on a whitelist.
    if (req.url ~ &quot;&lt;b&gt;^/wp-(login|admin|cron|json)&lt;/b&gt;&quot; &amp;amp;&amp;amp; client.ip !~ admin) {
        # Whitelist for all admin pages
        return (synth(403, &quot;IP address not authorized, please request access from &lt;b&gt;AdminEmail&lt;/b&gt;&quot; ));
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Now you can reload varnish and do some testing by adding and removing your local network or IP and ensure its working.  You can stop here but this isn&#39;t very convenient to manage or update form offsite.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;sudo service varnishd reload&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h2&gt;
Add a custom script to grant access to IP&#39;s.&lt;/h2&gt;
We have a custom script hidden our our server that it not indexed or published anywhere that our administrator can use to add new addressed on the fly. It asks for a name, location, and IP Address and will update the varnish.vcl file and reload the configuration when submitted.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/9fa4889c887e368fb4f187d466f035ba.js&quot;&gt;&lt;/script&gt;

You can post this script to a secret location on the web server. &amp;nbsp;You may want to add a password to it for extra security and don&#39;t link to it anywhere. You will also need to run the following commands.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;// Make writable by www user.
sudo chmod g+w /usr/local/etc/varnish.vcl
sudo chgrp www /usr/local/etc/varnish.vcl
// Add custom script to reload varnish.
echo &quot;#!/usr/local/bin/bash
service varnishd reload&quot; &amp;gt; ~/varnish-reload
// Move script and make it executable
sudo cp ~/varnish-reload /usr/local/bin/varnish-reload
rm ~/varnish-reload
sudo chmod +x /usr/local/bin/varnish-reload
&lt;/code&gt;&lt;/pre&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/9106519390891936790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/9106519390891936790?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/9106519390891936790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/9106519390891936790'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2017/06/restricting-access-at-in-varnish-server.html' title='Restricting Access at in Varnish Server Wide to Admin Areas'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-1473034016781359340</id><published>2017-06-19T14:35:00.002-04:00</published><updated>2017-09-08T11:24:16.386-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Perl"/><category scheme="http://www.blogger.com/atom/ns#" term="Scripting"/><title type='text'>Read XML Sitemap to a CSV File</title><content type='html'>You may need to process a sitemap for redirects or get a list of all your pages for some testing or monitoring process. &amp;nbsp;Here is a simple script you can run to convert your sitemap to a CSV file. &amp;nbsp;It even supports multi-file sitemaps.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Simply save this script &lt;code&gt;sitemap-to-csv.pl&lt;/code&gt; to your computer. You will need &lt;a href=&quot;https://www.perl.org/&quot; target=&quot;_blank&quot;&gt;Perl&lt;/a&gt; to run in.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/ee7e490146bc6a649c291193ee05d2e0.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
Then you can execute it from the command line with the following format:

&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;// This will automatically append /sitemap.xml
./sitemap-to-csv.pl &lt;b&gt;http://domain.com&lt;/b&gt;

// Or specify the exact file.
perl sitemap-to-csv.pl &lt;b&gt;http://domain.com&lt;/b&gt;

// With SSL domain authentication override.
PERL_LWP_SSL_VERIFY_HOSTNAME=0 ./sitemap-to-csv.pl &lt;b&gt;http://domain.com&lt;/b&gt;
&lt;/code&gt;&lt;/pre&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/1473034016781359340/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/1473034016781359340?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1473034016781359340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/1473034016781359340'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2017/06/read-xml-sitemap-to-csv-file.html' title='Read XML Sitemap to a CSV File'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4834984779808985463</id><published>2017-02-06T16:47:00.000-05:00</published><updated>2017-03-07T13:40:14.361-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache"/><category scheme="http://www.blogger.com/atom/ns#" term="FreeBSD"/><category scheme="http://www.blogger.com/atom/ns#" term="Varnish"/><title type='text'>Configure SSL Termination with Varnish Caching and HTTP/2</title><content type='html'>To create the fastest web pages possible in a LAMP stack can be a bit tricky. &amp;nbsp;This document covers configureing blazing fast HTTPS sites on Freebsd with Apache,
Varnish, Nginx, PHP, Mysql and letsencrypt.sh. &amp;nbsp;Of course you can swap out the PHP/Mysql parts as needed, or even use your own certificates instead of lets encrypt.sh as you need.&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;b&gt;Updated 2/16/2017&lt;/b&gt;: Added code to &lt;code&gt;nginx.conf&lt;/code&gt; for additional rewriting; added an extra check so requests don&#39;t end up with multiple &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; headers.&lt;br /&gt;
&lt;b&gt;Updated 2/27/2017&lt;/b&gt;: Added code to apache and varnish to seamlessly minify CSS and Javascript resources.&lt;br /&gt;
&lt;b&gt;Updated 3/1/2017&lt;/b&gt;: Added details about Varnish Purging, plugins, and custom code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Varnish software provides incredibly fast cached website
hosting in front of standard web services.&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;
&lt;/span&gt;It does however have one major limitation that it won’t serve HTTPS
content.&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp; &lt;/span&gt;This guide demonstraights
setting up a Nginx proxy service infront of the Varnish cache to serve cached
HTTPS content.&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp; &lt;/span&gt;This configuration also
allows for specific SSL sites to be served through Varnish caching and/or allows other sites to bypass the caching if the HTTPS to HTTP translation doesn&#39;t work properly.&amp;nbsp;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&lt;/span&gt;In
addition is seamlessly translates local domain HTTP links and resources to HTTPS so the
original site doesn’t need to be edited and can serve both HTTP and HTTPS from he same cached files. &amp;nbsp;In addition a header is added to seamlessly update external resources to HTTPS to prevent Mixed Content warnings. &amp;nbsp;In addition it will minify resources automatically thus simplifying the CMS/code side.&lt;br /&gt;
&lt;br /&gt;
This setup will allow the following request paths to serve content:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ0RtbxHLgbBscD5YlFRyGgQjB6ItVhxNHWmkkJSLLvDEBz1nk5aFPLDuDKQMoCmQk1NEr7FK64ANXWtH2IzVzRg8RcGkcCrjYirKEctpcqveVcZkTD6reHpik7eZvmqvysczO0PYcBUE/s1600/Diagram1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;HTTP/2 Varnish Cache Layout&quot; border=&quot;0&quot; height=&quot;428&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ0RtbxHLgbBscD5YlFRyGgQjB6ItVhxNHWmkkJSLLvDEBz1nk5aFPLDuDKQMoCmQk1NEr7FK64ANXWtH2IzVzRg8RcGkcCrjYirKEctpcqveVcZkTD6reHpik7eZvmqvysczO0PYcBUE/s640/Diagram1.png&quot; title=&quot;&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&amp;nbsp;Client Browser -&amp;gt; Varnish &lt;small&gt;&lt;em&gt;(http:80)&lt;/em&gt;&lt;/small&gt; -&amp;gt; Apache &lt;small&gt;&lt;em&gt;(http:8080)&lt;/em&gt;&lt;/small&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Normal HTTP request served through cache.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Client Browser -&amp;gt; Varnish &lt;small&gt;&lt;em&gt;(http:80)&lt;/em&gt;&lt;/small&gt; -&amp;gt; 301 to HTTPS Version&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Optional rule to force a site to use SSL if a non-SSL page is requested.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Client Browser -&amp;gt; Nginx &lt;small&gt;&lt;em&gt;(https:443)&lt;/em&gt;&lt;/small&gt; -&amp;gt; Varnish &lt;small&gt;&lt;em&gt;(http:80)&lt;/em&gt;&lt;/small&gt;
-&amp;gt; Apache &lt;small&gt;&lt;em&gt;(HTTP:8080)&lt;/em&gt;&lt;/small&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Normal HTTPS request served through cache&lt;/li&gt;
&lt;li&gt;All links to &lt;code&gt;HTTP://ThisDomainName/&lt;/code&gt; are converted to &lt;code&gt;HTTPS://ThisDomainName/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Header is added to upgrade external HTTP resources to HTTPS.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Client Browser -&amp;gt; Nginx &lt;small&gt;&lt;em&gt;(https:443)&lt;/em&gt;&lt;/small&gt; -&amp;gt; Apache
&lt;small&gt;&lt;em&gt;(HTTPS:44344)&lt;/em&gt;&lt;/small&gt;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;If my be needed bypass caching for specific sites if they don&#39;t work properly with cache configuration.&lt;/li&gt;
&lt;li&gt;It is also possible to just exclude specific sites or url&#39;s from caching and/or minify in the varnish configuration. That approach should be taken where possible and this used as the last resort.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
Some other notes:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;You could of course put these services on different servers, or have multiple IP Addresses on the server if you prefer, but I find it much simpler to logical separate things by putting the backend services on non-standard ports instead.&lt;/li&gt;
&lt;li&gt;You could host the frontend and backend components both in Nginx if you really wanted, but again this way things are more logical spirited and easier to follow.&lt;/li&gt;
&lt;li&gt;Nginx will use HTTP2 when available for much faster delivery of many small resource files. &amp;nbsp;Site auditing/speed test warnings thus far don&#39;t take this into account and will still complain about the number of files even though it&#39;s not relevant on HTTP2.&lt;/li&gt;
&lt;li&gt;Exceptions can be added in Varnish to easily prevent specific directories or URL&#39;s from being cached.&lt;/li&gt;
&lt;li&gt;By default any cookies that vary by request or by user will break the cache. &amp;nbsp;There are some predefined cookies that are ignored, and more can be added as needed. &amp;nbsp;In general a cookie shouldn&#39;t be changing how the server responds so in most cases you can safely ignore all cookies.&lt;/li&gt;
&lt;li&gt;letsencrypt.sh is used in this document to automate SSL certificates for all the sites. &amp;nbsp;Standard certificates can use used in places of specific sites if prefered.&lt;/li&gt;
&lt;li&gt;This configuration uses a Apache+Ruby instigation on minify CSS and JS scripts on the fly. &amp;nbsp;You would not normally want to do this because of the server cost of processing requests, but with the vanish cache layer this has not cost as the result is cached. &amp;nbsp;This greatly simplifies the backend/code and doesn&#39;t require pre-minified files, though if those are detected they will be passed through unchanged.&lt;/li&gt;
&lt;li&gt;In addition when using HTTP/2 you should disable File compilation plugins and features as they will actually slow the page down by loading more resources then needed. &amp;nbsp;In HTTP/2 a smaller lists of individual files is better then one larger compiled file.&lt;/li&gt;
&lt;/ul&gt;
Some example performance numbers:

&lt;i&gt;(Time to download HTML / All assets loaded)&lt;/i&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Serving a simple Wordpress page: Uncached&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;441ms; 1.6s&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Serving a simple Wordpress page: Cached&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;63ms, 1.1s&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Serving a complex Magneto storefront page: Uncached&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;With configurations: 7900ms, 9.25s&lt;/li&gt;
&lt;li&gt;Simple product: 1690s, 1.95s&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Serving a complex Magneto storefront page: Cached&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;With configurations: 121ms, 2.0s&lt;/li&gt;
&lt;li&gt;Simple product: 143ms, 1.0s&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;h2&gt;
Server configuration&lt;/h2&gt;
If you already have some of this set up you may not need to configure everything listed below.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Installing Required Packages&lt;/h3&gt;
This guide assumes the following in a FreeBSD environment. &amp;nbsp;Install them as needed or install your own preferences. &amp;nbsp;The setup will work in other linux systems as well though some of the services will be configured different and the file paths may change.
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Mysql/PHP/etc&lt;/li&gt;
&lt;li&gt;openssl&lt;/li&gt;
&lt;li&gt;Varnish 4 or grater&lt;/li&gt;
&lt;li&gt;Apache 2.4 or greater&lt;/li&gt;
&lt;li&gt;Nginx 1.11 or greater&lt;/li&gt;
&lt;li&gt;lets encrypt.sh 0.2 or greater (or you can use your own certificates)&lt;/li&gt;
&lt;li&gt;ruby 2.2 (optional, for seamless resource minification)&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;ruby-gems&lt;/li&gt;
&lt;li&gt;rubygem-cssmin&lt;/li&gt;
&lt;li&gt;rubygem-jsmin&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
Activate Services and Configure&lt;/h3&gt;
Once you have a servqer up and running with the required software you will want to activate the various services and configure them on boot. &amp;nbsp;The following should be added to your &lt;code&gt;/etc/rc.conf&lt;/code&gt; file to set the default services.
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;apache24_enable=&quot;YES&quot;
mysql_enable=&quot;YES&quot;
varnishd_enable=&quot;YES&quot;
varnishd_config=&quot;/usr/local/etc/varnish.vcl&quot;
#varnishd_extra_flags=&quot;-p http_resp_hdr_len=16192&quot;
#varnishd_listen=&quot;:8088&quot;
#varnishd_backend=&quot;localhost:80&quot;
#varnishd_jailuser=&quot;www&quot;
nginx_enable=&quot;YES&quot; # Proxy SSL for HTTP/2
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
In addition the following needs to be added to your&amp;nbsp;&lt;code&gt;/etc/periodic.conf file&lt;/code&gt; get SSL certificates to automatically renew.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;weekly_letsencrypt_enable=&quot;YES&quot;
#weekly_letsencrypt_deployscript=&quot;/usr/local/etc/letsencrypt.sh/deploy.sh&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Configure LetsEncrypt.sh&lt;/h3&gt;
LetsEncrypt.sh doesn&#39;t really put the certificates anyplace useful and the certificates need to be manipulated as well. &amp;nbsp;The following code needs to be added to the&amp;nbsp;&lt;code&gt;/usr/local/etc/letsencrypt.sh/hook.sh&lt;/code&gt; file inside the deploy_cert function.
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;# Copy certificates to standard SSL folder.
cp $CERTFILE /usr/local/etc/ssl/$DOMAIN.crt
cp $CHAINFILE /usr/local/etc/ssl/$DOMAIN.chain.crt
cp $KEYFILE /usr/local/etc/ssl/$DOMAIN.key
# Merge certificate chain files.
cat $CERTFILE $CHAINFILE &amp;gt; /usr/local/etc/ssl/$DOMAIN.merge.crt
# Adjust permissions so that web servers can access certificates.
chgrp www /usr/local/etc/ssl/$DOMAIN.crt /usr/local/etc/ssl/$DOMAIN.chain.crt /usr/local/etc/ssl/$DOMAIN.key /usr/local/etc/ssl/$DOMAIN.merge.crt
chmod g+r /usr/local/etc/ssl/$DOMAIN.crt /usr/local/etc/ssl/$DOMAIN.chain.crt /usr/local/etc/ssl/$DOMAIN.key /usr/local/etc/ssl/$DOMAIN.merge.crt 
echo &quot;Copied certs for $CERTFILE on `date`.&quot; &amp;gt;&amp;gt; /var/log/letsencrypt&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Configure PHP&lt;/h3&gt;
There is lots of possible configuration here that I wont get into but in brief you will probably want to enable OP Cache by setting &lt;code&gt;opcache.enable=1&lt;/code&gt;.  There is more information &lt;a href=&quot;http://php.net/manual/en/opcache.configuration.php&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Configure Backend Apache Web Server&lt;/h3&gt;
You will need to make a few changes to the default apache configuration as indicated below.
&lt;br /&gt;
&lt;br /&gt;
In &lt;code&gt;/usr/local/etc/apache24/httpd.conf&lt;/code&gt; make sure the following lines are uncommented or updated as indicated.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;# Change the HTTP listen port.
Listen 8080

# Load required Modules
LoadModule deflate_module libexec/apache24/mod_deflate.so
LoadModule ssl_module libexec/apache24/mod_ssl.so
LoadModule vhost_alias_module libexec/apache24/mod_vhost_alias.so
LoadModule rewrite_module libexec/apache24/mod_rewrite.so
LoadModule php5_module        libexec/apache24/libphp5.so
LoadModule ext_filter_module libexec/apache24/mod_ext_filter.so // Optional for minification
LoadModule expires_module libexec/apache24/mod_expires.so // Optional to inject expire headers

# Replace %h with %{X-Forwarded-For}i in all LogFormat settings
LogFormat &quot;%{X-Forwarded-For}i %l %u %t \&quot;%r\&quot; %&amp;gt;s %b \&quot;%{Referer}i\&quot; \&quot;%{User-Agent}i\&quot;&quot; combined
LogFormat &quot;%{X-Forwarded-For}i %l %u %t \&quot;%r\&quot; %&amp;gt;s %b&quot; common

# Virtual hosts
Include etc/apache24/extra/httpd-vhosts.conf

# Secure (SSL/TLS) connections
Include etc/apache24/extra/httpd-ssl.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Also edit the &lt;code&gt;/usr/local/etc/apache24/extra/httpd-ssl.conf&lt;/code&gt; file to change the HTTPS listen port.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;# Change the HTTPS listen port.
Listen 44344
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Create a new file &lt;code&gt;/usr/local/etc/apache24/modules.d/030_ext_filter_module.conf&lt;/code&gt; for the minification feature.
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;IfModule mod_ext_filter.c&amp;gt;
  ExtFilterDefine jsmin \
                  mode=output \
                  intype=application/javascript \
                  outtype=application/javascript \
                  cmd=&quot;/usr/local/bin/ruby -e &#39;require \&quot;rubygems\&quot;; require \&quot;jsmin\&quot;; code = STDIN.read;  begin; min = JSMin.minify(code); rescue; min = code; ensure; puts min; end;&#39;&quot;
  &amp;lt;If &quot;%{HTTP:X-Minify} =~ /js/&quot;&amp;gt;
    AddOutputFilter jsmin js
  &amp;lt;/If&amp;gt;
&amp;lt;/IfModule&amp;gt;

&amp;lt;IfModule mod_ext_filter.c&amp;gt;
  ExtFilterDefine cssmin \
                  mode=output \
                  intype=text/css \
                  outtype=text/css \
                  cmd=&quot;/usr/local/bin/ruby -e &#39;require \&quot;rubygems\&quot;; require \&quot;cssmin\&quot;; code = STDIN.read;  begin; min = CSSMin.minify(code); rescue; min = code; ensure; puts min; end;&#39;&quot;
  &amp;lt;If &quot;%{HTTP:X-Minify} =~ /css/&quot;&amp;gt;
    AddOutputFilter cssmin css
  &amp;lt;/If&amp;gt;
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Create a new file &lt;code&gt;/usr/local/etc/apache24/modules.d/040_expires_module.conf&lt;/code&gt; for the append expire headers feature. Tweak these values as you like.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;IfModule mod_expires.c&amp;gt;
ExpiresActive on
ExpiresByType image/gif &quot;access plus 1 months&quot;
ExpiresByType image/jpg &quot;access plus 1 months&quot;
ExpiresByType image/jpeg &quot;access plus 1 months&quot;
ExpiresByType image/png &quot;access plus 1 months&quot;
ExpiresByType text/js &quot;access plus 1 days&quot;
ExpiresByType text/javascript &quot;access plus 1 days&quot;
ExpiresByType application/javascript  &quot;access plus 1 days&quot;
ExpiresByType text/css  &quot;access plus 1 days&quot;
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
You can start Apache now with &lt;code&gt;sudo service apache24 start&lt;/code&gt; to verify the configuration though there are no sites to test yet.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Configure Varnish Caching Service&lt;/h3&gt;
Next you can set up a default rules file for the varnish caching.  This is rather complex and can have a lot of custom rules and variations depending your sites, software, and preference.  I have included the &lt;code&gt;/usr/local/etc/varnish.vcl&lt;/code&gt; file that I use, though some of the rules may not be applicable to your sites.  This file is configured for general Wordpress sites and Magento storefronts and there are some comments and details in the file.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/6edb86b51d5f65bf884ee4fb0c3fb762.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
You can start Varnish now with &lt;code&gt;sudo service varnishd start&lt;/code&gt; to verify the configuration though there are no sites to test yet.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Configure Frontend Nginx Proxy Server&lt;/h3&gt;
Finally configure the Nginx proxy service by editing you &lt;code&gt;/usr/local/etc/nginx/nginx.conf&lt;/code&gt; file and adding or updating the contents as indicated.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;http {
    # Append Access-Control-Allow-Origin only if it&#39;s not already present.
    map $upstream_http_access_control_allow_origin $access_control_allow_origin {
        &#39;&#39; &quot;https://$host always&quot;;
    }

    # Enable compression
    gzip  on;
    gzip_types text/css application/javascript;
    gzip_disable        &quot;MSIE [1-6]\.&quot;;
    gzip_comp_level 5;

    # Transcode all local resource, form actions, and links from HTTP to HTTPS.
    sub_filter &#39;http://$host/&#39; &#39;https://$host/&#39;;
    sub_filter &#39;http:\/\/$host\/&#39; &#39;https:\/\/$host\/&#39;;
    sub_filter_last_modified on;
    sub_filter_once off;
    sub_filter_types application/xml text/xml;

    # Add a header to tell the client browser to automatically request HTTPS resources form remote sites instead of HTTP to prevent mixed content warnings.
    add_header Content-Security-Policy upgrade-insecure-requests;
    #Add a header to allow for HTTPPS scripting calls to be handled by HTTP in some edge cases where the transcoded URL&#39;s missed something.
    add_header Access-Control-Allow-Origin $access_control_allow_origin;

    # Add default headers for the proxy request.
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-Port 443;
    proxy_set_header Accept-Encoding &quot;&quot;;
    proxy_read_timeout 600;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
You can start Nginx now with &lt;code&gt;sudo service nginx start&lt;/code&gt; to verify the configuration though there are no sites to test yet.&lt;br /&gt;
&lt;h2&gt;
Add and Configure a new Website&lt;/h2&gt;
Now that the server is configured properly you can set up your sites. &amp;nbsp;Repeat this process for each site.
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Configure DNS&lt;/h3&gt;
Configure your DNS to point at the server. &amp;nbsp;You may want to use a custom HOSTS record on your computer for testing instead or when migrating a site.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Load the Site Files&lt;/h3&gt;
Load your site file and configure as needed.  The instructions below assume your site files are loaded into folders using the site domain name like &lt;code&gt;/usr/local/www/&lt;b&gt;www.DomainName.com&lt;/b&gt;/&lt;/code&gt; but you can change this if you prefer.&lt;br /&gt;
&lt;br /&gt;
Though it seams counterintuitive you will want to disable resource compilation, minify, and compression in your CMS/site code. Compilation will actually slow down requests a but on HTTP/2 and cost extra bandwidth in many cases. Similarly compression is not needed in Apache as Varnish will take care of that.&lt;br /&gt;
&lt;br /&gt;
As for the CMS systems minifying files, this can be turned off and Apache can do this at runtime. &amp;nbsp;This way you can easily turn it off in your session for testing without affecting other users and you don&#39;t need to deal with the overhead and possible performance costs of many of the plugins. &amp;nbsp;This setup is very efficient because the files are minified just before they are cached in varnish.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Add an Apache Virtual Host&lt;/h3&gt;
Create the backend website in Apache by editing your &lt;code&gt;/usr/local/etc/apache24/extra/httpd-vhosts.conf&lt;/code&gt; file and adding a new section like this.  Make sure to replace the &lt;code&gt;DomainName&lt;/code&gt; with the proper value.
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:8080&amp;gt;
    #ServerAdmin AdminEmailAddress
    DocumentRoot /usr/local/www/&lt;b&gt;www.DomainName.com&lt;/b&gt;
    &amp;lt;Directory &quot;/usr/local/www/&lt;b&gt;www.DomainName.com&lt;/b&gt;&quot;&amp;gt;
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    &amp;lt;/Directory&amp;gt;
    ServerName &lt;b&gt;www.DomainName.com&lt;/b&gt;
    ServerAlias &lt;b&gt;DomainName.com&lt;/b&gt;
    Alias /.well-known/acme-challenge/ /usr/local/etc/letsencrypt.sh/.acme-challenges/
    ErrorLog &quot;/var/log/apache/&lt;b&gt;www.DomainName.com&lt;/b&gt;-error_log&quot;
    CustomLog &quot;/var/log/apache/&lt;b&gt;www.DomainName.com&lt;/b&gt;-access_log&quot; common
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
If your domain name starts with a www. you can also add the domain name minus the www into the &lt;code&gt;ServerAlias&lt;/code&gt; field so it responds to both.  If you have other domains that also point to the same site they should also be added to the &lt;code&gt;ServerAlias&lt;/code&gt; field, then you can add 301 redirect rules in a .HTACCESS file to 301 requests to a default hostname if you prefer.
&lt;br /&gt;
&lt;br /&gt;
You also need to update &lt;code&gt;/etc/hosts&lt;/code&gt; and find the &lt;code&gt;::1 localhost&lt;/code&gt; and &lt;code&gt;127.0.0.1 localhost&lt;/code&gt; lines and add your primary domain to the end of each like so.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;::1   localhost &lt;b&gt;www.DomainName.com&lt;/b&gt;
127.0.0.1  localhost &lt;b&gt;www.DomainName.com&lt;/b&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Finally restart Apache now with&amp;nbsp;&lt;code&gt;sudo service apache24 graceful&lt;/code&gt; to start up the new site and navigate to&amp;nbsp;&lt;code&gt;http://&lt;b&gt;www.DomainName.com&lt;/b&gt;&lt;/code&gt;&amp;nbsp;to see it, or on the server by typing &lt;code&gt;curl -i --http2 http://&lt;b&gt;www.DomainName.com&lt;/b&gt;&amp;nbsp;| less&lt;/code&gt;. &amp;nbsp;You can add &lt;code&gt;:8080&lt;/code&gt; to the end of the URL to see the uncached version, though depending on your network configuration this may not work.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Generate SSL Certificate&lt;/h3&gt;
To generate a new SSL Certificate for your site edit the &lt;code&gt;/usr/local/etc/letsencrypt.sh/domains.txt&lt;/code&gt; file and add your &lt;code&gt;DomainName&lt;/code&gt; to it along with all the alternate domain names and/or non-www versions like so.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&lt;b&gt;www.DomainName.com&lt;/b&gt; &lt;b&gt;DomainName.com&lt;/b&gt; &lt;b&gt;www.DomainName2.com&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Each separate site should be a separate line in this file and any alternate domain names listed in the &lt;code&gt;ServerAlias&lt;/code&gt; field in Apache should be included on the line after the primary domain name. &amp;nbsp;The primary domain name/first entry will be used below to configure the SSL sites. &amp;nbsp;Once done run the following command to generate the new certificates manually, from then on they will be automatically updated as needed.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;/usr/local/bin/letsencrypt.sh -c&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Configure Nginx Proxy Site&lt;/h3&gt;
And now on to the crux of this article, configuring proxied SSL sites through the Varnish caching service. &amp;nbsp;To do this create a new site in Nginx by editing your &lt;code&gt;/usr/local/etc/nginx/nginx.conf&lt;/code&gt; file updating the DomainName as appropriate.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;server {
    listen       443 ssl http2;
    server_name  &lt;b&gt;www.DomainName.com&lt;/b&gt; &lt;b&gt;DomainName.com&lt;/b&gt;;
    ssl_certificate    /usr/local/etc/ssl/&lt;b&gt;www.DomainName.com&lt;/b&gt;.merge.crt;
    ssl_certificate_key /usr/local/etc/ssl/&lt;b&gt;www.DomainName.com&lt;/b&gt;.key;

   location / {
            proxy_pass http://localhost:80;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Here you will need to again include the same additional domain specified in the&lt;code&gt;ServerAlias&lt;/code&gt; field in Apache after the primary domain name in the &lt;code&gt;server_name&lt;/code&gt; field. &amp;nbsp;You can restart Nginx now with&amp;nbsp;&lt;code&gt;sudo service nginx restart&lt;/code&gt;&amp;nbsp;and navigate to&amp;nbsp;&lt;code&gt;https://&lt;b&gt;www.DomainName.com&lt;/b&gt;&lt;/code&gt;&amp;nbsp;to see the SSL version of the site, or on the server by typing&amp;nbsp;&lt;code&gt;curl -i --http2 https://&lt;b&gt;www.DomainName.com&lt;/b&gt; | less&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Testing and Controlling Transparent Minification&lt;/h3&gt;
Minification should work by default most of the time. If your getting empty css or javascript files or nothing is being minified run the following commands on the server to ensure the proper packages are installed and working.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;p {
    color: red;
    font-weight: bold;
}&quot; | /usr/local/bin/ruby -e &#39;require &quot;rubygems&quot;; require &quot;cssmin&quot;; code = STDIN.read; begin; min = CSSMin.minify(code); rescue; min = code; ensure; puts min; end;&#39;

echo &quot;(function() {
    console.log(&#39;test&#39;);
})();&quot; | /usr/local/bin/ruby -e &#39;require &quot;rubygems&quot;; require &quot;jsmin&quot;; code = STDIN.read; begin; min = JSMin.minify(code); rescue; min = code; ensure; puts min; end;&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
You can switch override the default processing rules by setting a cookie in your browser.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;document.cookie=&quot;minify=disable;&quot; // disable for this domain
document.cookie=&quot;minify=force;&quot; // force for this domain
document.cookie=&quot;minify=;&quot; // use default rules for this domain
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
In addition in &lt;code&gt;/usr/local/etc/varnish.vcl&lt;/code&gt; you can permanently completely turn this feature off, block specific domains, or block individual scripts or css files from being processed. Also note that files named &lt;code&gt;.min.css&lt;/code&gt; or &lt;code&gt;.min.js&lt;/code&gt; won&#39;t be processed.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
SSL Related Issues&lt;/h3&gt;
This is where things can get somewhat complicated. &amp;nbsp;As Varnish can&#39;t process HTTPS content, we are serving the entire site through HTTP and the proxy server at the end is delivering it as HTTPS. &amp;nbsp;There are all sorts of issues this can cause in CMS systems though if for example the CMS system is checking the URL protocol and returning a 301 in an attempt to force SSL that we would end up in a redirect loop. &amp;nbsp;This would happen in Wordpress for example if we set the &lt;code&gt;Site Address URL&lt;/code&gt; or &lt;code&gt;WordPress Address URL&lt;/code&gt; to &lt;code&gt;https://&lt;/code&gt; instead of &lt;code&gt;http://&lt;/code&gt;. &amp;nbsp;Some systems like this we can simply leave the validation URL as &amp;nbsp;&lt;code&gt;http://&lt;b&gt;www.DomainName.com&lt;/b&gt;&lt;/code&gt;&amp;nbsp;and everything will work out correctly due to the URL translating logic in Nginx, but others may not. &amp;nbsp;Depending on your software it may also be possibly to identify the protocol validation code and add additional code to check the &lt;code&gt;X-Forwarded-Proto&lt;/code&gt; header in addition to the request protocol before redirecting. &amp;nbsp;You may also be able to disable some Force SSL settings to fix redirect issues, or in Wordpress deactivate and SSL plugins.&lt;br /&gt;
&lt;br /&gt;
Another issue you may run into is that caching is not occurring between different sessions or users. &amp;nbsp;This would generally happen because users are ending up with different cookies. &amp;nbsp;The following command will let you monitor requests as they come in to see if they are being cached or not and troubleshoot the issues. &amp;nbsp;You may need to add some more cookie exceptions in &lt;code&gt;/usr/local/etc/varnish.vcl&lt;/code&gt; and restart. &amp;nbsp;Some trial and error may be needed here.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;sudo varnishncsa  -F  &quot;%{X-Forwarded-For}i %{X-Forwarded-Proto}i://%{Host}i%U %{Varnish:handling}x %{Varnish:hitmiss}x&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Force SSL&lt;/h3&gt;
Assuming that everything is working correctly with the SSL configuration you can force all traffic or only specific sites to use SSL. &amp;nbsp;To do this we need to do it in the Varnish configuration as the backend is all non-SSL and we don&#39;t want redirect loops or complex conditional code. &amp;nbsp;To do this edit your&amp;nbsp;&lt;code&gt;/usr/local/etc/varnish.vcl&lt;/code&gt;&amp;nbsp;file find the &lt;code&gt;# Optinal rule to force SSL&lt;/code&gt; line.  Add or edit it as needed with your new &lt;code&gt;DomainName&lt;/code&gt;.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;# Optinal rule to force SSL Everywhere for listed domains.
if ( (req.http.host ~ &quot;^(&lt;b&gt;www.DomainName.com&lt;/b&gt;|&lt;b&gt;www.AnotherDomainName.com&lt;/b&gt;)&quot;) &amp;amp;&amp;amp; req.http.X-Forwarded-Proto !~ &quot;https&quot;) {
    set req.http.x-redir = &quot;https://&quot; + req.http.host + req.url;
    return (synth(750, &quot;&quot;));
}

# Or force all requests to SSL
if ( req.http.X-Forwarded-Proto !~ &quot;https&quot;) {
    set req.http.x-redir = &quot;https://&quot; + req.http.host + req.url;
    return (synth(750, &quot;&quot;));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Add Caching exceptions&lt;/h3&gt;
If you have directories or sections that should not be cached, or if there are headers or cookies that should prevent caching when present you can add rules to your&amp;nbsp;&lt;code&gt;/usr/local/etc/varnish.vcl&lt;/code&gt;&amp;nbsp;file to prevent caching in the &lt;code&gt;# Add additinal rules here&lt;/code&gt; section. &amp;nbsp;Here are some examples:
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;# Bypass shopping cart and checkout requests
if (req.url ~ &quot;/(cart|my-account|checkout|addons)&quot;) {
    # Don&#39;t cache, pass to backend
    return (pass);
}

# Either the admin pages or the login
if (req.url ~ &quot;/wp-(login|admin|cron)&quot;) {
    # Don&#39;t cache, pass to backend
    return (pass);
}

# Custom Scripts and API
if (req.url ~ &quot;/api/&quot;) {
    # Don&#39;t cache, pass to backend
    return (pass);
}
&lt;/code&gt;&lt;/pre&gt;
As you are making changes you can test them by running &lt;code&gt;sudo service varnishd reload&lt;/code&gt; so that the entire cache isn&#39;t cleared.  Additionally you can easily check which requests are hitting the cache with this command. 
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;sudo varnishncsa  -F  &quot;%{X-Forwarded-For}i %{Varnish:time_firstbyte}x %m %{X-Forwarded-Proto}i://%{Host}i%U %{Varnish:handling}x %{Varnihitmiss}x&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Configuring a CMS to Purge it&#39;s Cache&lt;/h3&gt;
The next major hurdle is that as you are eloping or tweaking your CMS/site you will quickly discover &amp;nbsp;that the varnish caching layer is caching changes and won&#39;t check the backend for updates. &amp;nbsp;You can restart the entire service with &lt;code&gt;sudo service varnishd restart&lt;/code&gt; but that will clear the cache for all your sites and it quite cumbersome. &amp;nbsp;Instead you will want to configure a plugin or custom code for your site to just clear it&#39;s cache on demand. &lt;br /&gt;
&lt;br /&gt;
To configure varnish integration in Magento log in to the backend and go to &lt;code&gt;Store -&amp;gt; Configuration -&amp;gt; &amp;nbsp;Advanced -&amp;gt; System -&amp;gt; Full Page Cache&lt;/code&gt;. &amp;nbsp;Set the &lt;b&gt;Caching Application&lt;/b&gt;&amp;nbsp;to &lt;b&gt;Varnish Cache&lt;/b&gt; and save the changes. &amp;nbsp;In addition you will need to edit your &lt;code&gt;app/etc/env.php&lt;/code&gt; file and &lt;a href=&quot;https://gist.github.com/pynej/c33891d4377c101247f7764460e07bbb&quot; target=&quot;_blank&quot;&gt;this section&lt;/a&gt;&amp;nbsp;at the end just before the last line. &amp;nbsp;Additionally &lt;a href=&quot;https://gist.github.com/pynej/3ef93e8583e0a9310a2ee79df83183bc&quot; target=&quot;_blank&quot;&gt;this patch&lt;/a&gt;&amp;nbsp;has to be applied to your Magneto install of it will clear all site&#39;s cache&#39;s instead of just the current site.&lt;br /&gt;
&lt;br /&gt;
In wordpress you simply need to install the &lt;a href=&quot;https://wordpress.org/plugins/varnish-http-purge/&quot; target=&quot;_blank&quot;&gt;Varnish HTTP Purge&lt;/a&gt; plugin and your good to go. &amp;nbsp;Please note that you will get unexpected results and poor performance if you didn&#39;t add the primary hostname to your servers&amp;nbsp;&lt;code&gt;/etc/hosts&lt;/code&gt; file as indicated above.&lt;br /&gt;
&lt;br /&gt;
If you have some other system of custom software you can create your own scripts or internal plugins to purge resources as needed. &amp;nbsp;A script simply needs to send HTTP request with a type of PURGE and a host header specifying the domain to purge. &amp;nbsp;It can purge the entire domain, or purge a specific url. &amp;nbsp;Some examples that can be used directly or converted into your CMS&#39;s language:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;# Do wildcard patching to purge a directory or the entire site
curl -H &quot;Host: &lt;b&gt;www.DomainName.com&lt;/b&gt;&quot; -H &quot;X-Purge-Method: regex&quot; -X PURGE http://127.0.0.1/.* 
# Purge a specific file/url including url parameter
curl -H &quot;Host: &lt;b&gt;www.DomainName.com&lt;/b&gt;&quot; -H &quot;X-Purge-Method: exact&quot; -X PURGE http://127.0.0.1/some/file.html?param
# Purge all versions of a file/url ignoring any parameter tokens
curl -H &quot;Host: &lt;b&gt;www.DomainName.com&lt;/b&gt;&quot; -H &quot;X-Purge-Method: default&quot; -X PURGE http://127.0.0.1/some/file.html 
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Bypass Caching Site&lt;/h3&gt;
If your you have some sites that are causing issues and not working properly through the Varnish cache, you can bypass the cache for specific sites. &amp;nbsp;To do this first you need to add a backed SSL site in Apache by editing your&amp;nbsp;&lt;code&gt;/usr/local/etc/apache24/extra/httpd-vhosts.conf&lt;/code&gt;&amp;nbsp;file and adding a new section like this. Make sure to replace the&amp;nbsp;&lt;code&gt;DomainName&lt;/code&gt;&amp;nbsp;with the proper value.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:44344&amp;gt;
    #ServerAdmin AdminEmailAddress
    DocumentRoot /usr/local/www/&lt;b&gt;www.DomainName.com&lt;/b&gt;
    &amp;lt;Directory &quot;/usr/local/www/&lt;b&gt;www.DomainName.com&lt;/b&gt;&quot;&amp;gt;
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
        AddOutputFilterByType DEFLATE text/css
        AddOutputFilterByType DEFLATE application/javascript
    &amp;lt;/Directory&amp;gt;
    ServerName &lt;b&gt;www.DomainName.com&lt;/b&gt;
    ServerAlias &lt;b&gt;AnotherDomainName.com&lt;/b&gt;
    ErrorLog &quot;/var/log/apache/&lt;b&gt;www.DomainName.com&lt;/b&gt;-error_log&quot;
    CustomLog &quot;/var/log/apache/&lt;b&gt;www.DomainName.com&lt;/b&gt;-access_log&quot; common
    SSLEngine on
    SSLCertificateFile /usr/local/etc/ssl/&lt;b&gt;www.DomainName.com&lt;/b&gt;.crt
    SSLCertificateKeyFile /usr/local/etc/ssl/&lt;b&gt;www.DomainName.com&lt;/b&gt;.key
    SSLCertificateChainFile /usr/local/etc/ssl/&lt;b&gt;www.DomainName.com&lt;/b&gt;.chain.crt
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Again if your domain name starts with a www. you can also add the domain name minus the www into the &lt;code&gt;ServerAlias&lt;/code&gt; field so it responds to both.  If you have other domains that also point to the same site they should also be added to the &lt;code&gt;ServerAlias&lt;/code&gt; field.  Then restart Apache now with&amp;nbsp;&lt;code&gt;sudo service apache24 graceful&lt;/code&gt;.
&lt;br /&gt;
&lt;br /&gt;
Next update the Nginx configuration by editing your &lt;code&gt;/usr/local/etc/nginx/nginx.conf&lt;/code&gt; file and changing the line below in the proper server section.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;proxy_pass http://localhost:&lt;b&gt;44344&lt;/b&gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Again restart Nginx now with&amp;nbsp;&lt;code&gt;sudo service nginx graceful&lt;/code&gt; and at this point the SSL site should work correctly, though its pages won&#39;t be cached any longer.</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4834984779808985463/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/4834984779808985463?isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4834984779808985463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4834984779808985463'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2017/02/configure-ssl-termination-with-varnish.html' title='Configure SSL Termination with Varnish Caching and HTTP/2'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ0RtbxHLgbBscD5YlFRyGgQjB6ItVhxNHWmkkJSLLvDEBz1nk5aFPLDuDKQMoCmQk1NEr7FK64ANXWtH2IzVzRg8RcGkcCrjYirKEctpcqveVcZkTD6reHpik7eZvmqvysczO0PYcBUE/s72-c/Diagram1.png" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2115617104474459091</id><published>2017-01-30T15:31:00.001-05:00</published><updated>2017-01-30T15:33:54.033-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="iContact"/><category scheme="http://www.blogger.com/atom/ns#" term="PHP"/><category scheme="http://www.blogger.com/atom/ns#" term="Programming"/><category scheme="http://www.blogger.com/atom/ns#" term="Wordpress"/><title type='text'>Easily post leads in Wordpress Fast Secure Contact Form Plugin to iContact</title><content type='html'>When using the &lt;a href=&quot;https://wordpress.org/plugins/si-contact-form/&quot; target=&quot;_blank&quot;&gt;Fast Secure Contact Form&lt;/a&gt; plugin for wordpress is is very very simply to seamlessly forward leads from any forms you wish to the &lt;a href=&quot;https://www.icontact.com/&quot; target=&quot;_blank&quot;&gt;iContact&lt;/a&gt; platform with very little configuration.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
First off create any lists in iContact you might what and and forms you need as normal. &amp;nbsp;You could set up one list per form, have multiple forms feed to one list, or even have one form post to more then one list. &amp;nbsp;When doing this you will need to record the List Id for each list you wish to post to. &amp;nbsp;You can get this by going to the list view iContact and click on the list name to edit it. &amp;nbsp;Then check the page URL for the ListId number like so:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;https://app.icontact.com/icp/core/mycontacts/lists/edit/&lt;b&gt;ListId&lt;/b&gt;/?token=...&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Next you will need to go into each form that you have created that you want to forward to iContact in your Wordpress site. &amp;nbsp;When looking at the form settings go to the Advanced tab and scroll down to the Silent Remote Sending Settings section. &amp;nbsp;Change the following settings there:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Silent Remote Sending: &lt;code&gt;Enabled: Method POST&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Silent Remote URL: &lt;code&gt;http://YouWordpressDomain/icontact-post.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Silent send fields to ignore&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;full_message&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;akismet&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;date_time&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ip_address&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;name_to&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;email_to&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;from_name&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Silent send fields to rename&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;first_name=firstName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;last_name=lastName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;from_email=email&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;company=companyname&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;phone=phone&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Silent send keys to add&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list_id=&lt;b&gt;ListId&lt;/b&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;br /&gt;
Where &lt;code&gt;ListId&lt;/code&gt; is the applicable id recorder for each form. &amp;nbsp;The ListId can even be a coma separated list of id&#39;s.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
As a final step you will need to upload the following connector script to your sites root directory and edit it add your iContact authentication information such that the Silent Remote Url entered above is correctly points to it. &amp;nbsp;If you have any issues there is a test line in the file that can be uncommented to supply sample data. &amp;nbsp;Also note that you may need to add to or tweak the Silent send fields list if you have changed field names or added custom fields that you want to forward.&lt;/div&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/04a31aa51c4da03b3d86e6adda67038a.js&quot;&gt;&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2115617104474459091/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/2115617104474459091?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2115617104474459091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2115617104474459091'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2017/01/easily-ost-leads-in-wordpress-fast.html' title='Easily post leads in Wordpress Fast Secure Contact Form Plugin to iContact'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4605342286434738235</id><published>2016-11-01T14:19:00.000-04:00</published><updated>2016-11-01T14:19:49.662-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="Programming"/><category scheme="http://www.blogger.com/atom/ns#" term="Umbraco"/><title type='text'>Guide to Custom Database Access in Umbraco 7 With Built in PetaPoco</title><content type='html'>Though there are a lot of ways to write custom database code, and in many place in Umbraco custom tables my no be needed as the CMS can handle most any custom data needs. There are reasons and use cases for using custom database tables and code including:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Reading data from external systems.&lt;/li&gt;
&lt;li&gt;Large datasets that we don’t want to clog out cache’s and Umbraco tree with.&lt;/li&gt;
&lt;li&gt;Performance sensitive querying where the Document -&amp;gt; Property models setup may not be preferable.&lt;/li&gt;
&lt;li&gt;Complex cross-joins and many-to-many relationships that may not be easy to replicate in Umbraco.&lt;/li&gt;
&lt;li&gt;Customer data, log data, or “write once” style form submissions where the data need not be in Umbraco trees.&lt;/li&gt;
&lt;li&gt;Data that may need to be easily accessed from external systems.&amp;nbsp;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;They can also use public Web API methods or custom views that read from the Umbraco core tables but writing data this was it less safe.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
Though there are a lot of ways to do the database code and everyone has their own preferences, this guide is using the same &lt;a href=&quot;http://www.toptensoftware.com/petapoco/&quot;&gt;PetaPoco&lt;/a&gt; system that Umbraco uses internal. &amp;nbsp;This has a few key benefits:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Very lightweight system yet powerful.&lt;/li&gt;
&lt;li&gt;Fully compatible with Umbraco models, methodologies, and serialization.&lt;/li&gt;
&lt;li&gt;Easy to implement and generate models.&lt;/li&gt;
&lt;li&gt;All code is using existing database code already present in the Umbraco Core to minimize overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
The system can be used in one of two ways. &amp;nbsp;You can create all our database models(POCOs) in .NET and then have Application Startup code that creates or updates any missing database objects or you can create all the database objects yourself in the database using standard management tools and have PetaPoco automatically generate models(POCOs) based off that database.&lt;br /&gt;
&lt;br /&gt;
We are going to be doing the second method in this article as I find it easier to maintain and design with but &lt;a href=&quot;http://www.wiliam.com.au/wiliam-blog/using-petapoco-with-umbraco-is-pretty-sweet&quot;&gt;this post&lt;/a&gt; has some example of working the other way. &amp;nbsp;Either way the second section of this article describes querying and extending models which may still be helpful.&lt;br /&gt;
&lt;h2&gt;
Automatically Generating POCO’s from a Database&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;First of you will need to install the &lt;code&gt;PetaPoco&lt;/code&gt; package from NuGet into your project or into a separate depended project.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;If you are using a separate project it will need to reference the &lt;code&gt;Umbraco.Core&lt;/code&gt; package.&lt;/li&gt;
&lt;li&gt;Once installed you need to locate the &lt;code&gt;Models/PetaPoco.cs&lt;/code&gt; file and exclude it from your project. &amp;nbsp;This is redundant as it is already included in the Umbraco dll’s and would cause confusion.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;You will need to add your Connection String to the &lt;code&gt;web.config&lt;/code&gt; file if it’s different then the Umbraco database and update the &lt;b&gt;ConnectionStringName&lt;/b&gt; below.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;If using a separate project you will also need to copy the &lt;b&gt;umbracoDbDSN&lt;/b&gt; or your custom connection string to that project’s &lt;code&gt;app.config&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;You will need to open up the &lt;code&gt;Models/Generated/Database.tt&lt;/code&gt; file and make some configuration changes. &amp;nbsp;These settings below should get you started.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Make sure you change the &lt;b&gt;ProjectNamespace&lt;/b&gt; to match what is set in the Default namespace of your projects properties.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;nbsp; &amp;nbsp; // Settings
&amp;nbsp; &amp;nbsp; ConnectionStringName = &quot;&quot;; // Uses last connection string in config if not specified
&amp;nbsp; &amp;nbsp; Namespace = &quot;ProjectNamespace.Models.Generated&quot;;
&amp;nbsp; &amp;nbsp; RepoName = &quot;&quot;;
&amp;nbsp; &amp;nbsp; GenerateOperations = false; // Don’t need these.
&amp;nbsp; &amp;nbsp; GeneratePocos = true; // We definitely want these.
&amp;nbsp; &amp;nbsp; GenerateCommon = false; // Don’t need any of this code.
&amp;nbsp; &amp;nbsp; ClassPrefix = &quot;&quot;;
&amp;nbsp; &amp;nbsp; ClassSuffix = &quot;&quot;;
&amp;nbsp; &amp;nbsp; TrackModifiedColumns = false; // We shouldn’t need change tracking logic for most things as we aren’t creating an entire world here. &amp;nbsp;You can use it if needed though.
&amp;nbsp; &amp;nbsp; ExplicitColumns = true;
&amp;nbsp; &amp;nbsp; ExcludePrefix = new string[] {&quot;cms&quot;, &quot;CMS&quot;, &quot;umbraco&quot;}; // This is telling the generator to exclude the Umbraco tables from the generated code.
&amp;nbsp; &amp;nbsp; IncludeViews = true; // You can create custom views to pull data or some CMS data is desired.

&amp;nbsp; &amp;nbsp; // Read schema
&amp;nbsp; &amp;nbsp; var tables = LoadTables();
&amp;nbsp; &amp;nbsp; tables[&quot;SomeTable&quot;].Ignore = true; &amp;nbsp;// This is how we can suppress other tables.&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Now you can save the &lt;code&gt;Database.tt&lt;/code&gt; file and Visual Studio will automatically generate the models. &amp;nbsp;You can expand the &lt;code&gt;Databse.tt&lt;/code&gt; file and select the resulting &lt;code&gt;Database.cs&lt;/code&gt; file to review the generated code.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h2&gt;
Extending Models with extra logic&lt;/h2&gt;
You can add partial class’s in the &lt;b&gt;Generated&lt;/b&gt; folder to extend your models and even attach interfaces and validation code. &amp;nbsp;Additional properties will not affect the database queries due to the explicit columns option.&lt;br /&gt;
&lt;br /&gt;
This sample shows using an interfaces, adding custom properties, using validation, and setting default values.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/9ba97304d35f3187f2d80fbbac12bfeb.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;h2&gt;
Examples of querying and updating the POCO’s in Umbraco&lt;/h2&gt;
There are many variations on this code but here are some brief examples. &amp;nbsp;More samples are available &lt;a href=&quot;http://www.toptensoftware.com/petapoco/&quot;&gt;here&lt;/a&gt;. &lt;br /&gt;
&lt;pre&gt;&lt;code&gt;using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using ProjectNamespace.Models.Generated;
using ProjectNamespace.Extensions;

// Fetch a set of rows with sorting, filtering, and pageing.
public IEnumerable&amp;lt;object&amp;gt; GetChildren(string orderBy, Direction orderDirection, string filter, long page, long itemsPerPage)
{
&amp;nbsp; &amp;nbsp; if (orderBy == &quot;fullName&quot;)
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; orderBy = &quot;(firstName + &#39; &#39; + lastName)&quot;;

&amp;nbsp; &amp;nbsp; var sql = new Sql();

&amp;nbsp; &amp;nbsp; if (!String.IsNullOrEmpty(filter))
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; sql.Where(@&quot;&#39;#&#39; + convert(nvarchar, id) = @0
OR CONVERT(nvarchar(30), dateAdded, 101) &amp;nbsp;LIKE @1
OR (firstName + &#39; &#39; + lastName) LIKE @1
OR companyName LIKE @1, filter, &#39;%&#39; + filter + &#39;%&#39;);

&amp;nbsp; &amp;nbsp; if (orderDirection == Direction.Ascending) sql.OrderBy(orderBy); else sql.OrderByDescending(orderBy);

&amp;nbsp; &amp;nbsp; try
&amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; if (page &amp;gt; 0 &amp;amp;&amp;amp; itemsPerPage &amp;gt; 0)
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; return DatabaseContext.Database.Fetch&amp;lt;FormChannelProgramSignupOverview&amp;gt;(page, itemsPerPage, sql);
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; else
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; return DatabaseContext.Database.Fetch&amp;lt;FormChannelProgramSignupOverview&amp;gt;(sql);
&amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; catch (Exception e)
&amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; throw new Exception(ApplicationContext.DatabaseContext.Database.LastSQL, e);
&amp;nbsp; &amp;nbsp; }
}

// Get a single item.
DatabaseContext.Database.SingleOrDefault&amp;lt;FormChannelProgramSignup&amp;gt;(id);

// Only load parts of a model.
var sql = new Sql()
&amp;nbsp; &amp;nbsp; .Select&amp;lt;FormChannelProgramSignup&amp;gt;(s =&amp;gt; s.id, s =&amp;gt; s.firstName, s =&amp;gt; s.lastName);
DatabaseContext.Database.Fetch&amp;lt;FormChannelProgramSignup&amp;gt;(sql);
// Note that instead of doing this it is far similar to create views in the database that select the columns you need. &amp;nbsp;They will generate their own POCO’s and you can query those instead. &amp;nbsp;This also means that only the loaded properties will be serialized to JSON code.

// Complex conditionals, this is much cleaner than trying to do the same thing with a single string.
var hasWhere = false;
var sql = new Sql();
if (!String.IsNullOrEmpty(filter[&quot;search&quot;]))
&amp;nbsp; &amp;nbsp; sql.AndWhere(&quot;&#39;#&#39; + convert(nvarchar, id) = @0 OR companyName like @1&quot;, ref hasWhere, filter[&quot;search&quot;], &#39;%&#39; + filter[&quot;search&quot;] + &#39;%&#39;);
if(!String.IsNullOrEmpty(filter[&quot;dateStart&quot;]))
&amp;nbsp; &amp;nbsp; sql.AndWhere(&quot;dateCreated &amp;gt;= @0&quot;, ref hasWhere, DateTime.Parse(filter[&quot;dateStart&quot;]));
if (!String.IsNullOrEmpty(filter[&quot;dateEnd&quot;]))
&amp;nbsp; &amp;nbsp; sql.AndWhere(&quot;dateCreated &amp;lt;= @0&quot;, ref hasWhere, DateTime.Parse(filter[&quot;dateEnd&quot;]));

// Insert, update, delete, and validation examples. &amp;nbsp;Again there are many more methods available.
var record = isNew ? new FormChannelProgramSignup() : DatabaseContext.Database.SingleOrDefault&amp;lt;FormChannelProgramSignup&amp;gt;(id);
record.lastName = “test”;
DatabaseContext.Database.Validate((IDataModel)record);
DatabaseContext.Database.Save(record);
DatabaseContext.Database.Delete(record);&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
These examples use these some extra helpers.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/c2c8002b7e832aa477eb720ab5bf12f7.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;h2&gt;
Implementing One to One, One to Many, and Many to Many Joins&lt;/h2&gt;
This can get a bit complicated to understand, but when set up correctly it works quite well. &amp;nbsp;In short you can do joins in the SQL code but need to create Relator code and attach those to the fetch code so that the rows are disassembled into associated models in the way you expect. &lt;br /&gt;
&lt;br /&gt;
The following examples are assuming the following Schema.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;ShopMarkets&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;MarketId&lt;/li&gt;
&lt;li&gt;Region&lt;/li&gt;
&lt;li&gt;Market&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;ShopMarketServices&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;MarketId&lt;/li&gt;
&lt;li&gt;ServiceId&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;ShopServices&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;ServiceId&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;ShopZipcodes&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;City&lt;/li&gt;
&lt;li&gt;State&lt;/li&gt;
&lt;li&gt;ZipCode&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
This is a relator that includes all three types of relationships. These should be placed in a &lt;code&gt;Models/Relators&lt;/code&gt; folder in your project.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/1b090028f4aab8ec991a7fc3b8fae66e.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
In addition some partial classes are needed in your &lt;code&gt;Models/Generated&lt;/code&gt; folder to add properties to store the related models.
&lt;script src=&quot;https://gist.github.com/pynej/6e8e2c9391b84ff648faf4ce1d7a5c60.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
To use it you would use the following SQL code. &amp;nbsp;Note that these are all using the same &lt;code&gt;Map&lt;/code&gt; function with different signatures but you could use different function names if you prefer for clarity.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;using Umbraco.Core.Persistence;
using ProjectNamespace.Models.Generated;
using ProjectNamespace.Models.Relators;

// One to Many Query
var sql = new Sql().Select(&quot;*&quot;).From&amp;lt;ShopService&amp;gt;(DatabaseContext.SqlSyntax)
&amp;nbsp; &amp;nbsp; .LeftJoin&amp;lt;ShopMarketService&amp;gt;(DatabaseContext.SqlSyntax)
&amp;nbsp; &amp;nbsp; .On&amp;lt;ShopService, ShopMarketService&amp;gt;(DatabaseContext.SqlSyntax, s =&amp;gt; s.ServiceId, s =&amp;gt; s.ServiceId)
&amp;nbsp; &amp;nbsp; .Where&amp;lt;ShopMarketService&amp;gt;(s =&amp;gt; s.MarketId == Current.MarketId);
DatabaseContext.Database.Fetch&amp;lt;ShopService, ShopMarketService, ShopMarketService&amp;gt;(new ShopMarketRelator().Map, sql);

// One to Many Query
var sql = new Sql().Select(&quot;*&quot;)..From&amp;lt;ShopMarket&amp;gt;(DatabaseContext.SqlSyntax)
&amp;nbsp; &amp;nbsp; .LeftJoin&amp;lt;ShopZipcode&amp;gt;(DatabaseContext.SqlSyntax)
&amp;nbsp; &amp;nbsp; .On(&quot;ShopMarkets.Region = ShopZipcodes.State AND ShopMarkets.Market = ShopZipcodes.City&quot;) // Can use .On&amp;lt;T1, T2&amp;gt;(left =&amp;gt; left.Field, right =&amp;gt; right.field) on similar joins.
&amp;nbsp; &amp;nbsp; .Where&amp;lt;ShopMarket&amp;gt;(s =&amp;gt; s.Region.InvariantEquals(CurrentCode) &amp;amp;&amp;amp; s.Market.InvariantEquals(CurrentMarket));
DatabaseContext.Database.Fetch&amp;lt;ShopMarket, ShopZipcode, ShopMarket&amp;gt;(new ShopMarketRelator().Map, sql).FirstOrDefault(); &amp;nbsp;// Must use Fetch().FirstOrDefault() here as Database.FirstOrDefault doesn’t have the proper signatures.

// Many to Many Query
var sql = new Sql().Select(&quot;*&quot;).From&amp;lt;ShopMarket&amp;gt;(DatabaseContext.SqlSyntax)
&amp;nbsp; &amp;nbsp; .LeftJoin&amp;lt;ShopMarketService&amp;gt;(DatabaseContext.SqlSyntax)
&amp;nbsp; &amp;nbsp; .On&amp;lt;ShopMarket, ShopMarketService&amp;gt;(DatabaseContext.SqlSyntax, s =&amp;gt; s.MarketId, s =&amp;gt; s.MarketId)
&amp;nbsp; &amp;nbsp; .LeftJoin&amp;lt;ShopService&amp;gt;(DatabaseContext.SqlSyntax)
&amp;nbsp; &amp;nbsp; .On&amp;lt;ShopMarketService, ShopService&amp;gt;(DatabaseContext.SqlSyntax, s =&amp;gt; s.ServiceId, s =&amp;gt; s.ServiceId)
DatabaseContext.Database.Fetch&amp;lt;ShopMarket, ShopMarketService, ShopService, ShopMarket&amp;gt;(new ShopMarketRelator().Map, sql).FirstOrDefault();&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Then you can use the added properties to access associated data.  In my tests the &lt;code&gt;Select(“*”)&lt;/code&gt; part was required or invalid SQL was generated but that may be fixed at some point.&lt;br /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4605342286434738235/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/4605342286434738235?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4605342286434738235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4605342286434738235'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2016/11/guide-to-custom-database-access-in.html' title='Guide to Custom Database Access in Umbraco 7 With Built in PetaPoco'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-467344226277045985</id><published>2016-10-31T16:49:00.000-04:00</published><updated>2016-10-31T16:51:03.999-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="Programming"/><category scheme="http://www.blogger.com/atom/ns#" term="Umbraco"/><title type='text'>Guide to using Strong Typed PublishedContnetModels in Umbraco 7</title><content type='html'>The recent Umbraco 7.4 release there is an included Model Builder tool. &amp;nbsp;This may not seem like a big deal but it can be very helpful to developers and I recommend embracing it. &amp;nbsp;In essence what it does it let you generate strong typed models in .Net based off your Document Types and Media Types and their hierarchy. &amp;nbsp;This can be done a few ways including at runtime in memory or with pre-compile DLL’s. &amp;nbsp;Then in Views, Macro’s and Controllers these strong typed models can be used instead of normal code to reduce potential coding errors and typos, and to provide IntelliSense in Visual Studio if you do editing there. &amp;nbsp;These changes for the most part are non-breaking and the old style can still be used.&lt;br /&gt;
&lt;br /&gt;
In short if will replace the first example template with the second.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@Model.Content.GetPropertyValue(“myProperty”)
@(Umbraco.Media(CurrentPage.myImage).umbracoFile)
@Model.Content.GetPropertyValue(“myProperty”, true, “Missing”)
@Umbraco.Field(&quot;myProperty &quot;, recursive: true, altFieldAlias: &quot;defaultProperty&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@inherits Umbraco.Web.Mvc.UmbracoTemplatePage&amp;lt;DocumentType&amp;gt;
@Model.Content.MyProperty
@(((Image)Model.Content).MyImage.UmbracoFile
@Model.Content.GetPropertyValue(s =&amp;gt; s.MyProperty, true, “Missing”)
@Model.Content.GetPropertyValue(s =&amp;gt; s.MyProperty, true, Model.Content.DefaultProperty)&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
This article describes the following approach: &amp;nbsp;Generating C# models in the live Umbraco Instance and then pulling them down into the generated models down into the source Visual Studio Project and pre-compiles them there in the main application DLL. &amp;nbsp;This has some major benefits over other approaches including:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;All code is pre-compiled and no generation or compilation is happening when the website is actually running or starting up.&lt;/li&gt;
&lt;li&gt;Models generation isn’t dependent on IIS Express and you don’t have to do desin work there or start up slow projects to manually create models.&lt;/li&gt;
&lt;li&gt;Models are generated the same place you edit the document types and generated automatically.&lt;/li&gt;
&lt;li&gt;Models are partial class’s that can be extended and can implement custom interfaces as needed.&lt;/li&gt;
&lt;li&gt;There is no need for any special visual studio plugins or deployment rules to make this work. &amp;nbsp;All parts described will work on any Visual Studio Editions.&lt;/li&gt;
&lt;/ul&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
To clarify we will have the following:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;A normal production/development server running IIS/Umbraco site.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Document and media Types would be edited here, basically whatever system you currently do Umbraco BackOffice work in now&lt;/li&gt;
&lt;li&gt;New model sources are automatically generated as changes are made, but the runtime DLL’s won’t be touched.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Developer box/Visual Studio containing the site project/Umbraco CMS from NuGet packages.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Project is configured to do deployments using Web Deploy.&lt;/li&gt;
&lt;li&gt;Models sources are synced down from the deployed instance back to the Visual Studio project.&lt;/li&gt;
&lt;li&gt;Generated Models may be extended with local code and are compiled into the main project DLL.&lt;/li&gt;
&lt;li&gt;View files can be edited here to take advantage of design-time debugging and full IntelliSense or in Umbraco BackOffice as desired.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;h2&gt;
Setup&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;So to start with we have a basic MVC Website Solution in Visual Studio pulling in the Umbraco NuGet project or existing Umbraco project.&lt;/li&gt;
&lt;li&gt;We have configured this site to use Web Deploy and have deployed a working site to a server that is up and running.&lt;/li&gt;
&lt;li&gt;We have created some Document Types and Media Types for our site in Umbraco’s BackOffice.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h3&gt;
Enable Model Generator&lt;/h3&gt;
First we will need to edit the web.config file and add a few settings to the AppSettings section.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;nbsp; &amp;nbsp; &amp;lt;add key=&quot;Umbraco.ModelsBuilder.Enable&quot; value=&quot;true&quot; /&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;add key=&quot;Umbraco.ModelsBuilder.ModelsMode&quot; value=&quot;LiveAppData&quot; /&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;add key=&quot;Umbraco.ModelsBuilder.ModelsDirectory&quot; value=&quot;~/PublishedContentModels/Generated&quot; /&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;add key=&quot;Umbraco.ModelsBuilder.ModelsNamespace&quot; value=&quot;ProjectNamespace.PublishedContentModels&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The first option is enabling the model builder, the second is telling the site to only generated model source code and to do so automatically when document and media types are updated.&lt;/li&gt;
&lt;li&gt;Next we are specifying a directory to save these source files into. We want this to be part of the project directory so we can easily retrieve updates.&lt;/li&gt;
&lt;li&gt;Now update the &lt;code&gt;ProjectNamespace&lt;/code&gt; to use for all the models. &amp;nbsp;You will want to use the same namespace that is specified as the Default namespace on the website project in Visual Studio as &lt;code&gt;ProjectNamespace&lt;/code&gt; portion so that you don’t have to manually change it every time you add or customize a model.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
Next you will want to edit the &lt;code&gt;~/Views/Web.config&lt;/code&gt; file add the same namespace above to the namespaces there.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;add namespace=&quot; ProjectNamespace.PublishedContentModels&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Add the Umbraco Core Property Value Package&lt;/h3&gt;
This &lt;a href=&quot;https://our.umbraco.org/projects/developer-tools/umbraco-core-property-value-converters&quot;&gt;package&lt;/a&gt; is needed to that generated models use &lt;code&gt;IPublishedContent&lt;/code&gt; types for all properties that use internal pickers instead of returning string or integer return types. &amp;nbsp;This enables you to use the first line of code instead of the second. &amp;nbsp;It also makes checking for null objects much easier.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@Model.Content.LinkedPage.Name
@Umbraco.Content((int)Model.Content.LinkedPage).Name.
@if(Model.Content.LinkedPage == null) {}&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Note that if you have an existing site this package will break any templates and macros that are expecting integer or string returns from property pickers. &amp;nbsp;You will need to go through the project and update such places to expect &lt;code&gt;IPublishedContent&lt;/code&gt; or &lt;code&gt;IEnumberable&amp;lt;IPublishedContent&amp;gt;&lt;/code&gt; as applicable. &amp;nbsp;This will not harm performance though as all nodes are cached.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Apply patch for Strong Typed GetPropertyValue methods&lt;/h3&gt;
You will want to compile and replace your &lt;code&gt;Umbraco.ModelsBuilder&lt;/code&gt; with the patched version available &lt;a href=&quot;https://github.com/pynej/Zbu.ModelsBuilder/tree/patch-add-strong-type&quot;&gt;here&lt;/a&gt; to enable some additional functions. &amp;nbsp;The following are not yet supported in the Umbraco release but work with this patch.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@Model.Content.GetPropertyValue(s =&amp;gt; s.BannerImage, true) // Search recursively up the tree.
@Model.Content.GetPropertyValue(s =&amp;gt; s.BannerImage, &quot;someOtherImage.gif&quot;) &amp;nbsp;// Specify a default value.
@Model.Content.GetProeprtyValue(s =&amp;gt; s.BannerImage, Model.Content.AlternateImage)&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h2&gt;
Syncing changes back to Visual Studio&lt;/h2&gt;
There is a bit more initial setup to get his working.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Once these changes are applied and deployed go into the Umbraco site and go to the &lt;b&gt;Developer Dashboard&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;Click on &lt;b&gt;Model Builder&lt;/b&gt; and click &lt;b&gt;Generate Models&lt;/b&gt;.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;You should only need to this once as future changes will automatically regenerate the sources but you can come here to manually do so if you have any issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;In your visual studio website project created a new folder at the root level named &lt;code&gt;PublishedContentModels&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Right Click&lt;/b&gt; on this new folder and select &lt;b&gt;Replace PublishedContentModels From Server&lt;/b&gt; to download all your generated models.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;You may need to toggle the &lt;b&gt;Show All Files&lt;/b&gt; option in the project view and/or refresh the project view to see the new files.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;The files will not be included in your project by default so &lt;b&gt;Right Click&lt;/b&gt; on the &lt;code&gt;Generated&lt;/code&gt; folder and select &lt;b&gt;Include In Project&lt;/b&gt; to include all models.&lt;/li&gt;
&lt;li&gt;Republish the website or test locally to load the new models.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h3&gt;
Syncing future changes to the Models&lt;/h3&gt;
In the future when you make Document Type or Media Type changes including adding or removing types or adding, removing, or changing properties you will need to do the following the synchronize the changes.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Right Click&lt;/b&gt; on the &lt;code&gt;Generated&lt;/code&gt; folder and select &lt;b&gt;Replace Generated From Server&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;Include or Exclude any files for models that may have been added or removed.&lt;/li&gt;
&lt;li&gt;Publish the website or manually copy over the main DLL to load the new models.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h3&gt;
Some Caveats about the Models&lt;/h3&gt;
In many cases no valid item may be specified or the linked item could have been removed. &amp;nbsp;As such you will need to check for NULL values or provide a default value.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@if(Model.Content.Resource != null)
var linkedPage = Model.Content.GetPropertyValue(s =&amp;gt; s.LinkedPage, Model.Content);&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Also you might expect the models to return strong typed Model for content pickers but they do not as the would create recursion issues. &amp;nbsp;Instead all documents and media are returned as IPublishedContent but that can still be cast to Strong Typed models as needed.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;var bannerImage = (Image)Model.Content.BannerImage;
var resourceItems = (IEnumberable&amp;lt;ResourceItem&amp;gt;)Mode.Content.ResourceItems;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Or you can add strong typed accessor’s for those properties.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;public partial class BasePage {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public BasePage TypedUmbracoInternalRedirectId { &amp;nbsp;get { return (BasePage)UmbracoInternalRedirectId; &amp;nbsp;} }
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public IEnumberable&amp;lt;ResourceItem&amp;gt;TypedResourceItems { &amp;nbsp;get { return (IEnumberable&amp;lt;ResourceItem&amp;gt;)ResourceItems; &amp;nbsp;} }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;
&lt;/h2&gt;
&lt;h2&gt;
&lt;br /&gt;&lt;/h2&gt;
&lt;h2&gt;
Using the new Published Content Models.&lt;/h2&gt;
There are lots of things that you can with the new models. &amp;nbsp;The first thing to deal with is changing your Templates and Partial Views to use the new PublishedContentModels.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&lt;b&gt;./uBlogsyPost.cshtml&lt;/b&gt;
@inherits UmbracoTemplatePage
@inherits UmbracoTemplatePage&amp;lt;UBlogsyPost&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&lt;b&gt;./ShopSite.chstml&lt;/b&gt;
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage&amp;lt;ShopSite&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Using Properties&lt;/h3&gt;
Properties can be replaced with calls like these:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@Model.Content.MyProperty
@(((Image)Model.Content).MyImage.UmbracoFile
@Model.Content.GetPropertyValue(s =&amp;gt; s.MyProperty, true, “Missing”)
@Model.Content.GetPropertyValue(s =&amp;gt; s.MyProperty, true, Model.Content.DefaultProperty)
var locations = Model.Contnet.Children&amp;lt;Location&amp;gt;();
var landingPage = Model.Content.AncestorOrSelf&amp;lt;LandingPage&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Using Inheritance&lt;/h3&gt;
The models will have the same inheritance as the site, so if you have a document structure like this:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;BasePage&lt;/li&gt;
&lt;li&gt;BasePage/TextPage&lt;/li&gt;
&lt;li&gt;BasePage/ImagePage&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
You could have the following templates:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;./BasePage.chstml [Master Template]
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage&amp;lt;BasePage&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;./TextPage.chstml
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage&amp;lt;TextPage&amp;gt;
&amp;nbsp; &amp;nbsp; Layout = &quot; BasePage.cshtml&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
In this way the master template has typed access to all its properties and the children have their applicable properties as well.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Using Compositions or Interfaces&lt;/h3&gt;
You can also create compositions or create your own Interfaces across multiple models that may share properties but don’t inherit from each other. &amp;nbsp;Take this example for instance:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;MainSite&lt;/li&gt;
&lt;li&gt;MainSite/BodyPage&lt;/li&gt;
&lt;li&gt;MainSite/CategoryPage&lt;/li&gt;
&lt;li&gt;Blog&lt;/li&gt;
&lt;li&gt;Blog/Landing&lt;/li&gt;
&lt;li&gt;Blog/Post&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
In this case the Main Site and blog may have similar properties but they don’t inherit from each other. &amp;nbsp;In this case you could create and interface and then add these two partials class&#39;s extend the existing models with consistent accessors.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/56bed18beb12b09c6c57498f8258a1f7.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
Then you can have a template use the Interface instead.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&lt;b&gt;./BasePage.chstml&lt;/b&gt;
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage&amp;lt;IBasePage&amp;gt;
&amp;nbsp; &amp;nbsp; Layout = &quot;BasePage.cshtml&quot;;
@Model.Content.PageTitle&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Note the &lt;code&gt;ImplementPropertyType&lt;/code&gt; decorations are necessary for &lt;code&gt;Model.Content.GetPropertyValue(s =&amp;gt; s.type)&lt;/code&gt; calls to work on the interface.&lt;br /&gt;
&lt;br /&gt;
You can accomplish the same thing by creating Compositions in Umbraco and the Composition will be auto-generated as interfaces though I haven’t tested this part out myself.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Additional custom properties and code&lt;/h3&gt;
Additional code can be added for any of these models in partial class’s in the &lt;code&gt;PublishedContentModels&lt;/code&gt; folder to add additional logic and move complex programming out of the template files and into the models.&lt;br /&gt;
&lt;br /&gt;
Here is an example of an &lt;code&gt;Image&lt;/code&gt; extended model that supports scaling images.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/01cf75679b9cf6dd66e67019c919e45d.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
This can be called like so:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@{
  var image = Model.Content.BannerImage.Scale(500, 250);
}
&amp;lt;img src=”@image.UmbracoFile” width=”@iamge.UmbracoWidth” height=”@iamge.UmbracoHeight”&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Conditionals Based off Current Document Type&lt;/h3&gt;
There may be lots of places in Master Templates and Partial Views where you are running conditional code based on the current document type of the page. &amp;nbsp;These should be replaced with strong typed check’s instead. &amp;nbsp;The first line is checking if a node is a specific type directly whereas the second line would return true if the node is that type of inherits from it in the hierarchy.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@if(Model.Content.DocumentTypeAlias == BodyPage.ModelTypeAlias)
@if(Model.Contnet is BodyPage)&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Or a similar check to see if an ancestor of a given type exists:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@if(Model.Content.AncestorOrSelf&amp;lt;uBlogsyLanding&amp;gt;() != null) {}&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
You can also cast a model to a more specific type. &amp;nbsp;For example:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@inherits UmbracoTemplatePage&amp;lt;BasePage&amp;gt;
@if(Model.Content is BodyPage) {
  var bodyPage = (BodyPage)Model.Content;
  @bodyPage.Contents
}&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Strong Type Tree Traversal&lt;/h3&gt;
There are many new variations on the common node traversal functions that let you specify a model type. &amp;nbsp;You may need to add &lt;code&gt;using Umbraco.Web;&lt;/code&gt; in your code to access them. A few examples:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;Model.Content.Parent&amp;lt;CategoryPage&amp;gt;();
Model.Content.Descendants&amp;lt;BodyPage&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Strong Typed Recursive and Default Text&lt;/h3&gt;
The following will only work for now if you use the custom patch listed abode and then regenerated your models.&lt;br /&gt;
&lt;br /&gt;
Load a property reclusively and providing a default value. &amp;nbsp;These will work exactly like the Umbraco equivalents.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@Model.Content.GetPropertyValue(s =&amp;gt; s.BannerImage, true)
@Model.Content.GetProeprtyValue(s =&amp;gt; s.BannerImage, Model.Content.AlternateImage)&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Issues in Macro Partial Views&lt;/h3&gt;
You may discover that you can’t specify types for macro’s in the same way as you might expect. &amp;nbsp;This is a limitation due to limitations with the &lt;code&gt;PartialViewMacroPage&lt;/code&gt; class and its cacheing. You may not &amp;nbsp;have the problem but for me the following example won’t work in a Macro Partial View currently.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@inherits Umbraco.Web.Macros.PartialViewMacroPage&amp;lt;BodyPage&amp;gt;
@Model.Content.PageTitle&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
There is a bit of a workaround that doesn’t add any processing overhead as the items are still cached properly, but it does make the code in Macro view files slightly different.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;@inherits Umbraco.Web.Macros.PartialViewMacroPage
@{
&amp;nbsp; var Content = Umbraco.TypedContent(Model.Content.Id);
&amp;nbsp; @Content.PageTitle
}&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/467344226277045985/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/467344226277045985?isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/467344226277045985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/467344226277045985'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2016/10/guide-to-using-strong-typed.html' title='Guide to using Strong Typed PublishedContnetModels in Umbraco 7'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4103788370696080699</id><published>2016-10-26T15:25:00.002-04:00</published><updated>2016-10-26T15:50:34.875-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache"/><category scheme="http://www.blogger.com/atom/ns#" term="Scripting"/><title type='text'>Removing Double Redirects or Redirect Chains form .htaccess files.</title><content type='html'>If you manage and large site using &lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_rewrite.html&quot;&gt;mod_rewrite&lt;/a&gt; or &lt;a href=&quot;http://www.helicontech.com/isapi_rewrite/&quot;&gt;ISAPI_Rewrite 3&lt;/a&gt; you will eventually run into issues maintaining the rewrite rules.  After doing a few redesigns or moving things around repeatedly you can end up with situations where one page redirects to another page then another and so on in a chain.  This is functional but inefficient, it can have a negative effect on user experience and on SEO value, and makes everything harder to maintain.  This tool is a simple script you can pass you &lt;code&gt;.htaccess&lt;/code&gt; file through to remove all these multi-step redirects and all pages redirect immediately on the first redirect to the final destination page.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
This is a perl program that you will need to save along with a copy of your &lt;code&gt;.htaccess&lt;/code&gt; file someplace with &lt;a href=&quot;https://www.perl.org/&quot;&gt;Perl&lt;/a&gt; installed.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/943b6002475ebc233eeeb5227c404b11.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
Once saved you can run it by opening a terminal and running the following command:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;perl simplify-rewrites filename&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
The Program will show a summary and a new &lt;code&gt;filename.new&lt;/code&gt; file will be generated with the de-duping applied.  You can use the following to get a list of all the changes for review.
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;diff -Na --unified=0 rw.txt rw.txt.new | less&lt;/code&gt;&lt;/pre&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4103788370696080699/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/4103788370696080699?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4103788370696080699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4103788370696080699'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2016/10/removing-double-redirects-or-redirect.html' title='Removing Double Redirects or Redirect Chains form .htaccess files.'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3241624271843758277</id><published>2016-10-25T20:31:00.000-04:00</published><updated>2016-10-26T14:30:02.440-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="macOS"/><category scheme="http://www.blogger.com/atom/ns#" term="OSX"/><category scheme="http://www.blogger.com/atom/ns#" term="Steam"/><title type='text'>Steam won&#39;t connect on mass Sierra 10.12</title><content type='html'>I was running into this odd issue after upgrading to OS X 10.12 and nothing I would do seamed to resolve it. &amp;nbsp;Tired tracking down error messages, reinstalling steam, changeling to beta and back, and other fixes. &amp;nbsp;Ended up doing the following fix which straightened everything out.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;h3&gt;
Installed the OS X Command Line Tools.&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Open&amp;nbsp;&lt;span style=&quot;background-color: white; color: #303030; font-family: &amp;quot;helvetica neue&amp;quot; , &amp;quot;helvetica&amp;quot; , &amp;quot;verdana&amp;quot; , &amp;quot;helvetica&amp;quot; , &amp;quot;tahoma&amp;quot; , &amp;quot;arial&amp;quot; , sans-serif; font-size: 16px;&quot;&gt;Terminal.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Type in &lt;code&gt;xcode-select --install&lt;/code&gt; and press enter.&lt;/li&gt;
&lt;li&gt;Click install in the popup dialog.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;br /&gt;
A very quick and simple fix, though I&#39;m not entirely clear why it was necessary. &amp;nbsp;I did have the command like tools set up before I upgraded so maybe something broke. &amp;nbsp;I also had a similar issues with Parallels for Mac after doing the upgrade.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3241624271843758277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/3241624271843758277?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3241624271843758277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3241624271843758277'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2016/10/steam-wont-connect-on-mass-sierra-1012.html' title='Steam won&#39;t connect on mass Sierra 10.12'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3838309171652069435</id><published>2015-12-28T11:32:00.001-05:00</published><updated>2016-10-26T12:59:12.678-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="OCR"/><category scheme="http://www.blogger.com/atom/ns#" term="OSX"/><category scheme="http://www.blogger.com/atom/ns#" term="Scripting"/><title type='text'>Quick and Dirty OCR on OS X for Free</title><content type='html'>Wait, Stop, Don&#39;t spend that $80+ on some fancy OCR software just yet. &amp;nbsp;There are some free open source tools out there and with a bit of work you can have very functional workflow using them and OS X. This guide will help you set up an applet you can drop images onto to OCR them to plain text. &amp;nbsp;This will not create PDF&#39;s, formatted, or indent documents, but rather just one large text block that is structured the same as the source text with line breaks. &amp;nbsp;The following tools are used, but instructions are provided below so you don&#39;t need to download anything from the sites.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://brew.sh/&quot;&gt;Homebrew&lt;/a&gt; The missing package manager for OS X&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tesseract-ocr/tesseract&quot;&gt;Tesseract&lt;/a&gt; Open Source OCR Engine&lt;/li&gt;
&lt;li&gt;Automator&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h3&gt;
Install Tools&lt;/h3&gt;
First off lets install Homebrew to simplify managing custom packages. &amp;nbsp;This will keep everything in a special /usr/local/ directory so it won&#39;t interfere with OS X&#39;s normal system. &amp;nbsp;To do this open up the Terminal application, copy and paste the following command, and press Return to execute it. &amp;nbsp;You will be prompted to authorize the installation and may be asked to install some OS X command line tools directly from Apple.

&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;ruby -e &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Next we need to install Tesseract and some supporting libraries. &amp;nbsp;Again int he Terminal window enter each of these lines and press Return to execute them separately. &amp;nbsp;They may take a few minutes to compleate before the command prompt returns.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;brew install imagemagick
brew install tesseract --all-languages
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
Configure Applet&lt;/h3&gt;
Next we need to create a user friendly way to do the OCR. &amp;nbsp;We can easily do this with Automator in OS X. &amp;nbsp;Open Automator and create a new Application. &amp;nbsp;On the left search for and add Run Shell Script and Display Notification in order. &amp;nbsp;In the Run Shell Script change Pass input to as arguments add the following code to that step.

&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;PATH=&quot;$PATH:/usr/local/bin&quot;
for var in &quot;$@&quot;
do
convert &quot;$var&quot; -resize 400% -type Grayscale - | tesseract -l eng - - | pbcopy 
done&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
As an alternate the following code will convert the entire result to a single line of text if that is preferred, but it may cause issues if there are columns of text on the image.

&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;PATH=&quot;$PATH:/usr/local/bin&quot;
for var in &quot;$@&quot;
do
text=`convert &quot;$var&quot; -resize 400% -type Grayscale - | tesseract -l eng - -`
ocr=&quot;$orc$text&quot;
done
echo $ocr | pbcopy&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Next add some text to the notification step so you know when the task is done processing. &amp;nbsp;I added a title of &quot;ORC Finished&quot; and a message if &quot;Text was copied to your clipboard.&quot;. &amp;nbsp;Then just save the application and give it a name.&lt;br /&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
Using the Applet&lt;/h3&gt;
To use the applet first fine it and d rag it down to your Dock to make a shortcut. &amp;nbsp;Then you can drag image files onto the application in the Dock and it will do it&#39;s magic. &amp;nbsp;Once you drop an image onto the Applet it will take a few seconds to process and you should see the notification pop up when it is done in the corner. &amp;nbsp;At this point you can past the resulting text into a program of your choice, clean it up, and so what you want with it.&lt;br /&gt;
&lt;br /&gt;
Other things that can be done with a bit of tweaking to the above scripts:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Processing multiple input files at once.&lt;/li&gt;
&lt;li&gt;Saving results to a text file on the desktop or source folder instead of the clipboard.&lt;/li&gt;
&lt;li&gt;Opening the resulting file automatically.&lt;/li&gt;
&lt;li&gt;Remove the notification step if desired.&lt;/li&gt;
&lt;li&gt;Create a Folder Action instead to automatically run on files added to a specified folder.&lt;/li&gt;
&lt;li&gt;Advanced tesseract options can be passed in the script but in my experience these were not needed.&lt;/li&gt;
&lt;/ul&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3838309171652069435/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/3838309171652069435?isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3838309171652069435'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3838309171652069435'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/12/quick-and-dirty-ocr-on-os-x-for-free.html' title='Quick and Dirty OCR on OS X for Free'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-7437235452486137344</id><published>2015-12-04T15:36:00.001-05:00</published><updated>2016-10-26T14:36:08.832-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="HTML"/><category scheme="http://www.blogger.com/atom/ns#" term="Javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="Slack"/><title type='text'>Bookmarklet to Post to Slack from your Bookmarks Bar</title><content type='html'>I use Slack, and one think we love to do is share new articles and discuss them. &amp;nbsp;But the good old copy and past the link was annoying me, so I created a Bookmarklet that will share your current page to a selected channel.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;br /&gt;
Just go to the following page and follow the instructions to build your own widget.&amp;nbsp;&lt;a href=&quot;http://68.40.119.10/slack.html&quot;&gt;http://68.40.119.10/slack.html&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
For peace of mind a brief overview of how this works: &amp;nbsp;This bookmarklet is purely javascript and no data is posted to any other servers besides Slack&#39;s Webhook to receive the post. &amp;nbsp;The tool linked above is purely a javascript tool to build and customize the widget. &amp;nbsp;It will ask for your Webhook URL but it does not post or track anything and simply embedded that URL in the widget it present to you. &amp;nbsp;The fact that it &quot;remembers&quot; your settings is simply done by updating the URL with a search hash which also isn&#39;t sent to the server.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/7437235452486137344/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/7437235452486137344?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7437235452486137344'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7437235452486137344'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/12/bookmarklet-to-post-to-slack-from-your.html' title='Bookmarklet to Post to Slack from your Bookmarks Bar'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-7311574109455777512</id><published>2015-10-19T20:46:00.000-04:00</published><updated>2016-10-26T14:36:56.859-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Computer Games"/><category scheme="http://www.blogger.com/atom/ns#" term="Fixes"/><title type='text'>Fix for Pillars of Eternity Blank Screen on OS X </title><content type='html'>Here is a quick fix for the blank/non-responsive screen in when going full screen in&amp;nbsp;&lt;a href=&quot;http://store.steampowered.com/app/291650/&quot;&gt;Pillars of Eternity&lt;/a&gt; on OS X (El Capitan).  Note that you will have to do this before you run the game each time as it will reset the settings with bad ones each launch.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;First open up the game and take it out of Full Screen mode if it in that mode.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Press &lt;code&gt;Command+F&lt;/code&gt; to take it out of Full Screen.&lt;/li&gt;
&lt;li&gt;Exit the game or press &lt;code&gt;Command+Q&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Press &lt;code&gt;Command+Space&lt;/code&gt; to start Spotlight Search.&lt;/li&gt;
&lt;li&gt;Start typing &lt;code&gt;Terminal&lt;/code&gt; and launch the Terminal application.&lt;/li&gt;
&lt;li&gt;Copy the following text as a single line into your clipboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;defaults write &quot;unity.Obsidian Entertainment.Pillars of Eternity&quot;  &quot;Screenmanager Is Fullscreen mode&quot; 0 &amp;amp;&amp;amp; defaults write &quot;unity.Obsidian Entertainment.Pillars of Eternity&quot;  &quot;Screenmanager Resolution Width&quot; 2560 &amp;amp;&amp;amp; defaults write &quot;unity.Obsidian Entertainment.Pillars of Eternity&quot;  &quot;Screenmanager Resolution Height&quot; 1440
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Click over to the Terminal window and Past the copied text.&lt;/li&gt;
&lt;li&gt;Press Enter to runt he command.&lt;/li&gt;
&lt;li&gt;Launch the game as normal.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Somewhat inconvenient granted; you could make a script to do it but I don&#39;t want to post any executable code online. &amp;nbsp;Also this above code will not require a password or generate and output when executed even though it works.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Also, you may need to change the width(2560) and height(1440) if your native resolution of your monitor is different.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/7311574109455777512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/7311574109455777512?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7311574109455777512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7311574109455777512'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/10/fix-for-pullers-of-eternity-blank.html' title='Fix for Pillars of Eternity Blank Screen on OS X '/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2465466358946843633</id><published>2015-10-08T15:51:00.000-04:00</published><updated>2016-10-26T14:38:01.388-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Chartist"/><category scheme="http://www.blogger.com/atom/ns#" term="Javascript"/><title type='text'>Making Time Based Graphs with Chartist</title><content type='html'>I have been implementing some custom charts with &lt;a href=&quot;https://gionkunz.github.io/chartist-js/index.html&quot;&gt;Chartist&lt;/a&gt; recently. &amp;nbsp;Overall things have gone very well with some simple tweaks and fiddling to get things exactly how I want. &amp;nbsp;It is very nice to be be able to easily adjust the charts with CSS right in the normal site code. &lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
The only problem I had was using Date based charts that don&#39;t have fixed intervals. &amp;nbsp;If the Dates or Times are at a fixed interval you can just write the values directly to the &lt;code&gt;labels&lt;/code&gt; variable and format them how you want very easily. &amp;nbsp;If you have a random or variable set of dates, say when inventory is logged which is done at a random, there is no easy way to graph this and keep the relative positions correct on the graph. &amp;nbsp;I don&#39;t want all the points one unit apart, as one could be two weeks and the next 2 months, but rather properly positioned based on the date provided.&lt;br /&gt;
&lt;br /&gt;
Anyway, I got it to work and where is a brief example/sample code to try out.
&lt;script src=&quot;https://gist.github.com/pynej/c3cb4ff205407419f95a.js&quot;&gt;&lt;/script&gt;
&lt;br&gt;
As you can see in the sample here the items are positioned properly.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDSQo8_ObYmqhCtWEjn4uwqUeRYy2AnHlnQGkUT-oR3N4JIVVfeScfA4W57xuQBVkBwwrlaDzO1v7-yXxekDCd1Q3eO0d9MsyUMsOz5KnZhwIt9Jd7Dv35K7ioN6Z11ZYgyKDdFsiBBfY/s1600/Screen+Shot+2015-10-08+at+3.47.59+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDSQo8_ObYmqhCtWEjn4uwqUeRYy2AnHlnQGkUT-oR3N4JIVVfeScfA4W57xuQBVkBwwrlaDzO1v7-yXxekDCd1Q3eO0d9MsyUMsOz5KnZhwIt9Jd7Dv35K7ioN6Z11ZYgyKDdFsiBBfY/s1600/Screen+Shot+2015-10-08+at+3.47.59+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2465466358946843633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/2465466358946843633?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2465466358946843633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2465466358946843633'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/10/making-time-based-graphs-with-chartist.html' title='Making Time Based Graphs with Chartist'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDSQo8_ObYmqhCtWEjn4uwqUeRYy2AnHlnQGkUT-oR3N4JIVVfeScfA4W57xuQBVkBwwrlaDzO1v7-yXxekDCd1Q3eO0d9MsyUMsOz5KnZhwIt9Jd7Dv35K7ioN6Z11ZYgyKDdFsiBBfY/s72-c/Screen+Shot+2015-10-08+at+3.47.59+PM.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-2055507397737290761</id><published>2015-10-02T14:28:00.002-04:00</published><updated>2015-10-02T14:29:19.562-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java"/><category scheme="http://www.blogger.com/atom/ns#" term="OSX"/><category scheme="http://www.blogger.com/atom/ns#" term="RescueTime"/><title type='text'>Problems with Java Based OS X Applications Hanging when Entering Text</title><content type='html'>So I found another problem with my new upgrade to OS X 10.11. &amp;nbsp;That is that the Java web application I was using, Adobe Ad Hoc Analysis in this case, would hard lock every time i tried to enter text in a text box. &amp;nbsp;The only solution was to force quit the application. &amp;nbsp;After much testing I found the culprit was the &lt;a href=&quot;https://www.rescuetime.com/&quot;&gt;RescueTime&lt;/a&gt; helper applet. &amp;nbsp;Basically this applet gets Accessibility access so that it can inspect windows and record time used but is glitching on the java windows and causing the lock up.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
There could be other programs causing the same issues even if you don&#39;t use RescueTime but are having the same problem. &amp;nbsp;To troubleshoot do the following.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;Close out of the java program that is locking up.&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;System Preference &amp;gt; Security &amp;amp; Privacy &amp;gt; Privacy &amp;gt; Accessibility&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Note with items are check and then uncheck them all.&lt;/li&gt;
&lt;li&gt;Restart your java program and attempt to reproduce the issue.&lt;/li&gt;
&lt;li&gt;If it is working correctly then quit, re-enable one item, and try again until you find the culprit.&lt;/li&gt;
&lt;li&gt;If you can&#39;t get around the error even with all Accessibility options off that you must have some other issue.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Anyway if you find any items fix the problem you can just leave them off though they might hinder there applications functionality.&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/2055507397737290761/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/2055507397737290761?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2055507397737290761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/2055507397737290761'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/10/problems-with-java-based-os-x.html' title='Problems with Java Based OS X Applications Hanging when Entering Text'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-7386797386614301712</id><published>2015-10-02T09:29:00.000-04:00</published><updated>2015-10-02T09:30:07.421-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="OSX"/><category scheme="http://www.blogger.com/atom/ns#" term="VirtualBox"/><title type='text'>OSX 10.11 Stuck On Grey Screen After Login</title><content type='html'>After upgrading to El Capitan yesterday I ran into an issue where my computer would boot up going through the loading bar and display the login screen if &amp;nbsp;enabled. &amp;nbsp;Upon logging in, or automatic login, the system would hang at a grey screen. &amp;nbsp;After some testing with the network I figured out that it was not actually locking up and would respond to traffic.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
To determine if your issues is due to the same problem wither SSH into the computer once booted if remote login was enabled or boot to &lt;a href=&quot;https://support.apple.com/en-us/HT201314&quot;&gt;Recovery Mode&lt;/a&gt; &amp;nbsp;and launch the Terminal. &amp;nbsp;Then run the following to see recently logged errors.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;tail /var/log/system.log | grep &#39;Segmentation fault&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Or if using the Recovery Mode terminal the following thought the drive name may be different.
&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;tail /Volumes/Macintosh\ HD/var/log/system.log | grep &#39;Segmentation fault&#39;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
If either of these commands produce any results that you have found your problem. &amp;nbsp;The culprit is Unsigned Kernel Extensions or .kext modules. &amp;nbsp;In the good old days developers could just install these as needed to add code level kernel functionality but no for improved security OS X requires that all Kernel Extensions be signed by the developer. &amp;nbsp;This is overall a good thing because these are privileged and could easily lead to malicious behavior by untrusted sources. &lt;br /&gt;
&lt;br /&gt;
Anyway the problem where is that the upgrade didn&#39;t disable the unsigned Kernel Extensions it found and instead just doest&#39;t load them. &amp;nbsp;This is fine and normally it would just mean that whatever applications depend on them would jus not work until updated but for whatever reason there are a few special cases that cause the login process to hang. &amp;nbsp;I ended up removing all the &lt;a href=&quot;https://www.virtualbox.org/wiki/Downloads&quot;&gt;Virtual Box&lt;/a&gt;&amp;nbsp;and &lt;a href=&quot;https://rogueamoeba.com/freebies/soundflower/&quot;&gt;Soundflower&lt;/a&gt; modules and then was able to boot without issue. &amp;nbsp;I have not tried to Re-install or upgrade wither yet but atlas my computer boots.&lt;br /&gt;
&lt;br /&gt;
To remove these again go to the Terminal and do the following. &amp;nbsp;You will nee to authorize them with your administrative password.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;sudo rm -Rf /System/Library/Extensions/Waco*
sudo rm -Rf /Library/Extensions/VBox*
sudo rm -Rf /Library/Application\ Support/VirtualBox/*
sudo rm -Rf /System/Library/Extensions/Soundflower*
sudo rm -Rf /var/folders/*
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Again note the additional prefix if using the Recovery Mode ant that thedick name may be different.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;sudo rm -Rf /Volumes/Macintosh\ HD/System/Library/Extensions/Waco*
sudo rm -Rf /Volumes/Macintosh\ HD/Library/Extensions/VBox*
sudo rm -Rf /Volumes/Macintosh\ HD/Library/Application\ Support/VirtualBox/*
sudo rm -Rf /Volumes/Macintosh\ HD/System/Library/Extensions/Soundflower*
sudo rm -Rf /Volumes/Macintosh\ HD/var/folders/*&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Finally just reboot and enjoy.</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/7386797386614301712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/7386797386614301712?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7386797386614301712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/7386797386614301712'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/10/osx-1011-stuck-on-grey-screen-after.html' title='OSX 10.11 Stuck On Grey Screen After Login'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-269853987454180909</id><published>2015-09-29T13:25:00.002-04:00</published><updated>2015-10-01T10:23:00.479-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Raspberry Pi"/><title type='text'>Raspberry Pi B Crashing when Running on SD Card</title><content type='html'>I have been having surtax crashes with my Raspberry Pi B for a few months now and it is a very irritating intermittent issue. &amp;nbsp;The unit basically locks up and even on resetting the power it won&#39;t always turn back on, but rather just sit there with no lights and no display and I end of having to cycle the power 2-5 times to get it to start again.&lt;br /&gt;
&lt;br /&gt;
Anyway, I finally figured out there it appears to be an issues with the SD card. &amp;nbsp;At first I thought that it was just an off brand card and the Raspberry Pi just didn&#39;t like it. &amp;nbsp;But after some testing and hands on trials I figured out that it appeared to be an actual problem with the electrical connection and even though the card seamed seated properly to poor connection would cause the card to be or become unreadable. &amp;nbsp;This was easy to verify by lightly pressing on the SD card and then turning on the Raspberry PI. &amp;nbsp;It turned right on and worked perfectly and as soon as I let go it died again. &amp;nbsp;I can&#39;t see any physical defects and the card looks to be seated and pressing firmly into the contacts but I guess it&#39;s not sufficient.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcN0VflndLLo1iJNdtpWzpsd9nTiVilCgGEmWVt7agxbsRFmXAjBVoPmMu3l0uwvnH0mzxtCBZy-oK6HNQafg828lP52-gVIEO8YZVVGVuDRxfOa-zQyzabAeIuQexH33vXiFYXvXhHAU/s1600/IMG_0069.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;135&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcN0VflndLLo1iJNdtpWzpsd9nTiVilCgGEmWVt7agxbsRFmXAjBVoPmMu3l0uwvnH0mzxtCBZy-oK6HNQafg828lP52-gVIEO8YZVVGVuDRxfOa-zQyzabAeIuQexH33vXiFYXvXhHAU/s320/IMG_0069.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
I found a simple solution, a simple light pressure chip clip/close pin on the SD-card solved the problem perfectly. &amp;nbsp;I added a bit of cloth above the SD card and ensured that the clasp was pressing just above the SD Card&#39;s contact points and placed it over the end of the Raspberry PI and it works like a charm.</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/269853987454180909/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/269853987454180909?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/269853987454180909'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/269853987454180909'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/09/raspberry-pi-b-crashing-when-running-on.html' title='Raspberry Pi B Crashing when Running on SD Card'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcN0VflndLLo1iJNdtpWzpsd9nTiVilCgGEmWVt7agxbsRFmXAjBVoPmMu3l0uwvnH0mzxtCBZy-oK6HNQafg828lP52-gVIEO8YZVVGVuDRxfOa-zQyzabAeIuQexH33vXiFYXvXhHAU/s72-c/IMG_0069.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-688204155102699561</id><published>2015-09-16T14:21:00.002-04:00</published><updated>2015-09-16T14:21:32.723-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Xbian"/><title type='text'>XBian RC2 ASS Subtitles not Displaying in MKV Videos</title><content type='html'>Minor issues I found when updating to the current release of XBian on my Raspberry Pi B. &amp;nbsp;Namely that subtitles would not work from MKV videos. &amp;nbsp;Heres a simple fix for the issue. &amp;nbsp;SSH into the xbian and run the following:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install libass5&lt;/code&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/688204155102699561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/688204155102699561?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/688204155102699561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/688204155102699561'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/09/xbian-rc2-ass-subtitles-not-displaying.html' title='XBian RC2 ASS Subtitles not Displaying in MKV Videos'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-4895845194462492108</id><published>2015-07-31T11:15:00.003-04:00</published><updated>2015-07-31T11:35:54.151-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="Umbraco"/><title type='text'>Drag and Drop in Umbraco 7</title><content type='html'>This code will let you add drag and drop logic to Umbraco v7 using the new AngularJS interface. &amp;nbsp;It doesn&#39;t require and backend code updates but can&#39;t be created as a package because there is no way to update the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;umbTree&lt;/span&gt; directive as needed. &amp;nbsp;There are only two files that need to be changed on the client. &amp;nbsp;Note that updates to umbraco will overwrite these changes. &amp;nbsp;Also note that due to lack of support in the underlying jQuery-sortable plugin for touch events this will not work on tablet devices.&lt;br /&gt;
&lt;br /&gt;
First a set of changes for the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;umbTree&lt;/span&gt; and &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;umbTreeItem&lt;/span&gt; directives in &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;umbraco/Js/&lt;span style=&quot;widows: 1;&quot;&gt;umbraco.directives.js&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/07f322978be5ebf2ef6c.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&amp;nbsp;This patch was generated on 7.2.8 so may not work on other versions automatically. &amp;nbsp;You can manually apply all the listed changes on any 7.* version and they should work as expected with no changes needed.&lt;br /&gt;
&lt;br /&gt;
Secondly, to allow for dragging items to collapsed nodes or nodes they don&#39;t already have children, the following needs to be added to the end of the line in &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;umbraco/assets/css/umbraco.css&lt;/span&gt;.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/pynej/a31f0ec901d916fe42a0.js&quot;&gt;&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/4895845194462492108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/4895845194462492108?isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4895845194462492108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/4895845194462492108'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/07/drag-and-drop-in-umbraco-7.html' title='Drag and Drop in Umbraco 7'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3342287939500063715</id><published>2015-05-29T10:41:00.003-04:00</published><updated>2015-05-29T10:41:36.623-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="Extensions"/><title type='text'>For Each to loop over HttpFileCollection Files</title><content type='html'>Recently I was working with file uploads in .NET and found that theres no clean way to loop over each uploaded file. &amp;nbsp;Here is a extension method for easily looping through the list and it allows filtering by field name and applying Regex to the file name.&lt;br /&gt;
&lt;h2&gt;
Samples:&lt;/h2&gt;
&lt;script src=&quot;https://gist.github.com/pynej/e1fd4b4c2a2253b92a37.js&quot;&gt;&lt;/script&gt;

&lt;h2&gt;
Source Code&lt;/h2&gt;
&lt;script src=&quot;https://gist.github.com/pynej/b8a6e62f5f87cfe9bdbb.js&quot;&gt;&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3342287939500063715/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/3342287939500063715?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3342287939500063715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3342287939500063715'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/05/for-each-to-loop-over.html' title='For Each to loop over HttpFileCollection Files'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8526508655697471701.post-3545365105006885632</id><published>2015-05-22T11:02:00.000-04:00</published><updated>2015-05-29T10:24:09.775-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Blacklist"/><category scheme="http://www.blogger.com/atom/ns#" term="IIS"/><title type='text'>Convert .htaccess deny rules to web.config</title><content type='html'>Recently I was looking for a easy way to convert a large list of spammer and hacker IP Addresses from Apache .htaccess allow/deny rules into a format I could use on our IIS Server. &amp;nbsp;I found that there is really no good way to do this but after some work and a bit of coding here is a solution.&lt;br /&gt;
&lt;br /&gt;
This solution will allow you to take a large list of access rules and apply them to an IIS website. &amp;nbsp;Additionally this is done in a file so there is no need to manually add records.&lt;br /&gt;
&lt;h2&gt;
Collect your List of Rules&lt;/h2&gt;
&lt;div&gt;
I used the following as a general blacklist of malicious and spammer client&#39;s to block. &amp;nbsp;There are other sources or you may have your own list. &amp;nbsp;Compile all the lists into one file and don&#39;t worry about duplicates, the script later on will resolve those.&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.wizcrafts.net/nigerian-blocklist.html&quot; style=&quot;widows: 1;&quot; target=&quot;_blank&quot;&gt;Nigerian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.wizcrafts.net/russian-blocklist.html&quot; style=&quot;widows: 1;&quot; target=&quot;_blank&quot;&gt;Russian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.wizcrafts.net/lacnic-blocklist.html&quot; style=&quot;widows: 1;&quot; target=&quot;_blank&quot;&gt;Lacnic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.wizcrafts.net/chinese-blocklist.html&quot; style=&quot;widows: 1;&quot; target=&quot;_blank&quot;&gt;Chinese&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h2&gt;
Convert the Rules to Web.config Equivalents&lt;/h2&gt;
&lt;div&gt;
I have created a simple javascript tool to convert the above lists into web.config rules. &amp;nbsp;To do this go &lt;a href=&quot;https://jsfiddle.net/m1p6hfon/7/embedded/result/&quot;&gt;here&lt;/a&gt;&amp;nbsp;and enter all your rules into the first text box. &amp;nbsp;Make sure you supply all rules at once so that they can be de-duped. &amp;nbsp;If the list does end up having duplicates then IIS will not start up properly.&lt;/div&gt;
&lt;h2&gt;
Ensure you have the Features Installed&lt;/h2&gt;
&lt;div&gt;
Next log onto your server and make sure that you have the &lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;Web Server &amp;gt; Security &amp;nbsp;&amp;gt; IP and Domain Restrictions&lt;/span&gt; role installed. &amp;nbsp;You may need to add it as it&#39;s not selected by default.&lt;/div&gt;
&lt;h2&gt;
Apply your new Rules&lt;/h2&gt;
&lt;div&gt;
Finally open up the web.config files for each of your main sites and add the generated text to the&amp;nbsp;&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;system.webServer/security/ipSecurity&lt;/span&gt; section. Save and reload the website to ensure it&#39;s working. &amp;nbsp;&lt;/div&gt;
&lt;h2&gt;
Example Web.config File&lt;/h2&gt;
&lt;script src=&quot;https://gist.github.com/pynej/6da36990f38e45fb6c5e.js&quot;&gt;&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://pynej.blogspot.com/feeds/3545365105006885632/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/8526508655697471701/3545365105006885632?isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3545365105006885632'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8526508655697471701/posts/default/3545365105006885632'/><link rel='alternate' type='text/html' href='http://pynej.blogspot.com/2015/05/convert-htaccess-deny-rules-to-webconfig.html' title='Convert .htaccess deny rules to web.config'/><author><name>PyneJ</name><uri>http://www.blogger.com/profile/16320800813047916791</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>