diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..5981227 --- /dev/null +++ b/404.html @@ -0,0 +1,433 @@ + + + + + + +Page Not Found - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Page Not Found +

+ + + +
+ + +
+ + + +

Sorry, but the page you were trying to view does not exist.

+ + +
+ + + + + + +
+ + +
+ + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..7762b7c --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +mgw.dumatics.com \ No newline at end of file diff --git a/aao-a150l-step-by-step-installation-process-for-linux4one/index.html b/aao-a150l-step-by-step-installation-process-for-linux4one/index.html new file mode 100644 index 0000000..e24b8c6 --- /dev/null +++ b/aao-a150l-step-by-step-installation-process-for-linux4one/index.html @@ -0,0 +1,790 @@ + + + + + + +AAO A150L Step by Step Installation Process for Linux4one - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

AAO A150L Step by Step Installation Process for Linux4one +

+ + +

+ + + + + + + + + + + + 11 minute read + + + +

+ + +
+ + +
+ +

Linux4one is the only Linux distro other than pre-installed Linpus that worked out of the box on my and two other Acer aspire one A150L which belonged to my friends. With the GUI same as that for Ubuntu Netbook Remix, it is a fantastic OS to have on your machine. The only downside I felt was that the icons looked little washed out compared to those on actual UNR but that is not so much of an issue. Everything pretty much worked out of the box which matters the most. I did find the splash screen at the time of start-up a little childish but again these are not major issues. +However if you would rather have a more professional look and feel, Ubuntu may be your choice. Refer to other post on step by step process for installing Ubuntu Netbook Remix but be forewarned that not everything works as well on UNR as of now, at least not for me. I am still working and finding out more on how these can work but given the fact that my knowledge of Linux is zero it can take some time. I have compiled these tutorials solely by hunting on net and felt this will be my way of giving back to community. I hope you find it useful. If you do find this useful, please don’t forget to put a comment. A thanks will be enough to keep me motivated. +Without further ado, the process is as below. +You will need:

+
    +
  • Two 2GB card with a USB Card Reader or a 2GB USB stick formatted to FAT32
  • +
  • One 1GB card with a USB Card Reader or a 1GB USB stick formatted to FAT32
  • +
  • One PC / Laptop with optical drive (DVD Drive) and windows installed as this tutorial uses a Windows based laptop.
  • +
  • Ensure that you AAO is having a constant AC supply and you are not relying on battery.
  • +
  • Lot of patience
  • +
+

1. Create a recovery USB for Acer Aspire One A150L: (Optional)

+

This should be quite straight forward. Insert the recovery DVD that came with your Acer Aspire One A150. Connect the 2GB Card / USB stick and start the process.
+On different laptops this may mean pressing a different function key (F1 to F12) but generally it will be F2, F8 or F12. +So once your laptop shows the first screen after switching on, at that time press the relevant key to enter the boot set-up, now goto boot and change the order of boot to select DVD as first preference. Now press F10 to save and exit. +Your laptop will now boot using the recovery DVD and should help you create the recovery USB.
+It is best to once test if the recovery USB is working or not. You can do so by now plugging your recovery DVD into AAO and starting it, press F12 and change the boot order to make USB disk the first option. If the recovery USB is created properly it will start the recovery process. +If this does not happen, you are among the unfortunate few who received a faulty DVD as I have seen many posts around these. In such situations, you will find this blogpost very handy:

+ +

Anyway, now one way or the other once your recovery USB is ready, we have minimised the risk of not being able to restore to factory conditions in case you do not like the end result of what we have set out to achieve.

+

2. Update BIOS Version to latest V.3309 (Optional)

+

You will be able to download the latest BIOS V.3309 from ACER website. The ftp link to the site is ftp://ftp.acer-euro.com/netbook/aspire_one_110/bios/ and http link is http://support.acer-euro.com/drivers/notebook/as_one_150.html +Once you have downloaded the latest version, you will need to make a bootable USB. I used Unetbootin which will be used later on for creating installatioon USB for Linux4one as well so you will need to now download it from here. +After downloading, connect your 1 GB Card / USB stick and run the unetbootin-windows-357.exe file and select the screen as shown below: +With this setup achieved, click OK. Once this is completed, extract all the files from BIOS zip file into the root of USB Drive. Remember it is important to have it in Root. +Now connect this USB drive to your Acer Aspire One, reboot it and press F12 to select the bootable device.System will show an error message around missing CD ROm. Ignore it. The AAO150 screen will now show A prompt A>. +Type C: in front of A> and press enter. This should change your drive to C>. Now type 3309.BAT. +This should start the flashing of BIOS to V.3309. Once completed AAO150 will reboot. +This is it. Your BIOS should now be updated to latest version.

+

3. Create a installation USB for Linux4one v1.1:

+

While the latest version of Linux4one is 1.5 and is available for download I was unable to make a successful installation USB from available iso. Therefore, I downloaded the V1.1 from this link:
+http://downloads.quellicheilpc.com/linux4one/1.1/index.html +I don’t know how long this version will be hosted there so I will at a later point in time try and upload this iso to some other filehost as well. +Once you have downloaded the iso, plug your 2GB card / USB and start Unetbootin. +Select the Disk image radio button as shown in figure and select the path for linux4one iso image that you have downloaded.Select the USB Drive and click OK. +This will take some time but once done it will ask for a restart. Do NOT restart and cancel it.
+Your installation USB is now ready.

+

4. Install Linux4one

+

Official Guide +Now plug the Installation USB made above and plug it into your AAO150 and boot it. Press F12 and select the boot device. +Once the system starts booting throgh USB you will be presented with the options, select Option "Install Linux4One". +Now the only place which turns out confusing could be when during installation it asks for partitioning. I did not want dual boot so I picked up option 2 which installs on whole hard drive.If you are not a power user, I don’t recommend having dual boot for AAO150 but that has to be your call. +Right, this will ensure that your Linux4one V1.1 is now installed. At this stage, you will reboot, configure location keyboard etc etc and restart once again.
+Check that all is working as expected so far. Only thing that I found not responding well was microphone. It was not working for Skype and hence the next step.

+

5. Download update script for Linux4one v1.5

+

Official Guide +Download the script from here onto your AAO150L using mozilla browser. +Now goto Accessories and click on "Terminal". As mozilla downloads directly to "Desktop" directory you will need to change your directore accordingly. The easiest way will be to click on the "Desktop" link in right hand side navigator on your main screen. This will open the browser, now note the path to Desktop. In my case it was /home/Ankit/Desktop/ so I gave the following commands:

+
cd /home/Ankit/Desktop/
+tar -xjvf linux4one-1.5.tar.bz2
+cd linux4one-1.5
+sudo ./aggiornamento.sh
+
+

After this it will start installing and ask several questions that you just type Y and press enter. +At one point it will also ask do you want to uninstall mozilla and you answer Y as anyway we will install it again in next step. +Right now with V1.5 the browser provided is IceCat which is same as mozilla anyway. +This will take about good 15 to 20 minutes but at the end you would have got latest version of Linux4one installed.

+

6. Install Mozilla (Optional):

+

With Linux4one installed, more than half of the work is already done but with last step mozilla was uninstalled and if you are used to Mozilla and would want to install it back it is quite straight forward. Just goto Administration and select Synaptic Package Manager. It will then ask you to enter your password to perform administrative tasks. You must now enter the password that you gave during installation. +It will now open a window. In Search field enter "mozilla" and it will show several options listed. If you scroll you will find an entry "firefox" under package. When you select that you can read the description that will be something like "Safe and easy web browser from Mozilla". Select the checkbox next to this entry and click "Mark for Installation". It will show you dependencies, just say yes to everything and then it will download and install Mozilla Browser for you.

+

7. Install and configure Skype

+

Goto www.skype.com and download the skype version for Ubuntu 7.04 - 8.04. Mozilla browser will download it to Desktop folder. Now once the download is complete right click on the downloaded file and click on "Open with "GDebi Package Installer". You will be asked for password, enter the password you gave during installation of Linux4one. After that Skype installation will begin. There will be some dialogue boxes on dependencies for which you just click Yes / OK. +Once skype is installed it will appear as an icon in your desktop under Internet. If you want it under favourite, right click the icon and select add to favourite. This can be done for all installed softwares.
+Now open skype, login and at left side bottom corner click on skype settings, select Options. Now in Options select "Video Devices" and click on Test to check that Webcam is set correct. +Next click on "Sound Devices" and click on Make a sound Test and Make a test call. If all is configured fine you should hear a voice and a test call will be made and in test call you will be asked to speak and what you speak will be played back that you should be able to hear.
+It did not work for me at first but when I selected HDA Intel (hw:Intel,0) in all three it worked. However for my friends netbook I had to do more circus and if you are not able to resolve your mic issues, it is time to move to next step.

+

8. Modify Mic settings (Remember : you need to do this only if in previous step mic did not work.)

+

Official Guide +While there is some explanation on Official guide it took me some time to figure out how to get those windows to look like they do in the screenshots there.
+First goto Preferences and scroll down. You will see "Sound" icon. Click on this and change "Sound Playback" and "Sound Capture" to OSS. +Next change the "Device" to "Realtek ALC268 (OSS Mixer)
+Now close this window and click on Volume icon in right hand corner of screen and then click on Edit and select device. Then for HDA Intel (ALSA Mixer) select all options that are not selected - Capture, Capture 1, Beep, Input, Input 1. I have borrowed the image from link above to keep information at one place. +Ensure that on last tab you select "Mic" and not "Front Mic". That is it. This should complete the mic settings. Now we need to check it in Skype. In official tutorial they suggest testing with recorder but that did not work for my friends AAO150 but skype did. I figure we anyway need it for skype so why bother with voice recorder at all. So over to next step then.

+

9. Test Skype

+

Test skype the same way we did in step 7 and see if it is working. Click on "Sound Devices" and click on Make a sound Test and Make a test call. If all is configured fine you should hear a voice and a test call will be made and in test call you will be asked to speak and what you speak will be played back that you should be able to hear. +If this does not work try different combinations from the dropdown one should definitely work.

+

10. Extra – Install Moneydance (Optional)

+

OK now is one piece of software that is not used by everyone. Moneydance is a moneymanagement tool. It is a paid software that I use for managing my finances and have grown quite fond of it and dependent on it at the same time and I really wanted this installed on my netbook and it was one of the not so easy things to install but with following instructions it should be piece of cake really.

+
    +
  1. Download linux version of Moneydance from this here. Mozilla will download it into your Desktop folder.
    +(http://www.moneydance.com/download/2008/installers/Moneydance_linux_x86.tar.gz)
  2. +
  3. Now goto Accessories -> Terminal.
  4. +
  5. Type cd /home/YourUserName/Desktop/ and press enter
  6. +
  7. Type tar xfvz Moneydance_linux_x86.tar.gz and press enter
  8. +
  9. It will print following kind of stuff:
  10. +
+
Moneydance/
+Moneydance/.install4j/
+Moneydance/.install4j/i4jruntime.jar
+Moneydance/Moneydance
+Moneydance/.install4j/firstrun
+Moneydance/.install4j/i4jparams.conf
+Moneydance/.install4j/MessagesDefault
+Moneydance/.install4j/user.jar
+Moneydance/appsrc.jar
+Moneydance/jcommon-1.0.12.jar
+Moneydance/jfreechart-1.0.9.jar
+Moneydance/license.txt
+Moneydance/moneydance.jar
+Moneydance/moneydance_icon32.png
+
+
    +
  1. Now it will present the command prompt again. Type cd Moneydance
  2. +
  3. Now Type ./Moneydance +
  4. +
+

It will display testing JVM in /usr and then open the Monedance software. +This is it software is now installed. +Unfortunately, this does not create any icon that you can directly click every-time and can only be run by clicking on Moneydance.sh file in Moneydance folder. So to make it easy do the following.

+
    +
  1. Goto Prefrences -> Main Menu -> Favourites.
  2. +
  3. Now click on "New Item". It will open a dialogue box 4 fields. In second field Enter "Moneydance".
  4. +
  5. In third field click on Browse and navigate to Desktop->Moneydance->Moneydance.sh
  6. +
  7. If you want to change the icon click on icon and select the one that you want.
  8. +
  9. Click OK.
  10. +
+

This is it you are all done. +Ensure that now you restart your Acer Aspire One 150 atleast three times before you start using as I noticed that it was only after three or four restarts that everything was well settled.

+

11. That's it friends....Now Enjoy.

+

Please let me know if you are stuck somewhere and I might be able to help.

+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..e6a83e0 --- /dev/null +++ b/about/index.html @@ -0,0 +1,751 @@ + + + + + + + Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

+

+ + + +
+ + +
+ + + +

Ankit Mittal

+
+
+
+ +
+
+ +
+ +

EXECUTIVE SUMMARY

+ +
+ +

Results-oriented Programme Manager, with over 21 years of experience in +implementation and management of complex IT and Business Transformation +initiatives. Passionate in the delivery of quality services and +demonstrated ability to influence senior management decisions. History +of results and progressive career growth through hands-on leadership, +elimination of barriers and teamwork skills.

+ +
+ +

KEY COMPETENCIES

+ +
+ +
    +
  • MSP registered practitioner. (Registration Number: MSPR/052974)
  • +
  • PRINCE 2 practitioner. (Registration Number: P2R/220948)
  • +
  • Lean and DFSS (Design for Six Sigma) certified.
  • +
  • Strong Stakeholder Management with ability to provide consultancy to +Senior Management
  • +
  • Commercially aware with an ability to translate technical jargon +from engineering teams into plain English for the benefit of +non-technical stakeholders and decision makers to ensure constant +and timely flow of relevant information across the project.
  • +
  • Experience of working and managing projects in both accelerated +(Agile) as well as controlled (waterfall) development models +and delivering in traditional on-prem as well as DevOps driven +cloud deployments
  • +
  • Managing multiple projects and programmes with large remote +team size (80+)and budgets of up to 12M GBP.
  • +
  • Over 15 years of Business Transformation Programme and Project +Management experience spanning across Financial Services +(Insurance and Banking), Aero-space, Manufacturing, Printing and +FMCG sectors.
  • +
  • Proven technical expertise in database technologies (MSSQL, MySQL +and various BI Suites), web technologies and using Python +for data analysis
  • +
  • Thorough knowledge of DevOps methodology and associated technologies +(Git, Github, GitLab, Ansible, Puppet, Jenkins, Selenium, Kubernetes) +and platforms (Azure, AWS).
  • +
+ +
+ +

PROFESSIONAL EXPERIENCE

+ +
+ +

Client: HP, various Govt. and Financial institutes across UK and Europe through IT Alliance.

+

Role: Senior Project Manager/Programme Manager/Management Consultant, 11/2014 –Till Date

+ +
    +
  • Responsible for Print Modernisation and Transformation +programme for two well known high street banks.
  • +
  • Delivered MPCS (Managed Print Cloud Service) for several +organisations including two international logistics provider, +a UK govt. organisation and Nordics postal service provider.
  • +
  • Delivered MPS (Managed Print Service) for two of the +well known Financial Institute in the UK with a global +presence across all continents with key focus on +EMEA, APAC and United States.
  • +
  • Driven the creation and maintenance of Knowledge Base +for North West Europe Market for print business operations +while capturing, creating and implementing business processes +delivering better customer experience and improved team +satisfaction.
  • +
  • Outlined and delivered the complete PMO governance process in line +with programme management best practices.
  • +
  • Matrix managed large teams - typically including a team of +upto 2 - 10 country project managers, 3 technical +consultants, 2 - 20 technicians, 2 PMO support staff, 2 - 4 Helpdesk +Operations Managers, 3 - 6 developers and 2 - 4 project support officers.
  • +
  • Engaged with operational and technical stakeholders from +multiple organisations to deploy Print Modernisation and +Transformation programmes as well as associated process changes, +embedding the helpdesk and implementing changes with benefit tracking.
  • +
  • Delivered new processes within HP to benefit tracking at 84% saving +with an overall reduction in effort of nearly 25 man-days each +month.
  • +
+ +
+ +

Client: Unilever, UK through CloudTalent.

+

Role: Senior Project Manager, 05/2014 –10/2014

+ +
    +
  • Engaged to manage a vendor selection and procurement project aimed +at delivering a new infrastructure, PaaS and SaaS contract for its +Enterprise Compute Service current mode of operation with a view to +be prepared for future mode of operation and associated +transformation.
  • +
  • Responsible for planning, monitoring and reporting progress of the +project, stakeholder communication and engagement, weekly reports to +senior stakeholders at Unilever and CloudTalent, maintaining RAID +log, resourcing and managing project costs for CloudTalent.
  • +
  • Managed the delivery of RFP to strict deadlines under high pressure +environment (5 months project).
  • +
  • Engaged stakeholders based in different geographies and managing +their expectations.
  • +
  • Prepared detailed weekly report slide decks and used them to drive +structured weekly client and internal delivery review.
  • +
  • Organised confidential RFP related activities and communication with +various internal and external stakeholders.
  • +
  • Managed and reported on costs and resource forecasts.
  • +
  • Facilitated communication between CloudTalent and Unilever Subject +Matter Experts to expedite decisions.
  • +
+ +
+ +

Client: Brammer PLC, Knutsford, UK

+

Role: Programme Manager, 04/2013 to 04/2014

+ +
    +
  • Engaged to set-up and manage the Trading Platform Programme aimed to +unify the IT infrastructure and a Target Operating Model in line +with both financial and technical strategic business goals.
  • +
  • Responsible for planning and monitoring the time, cost and quality +for the programme, stakeholder communication, identifying and +embedding change within the organisation with minimal operational +disruption, provide structured governance for organisation wide +projects that impacted the programme vision, reporting programme +progress to Senior Management Team and Board of Directors.
  • +
  • Rolled out the new service management framework that involved +migration of secondary and tertiary IT support from Manchester to +the IT co-sourcing partner based in Poland while keeping the primary +support in Manchester.
  • +
  • Rollout of iPAD across UK, Germany and Europe as a Sales Support +Tool integrated with the Microsoft Dynamics (CRM).
  • +
  • Implemented Enterprise Service Bus, Master Data Management database, +Transactions Database, Stock Database, abstracted layer for +localised ERP systems in different countries, Brammer e-shop for +European subsidiaries (Spain, Germany, Italy, Belgium, Poland and +Czech)
  • +
  • Initiated business transformation programme for pan-European Key +Accounts operations of Brammer aiming to deliver a target operating +model that supports collaborative cross border order management +while maximising savings for customer as well as Brammer.
  • +
  • Set-up project management internally within Brammer PLC and resource +management practices with IT co-sourcing partners, mentored Junior +Project Managers and PMO Manager to establish regular reporting, +developed and introduced dashboard templates and Graduate Trainee on +techniques involved in Business Analysis such as six sigma +workshops, process mapping.
  • +
+ +
+ +

Employer: Amor Transportation Sector, Daresbury, UK

+

Role: Programme Manager and Senior Project Manager, 11/2011 to 04/2013

+ +
    +
  • Engaged to deliver the programme aimed to development of a new +product called Chroma Human Resource Management System (CHRMS) as an +addition to the existing offering of Chroma Software Suite in +partnership with Manchester Airport while delivering implementation +of Chroma suite at several other airports in the UK and Europe.
  • +
  • Responsible for planning, monitoring and reporting the progress of +programme and other implementation projects to external and internal +stakeholders, optimising resource usage between programme and other +projects, negotiating costs for change requests, agreeing delivery +time-frame and roadmap for enhancement requests, pre-sales support.
  • +
  • Managed the complete development and delivery of new modular product +called Chroma Human Resource Management System (CHRMS).
  • +
  • Managed business change within Transport Sector to deliver new +governance and streamline culture between FS Walker-Hughes and Amor +Group post the merger of two companies.
  • +
  • Managed Infrastructure and Interface development projects part of +the EAL Separation programme.
  • +
  • Delivered AMOR's Chroma Airport Suite software across national and +international airports.
  • +
  • Provided responses to RFP’s for Australian, Norwegian, Dubai and +American airports with two successes while others were yet to +decide.
  • +
+ +
+ +

Employer: Barclays Corporate, Northwich, UK.

+

Role: Project Manager (Business Transformation and IT), 05/2008 – 09/2011

+ +
    +
  • Engaged to deliver business transformation and change initiatives +undertaken by the Operational Excellence Team of Barclays Corporate.
  • +
  • Responsible for planning, monitoring and reporting of projects, +adhering to project management practice guidelines, ensure +commercially realisable benefits.
  • +
  • Delivered a servicing activities hand-off model that created +capacity within sales realising a benefit of £350,000 in 3 months.
  • +
  • Managed end to end rollout of Siebel and TotalView Workforce +Management system.
  • +
  • Managed the move of IT support services, phased migration from +legacy to strategic VOIP solutions
  • +
  • Delivered the secure email solution for communication between +BARCORP and its business partners.
  • +
  • Delivered business transformation project aimed at segmentation of +client base following the organisation restructure
  • +
  • Delivered an enhanced complaints management process framework within +6 months making the Barclays Corporate compliant to Regulatory +requirements and closing over 70 audit observations.
  • +
  • Conducted feasibility study with an aim to recommend product that +will enable Automated KYC (Know Your Customer) and provided complete +recommendation.
  • +
+ +
+ +

Client: Aviva (then Norwich Union), York and Norwich, UK through Tata Consultancy Services.

+

Role: IT Project Manager, 07/2005 – 12/2007

+ +
    +
  • Engaged to deliver development project, enhancement and support +services to agreed cost, schedule and quality for Financial Services +Applications Group within NUIT.
  • +
  • Responsible for planning and governance of assigned projects, +managing staff and third party service providers and vendors, +identify resource requirements to feed into the business planning +process and interface between Business and IT community.
  • +
  • Managed an electronic funds transfer systems migration project aimed +at replacing the 9 stand-alone systems through an enhanced Payment +Preparation System to cater for Chaps, Inter account Transfers, +International Payments and Foreign drafts.
  • +
  • Managed policies migration with process to ensure phasing out of +retained policies.
  • +
  • Enhancements to the Payment Preparation System to transmit payments +via Hexagon ABC and ensure conformance to European Payments Council +Resolution of 2005 in respect of Euro cross-border payment standard +(BIC & IBAN).
  • +
  • Migration of old accounts from NU Cash-book to Heritage Cash-book +under Swiss-Re arrangement.
  • +
  • Delivered the system upgrade of finance application suite.
  • +
+ +

Client: GE Healthcare and Amersham Biosciences, Sweden through Tata Consultancy Services.

+

Role: IT Projects and Engagement Manager, 07/2004 - 07/2005

+ +
    +
  • Engaged to deliver the knowledge acquisition from Amersham +Biosciences IT Team based in Sweden and facilitate move of IT +Support Services to offshore.
  • +
  • Responsible for working with client services and business partners +to ensure a seamless transition, stakeholder management, resource on +boarding, progress reporting, to be the escalation point, managing +enhancement projects.
  • +
  • Built and delivered a successful outsourcing model aimed at reducing +the IT department’s 2004 operating costs by 30%.
  • +
  • Created a new work-flow for production implementation teams to +prioritize requests. Integrally involved with core team that +improved the process to separate support from development requests
  • +
  • Reduced the support team’s work-load by over 70% in two-months from +the original 300+ weekly requests by introducing new workflow and +collecting appropriate management information. As a result, team was +able to focus more on value-added enhancements and less on recurring +issues.
  • +
  • Managed complex enhancement projects involving multiple technologies +(JAVA, JDE, Websphere, Apache) with teams based at Sweden, UK, India +and US.
  • +
+ +
+ +

Client: Tata Consultancy Services. Support Groups, Mumbai, India

+

Role: Project Leader, ESS-Ultimatix, 09/2003 - 07/2004

+ +
    +
  • Project documentation, quality assurance, impact analysis, resource +planning related to Change Request and enhancements.
  • +
  • Server Administration, Database design and maintenance.
  • +
+ +
+ +

Client: Government of Andhra Pradesh, Hyderabad, India through Tata Consultancy Services.

+

Role: Developer/Module Leader, SmartGov, 04/2001 – 09/2003

+ +
    +
  • Designed, Developed and Tested Applications using PL/SQL stored +procedures, Lotus Script, JAVA, Javascript
  • +
+ +
+ + +
+ + + + + + +
+ + +
+ + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/access-files-folders-directories-from-linux-mint-on-android/index.html b/access-files-folders-directories-from-linux-mint-on-android/index.html new file mode 100644 index 0000000..a3c4512 --- /dev/null +++ b/access-files-folders-directories-from-linux-mint-on-android/index.html @@ -0,0 +1,735 @@ + + + + + + +Access (files / folders) Directories from Linux Mint on Android - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Access (files / folders) Directories from Linux Mint on Android +

+ + +

+ + + + + + + + + + + + 3 minute read + + + +

+ + +
+ + +
+ +

Fig-1 +Let me clear the air before anyone mentions. Yes, I know it’s directories and not folders and yes I know many still call these folders and this may encourage the wrong usage but my aim is to help not educate but hopefully making this the first line in post will have some educational effect :). +Right, so to start with, presented below is the problem statement: +We want to share particular folder on our home WiFi network such that all members of the family can access it using their gadget - laptop / smartphone / netbook etc. Now like a true project manager, I will scope it down by making an assumption that any gadget that runs a variant of linux - Android included. It’s not to say that the solution will not work on iOS or Windows, it’s to say that these operating systems are not covered but might as well work with right application downloaded.However the solution offered will make no such claims…:p. Having said that, I will actually only be covering Android in this article as for Linux on desktop, it is fairly straight forward by going through "Network" places. +Ok now to the exciting stuff:

+

Step 1: Decide which Directory (Folder) you want to share.

+

For purpose of this demonstration, let’s create a new folder called "DemoShare".

+

Step 2: Decide whether you want it to be accessed by specific users or anyone and everyone connected to your home network

+

a) If you would want the shared directory (folder) to be password protected go through all steps below.
+b) If you would want to give guest access to all users, skip to Step 4.

+

Step 3:

+

a) Create new User(s) - +Let’s create a new user - androshare. This can be done by following the images below: +Fig-2 +Fig-3 +Fig-4 +Fig-5 +Fig-6 +Fig-7 +Fig-8 +Obviously you are free to be creative with the username. Just replace androshare with your username in subsequent commands. +b) Create samba username(s)/password(s)
+Open the terminal and type the following command:
+sudo smbpasswd -a androshare. You will be asked for the root password and then new password for the username. +Once you type in new password, you will be asked to retype the new password. Again as shown in image.
+ +c) Restart Samba +Open the terminal and type the following command: +sudo service smbd restart

+

Step 4: Update Share Properties of the Directory(ies) to be shared

+ + + + + + + + + + + + + +
+Fig-10Goto the folder “DemoShare” created in Step 1, right click on the folder and select properties. On the properties dialogue box click on Share tab. Click on share the folder checkbox.
+Fig-11For scenario (a) of Step 2 click on checkbox - “Allow users to create and delete files in this folder” as shown in the figure below and click on Create Share.
+Fig-12 +For scenario (b) of Step 2, mark the “Guest access” Checkbox too as shown in next figure before clicking the Create Share button.</tr>
+Fig-11After the Create Share button is clicked following dialogue will appear. Select “Add the permissions automatically”</tr> +</table> +The folder icon will then change to show a share flag as shown below.Step 5: Download and install the file explorer app +ES File explorer is what I use and recommend. It can be downloaded from market here - https://market.android.com/details?id=com.estrongs.android.popStep 6: Configure the app to access the shared directory +On your computer, open the terminal and type +ifconfig +In the resulting information, locate wlan0 (last entry) and under that in second line you will find something like “inet addr: 192.168.1.74”, Note this down. +Open the app and goto LAN tab.Press the menu button and click on “New”. +Now click on “Server”. This will open the “New/Edit Samba Server Screen”.Complete it as shown with following information +| Field | Scenario A | Scenario B +|———–|———————–|———————– +| Server | IP Address from above | IP Address from above +| Username | Androshare | BLANK +| Password | As given in Step 3a | BLANK +| Anonymous | BLANK | Select the checkbox | +Click OK. +Now if you click on the IP address, it should show the shared directory. As you can see in the screenshot at the beginning of this post, I have already shared my Calibre Library and it does make life really simple. I can download files and e-books without actually going to my laptop. +That is all there is to it. +Hope you find it useful.
+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/arch-linux-nearly-a-year-on/index.html b/arch-linux-nearly-a-year-on/index.html new file mode 100644 index 0000000..614ea99 --- /dev/null +++ b/arch-linux-nearly-a-year-on/index.html @@ -0,0 +1,707 @@ + + + + + + +Arch Linux - nearly a year on…. - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Arch Linux - nearly a year on…. +

+ + +

+ + + + + + + + + + + + 6 minute read + + + +

+ + +
+ + +
+ +

I have been dwelling in the world of Arch Linux for just under a year now and must admit the experience is nothing less than liberating. Granted that the barrier to entry was big when I dived in, as a pure Arch install takes some reading and learning but the rewards are worth the pain.

+ +

While it may seem easier to stick with dear old .deb files in the end AUR has an equally large collection if not more and the fact that it’s a rolling release means that the initial set-up pain is very well negated by another fact that unless like me you are forced to change your hard disk or something that sinister; there is no reason to do a reinstall which is often the case with Debian based distros should you chose to upgrade to their latest offering.

+ +

I have read and heard words such as “Arch is not for faint hearted” or “Arch is for motivated newbie” and so on but in my opinion there are two types of Linux users:

+ +
    +
  1. Those who have an itch to tinker (someone like me)
  2. +
  3. Those who don’t care as long as they can get on with their day to day +business (someone like my wife)
  4. +
+ +

It’s the type 1 who is intended audience of this post because type 2 can’t be bothered. Now if you have an itch to tinker there will come a point where you will find that all existing distros have a limit on what you can tinker without messing something to the point where you feel like you have to give another distro a try and before you know it a reinstall is on hand, which ofcourse means type 2 user has to deal with the changes a new distro will bring and this in turn will make them frown and thereby increase the risk of them secretly cursing the day you - the type 1 user - encountered the world of Linux.

+ +

Now Arch is very stable and still gives you the freedom to tinker, learn and do all great things rather than worry about the cosmetics and allow your tinker angel to quench it’s thirst by allowing you to focus your learning potential on something that just doesn’t scratch the surface so you can feel good about yourself but actually allows you to learn a great deal in the process. The helpful Arch community usually has the answers for nearly every problem I encountered while trying to achieve my goals.

+ +

I do not think that for a Type 1 user - newbie or not - Arch is hard or unfriendly. There is lot more satisfaction once you have your desktop exactly the way you wanted. Getting applications is as easy as using software centre in other distros and now that I have been using it, I find using “yaourt” (Arch equivalent of Synaptics) much better and the fact that repositories are maintained on servers in nearly all locations, it does seem to get new updates to your favourite applications rather quickly too.

+ +

I will substantiate this claim with following examples:

+ +

Getting android sdk configured on debian based distros is a right pain in you know where but with Arch it’s what we like to call a doddle in Great Britain. Getting intellij IDE on other distro repositories is not an option but in Arch - you got it, it’s a doddle.

+ +

Why is that? It’s because Type 1 users when they switch to Arch they soon realise that they now have time on their hands which earlier used to disappear in distro hopping. So what do they do with this time? They search for software they want to use and if it isn’t in the repo then with their new found confidence they don’t shy away from building it from source and when they have done that they want to showcase their work and they can do so in AUR short for Arch User Repository - though with all that is already there this showcasing will only be possible if you genuinely need a very unique software not used by any Arch user - highly unlike scenario if you were to go by my experience . You will find nearly all software you can think of in AUR and since it’s being maintained by someone who needed it, chances are he / she will keep it updated for his / her use thereby ensuring you get the latest and greatest as it happens. What a wonderful model it is indeed.

+ +

Now if you are still not geared up to go all the way to start from scratch on Arch but want to get a flavour, why not start with the new kid on the block - Manjaro Linux?

+ +

Manjaro linux is an Arch based distro, with a rolling release schedule and will still give you the access to AUR. It takes away the pain of setting up your system on command line and does the hard work for you so you can get the benefits of Arch but not the pain. Seems like a win win especially for those who are repelled by the very thought of terminal based system configuration.

+ +

Manjaro Linux offers all leading desktop environments - Gnome, KDE, Openbox but their best offering is with XFCE. XFCE is very good as well what with it being light weight and not making your Type 2 users feel that the system has been rendered useless. The default configuration of Manjaro XFCE flavour does come with a very ready to use set-up yet enough opportunity to make it and configure just the way you like it. In addition a type 1 user will be pleasantly surprised that using XFCE does wonders to satisfy that itch to tinker.

+ +

The login manager used with XFCE is MDM but can be easily replaced to the slick lovely light and beautiful SLiM and by installing slimlock you can also get rid of the ugly default screen lock that comes bundled with XFCE.

+ +

Explore some more and key bindings on this desktop are much more satisfying and easier to manage than on Gnome or KDE - IMHO.

+ +

Manjaro also comes with GUI based synaptics equivalent called “packer” but like I said once you are used to “yaourt” interface everything else GUI based just seems pale in comparison becaue it does not give the same satisfaction of installing stuff.

+ +

The one situation where Manjaro might actually be a better choice than Arch is when you are dealing with a potential convert. Offer them Arch with yaourt and they might be very sceptical but offer Manjaro with packer and they just might convert. Then again for this scenario perhaps Linux Mint or Zorin OS may be even better choice.

+ +

Point is Arch should enter into anyone’s life where they make the choice but not when someone else is making that choice on their behalf as they may or may not develop the habit of tinkering. When and if they do, they will need to decide on their own to move to this platform but if you have been calling yourself a linux newbie and have found yourself reviewing new distros or worse distro hopping and by extension forum hopping, tailoring your linux install and generally enthusistic when it comes to all things Linux, I think it is time to get your feets wet and hands dirty. Get Arch experience in it’s purest form or by using Manjaro, you will not regret it.

+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/css/main.css b/assets/css/main.css new file mode 100644 index 0000000..037c18b --- /dev/null +++ b/assets/css/main.css @@ -0,0 +1,5 @@ +.author__urls.social-icons i,.author__urls.social-icons .svg-inline--fa,.page__footer-follow .social-icons i,.page__footer-follow .social-icons .svg-inline--fa{color:inherit}.ais-search-box .ais-search-box--input{background-color:#1f242c}.toc .nav__title{color:#f80729 !important}.toc__menu a:hover{color:#8bc34a !important}.sidebar .author__name{color:#fed136 !important}.site-title{color:#fed136 !important}.author__bio{color:#8bc34a !important}.site-subtitle{color:#8bc34a !important}.archive__item a{color:#fed136 !important}.image-cropper{width:220px;height:220px;position:relative;overflow:hidden;border-radius:50%;border:2px solid #f00}img.center{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}img.horizontal{height:100%;width:auto;max-width:9999px;max-width:max-content}.image-white{background-color:white}.notice,.notice--primary,.notice--info,.notice--success,.notice--warning,.notice--error,.notice--danger,.notice--tip{padding:10px;margin:10px 0}.notice:before,.notice--primary:before,.notice--info:before,.notice--success:before,.notice--warning:before,.notice--error:before,.notice--danger:before,.notice--tip:before{font-family:'Font Awesome 5 Free';font-style:normal;font-weight:900;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.notice:before{content:'\f249'}.notice--primary:before,.notice--tip:before{content:'\f0eb'}.notice--info:before{content:'\f05a'}.notice--success:before{content:'\f058'}.notice--warning:before{content:'\f071'}.notice--error:before{content:'\f188'}.notice--danger:before{content:'\f714'}.notice{color:#53752c !important;background-color:#e8f3cf !important;border-left:2em solid #8bc34a}.notice--primary,.notice--tip{color:#987d20 !important;background-color:#fff6cb !important;border-left:2em solid #fed136}.notice--info{color:#235e70 !important;background-color:#d8ebe5 !important;border-left:2em solid #3b9cba}.notice--success{color:#266426 !important;background-color:#d9edcd !important;border-left:2em solid #3fa63f}.notice--warning{color:#994300 !important;background-color:#ffe2c0 !important;border-left:2em solid #FF6F00}.notice--error{color:#861129 !important;background-color:#f9d2ce !important;border-left:2em solid #DF1C44}.notice--danger{color:#8f3937 !important;background-color:#fcdfd2 !important;border-left:2em solid #ee5f5b}/*! + * Minimal Mistakes Jekyll Theme 4.23.0 by Michael Rose + * Copyright 2013-2020 Michael Rose - mademistakes.com | @mmistakes + * Licensed under MIT (https://github.com/mmistakes/minimal-mistakes/blob/master/LICENSE) +*/.mfp-counter{font-family:Georgia,Times,serif}.mfp-bg{top:0;left:0;width:100%;height:100%;z-index:1042;overflow:hidden;position:fixed;background:#000;opacity:.8;filter:alpha(opacity=80)}.mfp-wrap{top:0;left:0;width:100%;height:100%;z-index:1043;position:fixed;outline:none !important;-webkit-backface-visibility:hidden}.mfp-container{text-align:center;position:absolute;width:100%;height:100%;left:0;top:0;padding:0 8px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.mfp-container:before{content:'';display:inline-block;height:100%;vertical-align:middle}.mfp-align-top .mfp-container:before{display:none}.mfp-content{position:relative;display:inline-block;vertical-align:middle;margin:0 auto;text-align:left;z-index:1045}.mfp-inline-holder .mfp-content,.mfp-ajax-holder .mfp-content{width:100%;cursor:auto}.mfp-ajax-cur{cursor:progress}.mfp-zoom-out-cur,.mfp-zoom-out-cur .mfp-image-holder .mfp-close{cursor:-moz-zoom-out;cursor:-webkit-zoom-out;cursor:zoom-out}.mfp-zoom{cursor:pointer;cursor:-webkit-zoom-in;cursor:-moz-zoom-in;cursor:zoom-in}.mfp-auto-cursor .mfp-content{cursor:auto}.mfp-close,.mfp-arrow,.mfp-preloader,.mfp-counter{-webkit-user-select:none;-moz-user-select:none;user-select:none}.mfp-loading.mfp-figure{display:none}.mfp-hide{display:none !important}.mfp-preloader{color:#ccc;position:absolute;top:50%;width:auto;text-align:center;margin-top:-0.8em;left:8px;right:8px;z-index:1044}.mfp-preloader a{color:#ccc}.mfp-preloader a:hover{color:#fff}.mfp-s-ready .mfp-preloader{display:none}.mfp-s-error .mfp-content{display:none}button.mfp-close,button.mfp-arrow{overflow:visible;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;display:block;outline:none;padding:0;z-index:1046;-webkit-box-shadow:none;box-shadow:none}button::-moz-focus-inner{padding:0;border:0}.mfp-close{width:44px;height:44px;line-height:44px;position:absolute;right:0;top:0;text-decoration:none;text-align:center;opacity:1;filter:alpha(opacity=100);padding:0 0 18px 10px;color:#fff;font-style:normal;font-size:28px;font-family:Georgia,Times,serif}.mfp-close:hover,.mfp-close:focus{opacity:1;filter:alpha(opacity=100)}.mfp-close:active{top:1px}.mfp-close-btn-in .mfp-close{color:#fff}.mfp-image-holder .mfp-close,.mfp-iframe-holder .mfp-close{color:#fff;right:-6px;text-align:right;padding-right:6px;width:100%}.mfp-counter{position:absolute;top:0;right:0;color:#ccc;font-size:12px;line-height:18px}.mfp-arrow{position:absolute;opacity:1;filter:alpha(opacity=100);margin:0;top:50%;margin-top:-55px;padding:0;width:90px;height:110px;-webkit-tap-highlight-color:transparent}.mfp-arrow:active{margin-top:-54px}.mfp-arrow:hover,.mfp-arrow:focus{opacity:1;filter:alpha(opacity=100)}.mfp-arrow:before,.mfp-arrow:after,.mfp-arrow .mfp-b,.mfp-arrow .mfp-a{content:'';display:block;width:0;height:0;position:absolute;left:0;top:0;margin-top:35px;margin-left:35px;border:medium inset transparent}.mfp-arrow:after,.mfp-arrow .mfp-a{border-top-width:13px;border-bottom-width:13px;top:8px}.mfp-arrow:before,.mfp-arrow .mfp-b{border-top-width:21px;border-bottom-width:21px;opacity:0.7}.mfp-arrow-left{left:0}.mfp-arrow-left:after,.mfp-arrow-left .mfp-a{border-right:17px solid #fff;margin-left:31px}.mfp-arrow-left:before,.mfp-arrow-left .mfp-b{margin-left:25px;border-right:27px solid #fff}.mfp-arrow-right{right:0}.mfp-arrow-right:after,.mfp-arrow-right .mfp-a{border-left:17px solid #fff;margin-left:39px}.mfp-arrow-right:before,.mfp-arrow-right .mfp-b{border-left:27px solid #fff}.mfp-iframe-holder{padding-top:40px;padding-bottom:40px}.mfp-iframe-holder .mfp-content{line-height:0;width:100%;max-width:900px}.mfp-iframe-holder .mfp-close{top:-40px}.mfp-iframe-scaler{width:100%;height:0;overflow:hidden;padding-top:56.25%}.mfp-iframe-scaler iframe{position:absolute;display:block;top:0;left:0;width:100%;height:100%;box-shadow:0 0 8px rgba(0,0,0,0.6);background:#000}img.mfp-img{width:auto;max-width:100%;height:auto;display:block;line-height:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:40px 0 40px;margin:0 auto}.mfp-figure{line-height:0}.mfp-figure:after{content:'';position:absolute;left:0;top:40px;bottom:40px;display:block;right:0;width:auto;height:auto;z-index:-1;box-shadow:0 0 8px rgba(0,0,0,0.6);background:#444}.mfp-figure small{color:#bdbdbd;display:block;font-size:12px;line-height:14px}.mfp-figure figure{margin:0}.mfp-figure figcaption{margin-top:0;margin-bottom:0}.mfp-bottom-bar{margin-top:-36px;position:absolute;top:100%;left:0;width:100%;cursor:auto}.mfp-title{text-align:left;line-height:18px;color:#f3f3f3;word-wrap:break-word;padding-right:36px}.mfp-image-holder .mfp-content{max-width:100%}.mfp-gallery .mfp-image-holder .mfp-figure{cursor:pointer}@media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px){.mfp-img-mobile .mfp-image-holder{padding-left:0;padding-right:0}.mfp-img-mobile img.mfp-img{padding:0}.mfp-img-mobile .mfp-figure:after{top:0;bottom:0}.mfp-img-mobile .mfp-figure small{display:inline;margin-left:5px}.mfp-img-mobile .mfp-bottom-bar{background:rgba(0,0,0,0.6);bottom:0;margin:0;top:auto;padding:3px 5px;position:fixed;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.mfp-img-mobile .mfp-bottom-bar:empty{padding:0}.mfp-img-mobile .mfp-counter{right:5px;top:3px}.mfp-img-mobile .mfp-close{top:0;right:0;width:35px;height:35px;line-height:35px;background:rgba(0,0,0,0.6);position:fixed;text-align:center;padding:0}}@media all and (max-width: 900px){.mfp-arrow{-webkit-transform:scale(0.75);transform:scale(0.75)}.mfp-arrow-left{-webkit-transform-origin:0;transform-origin:0}.mfp-arrow-right{-webkit-transform-origin:100%;transform-origin:100%}.mfp-container{padding-left:6px;padding-right:6px}}.mfp-ie7 .mfp-img{padding:0}.mfp-ie7 .mfp-bottom-bar{width:600px;left:50%;margin-left:-300px;margin-top:5px;padding-bottom:5px}.mfp-ie7 .mfp-container{padding:0}.mfp-ie7 .mfp-content{padding-top:44px}.mfp-ie7 .mfp-close{top:0;right:0;padding-top:0}a:focus,button:focus{outline:thin dotted #fed136;outline:5px auto #fed136;outline-offset:-2px}*{box-sizing:border-box}html{box-sizing:border-box;background-color:#252a34;font-size:16px;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}@media (min-width: 48em){html{font-size:18px}}@media (min-width: 64em){html{font-size:20px}}@media (min-width: 80em){html{font-size:22px}}body{margin:0}::-moz-selection{color:#fff;background:#000}::selection{color:#fff;background:#000}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}a{color:#fed136}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{max-width:100%;width:auto\9;height:auto;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{box-sizing:border-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}html{position:relative;min-height:100%}body{margin:0;padding:0;color:#eaeaea;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;line-height:1.5}body.overflow--hidden{overflow:hidden}h1,h2,h3,h4,h5,h6{margin:2em 0 0.5em;line-height:1.2;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-weight:bold}h1{margin-top:0;font-size:1.563em}h2{font-size:1.25em}h3{font-size:1.125em}h4{font-size:1.0625em}h5{font-size:1.03125em}h6{font-size:1em}small,.small{font-size:.75em}p{margin-bottom:1.3em}u,ins{text-decoration:none;border-bottom:1px solid #eaeaea}u a,ins a{color:inherit}del a{color:inherit}p,pre,blockquote,ul,ol,dl,figure,table,fieldset{orphans:3;widows:3}abbr[title],abbr[data-original-title]{text-decoration:none;cursor:help;border-bottom:1px dotted #eaeaea}blockquote{margin:2em 1em 2em 0;padding-left:1em;padding-right:1em;font-style:italic;border-left:0.25em solid #fed136}blockquote cite{font-style:italic}blockquote cite:before{content:"\2014";padding-right:5px}a:visited{color:#bf9d29}a:hover{color:#8bc34a;outline:0}tt,code,kbd,samp,pre{font-family:Monaco,Consolas,"Lucida Console",monospace}pre{overflow-x:auto}p>code,a>code,li>code,figcaption>code,td>code{padding-top:0.1rem;padding-bottom:0.1rem;font-size:0.8em;background:#1f242c;border-radius:4px}p>code:before,p>code:after,a>code:before,a>code:after,li>code:before,li>code:after,figcaption>code:before,figcaption>code:after,td>code:before,td>code:after{letter-spacing:-0.2em;content:"\00a0"}hr{display:block;margin:1em 0;border:0;border-top:1px solid #51555d}ul li,ol li{margin-bottom:0.5em}li ul,li ol{margin-top:0.5em}figure{display:-webkit-box;display:flex;-webkit-box-pack:justify;justify-content:space-between;-webkit-box-align:start;align-items:flex-start;flex-wrap:wrap;margin:2em 0}figure img,figure iframe,figure .fluid-width-video-wrapper{margin-bottom:1em}figure img{width:100%;border-radius:4px;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}figure>a{display:block}@media (min-width: 37.5em){figure.half>a,figure.half>img{width:calc(50% - 0.5em)}}figure.half figcaption{width:100%}@media (min-width: 37.5em){figure.third>a,figure.third>img{width:calc(33.3333% - 0.5em)}}figure.third figcaption{width:100%}figcaption{margin-bottom:0.5em;color:#eaeaea;font-family:Georgia,Times,serif;font-size:.75em}figcaption a{-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}figcaption a:hover{color:#8bc34a}svg:not(:root){overflow:hidden}nav ul{margin:0;padding:0}nav li{list-style:none}nav a{text-decoration:none}nav ul li,nav ol li{margin-bottom:0}nav li ul,nav li ol{margin-top:0}b,i,strong,em,blockquote,p,q,span,figure,img,h1,h2,header,input,a,tr,td,form button,input[type="submit"],.btn,.highlight,.archive__item-teaser{-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}form{margin:0 0 5px 0;padding:1em;background-color:#1f242c}form fieldset{margin-bottom:5px;padding:0;border-width:0}form legend{display:block;width:100%;margin-bottom:10px;*margin-left:-7px;padding:0;color:#eaeaea;border:0;white-space:normal}form p{margin-bottom:2.5px}form ul{list-style-type:none;margin:0 0 5px 0;padding:0}form br{display:none}label,input,button,select,textarea{vertical-align:baseline;*vertical-align:middle}input,button,select,textarea{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif}label{display:block;margin-bottom:0.25em;color:#eaeaea;cursor:pointer}label small{font-size:.75em}label input,label textarea,label select{display:block}input,textarea,select{display:inline-block;width:100%;padding:0.25em;margin-bottom:0.5em;color:#eaeaea;background-color:#252a34;border:#51555d;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.125)}.input-mini{width:60px}.input-small{width:90px}input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;border-radius:0;border:0 \9;box-shadow:none}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="image"]{border:0}input[type="file"]{width:auto;padding:initial;line-height:initial;border:initial;background-color:transparent;background-color:initial;box-shadow:none}input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;cursor:pointer;*overflow:visible}select,input[type="file"]{*margin-top:4px}select{width:auto;background-color:#fff}select[multiple],select[size]{height:auto}textarea{resize:vertical;height:auto;overflow:auto;vertical-align:top}input[type="hidden"]{display:none}.form{position:relative}.radio,.checkbox{padding-left:18px;font-weight:normal}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{opacity:0.5;cursor:not-allowed}input:focus,textarea:focus{border-color:#fed136;outline:0;outline:thin dotted \9;box-shadow:inset 0 1px 3px rgba(234,234,234,0.06),0 0 5px rgba(254,209,54,0.7)}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{box-shadow:none}.help-block,.help-inline{color:#eaeaea}.help-block{display:block;margin-bottom:1em;line-height:1em}.help-inline{display:inline-block;vertical-align:middle;padding-left:5px}.form-group{margin-bottom:5px;padding:0;border-width:0}.form-inline input,.form-inline textarea,.form-inline select{display:inline-block;margin-bottom:0}.form-inline label{display:inline-block}.form-inline .radio,.form-inline .checkbox,.form-inline .radio{padding-left:0;margin-bottom:0;vertical-align:middle}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-left:0;margin-right:3px}.form-search input,.form-search textarea,.form-search select{display:inline-block;margin-bottom:0}.form-search .search-query{padding-left:14px;padding-right:14px;margin-bottom:0;border-radius:14px}.form-search label{display:inline-block}.form-search .radio,.form-search .checkbox,.form-inline .radio{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"]{float:left;margin-left:0;margin-right:3px}.form--loading:before{content:""}.form--loading .form__spinner{display:block}.form:before{position:absolute;top:0;left:0;width:100%;height:100%;background-color:rgba(255,255,255,0.7);z-index:10}.form__spinner{display:none;position:absolute;top:50%;left:50%;z-index:11}table{display:block;margin-bottom:1em;width:100%;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em;border-collapse:collapse;overflow-x:auto}table+table{margin-top:1em}thead{background-color:#51555d;border-bottom:2px solid #3d4046}th{padding:0.5em;font-weight:bold;text-align:left}td{padding:0.5em;border-bottom:1px solid #3d4046}tr,td,th{vertical-align:middle}@-webkit-keyframes intro{0%{opacity:0}100%{opacity:1}}@keyframes intro{0%{opacity:0}100%{opacity:1}}.btn{display:inline-block;margin-bottom:0.25em;padding:0.5em 1em;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em;font-weight:bold;text-align:center;text-decoration:none;border-width:0;border-radius:4px;cursor:pointer}.btn .icon{margin-right:0.5em}.btn .icon+.hidden{margin-left:-0.5em}.btn--primary{background-color:#fed136;color:#3d4144}.btn--primary:visited{background-color:#fed136;color:#3d4144}.btn--primary:hover{background-color:#cba72b;color:#fff}.btn--inverse{background-color:#fff;color:#3d4144;border:1px solid #51555d}.btn--inverse:visited{background-color:#fff;color:#3d4144}.btn--inverse:hover{background-color:#ccc;color:#3d4144}.btn--light-outline{background-color:transparent;color:#fff;border:1px solid #fff}.btn--light-outline:visited{background-color:transparent;color:#fff}.btn--light-outline:hover{background-color:rgba(0,0,0,0.2);color:#fff}.btn--success{background-color:#3fa63f;color:#fff}.btn--success:visited{background-color:#3fa63f;color:#fff}.btn--success:hover{background-color:#328532;color:#fff}.btn--warning{background-color:#FF6F00;color:#fff}.btn--warning:visited{background-color:#FF6F00;color:#fff}.btn--warning:hover{background-color:#cc5900;color:#fff}.btn--danger{background-color:#ee5f5b;color:#fff}.btn--danger:visited{background-color:#ee5f5b;color:#fff}.btn--danger:hover{background-color:#be4c49;color:#fff}.btn--info{background-color:#3b9cba;color:#fff}.btn--info:visited{background-color:#3b9cba;color:#fff}.btn--info:hover{background-color:#2f7d95;color:#fff}.btn--facebook{background-color:#3b5998;color:#fff}.btn--facebook:visited{background-color:#3b5998;color:#fff}.btn--facebook:hover{background-color:#2f477a;color:#fff}.btn--twitter{background-color:#55acee;color:#fff}.btn--twitter:visited{background-color:#55acee;color:#fff}.btn--twitter:hover{background-color:#448abe;color:#fff}.btn--linkedin{background-color:#007bb6;color:#fff}.btn--linkedin:visited{background-color:#007bb6;color:#fff}.btn--linkedin:hover{background-color:#006292;color:#fff}.btn--block{display:block;width:100%}.btn--block+.btn--block{margin-top:0.25em}.btn--disabled{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);box-shadow:none;opacity:0.65}.btn--x-large{font-size:1.25em}.btn--large{font-size:1em}.btn--small{font-size:.6875em}.notice{margin:2em 0 !important;padding:1em;color:#eaeaea;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em !important;text-indent:initial;background-color:#434851;border-radius:4px;box-shadow:0 1px 1px rgba(189,193,196,0.25)}.notice h4{margin-top:0 !important;margin-bottom:0.75em;line-height:inherit}.page__content .notice h4{margin-bottom:0;font-size:1em}.notice p:last-child{margin-bottom:0 !important}.notice h4+p{margin-top:0;padding-top:0}.notice a{color:#aaaeb0}.notice a:hover{color:#5f6162}.notice code{background-color:#343942}.notice pre code{background-color:inherit}.notice ul:last-child{margin-bottom:0}.notice--primary{margin:2em 0 !important;padding:1em;color:#eaeaea;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em !important;text-indent:initial;background-color:#504b34;border-radius:4px;box-shadow:0 1px 1px rgba(254,209,54,0.25)}.notice--primary h4{margin-top:0 !important;margin-bottom:0.75em;line-height:inherit}.page__content .notice--primary h4{margin-bottom:0;font-size:1em}.notice--primary p:last-child{margin-bottom:0 !important}.notice--primary h4+p{margin-top:0;padding-top:0}.notice--primary a{color:#e5bc31}.notice--primary a:hover{color:#7f691b}.notice--primary code{background-color:#3b3b34}.notice--primary pre code{background-color:inherit}.notice--primary ul:last-child{margin-bottom:0}.notice--info{margin:2em 0 !important;padding:1em;color:#eaeaea;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em !important;text-indent:initial;background-color:#29414f;border-radius:4px;box-shadow:0 1px 1px rgba(59,156,186,0.25)}.notice--info h4{margin-top:0 !important;margin-bottom:0.75em;line-height:inherit}.page__content .notice--info h4{margin-bottom:0;font-size:1em}.notice--info p:last-child{margin-bottom:0 !important}.notice--info h4+p{margin-top:0;padding-top:0}.notice--info a{color:#358ca7}.notice--info a:hover{color:#1e4e5d}.notice--info code{background-color:#273541}.notice--info pre code{background-color:inherit}.notice--info ul:last-child{margin-bottom:0}.notice--warning{margin:2em 0 !important;padding:1em;color:#eaeaea;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em !important;text-indent:initial;background-color:#51382a;border-radius:4px;box-shadow:0 1px 1px rgba(255,111,0,0.25)}.notice--warning h4{margin-top:0 !important;margin-bottom:0.75em;line-height:inherit}.page__content .notice--warning h4{margin-bottom:0;font-size:1em}.notice--warning p:last-child{margin-bottom:0 !important}.notice--warning h4+p{margin-top:0;padding-top:0}.notice--warning a{color:#e66400}.notice--warning a:hover{color:#803800}.notice--warning code{background-color:#3b312f}.notice--warning pre code{background-color:inherit}.notice--warning ul:last-child{margin-bottom:0}.notice--success{margin:2em 0 !important;padding:1em;color:#eaeaea;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em !important;text-indent:initial;background-color:#2a4336;border-radius:4px;box-shadow:0 1px 1px rgba(63,166,63,0.25)}.notice--success h4{margin-top:0 !important;margin-bottom:0.75em;line-height:inherit}.page__content .notice--success h4{margin-bottom:0;font-size:1em}.notice--success p:last-child{margin-bottom:0 !important}.notice--success h4+p{margin-top:0;padding-top:0}.notice--success a{color:#399539}.notice--success a:hover{color:#205320}.notice--success code{background-color:#283635}.notice--success pre code{background-color:inherit}.notice--success ul:last-child{margin-bottom:0}.notice--danger{margin:2em 0 !important;padding:1em;color:#eaeaea;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em !important;text-indent:initial;background-color:#4d353c;border-radius:4px;box-shadow:0 1px 1px rgba(238,95,91,0.25)}.notice--danger h4{margin-top:0 !important;margin-bottom:0.75em;line-height:inherit}.page__content .notice--danger h4{margin-bottom:0;font-size:1em}.notice--danger p:last-child{margin-bottom:0 !important}.notice--danger h4+p{margin-top:0;padding-top:0}.notice--danger a{color:#d65652}.notice--danger a:hover{color:#77302e}.notice--danger code{background-color:#392f38}.notice--danger pre code{background-color:inherit}.notice--danger ul:last-child{margin-bottom:0}.masthead{position:relative;border-bottom:1px solid #51555d;-webkit-animation:intro 0.3s both;animation:intro 0.3s both;-webkit-animation-delay:0.15s;animation-delay:0.15s;z-index:20}.masthead__inner-wrap{clear:both;margin-left:auto;margin-right:auto;padding:1em;max-width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif}.masthead__inner-wrap::after{clear:both;content:"";display:table}@media (min-width: 80em){.masthead__inner-wrap{max-width:1280px}}.masthead__inner-wrap nav{z-index:10}.masthead__inner-wrap a{text-decoration:none}.site-logo img{max-height:2rem}.site-title{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-item-align:center;align-self:center;font-weight:bold}.site-subtitle{display:block;font-size:.625em}.masthead__menu{float:left;margin-left:0;margin-right:0;width:100%;clear:both}.masthead__menu .site-nav{margin-left:0}@media (min-width: 37.5em){.masthead__menu .site-nav{float:right}}.masthead__menu ul{margin:0;padding:0;clear:both;list-style-type:none}.masthead__menu-item{display:block;list-style-type:none;white-space:nowrap}.masthead__menu-item--lg{padding-right:2em;font-weight:700}.breadcrumbs{clear:both;margin:0 auto;max-width:100%;padding-left:1em;padding-right:1em;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;-webkit-animation:intro 0.3s both;animation:intro 0.3s both;-webkit-animation-delay:0.3s;animation-delay:0.3s}.breadcrumbs::after{clear:both;content:"";display:table}@media (min-width: 80em){.breadcrumbs{max-width:1280px}}.breadcrumbs ol{padding:0;list-style:none;font-size:.75em}@media (min-width: 64em){.breadcrumbs ol{float:right;width:calc(100% - 200px)}}@media (min-width: 80em){.breadcrumbs ol{width:calc(100% - 300px)}}.breadcrumbs li{display:inline}.breadcrumbs .current{font-weight:bold}.pagination{clear:both;float:left;margin-top:1em;padding-top:1em;width:100%}.pagination::after{clear:both;content:"";display:table}.pagination ul{margin:0;padding:0;list-style-type:none;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif}.pagination li{display:block;float:left;margin-left:-1px}.pagination li a{display:block;margin-bottom:0.25em;padding:0.5em 1em;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:14px;font-weight:bold;line-height:1.5;text-align:center;text-decoration:none;color:#eaeaea;border:1px solid #3d4046;border-radius:0}.pagination li a:hover{color:#8bc34a}.pagination li a.current,.pagination li a.current.disabled{color:#fff;background:#fed136}.pagination li a.disabled{color:rgba(234,234,234,0.5);pointer-events:none;cursor:not-allowed}.pagination li:first-child{margin-left:0}.pagination li:first-child a{border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination li:last-child a{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination--pager{display:block;padding:1em 2em;float:left;width:50%;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:1em;font-weight:bold;text-align:center;text-decoration:none;color:#eaeaea;border:1px solid #3d4046;border-radius:4px}.pagination--pager:hover{background-color:#eaeaea;color:#3d4144}.pagination--pager:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.pagination--pager:last-child{margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.pagination--pager.disabled{color:rgba(234,234,234,0.5);pointer-events:none;cursor:not-allowed}.page__content+.pagination,.page__meta+.pagination,.comment__date+.pagination,.page__share+.pagination,.page__comments+.pagination{margin-top:2em;padding-top:2em;border-top:1px solid #51555d}.greedy-nav{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:2em;background:#252a34}.greedy-nav a{display:block;margin:0 1rem;color:#eaeaea;text-decoration:none;-webkit-transition:none;transition:none}.greedy-nav a:hover{color:#bbb}.greedy-nav a.site-logo{margin-left:0;margin-right:0.5rem}.greedy-nav a.site-title{margin-left:0}.greedy-nav img{-webkit-transition:none;transition:none}.greedy-nav__toggle{-ms-flex-item-align:center;align-self:center;height:2rem;border:0;outline:none;background-color:transparent;cursor:pointer}.greedy-nav .visible-links{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:hidden}.greedy-nav .visible-links li{-webkit-box-flex:0;-ms-flex:none;flex:none}.greedy-nav .visible-links a{position:relative}.greedy-nav .visible-links a:before{content:"";position:absolute;left:0;bottom:0;height:4px;background:#fed136;width:100%;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;-webkit-transform:scaleX(0) translate3d(0, 0, 0);transform:scaleX(0) translate3d(0, 0, 0)}.greedy-nav .visible-links a:hover:before{-webkit-transform:scaleX(1);-ms-transform:scaleX(1);transform:scaleX(1)}.greedy-nav .hidden-links{position:absolute;top:100%;right:0;margin-top:15px;padding:5px;border:1px solid #51555d;border-radius:4px;background:#252a34;-webkit-box-shadow:0 2px 4px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);box-shadow:0 2px 4px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}.greedy-nav .hidden-links.hidden{display:none}.greedy-nav .hidden-links a{margin:0;padding:10px 20px;font-size:1em}.greedy-nav .hidden-links a:hover{color:#bbb;background:#1a1d24}.greedy-nav .hidden-links:before{content:"";position:absolute;top:-11px;right:10px;width:0;border-style:solid;border-width:0 10px 10px;border-color:#51555d transparent;display:block;z-index:0}.greedy-nav .hidden-links:after{content:"";position:absolute;top:-10px;right:10px;width:0;border-style:solid;border-width:0 10px 10px;border-color:#252a34 transparent;display:block;z-index:1}.greedy-nav .hidden-links li{display:block;border-bottom:1px solid #51555d}.greedy-nav .hidden-links li:last-child{border-bottom:none}.no-js .greedy-nav .visible-links{-ms-flex-wrap:wrap;flex-wrap:wrap;overflow:visible}.nav__list{margin-bottom:1.5em}.nav__list input[type="checkbox"],.nav__list label{display:none}@media (max-width: 63.9375em){.nav__list label{position:relative;display:inline-block;padding:0.5em 2.5em 0.5em 1em;color:#7a8288;font-size:.75em;font-weight:bold;border:1px solid #bdc1c4;border-radius:4px;z-index:20;-webkit-transition:0.2s ease-out;transition:0.2s ease-out;cursor:pointer}.nav__list label:before,.nav__list label:after{content:"";position:absolute;right:1em;top:1.25em;width:0.75em;height:0.125em;line-height:1;background-color:#7a8288;-webkit-transition:0.2s ease-out;transition:0.2s ease-out}.nav__list label:after{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.nav__list label:hover{color:#fff;border-color:#7a8288;background-color:#333}.nav__list label:hover:before,.nav__list label:hover:after{background-color:#fff}.nav__list input:checked+label{color:white;background-color:#333}.nav__list input:checked+label:before,.nav__list input:checked+label:after{background-color:#fff}.nav__list label:hover:after{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.nav__list input:checked+label:hover:after{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}.nav__list ul{margin-bottom:1em}.nav__list a{display:block;padding:0.25em 0}}@media (max-width: 63.9375em) and (min-width: 64em){.nav__list a{padding-top:0.125em;padding-bottom:0.125em}}@media (max-width: 63.9375em){.nav__list a:hover{text-decoration:underline}}.nav__list .nav__items{margin:0;font-size:1.25rem}.nav__list .nav__items a{color:inherit}.nav__list .nav__items .active{margin-left:-0.5em;padding-left:0.5em;padding-right:0.5em;font-weight:bold}@media (max-width: 63.9375em){.nav__list .nav__items{position:relative;max-height:0;opacity:0%;overflow:hidden;z-index:10;-webkit-transition:0.3s ease-in-out;transition:0.3s ease-in-out;-webkit-transform:translate(0, 10%);-ms-transform:translate(0, 10%);transform:translate(0, 10%)}}@media (max-width: 63.9375em){.nav__list input:checked ~ .nav__items{-webkit-transition:0.5s ease-in-out;transition:0.5s ease-in-out;max-height:9999px;overflow:visible;opacity:1;margin-top:1em;-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);transform:translate(0, 0)}}.nav__title{margin:0;padding:0.5rem 0.75rem;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:1em;font-weight:bold}.nav__sub-title{display:block;margin:0.5rem 0;padding:0.25rem 0;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em;font-weight:bold;text-transform:uppercase;border-bottom:1px solid #51555d}.toc{font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;color:#7a8288;background-color:#252a34;border:1px solid #51555d;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.125);box-shadow:0 1px 1px rgba(0,0,0,0.125)}.toc .nav__title{color:#fff;font-size:.75em;background:#fed136;border-top-left-radius:4px;border-top-right-radius:4px}.toc .active a{background-color:#fff6d7;color:#3d4144}.toc__menu{margin:0;padding:0;width:100%;list-style:none;font-size:.75em}@media (min-width: 64em){.toc__menu{font-size:.6875em}}.toc__menu a{display:block;padding:0.25rem 0.75rem;color:#eaeaea;font-weight:bold;line-height:1.5;border-bottom:1px solid #51555d}.toc__menu a:hover{color:#eaeaea}.toc__menu li ul>li a{padding-left:1.25rem;font-weight:normal}.toc__menu li ul li ul>li a{padding-left:1.75rem}.toc__menu li ul li ul li ul>li a{padding-left:2.25rem}.toc__menu li ul li ul li ul li ul>li a{padding-left:2.75rem}.toc__menu li ul li ul li ul li ul li ul>li a{padding-left:3.25rem}.page__footer{clear:both;float:left;margin-left:0;margin-right:0;width:100%;margin-top:3em;color:#eaeaea;-webkit-animation:intro 0.3s both;animation:intro 0.3s both;-webkit-animation-delay:0.45s;animation-delay:0.45s;background-color:#1a1d24}.page__footer::after{clear:both;content:"";display:table}.page__footer footer{clear:both;margin-left:auto;margin-right:auto;margin-top:2em;max-width:100%;padding:0 1em 2em}.page__footer footer::after{clear:both;content:"";display:table}@media (min-width: 80em){.page__footer footer{max-width:1280px}}.page__footer a{color:inherit;text-decoration:none}.page__footer a:hover{text-decoration:underline}.page__footer .fas,.page__footer .fab,.page__footer .far,.page__footer .fal{color:#eaeaea}.page__footer-copyright{font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.6875em}.page__footer-follow ul{margin:0;padding:0;list-style-type:none}.page__footer-follow li{display:inline-block;padding-top:5px;padding-bottom:5px;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em;text-transform:uppercase}.page__footer-follow li+li:before{content:"";padding-right:5px}.page__footer-follow a{padding-right:10px;font-weight:bold}.page__footer-follow .social-icons a{white-space:nowrap}.layout--search .archive__item-teaser{margin-bottom:0.25em}.search__toggle{margin-left:1rem;margin-right:1rem;height:2rem;border:0;outline:none;color:#fed136;background-color:transparent;cursor:pointer;-webkit-transition:0.2s;transition:0.2s}.search__toggle:hover{color:#bf9d29}.search-icon{width:100%;height:100%}.search-content{display:none;visibility:hidden;padding-top:1em;padding-bottom:1em}.search-content__inner-wrap{width:100%;margin-left:auto;margin-right:auto;padding-left:1em;padding-right:1em;-webkit-animation:intro 0.3s both;animation:intro 0.3s both;-webkit-animation-delay:0.15s;animation-delay:0.15s}@media (min-width: 80em){.search-content__inner-wrap{max-width:1280px}}.search-content__form{background-color:transparent}.search-content .search-input{display:block;margin-bottom:0;padding:0;border:none;outline:none;box-shadow:none;background-color:transparent;font-size:1.563em}@media (min-width: 64em){.search-content .search-input{font-size:1.953em}}@media (min-width: 80em){.search-content .search-input{font-size:2.441em}}.search-content.is--visible{display:block;visibility:visible}.search-content.is--visible::after{content:"";display:block}.search-content .results__found{margin-top:0.5em;font-size:.75em}.search-content .archive__item{margin-bottom:2em}@media (min-width: 64em){.search-content .archive__item{width:75%}}@media (min-width: 80em){.search-content .archive__item{width:50%}}.search-content .archive__item-title{margin-top:0}.search-content .archive__item-excerpt{margin-bottom:0}.ais-search-box{max-width:100% !important;margin-bottom:2em}.archive__item-title .ais-Highlight{color:#fed136;font-style:normal;text-decoration:underline}.archive__item-excerpt .ais-Highlight{color:#fed136;font-style:normal;font-weight:bold}div.highlighter-rouge,figure.highlight{position:relative;margin-bottom:1em;background:#263238;color:#eff;font-family:Monaco,Consolas,"Lucida Console",monospace;font-size:.75em;line-height:1.8;border-radius:4px}div.highlighter-rouge>pre,div.highlighter-rouge pre.highlight,figure.highlight>pre,figure.highlight pre.highlight{margin:0;padding:1em}.highlight table{margin-bottom:0;font-size:1em;border:0}.highlight table td{padding:0;width:calc(100% - 1em);border:0}.highlight table td.gutter,.highlight table td.rouge-gutter{padding-right:1em;width:1em;color:#b2ccd6;border-right:1px solid #b2ccd6;text-align:right}.highlight table td.code,.highlight table td.rouge-code{padding-left:1em}.highlight table pre{margin:0}.highlight pre{width:100%}.highlight .hll{background-color:#eff}.highlight .c{color:#b2ccd6}.highlight .err{color:#f07178}.highlight .k{color:#c792ea}.highlight .l{color:#f78c6c}.highlight .n{color:#eff}.highlight .o{color:#89ddff}.highlight .p{color:#eff}.highlight .cm{color:#b2ccd6}.highlight .cp{color:#b2ccd6}.highlight .c1{color:#b2ccd6}.highlight .cs{color:#b2ccd6}.highlight .gd{color:#f07178}.highlight .ge{font-style:italic}.highlight .gh{color:#eff;font-weight:bold}.highlight .gi{color:#c3e88d}.highlight .gp{color:#b2ccd6;font-weight:bold}.highlight .gs{font-weight:bold}.highlight .gu{color:#89ddff;font-weight:bold}.highlight .kc{color:#c792ea}.highlight .kd{color:#c792ea}.highlight .kn{color:#89ddff}.highlight .kp{color:#c792ea}.highlight .kr{color:#c792ea}.highlight .kt{color:#ffcb6b}.highlight .ld{color:#c3e88d}.highlight .m{color:#f78c6c}.highlight .s{color:#c3e88d}.highlight .na{color:#82aaff}.highlight .nb{color:#eff}.highlight .nc{color:#ffcb6b}.highlight .no{color:#f07178}.highlight .nd{color:#89ddff}.highlight .ni{color:#eff}.highlight .ne{color:#f07178}.highlight .nf{color:#82aaff}.highlight .nl{color:#eff}.highlight .nn{color:#ffcb6b}.highlight .nx{color:#82aaff}.highlight .py{color:#eff}.highlight .nt{color:#89ddff}.highlight .nv{color:#f07178}.highlight .ow{color:#89ddff}.highlight .w{color:#eff}.highlight .mf{color:#f78c6c}.highlight .mh{color:#f78c6c}.highlight .mi{color:#f78c6c}.highlight .mo{color:#f78c6c}.highlight .sb{color:#c3e88d}.highlight .sc{color:#eff}.highlight .sd{color:#b2ccd6}.highlight .s2{color:#c3e88d}.highlight .se{color:#f78c6c}.highlight .sh{color:#c3e88d}.highlight .si{color:#f78c6c}.highlight .sx{color:#c3e88d}.highlight .sr{color:#c3e88d}.highlight .s1{color:#c3e88d}.highlight .ss{color:#c3e88d}.highlight .bp{color:#eff}.highlight .vc{color:#f07178}.highlight .vg{color:#f07178}.highlight .vi{color:#f07178}.highlight .il{color:#f78c6c}.gist th,.gist td{border-bottom:0}.hidden,.is--hidden{display:none;visibility:hidden}.load{display:none}.transparent{opacity:0}.visually-hidden,.screen-reader-text,.screen-reader-text span,.screen-reader-shortcut{position:absolute !important;clip:rect(1px, 1px, 1px, 1px);height:1px !important;width:1px !important;border:0 !important;overflow:hidden}body:hover .visually-hidden a,body:hover .visually-hidden input,body:hover .visually-hidden button{display:none !important}.screen-reader-text:focus,.screen-reader-shortcut:focus{clip:auto !important;height:auto !important;width:auto !important;display:block;font-size:1em;font-weight:bold;padding:15px 23px 14px;background:#fff;z-index:100000;text-decoration:none;box-shadow:0 0 2px 2px rgba(0,0,0,0.6)}.skip-link{position:fixed;z-index:20;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;white-space:nowrap}.skip-link li{height:0;width:0;list-style:none}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.task-list{padding:0}.task-list li{list-style-type:none}.task-list .task-list-item-checkbox{margin-right:0.5em;opacity:1}.task-list .task-list{margin-left:1em}.cf{clear:both}.wrapper{margin-left:auto;margin-right:auto;width:100%}.align-left{display:block;margin-left:auto;margin-right:auto}@media (min-width: 37.5em){.align-left{float:left;margin-right:1em}}.align-right{display:block;margin-left:auto;margin-right:auto}@media (min-width: 37.5em){.align-right{float:right;margin-left:1em}}.align-center{display:block;margin-left:auto;margin-right:auto}@media (min-width: 64em){.full{margin-right:-20.3389830508% !important}}.icon{display:inline-block;fill:currentColor;width:1em;height:1.1em;line-height:1;position:relative;top:-0.1em;vertical-align:middle}.social-icons .fas,.social-icons .fab,.social-icons .far,.social-icons .fal{color:#eaeaea}.social-icons .fa-behance,.social-icons .fa-behance-square{color:#1769ff}.social-icons .fa-bitbucket{color:#205081}.social-icons .fa-dribbble,.social-icons .fa-dribble-square{color:#ea4c89}.social-icons .fa-facebook,.social-icons .fa-facebook-square,.social-icons .fa-facebook-f{color:#3b5998}.social-icons .fa-flickr{color:#ff0084}.social-icons .fa-foursquare{color:#0072b1}.social-icons .fa-github,.social-icons .fa-github-alt,.social-icons .fa-github-square{color:#171516}.social-icons .fa-gitlab{color:#e24329}.social-icons .fa-instagram{color:#517fa4}.social-icons .fa-keybase{color:#ef7639}.social-icons .fa-lastfm,.social-icons .fa-lastfm-square{color:#d51007}.social-icons .fa-linkedin,.social-icons .fa-linkedin-in{color:#007bb6}.social-icons .fa-mastodon,.social-icons .fa-mastodon-square{color:#2b90d9}.social-icons .fa-pinterest,.social-icons .fa-pinterest-p,.social-icons .fa-pinterest-square{color:#cb2027}.social-icons .fa-reddit{color:#ff4500}.social-icons .fa-rss,.social-icons .fa-rss-square{color:#fa9b39}.social-icons .fa-soundcloud{color:#f30}.social-icons .fa-stack-exchange,.social-icons .fa-stack-overflow{color:#fe7a15}.social-icons .fa-tumblr,.social-icons .fa-tumblr-square{color:#32506d}.social-icons .fa-twitter,.social-icons .fa-twitter-square{color:#55acee}.social-icons .fa-vimeo,.social-icons .fa-vimeo-square,.social-icons .fa-vimeo-v{color:#1ab7ea}.social-icons .fa-vine{color:#00bf8f}.social-icons .fa-youtube{color:#b00}.social-icons .fa-xing,.social-icons .fa-xing-square{color:#006567}.navicon{position:relative;width:1.5rem;height:.25rem;background:#fed136;margin:auto;-webkit-transition:0.3s;transition:0.3s}.navicon:before,.navicon:after{content:"";position:absolute;left:0;width:1.5rem;height:.25rem;background:#fed136;-webkit-transition:0.3s;transition:0.3s}.navicon:before{top:-.5rem}.navicon:after{bottom:-.5rem}.close .navicon{background:transparent}.close .navicon:before,.close .navicon:after{-webkit-transform-origin:50% 50%;-ms-transform-origin:50% 50%;transform-origin:50% 50%;top:0;width:1.5rem}.close .navicon:before{-webkit-transform:rotate3d(0, 0, 1, 45deg);transform:rotate3d(0, 0, 1, 45deg)}.close .navicon:after{-webkit-transform:rotate3d(0, 0, 1, -45deg);transform:rotate3d(0, 0, 1, -45deg)}@supports (pointer-events: none){.greedy-nav__toggle:before{content:'';position:fixed;top:0;left:0;width:100%;height:100%;opacity:0;background-color:#252a34;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;pointer-events:none}}.greedy-nav__toggle.close:before{opacity:0.9;-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;pointer-events:auto}.greedy-nav__toggle:hover .navicon,.greedy-nav__toggle:hover .navicon:before,.greedy-nav__toggle:hover .navicon:after{background:#bf9d29}.greedy-nav__toggle:hover.close .navicon{background:transparent}@media (min-width: 64em){.sticky{clear:both;position:-webkit-sticky;position:sticky;top:2em}.sticky::after{clear:both;content:"";display:table}.sticky>*{display:block}}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.show-modal{overflow:hidden;position:relative}.show-modal:before{position:absolute;content:"";top:0;left:0;width:100%;height:100%;z-index:999;background-color:rgba(255,255,255,0.85)}.show-modal .modal{display:block}.modal{display:none;position:fixed;width:300px;top:50%;left:50%;margin-left:-150px;margin-top:-150px;min-height:0;z-index:9999;background:#fff;border:1px solid #51555d;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.125)}.modal__title{margin:0;padding:0.5em 1em}.modal__supporting-text{padding:0 1em 0.5em 1em}.modal__actions{padding:0.5em 1em;border-top:1px solid #51555d}.footnote{color:#9ba1a6;text-decoration:none}.footnotes{color:#9ba1a6}.footnotes ol,.footnotes li,.footnotes p{margin-bottom:0;font-size:.75em}a.reversefootnote{color:#7a8288;text-decoration:none}a.reversefootnote:hover{text-decoration:underline}.required{color:#ee5f5b;font-weight:bold}.gsc-control-cse table,.gsc-control-cse tr,.gsc-control-cse td{border:0}.responsive-video-container{position:relative;margin-bottom:1em;padding-bottom:56.25%;height:0;overflow:hidden;max-width:100%}.responsive-video-container iframe,.responsive-video-container object,.responsive-video-container embed{position:absolute;top:0;left:0;width:100%;height:100%}:-webkit-full-screen-ancestor .masthead,:-webkit-full-screen-ancestor .page__footer{position:static}#main{clear:both;margin-left:auto;margin-right:auto;padding-left:1em;padding-right:1em;-webkit-animation:intro 0.3s both;animation:intro 0.3s both;max-width:100%;-webkit-animation-delay:0.15s;animation-delay:0.15s}#main::after{clear:both;content:"";display:table}@media (min-width: 80em){#main{max-width:1280px}}body{display:-webkit-box;display:-ms-flexbox;display:flex;min-height:100vh;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.initial-content,.search-content{flex:1 0 auto}@media (min-width: 64em){.page{float:right;width:calc(100% - 200px);padding-right:200px}}@media (min-width: 80em){.page{width:calc(100% - 300px);padding-right:300px}}.page .page__inner-wrap{float:left;margin-top:1em;margin-left:0;margin-right:0;width:100%;clear:both}.page .page__inner-wrap .page__content,.page .page__inner-wrap .page__meta,.page .page__inner-wrap .comment__date,.page .page__inner-wrap .page__share{position:relative;float:left;margin-left:0;margin-right:0;width:100%;clear:both}.page__title{margin-top:0;line-height:1}.page__title+.page__meta,.page__title+.comment__date{margin-top:-0.5em}.page__lead{font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:1.25em}.page__content h2{padding-bottom:0.5em;border-bottom:1px solid #51555d}.page__content h1 .header-link,.page__content h2 .header-link,.page__content h3 .header-link,.page__content h4 .header-link,.page__content h5 .header-link,.page__content h6 .header-link{position:relative;left:0.5em;opacity:0;font-size:0.8em;-webkit-transition:opacity 0.2s ease-in-out 0.1s;-moz-transition:opacity 0.2s ease-in-out 0.1s;-o-transition:opacity 0.2s ease-in-out 0.1s;transition:opacity 0.2s ease-in-out 0.1s}.page__content h1:hover .header-link,.page__content h2:hover .header-link,.page__content h3:hover .header-link,.page__content h4:hover .header-link,.page__content h5:hover .header-link,.page__content h6:hover .header-link{opacity:1}.page__content p,.page__content li,.page__content dl{font-size:1em}.page__content p{margin:0 0 1.3em}.page__content a:not(.btn):hover{text-decoration:underline}.page__content a:not(.btn):hover img{box-shadow:0 0 10px rgba(0,0,0,0.25)}.page__content dt{margin-top:1em;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-weight:bold}.page__content dd{margin-left:1em;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em}.page__content .small{font-size:.75em}.page__content blockquote+.small{margin-top:-1.5em;padding-left:1.25rem}.page__hero{position:relative;margin-bottom:2em;clear:both;-webkit-animation:intro 0.3s both;animation:intro 0.3s both;-webkit-animation-delay:0.25s;animation-delay:0.25s}.page__hero::after{clear:both;content:"";display:table}.page__hero--overlay{position:relative;margin-bottom:2em;padding:3em 0;clear:both;background-size:cover;background-repeat:no-repeat;background-position:center;-webkit-animation:intro 0.3s both;animation:intro 0.3s both;-webkit-animation-delay:0.25s;animation-delay:0.25s}.page__hero--overlay::after{clear:both;content:"";display:table}.page__hero--overlay a{color:#fff}.page__hero--overlay .wrapper{padding-left:1em;padding-right:1em}@media (min-width: 80em){.page__hero--overlay .wrapper{max-width:1280px}}.page__hero--overlay .page__title,.page__hero--overlay .page__meta,.page__hero--overlay .comment__date,.page__hero--overlay .page__lead,.page__hero--overlay .btn{color:#fff;text-shadow:1px 1px 4px rgba(0,0,0,0.5)}.page__hero--overlay .page__lead{max-width:768px}.page__hero--overlay .page__title{font-size:1.953em}@media (min-width: 37.5em){.page__hero--overlay .page__title{font-size:2.441em}}.page__hero-image{width:100%;height:auto;-ms-interpolation-mode:bicubic}.page__hero-caption{position:absolute;bottom:0;right:0;margin:0 auto;padding:2px 5px;color:#fff;font-family:Georgia,Times,serif;font-size:.6875em;background:#000;text-align:right;z-index:5;opacity:0.5;border-radius:4px 0 0 0}@media (min-width: 64em){.page__hero-caption{padding:5px 10px}}.page__hero-caption a{color:#fff;text-decoration:none}.page__share{margin-top:2em;padding-top:1em;border-top:1px solid #51555d}@media (max-width: 37.5em){.page__share .btn span{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}}.page__share-title{margin-bottom:10px;font-size:.75em;text-transform:uppercase}.page__meta,.comment__date{margin-top:2em;color:#eaeaea;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em}.page__meta p,.comment__date p{margin:0}.page__meta a,.comment__date a{color:inherit}.page__meta-title{margin-bottom:10px;font-size:.75em;text-transform:uppercase}.page__meta-sep::before{content:"\2022";padding-left:0.5em;padding-right:0.5em}.page__taxonomy .sep{display:none}.page__taxonomy strong{margin-right:10px}.page__taxonomy-item{display:inline-block;margin-right:5px;margin-bottom:8px;padding:5px 10px;text-decoration:none;border:1px solid #3d4046;border-radius:4px}.page__taxonomy-item:hover{text-decoration:none;color:#8bc34a}.taxonomy__section{margin-bottom:2em;padding-bottom:1em}.taxonomy__section:not(:last-child){border-bottom:solid 1px #51555d}.taxonomy__section .archive__item-title{margin-top:0}.taxonomy__section .archive__subtitle{clear:both;border:0}.taxonomy__section+.taxonomy__section{margin-top:2em}.taxonomy__title{margin-bottom:0.5em;color:#eaeaea}.taxonomy__count{color:#eaeaea}.taxonomy__index{display:grid;grid-column-gap:2em;grid-template-columns:repeat(2, 1fr);margin:1.414em 0;padding:0;font-size:0.75em;list-style:none}@media (min-width: 64em){.taxonomy__index{grid-template-columns:repeat(3, 1fr)}}.taxonomy__index a{display:-webkit-box;display:-ms-flexbox;display:flex;padding:0.25em 0;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;color:inherit;text-decoration:none;border-bottom:1px solid #51555d}.back-to-top{display:block;clear:both;color:#eaeaea;font-size:0.6em;text-transform:uppercase;text-align:right;text-decoration:none}.page__comments{float:left;margin-left:0;margin-right:0;width:100%;clear:both}.page__comments-title{margin-top:2rem;margin-bottom:10px;padding-top:2rem;font-size:.75em;border-top:1px solid #51555d;text-transform:uppercase}.page__comments-form{-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.page__comments-form.disabled input,.page__comments-form.disabled button,.page__comments-form.disabled textarea,.page__comments-form.disabled label{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);box-shadow:none;opacity:0.65}.comment{clear:both;margin:1em 0}.comment::after{clear:both;content:"";display:table}.comment:not(:last-child){border-bottom:1px solid #51555d}.comment__avatar-wrapper{float:left;width:60px;height:60px}@media (min-width: 64em){.comment__avatar-wrapper{width:100px;height:100px}}.comment__avatar{width:40px;height:40px;border-radius:50%}@media (min-width: 64em){.comment__avatar{width:80px;height:80px;padding:5px;border:1px solid #51555d}}.comment__content-wrapper{float:right;width:calc(100% - 60px)}@media (min-width: 64em){.comment__content-wrapper{width:calc(100% - 100px)}}.comment__author{margin:0}.comment__author a{text-decoration:none}.comment__date{margin:0}.comment__date a{text-decoration:none}.page__related{clear:both;float:left;margin-top:2em;padding-top:1em;border-top:1px solid #51555d}.page__related::after{clear:both;content:"";display:table}@media (min-width: 64em){.page__related{float:right;width:calc(100% - 200px)}}@media (min-width: 80em){.page__related{width:calc(100% - 300px)}}.page__related a{color:inherit;text-decoration:none}.page__related-title{margin-bottom:10px;font-size:.75em;text-transform:uppercase}@media (min-width: 64em){.wide .page{padding-right:0}}@media (min-width: 80em){.wide .page{padding-right:0}}@media (min-width: 64em){.wide .page__related{padding-right:0}}@media (min-width: 80em){.wide .page__related{padding-right:0}}.archive{margin-top:1em;margin-bottom:2em}@media (min-width: 64em){.archive{float:right;width:calc(100% - 200px);padding-right:200px}}@media (min-width: 80em){.archive{width:calc(100% - 300px);padding-right:300px}}.archive__item{position:relative}.archive__item a{position:relative;z-index:10}.archive__item a[rel="permalink"]{position:static}.archive__subtitle{margin:1.414em 0 0.5em;padding-bottom:0.5em;font-size:1em;color:#eaeaea;border-bottom:1px solid #51555d}.archive__subtitle+.list__item .archive__item-title{margin-top:0.5em}.archive__item-title{margin-bottom:0.25em;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;line-height:initial;overflow:hidden;text-overflow:ellipsis}.archive__item-title a[rel="permalink"]::before{content:'';position:absolute;left:0;top:0;right:0;bottom:0}.archive__item-title a+a{opacity:0.5}.page__content .archive__item-title{margin-top:1em;border-bottom:none}.archive__item-excerpt{margin-top:0;font-size:.75em}.archive__item-excerpt+p{text-indent:0}.archive__item-excerpt a{position:relative}.archive__item-teaser{position:relative;border-radius:4px;overflow:hidden}.archive__item-teaser img{width:100%}.archive__item-caption{position:absolute;bottom:0;right:0;margin:0 auto;padding:2px 5px;color:#fff;font-family:Georgia,Times,serif;font-size:.625em;background:#000;text-align:right;z-index:5;opacity:0.5;border-radius:4px 0 0 0}@media (min-width: 64em){.archive__item-caption{padding:5px 10px}}.archive__item-caption a{color:#fff;text-decoration:none}.list__item .page__meta,.list__item .comment__date{margin:0 0 4px;font-size:0.6em}@media (min-width: 64em){.archive .grid__wrapper{margin-right:-200px}}@media (min-width: 80em){.archive .grid__wrapper{margin-right:-300px}}.grid__item{margin-bottom:2em}@media (min-width: 37.5em){.grid__item{float:left;width:48.9795918367%}.grid__item:nth-child(2n+1){clear:both;margin-left:0}.grid__item:nth-child(2n+2){clear:none;margin-left:2.0408163265%}}@media (min-width: 48em){.grid__item{margin-left:0;margin-right:0;width:23.7288135593%}.grid__item:nth-child(2n+1){clear:none}.grid__item:nth-child(4n+1){clear:both}.grid__item:nth-child(4n+2){clear:none;margin-left:1.6949152542%}.grid__item:nth-child(4n+3){clear:none;margin-left:1.6949152542%}.grid__item:nth-child(4n+4){clear:none;margin-left:1.6949152542%}}.grid__item .page__meta,.grid__item .comment__date{margin:0 0 4px;font-size:0.6em}.grid__item .page__meta-sep{display:block}.grid__item .page__meta-sep::before{display:none}.grid__item .archive__item-title{margin-top:0.5em;font-size:1em}.grid__item .archive__item-excerpt{display:none}@media (min-width: 48em){.grid__item .archive__item-excerpt{display:block;font-size:.75em}}@media (min-width: 37.5em){.grid__item .archive__item-teaser{max-height:200px}}@media (min-width: 48em){.grid__item .archive__item-teaser{max-height:120px}}.feature__wrapper{clear:both;margin-bottom:2em;border-bottom:1px solid #51555d}.feature__wrapper::after{clear:both;content:"";display:table}.feature__wrapper .archive__item-title{margin-bottom:0}.feature__item{position:relative;margin-bottom:2em;font-size:1.125em}@media (min-width: 37.5em){.feature__item{float:left;margin-bottom:0;width:32.2033898305%}.feature__item:nth-child(3n+1){clear:both;margin-left:0}.feature__item:nth-child(3n+2){clear:none;margin-left:1.6949152542%}.feature__item:nth-child(3n+3){clear:none;margin-left:1.6949152542%}.feature__item .feature__item-teaser{max-height:200px;overflow:hidden}}.feature__item .archive__item-body{padding-left:1.6949152542%;padding-right:1.6949152542%}.feature__item a.btn::before{content:'';position:absolute;left:0;top:0;right:0;bottom:0}.feature__item--left{position:relative;float:left;margin-left:0;margin-right:0;width:100%;clear:both;font-size:1.125em}.feature__item--left .archive__item{float:left}.feature__item--left .archive__item-teaser{margin-bottom:2em}.feature__item--left a.btn::before{content:'';position:absolute;left:0;top:0;right:0;bottom:0}@media (min-width: 37.5em){.feature__item--left .archive__item-teaser{float:left;width:40.6779661017%}.feature__item--left .archive__item-body{float:right;padding-left:1.6949152542%;padding-right:1.6949152542%;width:57.6271186441%}}.feature__item--right{position:relative;float:left;margin-left:0;margin-right:0;width:100%;clear:both;font-size:1.125em}.feature__item--right .archive__item{float:left}.feature__item--right .archive__item-teaser{margin-bottom:2em}.feature__item--right a.btn::before{content:'';position:absolute;left:0;top:0;right:0;bottom:0}@media (min-width: 37.5em){.feature__item--right{text-align:right}.feature__item--right .archive__item-teaser{float:right;width:40.6779661017%}.feature__item--right .archive__item-body{float:left;width:57.6271186441%;padding-left:1.6949152542%;padding-right:1.6949152542%}}.feature__item--center{position:relative;float:left;margin-left:0;margin-right:0;width:100%;clear:both;font-size:1.125em}.feature__item--center .archive__item{float:left;width:100%}.feature__item--center .archive__item-teaser{margin-bottom:2em}.feature__item--center a.btn::before{content:'';position:absolute;left:0;top:0;right:0;bottom:0}@media (min-width: 37.5em){.feature__item--center{text-align:center}.feature__item--center .archive__item-teaser{margin:0 auto;width:40.6779661017%}.feature__item--center .archive__item-body{margin:0 auto;width:57.6271186441%}}.archive .feature__wrapper .archive__item-title{margin-top:0.25em;font-size:1em}.archive .feature__item,.archive .feature__item--left,.archive .feature__item--center,.archive .feature__item--right{font-size:1em}@media (min-width: 64em){.wide .archive{padding-right:0}}@media (min-width: 80em){.wide .archive{padding-right:0}}.layout--single .feature__wrapper{display:inline-block}.sidebar{clear:both}.sidebar::after{clear:both;content:"";display:table}@media (min-width: 64em){.sidebar{float:left;width:calc(200px - 1em);opacity:0.75;-webkit-transition:opacity 0.2s ease-in-out;transition:opacity 0.2s ease-in-out}.sidebar:hover{opacity:1}.sidebar.sticky{overflow-y:auto;max-height:calc(100vh - 2em - 2em)}}@media (min-width: 80em){.sidebar{width:calc(300px - 1em)}}.sidebar>*{margin-top:1em;margin-bottom:1em}.sidebar h2,.sidebar h3,.sidebar h4,.sidebar h5,.sidebar h6{margin-bottom:0;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif}.sidebar p,.sidebar li{font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:.75em;line-height:1.5}.sidebar img{width:100%}.sidebar img.emoji{width:20px;height:20px}.sidebar__right{margin-bottom:1em}@media (min-width: 64em){.sidebar__right{position:absolute;top:0;right:0;width:200px;margin-right:-200px;padding-left:1em;z-index:10}.sidebar__right.sticky{clear:both;position:-webkit-sticky;position:sticky;top:2em;float:right}.sidebar__right.sticky::after{clear:both;content:"";display:table}}@media (min-width: 80em){.sidebar__right{width:300px;margin-right:-300px}}@media (min-width: 64em){.splash .sidebar__right{position:relative;float:right;margin-right:0}}@media (min-width: 80em){.splash .sidebar__right{margin-right:0}}.author__avatar{display:table-cell;vertical-align:top;width:36px;height:36px}@media (min-width: 64em){.author__avatar{display:block;width:auto;height:auto}}.author__avatar img{max-width:110px;border-radius:50%}@media (min-width: 64em){.author__avatar img{padding:5px;border:1px solid #51555d}}.author__content{display:table-cell;vertical-align:top;padding-left:15px;padding-right:25px;line-height:1}@media (min-width: 64em){.author__content{display:block;width:100%;padding-left:0;padding-right:0}}.author__content a{color:inherit;text-decoration:none}.author__name{margin:0}@media (min-width: 64em){.author__name{margin-top:10px;margin-bottom:10px}}.sidebar .author__name{font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;font-size:1em}.author__bio{margin:0}@media (min-width: 64em){.author__bio{margin-top:10px;margin-bottom:20px}}.author__urls-wrapper{position:relative;display:table-cell;vertical-align:middle;font-family:-apple-system,BlinkMacSystemFont,"Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Arial,sans-serif;z-index:20;cursor:pointer}.author__urls-wrapper li:last-child a{margin-bottom:0}.author__urls-wrapper .author__urls span.label{padding-left:5px}@media (min-width: 64em){.author__urls-wrapper{display:block}}.author__urls-wrapper button{position:relative;margin-bottom:0}@supports (pointer-events: none){.author__urls-wrapper button:before{content:'';position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none}}.author__urls-wrapper button.open:before{pointer-events:auto}@media (min-width: 64em){.author__urls-wrapper button{display:none}}.author__urls{display:none;position:absolute;right:0;margin-top:15px;padding:10px;list-style-type:none;border:1px solid #51555d;border-radius:4px;background:#252a34;box-shadow:0 2px 4px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);cursor:default}.author__urls.is--visible{display:block}@media (min-width: 64em){.author__urls{display:block;position:relative;margin:0;padding:0;border:0;background:transparent;box-shadow:none}}.author__urls:before{display:block;content:"";position:absolute;top:-11px;left:calc(50% - 10px);width:0;border-style:solid;border-width:0 10px 10px;border-color:#51555d transparent;z-index:0}@media (min-width: 64em){.author__urls:before{display:none}}.author__urls:after{display:block;content:"";position:absolute;top:-10px;left:calc(50% - 10px);width:0;border-style:solid;border-width:0 10px 10px;border-color:#252a34 transparent;z-index:1}@media (min-width: 64em){.author__urls:after{display:none}}.author__urls ul{padding:10px;list-style-type:none}.author__urls li{white-space:nowrap}.author__urls a{display:block;margin-bottom:5px;padding-right:5px;padding-top:2px;padding-bottom:2px;color:inherit;font-size:1em;text-decoration:none}.author__urls a:hover{text-decoration:underline}.wide .sidebar__right{margin-bottom:1em}@media (min-width: 64em){.wide .sidebar__right{position:initial;top:initial;right:initial;width:initial;margin-right:initial;padding-left:initial;z-index:initial}.wide .sidebar__right.sticky{float:none}}@media (min-width: 80em){.wide .sidebar__right{width:initial;margin-right:initial}}@media print{[hidden]{display:none}*{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}html{margin:0;padding:0;min-height:auto !important;font-size:16px}body{margin:0 auto;background:#fff !important;color:#000 !important;font-size:1rem;line-height:1.5;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}h1,h2,h3,h4,h5,h6{color:#000;line-height:1.2;margin-bottom:0.75rem;margin-top:0}h1{font-size:2.5rem}h2{font-size:2rem}h3{font-size:1.75rem}h4{font-size:1.5rem}h5{font-size:1.25rem}h6{font-size:1rem}a,a:visited{color:#000;text-decoration:underline;word-wrap:break-word}table{border-collapse:collapse}thead{display:table-header-group}table,th,td{border-bottom:1px solid #000}td,th{padding:8px 16px}img{border:0;display:block;max-width:100% !important;vertical-align:middle}hr{border:0;border-bottom:2px solid #bbb;height:0;margin:2.25rem 0;padding:0}dt{font-weight:bold}dd{margin:0;margin-bottom:0.75rem}abbr[title],acronym[title]{border:0;text-decoration:none}table,blockquote,pre,code,figure,li,hr,ul,ol,a,tr{page-break-inside:avoid}h2,h3,h4,p,a{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid;page-break-inside:avoid}h1+p,h2+p,h3+p{page-break-before:avoid}img{page-break-after:auto;page-break-before:auto;page-break-inside:avoid}pre{white-space:pre-wrap !important;word-wrap:break-word}a[href^='http://']:after,a[href^='https://']:after,a[href^='ftp://']:after{content:" (" attr(href) ")";font-size:80%}abbr[title]:after,acronym[title]:after{content:" (" attr(title) ")"}#main{max-width:100%}.page{margin:0;padding:0;width:100%}.page-break,.page-break-before{page-break-before:always}.page-break-after{page-break-after:always}.no-print{display:none}a.no-reformat:after{content:''}abbr[title].no-reformat:after,acronym[title].no-reformat:after{content:''}.page__hero-caption{color:#000 !important;background:#fff !important;opacity:1}.page__hero-caption a{color:#000 !important}.masthead,.toc,.page__share,.page__related,.pagination,.ads,.page__footer,.page__comments-form,.author__avatar,.author__content,.author__urls-wrapper,.nav__list,.sidebar,.adsbygoogle{display:none !important;height:1px !important}}#main{max-width:100%;font-size:80%}.notice a{color:#399539}.notice code{background-color:#343942;color:white}.notice--primary code{background-color:#3b3b34;color:yellow}.notice--info code{background-color:#273541;color:white}.notice--warning code{background-color:#3b312f;color:yellow}.notice--danger code{background-color:#392f38;color:white}.notice--success code{background-color:#283635;color:white}#scroll-to-top{display:block;position:fixed;font-size:21px;bottom:25px;right:10%;opacity:0;z-index:99;text-align:center;transform:translateY(30%);transition:transform 0.5s ease-in-out, opacity 0.5s ease-in-out}@media (max-width: 600px){#scroll-to-top{font-size:20px}}@media (max-width: 600px){#scroll-to-top{right:5%;bottom:15px}}#scroll-to-top :last-child{transition:transform 0.7s ease-in-out}#scroll-to-top :last-child:active,#scroll-to-top :last-child:hover{transform:translateY(-10%)}#scroll-to-top span{cursor:pointer;color:#1a1d24}#scroll-to-top span:hover .up-arrow,#scroll-to-top span:active .up-arrow{color:#00adb5} diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..2440b2e --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1 @@ +/*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}template,[hidden]{display:none !important}a{background-color:transparent}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}button,input,select,textarea{font:inherit;margin:0}optgroup{font-weight:bold}button,input{overflow:visible}button,select{text-transform:none}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:0.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}*{box-sizing:border-box}input,select,textarea,button{font-family:inherit;font-size:inherit;line-height:inherit}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:14px;line-height:1.5;color:#24292e;background-color:#fff}a{color:#0366d6;text-decoration:none}a:hover{text-decoration:underline}b,strong{font-weight:600}hr,.rule{height:0;margin:15px 0;overflow:hidden;background:transparent;border:0;border-bottom:1px solid #dfe2e5}hr::before,.rule::before{display:table;content:""}hr::after,.rule::after{display:table;clear:both;content:""}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}button{cursor:pointer;border-radius:0}[hidden][hidden]{display:none !important}details summary{cursor:pointer}details:not([open])>*:not(summary){display:none !important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:0}h1{font-size:32px;font-weight:600}h2{font-size:24px;font-weight:600}h3{font-size:20px;font-weight:600}h4{font-size:16px;font-weight:600}h5{font-size:14px;font-weight:600}h6{font-size:12px;font-weight:600}p{margin-top:0;margin-bottom:10px}small{font-size:90%}blockquote{margin:0}ul,ol{padding-left:0;margin-top:0;margin-bottom:0}ol ol,ul ol{list-style-type:lower-roman}ul ul ol,ul ol ol,ol ul ol,ol ol ol{list-style-type:lower-alpha}dd{margin-left:0}tt,code{font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:12px}pre{margin-top:0;margin-bottom:0;font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:12px}.octicon{vertical-align:text-bottom}.anim-fade-in{animation-name:fade-in;animation-duration:1s;animation-timing-function:ease-in-out}.anim-fade-in.fast{animation-duration:300ms}@keyframes fade-in{0%{opacity:0}100%{opacity:1}}.anim-fade-out{animation-name:fade-out;animation-duration:1s;animation-timing-function:ease-out}.anim-fade-out.fast{animation-duration:0.3s}@keyframes fade-out{0%{opacity:1}100%{opacity:0}}.anim-fade-up{opacity:0;animation-name:fade-up;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease-out;animation-delay:1s}@keyframes fade-up{0%{opacity:0.8;transform:translateY(100%)}100%{opacity:1;transform:translateY(0)}}.anim-fade-down{animation-name:fade-down;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease-in}@keyframes fade-down{0%{opacity:1;transform:translateY(0)}100%{opacity:0.5;transform:translateY(100%)}}.anim-grow-x{width:0%;animation-name:grow-x;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease;animation-delay:0.5s}@keyframes grow-x{to{width:100%}}.anim-shrink-x{animation-name:shrink-x;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease-in-out;animation-delay:0.5s}@keyframes shrink-x{to{width:0%}}.anim-scale-in{animation-name:scale-in;animation-duration:0.15s;animation-timing-function:cubic-bezier(0.2, 0, 0.13, 1.5)}@keyframes scale-in{0%{opacity:0;transform:scale(0.5)}100%{opacity:1;transform:scale(1)}}.anim-pulse{animation-name:pulse;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}@keyframes pulse{0%{opacity:0.3}10%{opacity:1}100%{opacity:0.3}}.anim-pulse-in{animation-name:pulse-in;animation-duration:0.5s}@keyframes pulse-in{0%{transform:scale3d(1, 1, 1)}50%{transform:scale3d(1.1, 1.1, 1.1)}100%{transform:scale3d(1, 1, 1)}}.hover-grow{transition:transform 0.3s;backface-visibility:hidden}.hover-grow:hover{transform:scale(1.025)}.border{border:1px #e1e4e8 solid !important}.border-y{border-top:1px #e1e4e8 solid !important;border-bottom:1px #e1e4e8 solid !important}.border-0{border:0 !important}.border-dashed{border-style:dashed !important}.border-blue{border-color:#0366d6 !important}.border-blue-light{border-color:#c8e1ff !important}.border-green{border-color:#34d058 !important}.border-green-light{border-color:#a2cbac !important}.border-red{border-color:#d73a49 !important}.border-red-light{border-color:#cea0a5 !important}.border-purple{border-color:#6f42c1 !important}.border-yellow{border-color:#d9d0a5 !important}.border-gray-light{border-color:#eaecef !important}.border-gray-dark{border-color:#d1d5da !important}.border-black-fade{border-color:rgba(27,31,35,0.15) !important}.border-top{border-top:1px #e1e4e8 solid !important}.border-right{border-right:1px #e1e4e8 solid !important}.border-bottom{border-bottom:1px #e1e4e8 solid !important}.border-left{border-left:1px #e1e4e8 solid !important}.border-top-0{border-top:0 !important}.border-right-0{border-right:0 !important}.border-bottom-0{border-bottom:0 !important}.border-left-0{border-left:0 !important}.rounded-0{border-radius:0 !important}.rounded-1{border-radius:3px !important}.rounded-2{border-radius:6px !important}.rounded-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}@media (min-width: 544px){.border-sm-top{border-top:1px #e1e4e8 solid !important}.border-sm-right{border-right:1px #e1e4e8 solid !important}.border-sm-bottom{border-bottom:1px #e1e4e8 solid !important}.border-sm-left{border-left:1px #e1e4e8 solid !important}.border-sm-top-0{border-top:0 !important}.border-sm-right-0{border-right:0 !important}.border-sm-bottom-0{border-bottom:0 !important}.border-sm-left-0{border-left:0 !important}.rounded-sm-0{border-radius:0 !important}.rounded-sm-1{border-radius:3px !important}.rounded-sm-2{border-radius:6px !important}.rounded-sm-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-sm-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-sm-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-sm-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-sm-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-sm-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-sm-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-sm-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-sm-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-sm-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-sm-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-sm-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}@media (min-width: 768px){.border-md-top{border-top:1px #e1e4e8 solid !important}.border-md-right{border-right:1px #e1e4e8 solid !important}.border-md-bottom{border-bottom:1px #e1e4e8 solid !important}.border-md-left{border-left:1px #e1e4e8 solid !important}.border-md-top-0{border-top:0 !important}.border-md-right-0{border-right:0 !important}.border-md-bottom-0{border-bottom:0 !important}.border-md-left-0{border-left:0 !important}.rounded-md-0{border-radius:0 !important}.rounded-md-1{border-radius:3px !important}.rounded-md-2{border-radius:6px !important}.rounded-md-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-md-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-md-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-md-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-md-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-md-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-md-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-md-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-md-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-md-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-md-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-md-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}@media (min-width: 1012px){.border-lg-top{border-top:1px #e1e4e8 solid !important}.border-lg-right{border-right:1px #e1e4e8 solid !important}.border-lg-bottom{border-bottom:1px #e1e4e8 solid !important}.border-lg-left{border-left:1px #e1e4e8 solid !important}.border-lg-top-0{border-top:0 !important}.border-lg-right-0{border-right:0 !important}.border-lg-bottom-0{border-bottom:0 !important}.border-lg-left-0{border-left:0 !important}.rounded-lg-0{border-radius:0 !important}.rounded-lg-1{border-radius:3px !important}.rounded-lg-2{border-radius:6px !important}.rounded-lg-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-lg-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-lg-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-lg-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-lg-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-lg-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-lg-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-lg-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-lg-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-lg-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-lg-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-lg-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}@media (min-width: 1280px){.border-xl-top{border-top:1px #e1e4e8 solid !important}.border-xl-right{border-right:1px #e1e4e8 solid !important}.border-xl-bottom{border-bottom:1px #e1e4e8 solid !important}.border-xl-left{border-left:1px #e1e4e8 solid !important}.border-xl-top-0{border-top:0 !important}.border-xl-right-0{border-right:0 !important}.border-xl-bottom-0{border-bottom:0 !important}.border-xl-left-0{border-left:0 !important}.rounded-xl-0{border-radius:0 !important}.rounded-xl-1{border-radius:3px !important}.rounded-xl-2{border-radius:6px !important}.rounded-xl-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-xl-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-xl-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-xl-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-xl-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-xl-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-xl-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-xl-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-xl-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-xl-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-xl-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-xl-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}.circle{border-radius:50% !important}.box-shadow{box-shadow:0 1px 1px rgba(27,31,35,0.1) !important}.box-shadow-medium{box-shadow:0 1px 5px rgba(27,31,35,0.15) !important}.box-shadow-large{box-shadow:0 1px 15px rgba(27,31,35,0.15) !important}.box-shadow-extra-large{box-shadow:0 10px 50px rgba(27,31,35,0.07) !important}.box-shadow-none{box-shadow:none !important}.bg-white{background-color:#fff !important}.bg-blue{background-color:#0366d6 !important}.bg-blue-light{background-color:#f1f8ff !important}.bg-gray-dark{background-color:#24292e !important}.bg-gray{background-color:#f6f8fa !important}.bg-gray-light{background-color:#fafbfc !important}.bg-green{background-color:#28a745 !important}.bg-green-light{background-color:#dcffe4 !important}.bg-red{background-color:#d73a49 !important}.bg-red-light{background-color:#ffdce0 !important}.bg-yellow{background-color:#ffd33d !important}.bg-yellow-light{background-color:#fff5b1 !important}.bg-purple{background-color:#6f42c1 !important}.bg-purple-light{background-color:#f5f0ff !important}.bg-shade-gradient{background-image:linear-gradient(180deg, rgba(27,31,35,0.065), rgba(27,31,35,0)) !important;background-repeat:no-repeat !important;background-size:100% 200px !important}.text-blue{color:#0366d6 !important}.text-red{color:#cb2431 !important}.text-gray-light{color:#6a737d !important}.text-gray{color:#586069 !important}.text-gray-dark{color:#24292e !important}.text-green{color:#28a745 !important}.text-orange{color:#a04100 !important}.text-orange-light{color:#e36209 !important}.text-purple{color:#6f42c1 !important}.text-white{color:#fff !important}.text-inherit{color:inherit !important}.text-pending{color:#b08800 !important}.bg-pending{color:#dbab09 !important}.link-gray{color:#586069 !important}.link-gray:hover{color:#0366d6 !important}.link-gray-dark{color:#24292e !important}.link-gray-dark:hover{color:#0366d6 !important}.link-hover-blue:hover{color:#0366d6 !important}.muted-link{color:#586069 !important}.muted-link:hover{color:#0366d6 !important;text-decoration:none}.details-overlay[open]>summary::before{position:fixed;top:0;right:0;bottom:0;left:0;z-index:80;display:block;cursor:default;content:" ";background:transparent}.details-overlay-dark[open]>summary::before{z-index:99;background:rgba(27,31,35,0.5)}.flex-row{flex-direction:row !important}.flex-row-reverse{flex-direction:row-reverse !important}.flex-column{flex-direction:column !important}.flex-wrap{flex-wrap:wrap !important}.flex-nowrap{flex-wrap:nowrap !important}.flex-justify-start{justify-content:flex-start !important}.flex-justify-end{justify-content:flex-end !important}.flex-justify-center{justify-content:center !important}.flex-justify-between{justify-content:space-between !important}.flex-justify-around{justify-content:space-around !important}.flex-items-start{align-items:flex-start !important}.flex-items-end{align-items:flex-end !important}.flex-items-center{align-items:center !important}.flex-items-baseline{align-items:baseline !important}.flex-items-stretch{align-items:stretch !important}.flex-content-start{align-content:flex-start !important}.flex-content-end{align-content:flex-end !important}.flex-content-center{align-content:center !important}.flex-content-between{align-content:space-between !important}.flex-content-around{align-content:space-around !important}.flex-content-stretch{align-content:stretch !important}.flex-auto{flex:1 1 auto !important}.flex-shrink-0{flex-shrink:0 !important}.flex-self-auto{align-self:auto !important}.flex-self-start{align-self:flex-start !important}.flex-self-end{align-self:flex-end !important}.flex-self-center{align-self:center !important}.flex-self-baseline{align-self:baseline !important}.flex-self-stretch{align-self:stretch !important}.flex-item-equal{flex-grow:1;flex-basis:0}@media (min-width: 544px){.flex-sm-row{flex-direction:row !important}.flex-sm-row-reverse{flex-direction:row-reverse !important}.flex-sm-column{flex-direction:column !important}.flex-sm-wrap{flex-wrap:wrap !important}.flex-sm-nowrap{flex-wrap:nowrap !important}.flex-sm-justify-start{justify-content:flex-start !important}.flex-sm-justify-end{justify-content:flex-end !important}.flex-sm-justify-center{justify-content:center !important}.flex-sm-justify-between{justify-content:space-between !important}.flex-sm-justify-around{justify-content:space-around !important}.flex-sm-items-start{align-items:flex-start !important}.flex-sm-items-end{align-items:flex-end !important}.flex-sm-items-center{align-items:center !important}.flex-sm-items-baseline{align-items:baseline !important}.flex-sm-items-stretch{align-items:stretch !important}.flex-sm-content-start{align-content:flex-start !important}.flex-sm-content-end{align-content:flex-end !important}.flex-sm-content-center{align-content:center !important}.flex-sm-content-between{align-content:space-between !important}.flex-sm-content-around{align-content:space-around !important}.flex-sm-content-stretch{align-content:stretch !important}.flex-sm-auto{flex:1 1 auto !important}.flex-sm-shrink-0{flex-shrink:0 !important}.flex-sm-self-auto{align-self:auto !important}.flex-sm-self-start{align-self:flex-start !important}.flex-sm-self-end{align-self:flex-end !important}.flex-sm-self-center{align-self:center !important}.flex-sm-self-baseline{align-self:baseline !important}.flex-sm-self-stretch{align-self:stretch !important}.flex-sm-item-equal{flex-grow:1;flex-basis:0}}@media (min-width: 768px){.flex-md-row{flex-direction:row !important}.flex-md-row-reverse{flex-direction:row-reverse !important}.flex-md-column{flex-direction:column !important}.flex-md-wrap{flex-wrap:wrap !important}.flex-md-nowrap{flex-wrap:nowrap !important}.flex-md-justify-start{justify-content:flex-start !important}.flex-md-justify-end{justify-content:flex-end !important}.flex-md-justify-center{justify-content:center !important}.flex-md-justify-between{justify-content:space-between !important}.flex-md-justify-around{justify-content:space-around !important}.flex-md-items-start{align-items:flex-start !important}.flex-md-items-end{align-items:flex-end !important}.flex-md-items-center{align-items:center !important}.flex-md-items-baseline{align-items:baseline !important}.flex-md-items-stretch{align-items:stretch !important}.flex-md-content-start{align-content:flex-start !important}.flex-md-content-end{align-content:flex-end !important}.flex-md-content-center{align-content:center !important}.flex-md-content-between{align-content:space-between !important}.flex-md-content-around{align-content:space-around !important}.flex-md-content-stretch{align-content:stretch !important}.flex-md-auto{flex:1 1 auto !important}.flex-md-shrink-0{flex-shrink:0 !important}.flex-md-self-auto{align-self:auto !important}.flex-md-self-start{align-self:flex-start !important}.flex-md-self-end{align-self:flex-end !important}.flex-md-self-center{align-self:center !important}.flex-md-self-baseline{align-self:baseline !important}.flex-md-self-stretch{align-self:stretch !important}.flex-md-item-equal{flex-grow:1;flex-basis:0}}@media (min-width: 1012px){.flex-lg-row{flex-direction:row !important}.flex-lg-row-reverse{flex-direction:row-reverse !important}.flex-lg-column{flex-direction:column !important}.flex-lg-wrap{flex-wrap:wrap !important}.flex-lg-nowrap{flex-wrap:nowrap !important}.flex-lg-justify-start{justify-content:flex-start !important}.flex-lg-justify-end{justify-content:flex-end !important}.flex-lg-justify-center{justify-content:center !important}.flex-lg-justify-between{justify-content:space-between !important}.flex-lg-justify-around{justify-content:space-around !important}.flex-lg-items-start{align-items:flex-start !important}.flex-lg-items-end{align-items:flex-end !important}.flex-lg-items-center{align-items:center !important}.flex-lg-items-baseline{align-items:baseline !important}.flex-lg-items-stretch{align-items:stretch !important}.flex-lg-content-start{align-content:flex-start !important}.flex-lg-content-end{align-content:flex-end !important}.flex-lg-content-center{align-content:center !important}.flex-lg-content-between{align-content:space-between !important}.flex-lg-content-around{align-content:space-around !important}.flex-lg-content-stretch{align-content:stretch !important}.flex-lg-auto{flex:1 1 auto !important}.flex-lg-shrink-0{flex-shrink:0 !important}.flex-lg-self-auto{align-self:auto !important}.flex-lg-self-start{align-self:flex-start !important}.flex-lg-self-end{align-self:flex-end !important}.flex-lg-self-center{align-self:center !important}.flex-lg-self-baseline{align-self:baseline !important}.flex-lg-self-stretch{align-self:stretch !important}.flex-lg-item-equal{flex-grow:1;flex-basis:0}}@media (min-width: 1280px){.flex-xl-row{flex-direction:row !important}.flex-xl-row-reverse{flex-direction:row-reverse !important}.flex-xl-column{flex-direction:column !important}.flex-xl-wrap{flex-wrap:wrap !important}.flex-xl-nowrap{flex-wrap:nowrap !important}.flex-xl-justify-start{justify-content:flex-start !important}.flex-xl-justify-end{justify-content:flex-end !important}.flex-xl-justify-center{justify-content:center !important}.flex-xl-justify-between{justify-content:space-between !important}.flex-xl-justify-around{justify-content:space-around !important}.flex-xl-items-start{align-items:flex-start !important}.flex-xl-items-end{align-items:flex-end !important}.flex-xl-items-center{align-items:center !important}.flex-xl-items-baseline{align-items:baseline !important}.flex-xl-items-stretch{align-items:stretch !important}.flex-xl-content-start{align-content:flex-start !important}.flex-xl-content-end{align-content:flex-end !important}.flex-xl-content-center{align-content:center !important}.flex-xl-content-between{align-content:space-between !important}.flex-xl-content-around{align-content:space-around !important}.flex-xl-content-stretch{align-content:stretch !important}.flex-xl-auto{flex:1 1 auto !important}.flex-xl-shrink-0{flex-shrink:0 !important}.flex-xl-self-auto{align-self:auto !important}.flex-xl-self-start{align-self:flex-start !important}.flex-xl-self-end{align-self:flex-end !important}.flex-xl-self-center{align-self:center !important}.flex-xl-self-baseline{align-self:baseline !important}.flex-xl-self-stretch{align-self:stretch !important}.flex-xl-item-equal{flex-grow:1;flex-basis:0}}.position-static{position:static !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.top-0{top:0 !important}.right-0{right:0 !important}.bottom-0{bottom:0 !important}.left-0{left:0 !important}.v-align-middle{vertical-align:middle !important}.v-align-top{vertical-align:top !important}.v-align-bottom{vertical-align:bottom !important}.v-align-text-top{vertical-align:text-top !important}.v-align-text-bottom{vertical-align:text-bottom !important}.v-align-baseline{vertical-align:baseline !important}.overflow-hidden{overflow:hidden !important}.overflow-scroll{overflow:scroll !important}.overflow-auto{overflow:auto !important}.clearfix::before{display:table;content:""}.clearfix::after{display:table;clear:both;content:""}.float-left{float:left !important}.float-right{float:right !important}.float-none{float:none !important}@media (min-width: 544px){.float-sm-left{float:left !important}.float-sm-right{float:right !important}.float-sm-none{float:none !important}}@media (min-width: 768px){.float-md-left{float:left !important}.float-md-right{float:right !important}.float-md-none{float:none !important}}@media (min-width: 1012px){.float-lg-left{float:left !important}.float-lg-right{float:right !important}.float-lg-none{float:none !important}}@media (min-width: 1280px){.float-xl-left{float:left !important}.float-xl-right{float:right !important}.float-xl-none{float:none !important}}.width-fit{max-width:100% !important}.width-full{width:100% !important}.height-fit{max-height:100% !important}.height-full{height:100% !important}.min-width-0{min-width:0 !important}.direction-rtl{direction:rtl !important}.direction-ltr{direction:ltr !important}@media (min-width: 544px){.direction-sm-rtl{direction:rtl !important}.direction-sm-ltr{direction:ltr !important}}@media (min-width: 768px){.direction-md-rtl{direction:rtl !important}.direction-md-ltr{direction:ltr !important}}@media (min-width: 1012px){.direction-lg-rtl{direction:rtl !important}.direction-lg-ltr{direction:ltr !important}}@media (min-width: 1280px){.direction-xl-rtl{direction:rtl !important}.direction-xl-ltr{direction:ltr !important}}.m-0{margin:0 !important}.mt-0{margin-top:0 !important}.mr-0{margin-right:0 !important}.mb-0{margin-bottom:0 !important}.ml-0{margin-left:0 !important}.mx-0{margin-right:0 !important;margin-left:0 !important}.my-0{margin-top:0 !important;margin-bottom:0 !important}.m-1{margin:4px !important}.mt-1{margin-top:4px !important}.mr-1{margin-right:4px !important}.mb-1{margin-bottom:4px !important}.ml-1{margin-left:4px !important}.mt-n1{margin-top:-4px !important}.mr-n1{margin-right:-4px !important}.mb-n1{margin-bottom:-4px !important}.ml-n1{margin-left:-4px !important}.mx-1{margin-right:4px !important;margin-left:4px !important}.my-1{margin-top:4px !important;margin-bottom:4px !important}.m-2{margin:8px !important}.mt-2{margin-top:8px !important}.mr-2{margin-right:8px !important}.mb-2{margin-bottom:8px !important}.ml-2{margin-left:8px !important}.mt-n2{margin-top:-8px !important}.mr-n2{margin-right:-8px !important}.mb-n2{margin-bottom:-8px !important}.ml-n2{margin-left:-8px !important}.mx-2{margin-right:8px !important;margin-left:8px !important}.my-2{margin-top:8px !important;margin-bottom:8px !important}.m-3{margin:16px !important}.mt-3{margin-top:16px !important}.mr-3{margin-right:16px !important}.mb-3{margin-bottom:16px !important}.ml-3{margin-left:16px !important}.mt-n3{margin-top:-16px !important}.mr-n3{margin-right:-16px !important}.mb-n3{margin-bottom:-16px !important}.ml-n3{margin-left:-16px !important}.mx-3{margin-right:16px !important;margin-left:16px !important}.my-3{margin-top:16px !important;margin-bottom:16px !important}.m-4{margin:24px !important}.mt-4{margin-top:24px !important}.mr-4{margin-right:24px !important}.mb-4{margin-bottom:24px !important}.ml-4{margin-left:24px !important}.mt-n4{margin-top:-24px !important}.mr-n4{margin-right:-24px !important}.mb-n4{margin-bottom:-24px !important}.ml-n4{margin-left:-24px !important}.mx-4{margin-right:24px !important;margin-left:24px !important}.my-4{margin-top:24px !important;margin-bottom:24px !important}.m-5{margin:32px !important}.mt-5{margin-top:32px !important}.mr-5{margin-right:32px !important}.mb-5{margin-bottom:32px !important}.ml-5{margin-left:32px !important}.mt-n5{margin-top:-32px !important}.mr-n5{margin-right:-32px !important}.mb-n5{margin-bottom:-32px !important}.ml-n5{margin-left:-32px !important}.mx-5{margin-right:32px !important;margin-left:32px !important}.my-5{margin-top:32px !important;margin-bottom:32px !important}.m-6{margin:40px !important}.mt-6{margin-top:40px !important}.mr-6{margin-right:40px !important}.mb-6{margin-bottom:40px !important}.ml-6{margin-left:40px !important}.mt-n6{margin-top:-40px !important}.mr-n6{margin-right:-40px !important}.mb-n6{margin-bottom:-40px !important}.ml-n6{margin-left:-40px !important}.mx-6{margin-right:40px !important;margin-left:40px !important}.my-6{margin-top:40px !important;margin-bottom:40px !important}.mx-auto{margin-right:auto !important;margin-left:auto !important}@media (min-width: 544px){.m-sm-0{margin:0 !important}.mt-sm-0{margin-top:0 !important}.mr-sm-0{margin-right:0 !important}.mb-sm-0{margin-bottom:0 !important}.ml-sm-0{margin-left:0 !important}.mx-sm-0{margin-right:0 !important;margin-left:0 !important}.my-sm-0{margin-top:0 !important;margin-bottom:0 !important}.m-sm-1{margin:4px !important}.mt-sm-1{margin-top:4px !important}.mr-sm-1{margin-right:4px !important}.mb-sm-1{margin-bottom:4px !important}.ml-sm-1{margin-left:4px !important}.mt-sm-n1{margin-top:-4px !important}.mr-sm-n1{margin-right:-4px !important}.mb-sm-n1{margin-bottom:-4px !important}.ml-sm-n1{margin-left:-4px !important}.mx-sm-1{margin-right:4px !important;margin-left:4px !important}.my-sm-1{margin-top:4px !important;margin-bottom:4px !important}.m-sm-2{margin:8px !important}.mt-sm-2{margin-top:8px !important}.mr-sm-2{margin-right:8px !important}.mb-sm-2{margin-bottom:8px !important}.ml-sm-2{margin-left:8px !important}.mt-sm-n2{margin-top:-8px !important}.mr-sm-n2{margin-right:-8px !important}.mb-sm-n2{margin-bottom:-8px !important}.ml-sm-n2{margin-left:-8px !important}.mx-sm-2{margin-right:8px !important;margin-left:8px !important}.my-sm-2{margin-top:8px !important;margin-bottom:8px !important}.m-sm-3{margin:16px !important}.mt-sm-3{margin-top:16px !important}.mr-sm-3{margin-right:16px !important}.mb-sm-3{margin-bottom:16px !important}.ml-sm-3{margin-left:16px !important}.mt-sm-n3{margin-top:-16px !important}.mr-sm-n3{margin-right:-16px !important}.mb-sm-n3{margin-bottom:-16px !important}.ml-sm-n3{margin-left:-16px !important}.mx-sm-3{margin-right:16px !important;margin-left:16px !important}.my-sm-3{margin-top:16px !important;margin-bottom:16px !important}.m-sm-4{margin:24px !important}.mt-sm-4{margin-top:24px !important}.mr-sm-4{margin-right:24px !important}.mb-sm-4{margin-bottom:24px !important}.ml-sm-4{margin-left:24px !important}.mt-sm-n4{margin-top:-24px !important}.mr-sm-n4{margin-right:-24px !important}.mb-sm-n4{margin-bottom:-24px !important}.ml-sm-n4{margin-left:-24px !important}.mx-sm-4{margin-right:24px !important;margin-left:24px !important}.my-sm-4{margin-top:24px !important;margin-bottom:24px !important}.m-sm-5{margin:32px !important}.mt-sm-5{margin-top:32px !important}.mr-sm-5{margin-right:32px !important}.mb-sm-5{margin-bottom:32px !important}.ml-sm-5{margin-left:32px !important}.mt-sm-n5{margin-top:-32px !important}.mr-sm-n5{margin-right:-32px !important}.mb-sm-n5{margin-bottom:-32px !important}.ml-sm-n5{margin-left:-32px !important}.mx-sm-5{margin-right:32px !important;margin-left:32px !important}.my-sm-5{margin-top:32px !important;margin-bottom:32px !important}.m-sm-6{margin:40px !important}.mt-sm-6{margin-top:40px !important}.mr-sm-6{margin-right:40px !important}.mb-sm-6{margin-bottom:40px !important}.ml-sm-6{margin-left:40px !important}.mt-sm-n6{margin-top:-40px !important}.mr-sm-n6{margin-right:-40px !important}.mb-sm-n6{margin-bottom:-40px !important}.ml-sm-n6{margin-left:-40px !important}.mx-sm-6{margin-right:40px !important;margin-left:40px !important}.my-sm-6{margin-top:40px !important;margin-bottom:40px !important}.mx-sm-auto{margin-right:auto !important;margin-left:auto !important}}@media (min-width: 768px){.m-md-0{margin:0 !important}.mt-md-0{margin-top:0 !important}.mr-md-0{margin-right:0 !important}.mb-md-0{margin-bottom:0 !important}.ml-md-0{margin-left:0 !important}.mx-md-0{margin-right:0 !important;margin-left:0 !important}.my-md-0{margin-top:0 !important;margin-bottom:0 !important}.m-md-1{margin:4px !important}.mt-md-1{margin-top:4px !important}.mr-md-1{margin-right:4px !important}.mb-md-1{margin-bottom:4px !important}.ml-md-1{margin-left:4px !important}.mt-md-n1{margin-top:-4px !important}.mr-md-n1{margin-right:-4px !important}.mb-md-n1{margin-bottom:-4px !important}.ml-md-n1{margin-left:-4px !important}.mx-md-1{margin-right:4px !important;margin-left:4px !important}.my-md-1{margin-top:4px !important;margin-bottom:4px !important}.m-md-2{margin:8px !important}.mt-md-2{margin-top:8px !important}.mr-md-2{margin-right:8px !important}.mb-md-2{margin-bottom:8px !important}.ml-md-2{margin-left:8px !important}.mt-md-n2{margin-top:-8px !important}.mr-md-n2{margin-right:-8px !important}.mb-md-n2{margin-bottom:-8px !important}.ml-md-n2{margin-left:-8px !important}.mx-md-2{margin-right:8px !important;margin-left:8px !important}.my-md-2{margin-top:8px !important;margin-bottom:8px !important}.m-md-3{margin:16px !important}.mt-md-3{margin-top:16px !important}.mr-md-3{margin-right:16px !important}.mb-md-3{margin-bottom:16px !important}.ml-md-3{margin-left:16px !important}.mt-md-n3{margin-top:-16px !important}.mr-md-n3{margin-right:-16px !important}.mb-md-n3{margin-bottom:-16px !important}.ml-md-n3{margin-left:-16px !important}.mx-md-3{margin-right:16px !important;margin-left:16px !important}.my-md-3{margin-top:16px !important;margin-bottom:16px !important}.m-md-4{margin:24px !important}.mt-md-4{margin-top:24px !important}.mr-md-4{margin-right:24px !important}.mb-md-4{margin-bottom:24px !important}.ml-md-4{margin-left:24px !important}.mt-md-n4{margin-top:-24px !important}.mr-md-n4{margin-right:-24px !important}.mb-md-n4{margin-bottom:-24px !important}.ml-md-n4{margin-left:-24px !important}.mx-md-4{margin-right:24px !important;margin-left:24px !important}.my-md-4{margin-top:24px !important;margin-bottom:24px !important}.m-md-5{margin:32px !important}.mt-md-5{margin-top:32px !important}.mr-md-5{margin-right:32px !important}.mb-md-5{margin-bottom:32px !important}.ml-md-5{margin-left:32px !important}.mt-md-n5{margin-top:-32px !important}.mr-md-n5{margin-right:-32px !important}.mb-md-n5{margin-bottom:-32px !important}.ml-md-n5{margin-left:-32px !important}.mx-md-5{margin-right:32px !important;margin-left:32px !important}.my-md-5{margin-top:32px !important;margin-bottom:32px !important}.m-md-6{margin:40px !important}.mt-md-6{margin-top:40px !important}.mr-md-6{margin-right:40px !important}.mb-md-6{margin-bottom:40px !important}.ml-md-6{margin-left:40px !important}.mt-md-n6{margin-top:-40px !important}.mr-md-n6{margin-right:-40px !important}.mb-md-n6{margin-bottom:-40px !important}.ml-md-n6{margin-left:-40px !important}.mx-md-6{margin-right:40px !important;margin-left:40px !important}.my-md-6{margin-top:40px !important;margin-bottom:40px !important}.mx-md-auto{margin-right:auto !important;margin-left:auto !important}}@media (min-width: 1012px){.m-lg-0{margin:0 !important}.mt-lg-0{margin-top:0 !important}.mr-lg-0{margin-right:0 !important}.mb-lg-0{margin-bottom:0 !important}.ml-lg-0{margin-left:0 !important}.mx-lg-0{margin-right:0 !important;margin-left:0 !important}.my-lg-0{margin-top:0 !important;margin-bottom:0 !important}.m-lg-1{margin:4px !important}.mt-lg-1{margin-top:4px !important}.mr-lg-1{margin-right:4px !important}.mb-lg-1{margin-bottom:4px !important}.ml-lg-1{margin-left:4px !important}.mt-lg-n1{margin-top:-4px !important}.mr-lg-n1{margin-right:-4px !important}.mb-lg-n1{margin-bottom:-4px !important}.ml-lg-n1{margin-left:-4px !important}.mx-lg-1{margin-right:4px !important;margin-left:4px !important}.my-lg-1{margin-top:4px !important;margin-bottom:4px !important}.m-lg-2{margin:8px !important}.mt-lg-2{margin-top:8px !important}.mr-lg-2{margin-right:8px !important}.mb-lg-2{margin-bottom:8px !important}.ml-lg-2{margin-left:8px !important}.mt-lg-n2{margin-top:-8px !important}.mr-lg-n2{margin-right:-8px !important}.mb-lg-n2{margin-bottom:-8px !important}.ml-lg-n2{margin-left:-8px !important}.mx-lg-2{margin-right:8px !important;margin-left:8px !important}.my-lg-2{margin-top:8px !important;margin-bottom:8px !important}.m-lg-3{margin:16px !important}.mt-lg-3{margin-top:16px !important}.mr-lg-3{margin-right:16px !important}.mb-lg-3{margin-bottom:16px !important}.ml-lg-3{margin-left:16px !important}.mt-lg-n3{margin-top:-16px !important}.mr-lg-n3{margin-right:-16px !important}.mb-lg-n3{margin-bottom:-16px !important}.ml-lg-n3{margin-left:-16px !important}.mx-lg-3{margin-right:16px !important;margin-left:16px !important}.my-lg-3{margin-top:16px !important;margin-bottom:16px !important}.m-lg-4{margin:24px !important}.mt-lg-4{margin-top:24px !important}.mr-lg-4{margin-right:24px !important}.mb-lg-4{margin-bottom:24px !important}.ml-lg-4{margin-left:24px !important}.mt-lg-n4{margin-top:-24px !important}.mr-lg-n4{margin-right:-24px !important}.mb-lg-n4{margin-bottom:-24px !important}.ml-lg-n4{margin-left:-24px !important}.mx-lg-4{margin-right:24px !important;margin-left:24px !important}.my-lg-4{margin-top:24px !important;margin-bottom:24px !important}.m-lg-5{margin:32px !important}.mt-lg-5{margin-top:32px !important}.mr-lg-5{margin-right:32px !important}.mb-lg-5{margin-bottom:32px !important}.ml-lg-5{margin-left:32px !important}.mt-lg-n5{margin-top:-32px !important}.mr-lg-n5{margin-right:-32px !important}.mb-lg-n5{margin-bottom:-32px !important}.ml-lg-n5{margin-left:-32px !important}.mx-lg-5{margin-right:32px !important;margin-left:32px !important}.my-lg-5{margin-top:32px !important;margin-bottom:32px !important}.m-lg-6{margin:40px !important}.mt-lg-6{margin-top:40px !important}.mr-lg-6{margin-right:40px !important}.mb-lg-6{margin-bottom:40px !important}.ml-lg-6{margin-left:40px !important}.mt-lg-n6{margin-top:-40px !important}.mr-lg-n6{margin-right:-40px !important}.mb-lg-n6{margin-bottom:-40px !important}.ml-lg-n6{margin-left:-40px !important}.mx-lg-6{margin-right:40px !important;margin-left:40px !important}.my-lg-6{margin-top:40px !important;margin-bottom:40px !important}.mx-lg-auto{margin-right:auto !important;margin-left:auto !important}}@media (min-width: 1280px){.m-xl-0{margin:0 !important}.mt-xl-0{margin-top:0 !important}.mr-xl-0{margin-right:0 !important}.mb-xl-0{margin-bottom:0 !important}.ml-xl-0{margin-left:0 !important}.mx-xl-0{margin-right:0 !important;margin-left:0 !important}.my-xl-0{margin-top:0 !important;margin-bottom:0 !important}.m-xl-1{margin:4px !important}.mt-xl-1{margin-top:4px !important}.mr-xl-1{margin-right:4px !important}.mb-xl-1{margin-bottom:4px !important}.ml-xl-1{margin-left:4px !important}.mt-xl-n1{margin-top:-4px !important}.mr-xl-n1{margin-right:-4px !important}.mb-xl-n1{margin-bottom:-4px !important}.ml-xl-n1{margin-left:-4px !important}.mx-xl-1{margin-right:4px !important;margin-left:4px !important}.my-xl-1{margin-top:4px !important;margin-bottom:4px !important}.m-xl-2{margin:8px !important}.mt-xl-2{margin-top:8px !important}.mr-xl-2{margin-right:8px !important}.mb-xl-2{margin-bottom:8px !important}.ml-xl-2{margin-left:8px !important}.mt-xl-n2{margin-top:-8px !important}.mr-xl-n2{margin-right:-8px !important}.mb-xl-n2{margin-bottom:-8px !important}.ml-xl-n2{margin-left:-8px !important}.mx-xl-2{margin-right:8px !important;margin-left:8px !important}.my-xl-2{margin-top:8px !important;margin-bottom:8px !important}.m-xl-3{margin:16px !important}.mt-xl-3{margin-top:16px !important}.mr-xl-3{margin-right:16px !important}.mb-xl-3{margin-bottom:16px !important}.ml-xl-3{margin-left:16px !important}.mt-xl-n3{margin-top:-16px !important}.mr-xl-n3{margin-right:-16px !important}.mb-xl-n3{margin-bottom:-16px !important}.ml-xl-n3{margin-left:-16px !important}.mx-xl-3{margin-right:16px !important;margin-left:16px !important}.my-xl-3{margin-top:16px !important;margin-bottom:16px !important}.m-xl-4{margin:24px !important}.mt-xl-4{margin-top:24px !important}.mr-xl-4{margin-right:24px !important}.mb-xl-4{margin-bottom:24px !important}.ml-xl-4{margin-left:24px !important}.mt-xl-n4{margin-top:-24px !important}.mr-xl-n4{margin-right:-24px !important}.mb-xl-n4{margin-bottom:-24px !important}.ml-xl-n4{margin-left:-24px !important}.mx-xl-4{margin-right:24px !important;margin-left:24px !important}.my-xl-4{margin-top:24px !important;margin-bottom:24px !important}.m-xl-5{margin:32px !important}.mt-xl-5{margin-top:32px !important}.mr-xl-5{margin-right:32px !important}.mb-xl-5{margin-bottom:32px !important}.ml-xl-5{margin-left:32px !important}.mt-xl-n5{margin-top:-32px !important}.mr-xl-n5{margin-right:-32px !important}.mb-xl-n5{margin-bottom:-32px !important}.ml-xl-n5{margin-left:-32px !important}.mx-xl-5{margin-right:32px !important;margin-left:32px !important}.my-xl-5{margin-top:32px !important;margin-bottom:32px !important}.m-xl-6{margin:40px !important}.mt-xl-6{margin-top:40px !important}.mr-xl-6{margin-right:40px !important}.mb-xl-6{margin-bottom:40px !important}.ml-xl-6{margin-left:40px !important}.mt-xl-n6{margin-top:-40px !important}.mr-xl-n6{margin-right:-40px !important}.mb-xl-n6{margin-bottom:-40px !important}.ml-xl-n6{margin-left:-40px !important}.mx-xl-6{margin-right:40px !important;margin-left:40px !important}.my-xl-6{margin-top:40px !important;margin-bottom:40px !important}.mx-xl-auto{margin-right:auto !important;margin-left:auto !important}}.p-0{padding:0 !important}.pt-0{padding-top:0 !important}.pr-0{padding-right:0 !important}.pb-0{padding-bottom:0 !important}.pl-0{padding-left:0 !important}.px-0{padding-right:0 !important;padding-left:0 !important}.py-0{padding-top:0 !important;padding-bottom:0 !important}.p-1{padding:4px !important}.pt-1{padding-top:4px !important}.pr-1{padding-right:4px !important}.pb-1{padding-bottom:4px !important}.pl-1{padding-left:4px !important}.px-1{padding-right:4px !important;padding-left:4px !important}.py-1{padding-top:4px !important;padding-bottom:4px !important}.p-2{padding:8px !important}.pt-2{padding-top:8px !important}.pr-2{padding-right:8px !important}.pb-2{padding-bottom:8px !important}.pl-2{padding-left:8px !important}.px-2{padding-right:8px !important;padding-left:8px !important}.py-2{padding-top:8px !important;padding-bottom:8px !important}.p-3{padding:16px !important}.pt-3{padding-top:16px !important}.pr-3{padding-right:16px !important}.pb-3{padding-bottom:16px !important}.pl-3{padding-left:16px !important}.px-3{padding-right:16px !important;padding-left:16px !important}.py-3{padding-top:16px !important;padding-bottom:16px !important}.p-4{padding:24px !important}.pt-4{padding-top:24px !important}.pr-4{padding-right:24px !important}.pb-4{padding-bottom:24px !important}.pl-4{padding-left:24px !important}.px-4{padding-right:24px !important;padding-left:24px !important}.py-4{padding-top:24px !important;padding-bottom:24px !important}.p-5{padding:32px !important}.pt-5{padding-top:32px !important}.pr-5{padding-right:32px !important}.pb-5{padding-bottom:32px !important}.pl-5{padding-left:32px !important}.px-5{padding-right:32px !important;padding-left:32px !important}.py-5{padding-top:32px !important;padding-bottom:32px !important}.p-6{padding:40px !important}.pt-6{padding-top:40px !important}.pr-6{padding-right:40px !important}.pb-6{padding-bottom:40px !important}.pl-6{padding-left:40px !important}.px-6{padding-right:40px !important;padding-left:40px !important}.py-6{padding-top:40px !important;padding-bottom:40px !important}@media (min-width: 544px){.p-sm-0{padding:0 !important}.pt-sm-0{padding-top:0 !important}.pr-sm-0{padding-right:0 !important}.pb-sm-0{padding-bottom:0 !important}.pl-sm-0{padding-left:0 !important}.px-sm-0{padding-right:0 !important;padding-left:0 !important}.py-sm-0{padding-top:0 !important;padding-bottom:0 !important}.p-sm-1{padding:4px !important}.pt-sm-1{padding-top:4px !important}.pr-sm-1{padding-right:4px !important}.pb-sm-1{padding-bottom:4px !important}.pl-sm-1{padding-left:4px !important}.px-sm-1{padding-right:4px !important;padding-left:4px !important}.py-sm-1{padding-top:4px !important;padding-bottom:4px !important}.p-sm-2{padding:8px !important}.pt-sm-2{padding-top:8px !important}.pr-sm-2{padding-right:8px !important}.pb-sm-2{padding-bottom:8px !important}.pl-sm-2{padding-left:8px !important}.px-sm-2{padding-right:8px !important;padding-left:8px !important}.py-sm-2{padding-top:8px !important;padding-bottom:8px !important}.p-sm-3{padding:16px !important}.pt-sm-3{padding-top:16px !important}.pr-sm-3{padding-right:16px !important}.pb-sm-3{padding-bottom:16px !important}.pl-sm-3{padding-left:16px !important}.px-sm-3{padding-right:16px !important;padding-left:16px !important}.py-sm-3{padding-top:16px !important;padding-bottom:16px !important}.p-sm-4{padding:24px !important}.pt-sm-4{padding-top:24px !important}.pr-sm-4{padding-right:24px !important}.pb-sm-4{padding-bottom:24px !important}.pl-sm-4{padding-left:24px !important}.px-sm-4{padding-right:24px !important;padding-left:24px !important}.py-sm-4{padding-top:24px !important;padding-bottom:24px !important}.p-sm-5{padding:32px !important}.pt-sm-5{padding-top:32px !important}.pr-sm-5{padding-right:32px !important}.pb-sm-5{padding-bottom:32px !important}.pl-sm-5{padding-left:32px !important}.px-sm-5{padding-right:32px !important;padding-left:32px !important}.py-sm-5{padding-top:32px !important;padding-bottom:32px !important}.p-sm-6{padding:40px !important}.pt-sm-6{padding-top:40px !important}.pr-sm-6{padding-right:40px !important}.pb-sm-6{padding-bottom:40px !important}.pl-sm-6{padding-left:40px !important}.px-sm-6{padding-right:40px !important;padding-left:40px !important}.py-sm-6{padding-top:40px !important;padding-bottom:40px !important}}@media (min-width: 768px){.p-md-0{padding:0 !important}.pt-md-0{padding-top:0 !important}.pr-md-0{padding-right:0 !important}.pb-md-0{padding-bottom:0 !important}.pl-md-0{padding-left:0 !important}.px-md-0{padding-right:0 !important;padding-left:0 !important}.py-md-0{padding-top:0 !important;padding-bottom:0 !important}.p-md-1{padding:4px !important}.pt-md-1{padding-top:4px !important}.pr-md-1{padding-right:4px !important}.pb-md-1{padding-bottom:4px !important}.pl-md-1{padding-left:4px !important}.px-md-1{padding-right:4px !important;padding-left:4px !important}.py-md-1{padding-top:4px !important;padding-bottom:4px !important}.p-md-2{padding:8px !important}.pt-md-2{padding-top:8px !important}.pr-md-2{padding-right:8px !important}.pb-md-2{padding-bottom:8px !important}.pl-md-2{padding-left:8px !important}.px-md-2{padding-right:8px !important;padding-left:8px !important}.py-md-2{padding-top:8px !important;padding-bottom:8px !important}.p-md-3{padding:16px !important}.pt-md-3{padding-top:16px !important}.pr-md-3{padding-right:16px !important}.pb-md-3{padding-bottom:16px !important}.pl-md-3{padding-left:16px !important}.px-md-3{padding-right:16px !important;padding-left:16px !important}.py-md-3{padding-top:16px !important;padding-bottom:16px !important}.p-md-4{padding:24px !important}.pt-md-4{padding-top:24px !important}.pr-md-4{padding-right:24px !important}.pb-md-4{padding-bottom:24px !important}.pl-md-4{padding-left:24px !important}.px-md-4{padding-right:24px !important;padding-left:24px !important}.py-md-4{padding-top:24px !important;padding-bottom:24px !important}.p-md-5{padding:32px !important}.pt-md-5{padding-top:32px !important}.pr-md-5{padding-right:32px !important}.pb-md-5{padding-bottom:32px !important}.pl-md-5{padding-left:32px !important}.px-md-5{padding-right:32px !important;padding-left:32px !important}.py-md-5{padding-top:32px !important;padding-bottom:32px !important}.p-md-6{padding:40px !important}.pt-md-6{padding-top:40px !important}.pr-md-6{padding-right:40px !important}.pb-md-6{padding-bottom:40px !important}.pl-md-6{padding-left:40px !important}.px-md-6{padding-right:40px !important;padding-left:40px !important}.py-md-6{padding-top:40px !important;padding-bottom:40px !important}}@media (min-width: 1012px){.p-lg-0{padding:0 !important}.pt-lg-0{padding-top:0 !important}.pr-lg-0{padding-right:0 !important}.pb-lg-0{padding-bottom:0 !important}.pl-lg-0{padding-left:0 !important}.px-lg-0{padding-right:0 !important;padding-left:0 !important}.py-lg-0{padding-top:0 !important;padding-bottom:0 !important}.p-lg-1{padding:4px !important}.pt-lg-1{padding-top:4px !important}.pr-lg-1{padding-right:4px !important}.pb-lg-1{padding-bottom:4px !important}.pl-lg-1{padding-left:4px !important}.px-lg-1{padding-right:4px !important;padding-left:4px !important}.py-lg-1{padding-top:4px !important;padding-bottom:4px !important}.p-lg-2{padding:8px !important}.pt-lg-2{padding-top:8px !important}.pr-lg-2{padding-right:8px !important}.pb-lg-2{padding-bottom:8px !important}.pl-lg-2{padding-left:8px !important}.px-lg-2{padding-right:8px !important;padding-left:8px !important}.py-lg-2{padding-top:8px !important;padding-bottom:8px !important}.p-lg-3{padding:16px !important}.pt-lg-3{padding-top:16px !important}.pr-lg-3{padding-right:16px !important}.pb-lg-3{padding-bottom:16px !important}.pl-lg-3{padding-left:16px !important}.px-lg-3{padding-right:16px !important;padding-left:16px !important}.py-lg-3{padding-top:16px !important;padding-bottom:16px !important}.p-lg-4{padding:24px !important}.pt-lg-4{padding-top:24px !important}.pr-lg-4{padding-right:24px !important}.pb-lg-4{padding-bottom:24px !important}.pl-lg-4{padding-left:24px !important}.px-lg-4{padding-right:24px !important;padding-left:24px !important}.py-lg-4{padding-top:24px !important;padding-bottom:24px !important}.p-lg-5{padding:32px !important}.pt-lg-5{padding-top:32px !important}.pr-lg-5{padding-right:32px !important}.pb-lg-5{padding-bottom:32px !important}.pl-lg-5{padding-left:32px !important}.px-lg-5{padding-right:32px !important;padding-left:32px !important}.py-lg-5{padding-top:32px !important;padding-bottom:32px !important}.p-lg-6{padding:40px !important}.pt-lg-6{padding-top:40px !important}.pr-lg-6{padding-right:40px !important}.pb-lg-6{padding-bottom:40px !important}.pl-lg-6{padding-left:40px !important}.px-lg-6{padding-right:40px !important;padding-left:40px !important}.py-lg-6{padding-top:40px !important;padding-bottom:40px !important}}@media (min-width: 1280px){.p-xl-0{padding:0 !important}.pt-xl-0{padding-top:0 !important}.pr-xl-0{padding-right:0 !important}.pb-xl-0{padding-bottom:0 !important}.pl-xl-0{padding-left:0 !important}.px-xl-0{padding-right:0 !important;padding-left:0 !important}.py-xl-0{padding-top:0 !important;padding-bottom:0 !important}.p-xl-1{padding:4px !important}.pt-xl-1{padding-top:4px !important}.pr-xl-1{padding-right:4px !important}.pb-xl-1{padding-bottom:4px !important}.pl-xl-1{padding-left:4px !important}.px-xl-1{padding-right:4px !important;padding-left:4px !important}.py-xl-1{padding-top:4px !important;padding-bottom:4px !important}.p-xl-2{padding:8px !important}.pt-xl-2{padding-top:8px !important}.pr-xl-2{padding-right:8px !important}.pb-xl-2{padding-bottom:8px !important}.pl-xl-2{padding-left:8px !important}.px-xl-2{padding-right:8px !important;padding-left:8px !important}.py-xl-2{padding-top:8px !important;padding-bottom:8px !important}.p-xl-3{padding:16px !important}.pt-xl-3{padding-top:16px !important}.pr-xl-3{padding-right:16px !important}.pb-xl-3{padding-bottom:16px !important}.pl-xl-3{padding-left:16px !important}.px-xl-3{padding-right:16px !important;padding-left:16px !important}.py-xl-3{padding-top:16px !important;padding-bottom:16px !important}.p-xl-4{padding:24px !important}.pt-xl-4{padding-top:24px !important}.pr-xl-4{padding-right:24px !important}.pb-xl-4{padding-bottom:24px !important}.pl-xl-4{padding-left:24px !important}.px-xl-4{padding-right:24px !important;padding-left:24px !important}.py-xl-4{padding-top:24px !important;padding-bottom:24px !important}.p-xl-5{padding:32px !important}.pt-xl-5{padding-top:32px !important}.pr-xl-5{padding-right:32px !important}.pb-xl-5{padding-bottom:32px !important}.pl-xl-5{padding-left:32px !important}.px-xl-5{padding-right:32px !important;padding-left:32px !important}.py-xl-5{padding-top:32px !important;padding-bottom:32px !important}.p-xl-6{padding:40px !important}.pt-xl-6{padding-top:40px !important}.pr-xl-6{padding-right:40px !important}.pb-xl-6{padding-bottom:40px !important}.pl-xl-6{padding-left:40px !important}.px-xl-6{padding-right:40px !important;padding-left:40px !important}.py-xl-6{padding-top:40px !important;padding-bottom:40px !important}}.p-responsive{padding-right:16px !important;padding-left:16px !important}@media (min-width: 544px){.p-responsive{padding-right:40px !important;padding-left:40px !important}}@media (min-width: 1012px){.p-responsive{padding-right:16px !important;padding-left:16px !important}}.h1{font-size:26px !important}@media (min-width: 768px){.h1{font-size:32px !important}}.h2{font-size:22px !important}@media (min-width: 768px){.h2{font-size:24px !important}}.h3{font-size:18px !important}@media (min-width: 768px){.h3{font-size:20px !important}}.h4{font-size:16px !important}.h5{font-size:14px !important}.h6{font-size:12px !important}.h1,.h2,.h3,.h4,.h5,.h6{font-weight:600 !important}.f1{font-size:26px !important}@media (min-width: 768px){.f1{font-size:32px !important}}.f2{font-size:22px !important}@media (min-width: 768px){.f2{font-size:24px !important}}.f3{font-size:18px !important}@media (min-width: 768px){.f3{font-size:20px !important}}.f4{font-size:16px !important}@media (min-width: 768px){.f4{font-size:16px !important}}.f5{font-size:14px !important}.f6{font-size:12px !important}.f00-light{font-size:40px !important;font-weight:300 !important}@media (min-width: 768px){.f00-light{font-size:48px !important}}.f0-light{font-size:32px !important;font-weight:300 !important}@media (min-width: 768px){.f0-light{font-size:40px !important}}.f1-light{font-size:26px !important;font-weight:300 !important}@media (min-width: 768px){.f1-light{font-size:32px !important}}.f2-light{font-size:22px !important;font-weight:300 !important}@media (min-width: 768px){.f2-light{font-size:24px !important}}.f3-light{font-size:18px !important;font-weight:300 !important}@media (min-width: 768px){.f3-light{font-size:20px !important}}.text-small{font-size:12px !important}.lead{margin-bottom:30px;font-size:20px;font-weight:300;color:#586069}.lh-condensed-ultra{line-height:1 !important}.lh-condensed{line-height:1.25 !important}.lh-default{line-height:1.5 !important}.lh-0{line-height:0 !important}.text-right{text-align:right !important}.text-left{text-align:left !important}.text-center{text-align:center !important}@media (min-width: 544px){.text-sm-right{text-align:right !important}.text-sm-left{text-align:left !important}.text-sm-center{text-align:center !important}}@media (min-width: 768px){.text-md-right{text-align:right !important}.text-md-left{text-align:left !important}.text-md-center{text-align:center !important}}@media (min-width: 1012px){.text-lg-right{text-align:right !important}.text-lg-left{text-align:left !important}.text-lg-center{text-align:center !important}}@media (min-width: 1280px){.text-xl-right{text-align:right !important}.text-xl-left{text-align:left !important}.text-xl-center{text-align:center !important}}.text-normal{font-weight:400 !important}.text-bold{font-weight:600 !important}.text-italic{font-style:italic !important}.text-uppercase{text-transform:uppercase !important}.text-underline{text-decoration:underline !important}.no-underline{text-decoration:none !important}.no-wrap{white-space:nowrap !important}.ws-normal{white-space:normal !important}.wb-break-all{word-break:break-all !important}.text-emphasized{font-weight:600;color:#24292e}.list-style-none{list-style:none !important}.text-shadow-dark{text-shadow:0 1px 1px rgba(27,31,35,0.25),0 1px 25px rgba(27,31,35,0.75)}.text-shadow-light{text-shadow:0 1px 0 rgba(255,255,255,0.5)}.text-mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace}.user-select-none{user-select:none !important}.d-block{display:block !important}.d-flex{display:flex !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-inline-flex{display:inline-flex !important}.d-none{display:none !important}.d-table{display:table !important}.d-table-cell{display:table-cell !important}@media (min-width: 544px){.d-sm-block{display:block !important}.d-sm-flex{display:flex !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-inline-flex{display:inline-flex !important}.d-sm-none{display:none !important}.d-sm-table{display:table !important}.d-sm-table-cell{display:table-cell !important}}@media (min-width: 768px){.d-md-block{display:block !important}.d-md-flex{display:flex !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-inline-flex{display:inline-flex !important}.d-md-none{display:none !important}.d-md-table{display:table !important}.d-md-table-cell{display:table-cell !important}}@media (min-width: 1012px){.d-lg-block{display:block !important}.d-lg-flex{display:flex !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-inline-flex{display:inline-flex !important}.d-lg-none{display:none !important}.d-lg-table{display:table !important}.d-lg-table-cell{display:table-cell !important}}@media (min-width: 1280px){.d-xl-block{display:block !important}.d-xl-flex{display:flex !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-inline-flex{display:inline-flex !important}.d-xl-none{display:none !important}.d-xl-table{display:table !important}.d-xl-table-cell{display:table-cell !important}}.v-hidden{visibility:hidden !important}.v-visible{visibility:visible !important}@media (max-width: 544px){.hide-sm{display:none !important}}@media (min-width: 544px) and (max-width: 768px){.hide-md{display:none !important}}@media (min-width: 768px) and (max-width: 1012px){.hide-lg{display:none !important}}@media (min-width: 1012px){.hide-xl{display:none !important}}.table-fixed{table-layout:fixed !important}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);word-wrap:normal;border:0}.show-on-focus{position:absolute;width:1px;height:1px;margin:0;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}.show-on-focus:focus{z-index:20;width:auto;height:auto;clip:auto}.container{width:980px;margin-right:auto;margin-left:auto}.container::before{display:table;content:""}.container::after{display:table;clear:both;content:""}.container-md{max-width:768px;margin-right:auto;margin-left:auto}.container-lg{max-width:1012px;margin-right:auto;margin-left:auto}.container-xl{max-width:1280px;margin-right:auto;margin-left:auto}.columns{margin-right:-10px;margin-left:-10px}.columns::before{display:table;content:""}.columns::after{display:table;clear:both;content:""}.column{float:left;padding-right:10px;padding-left:10px}.one-third{width:33.333333%}.two-thirds{width:66.666667%}.one-fourth{width:25%}.one-half{width:50%}.three-fourths{width:75%}.one-fifth{width:20%}.four-fifths{width:80%}.centered{display:block;float:none;margin-right:auto;margin-left:auto}.col-1{width:8.3333333333%}.col-2{width:16.6666666667%}.col-3{width:25%}.col-4{width:33.3333333333%}.col-5{width:41.6666666667%}.col-6{width:50%}.col-7{width:58.3333333333%}.col-8{width:66.6666666667%}.col-9{width:75%}.col-10{width:83.3333333333%}.col-11{width:91.6666666667%}.col-12{width:100%}@media (min-width: 544px){.col-sm-1{width:8.3333333333%}.col-sm-2{width:16.6666666667%}.col-sm-3{width:25%}.col-sm-4{width:33.3333333333%}.col-sm-5{width:41.6666666667%}.col-sm-6{width:50%}.col-sm-7{width:58.3333333333%}.col-sm-8{width:66.6666666667%}.col-sm-9{width:75%}.col-sm-10{width:83.3333333333%}.col-sm-11{width:91.6666666667%}.col-sm-12{width:100%}}@media (min-width: 768px){.col-md-1{width:8.3333333333%}.col-md-2{width:16.6666666667%}.col-md-3{width:25%}.col-md-4{width:33.3333333333%}.col-md-5{width:41.6666666667%}.col-md-6{width:50%}.col-md-7{width:58.3333333333%}.col-md-8{width:66.6666666667%}.col-md-9{width:75%}.col-md-10{width:83.3333333333%}.col-md-11{width:91.6666666667%}.col-md-12{width:100%}}@media (min-width: 1012px){.col-lg-1{width:8.3333333333%}.col-lg-2{width:16.6666666667%}.col-lg-3{width:25%}.col-lg-4{width:33.3333333333%}.col-lg-5{width:41.6666666667%}.col-lg-6{width:50%}.col-lg-7{width:58.3333333333%}.col-lg-8{width:66.6666666667%}.col-lg-9{width:75%}.col-lg-10{width:83.3333333333%}.col-lg-11{width:91.6666666667%}.col-lg-12{width:100%}}@media (min-width: 1280px){.col-xl-1{width:8.3333333333%}.col-xl-2{width:16.6666666667%}.col-xl-3{width:25%}.col-xl-4{width:33.3333333333%}.col-xl-5{width:41.6666666667%}.col-xl-6{width:50%}.col-xl-7{width:58.3333333333%}.col-xl-8{width:66.6666666667%}.col-xl-9{width:75%}.col-xl-10{width:83.3333333333%}.col-xl-11{width:91.6666666667%}.col-xl-12{width:100%}}.gutter{margin-right:-16px;margin-left:-16px}.gutter>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-condensed{margin-right:-8px;margin-left:-8px}.gutter-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-spacious{margin-right:-24px;margin-left:-24px}.gutter-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}@media (min-width: 544px){.gutter-sm{margin-right:-16px;margin-left:-16px}.gutter-sm>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-sm-condensed{margin-right:-8px;margin-left:-8px}.gutter-sm-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-sm-spacious{margin-right:-24px;margin-left:-24px}.gutter-sm-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}}@media (min-width: 768px){.gutter-md{margin-right:-16px;margin-left:-16px}.gutter-md>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-md-condensed{margin-right:-8px;margin-left:-8px}.gutter-md-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-md-spacious{margin-right:-24px;margin-left:-24px}.gutter-md-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}}@media (min-width: 1012px){.gutter-lg{margin-right:-16px;margin-left:-16px}.gutter-lg>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-lg-condensed{margin-right:-8px;margin-left:-8px}.gutter-lg-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-lg-spacious{margin-right:-24px;margin-left:-24px}.gutter-lg-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}}@media (min-width: 1280px){.gutter-xl{margin-right:-16px;margin-left:-16px}.gutter-xl>[class*="col-"]{padding-right:16px !important;padding-left:16px !important}.gutter-xl-condensed{margin-right:-8px;margin-left:-8px}.gutter-xl-condensed>[class*="col-"]{padding-right:8px !important;padding-left:8px !important}.gutter-xl-spacious{margin-right:-24px;margin-left:-24px}.gutter-xl-spacious>[class*="col-"]{padding-right:24px !important;padding-left:24px !important}}.offset-1{margin-left:8.3333333333% !important}.offset-2{margin-left:16.6666666667% !important}.offset-3{margin-left:25% !important}.offset-4{margin-left:33.3333333333% !important}.offset-5{margin-left:41.6666666667% !important}.offset-6{margin-left:50% !important}.offset-7{margin-left:58.3333333333% !important}.offset-8{margin-left:66.6666666667% !important}.offset-9{margin-left:75% !important}.offset-10{margin-left:83.3333333333% !important}.offset-11{margin-left:91.6666666667% !important}@media (min-width: 544px){.offset-sm-1{margin-left:8.3333333333% !important}.offset-sm-2{margin-left:16.6666666667% !important}.offset-sm-3{margin-left:25% !important}.offset-sm-4{margin-left:33.3333333333% !important}.offset-sm-5{margin-left:41.6666666667% !important}.offset-sm-6{margin-left:50% !important}.offset-sm-7{margin-left:58.3333333333% !important}.offset-sm-8{margin-left:66.6666666667% !important}.offset-sm-9{margin-left:75% !important}.offset-sm-10{margin-left:83.3333333333% !important}.offset-sm-11{margin-left:91.6666666667% !important}}@media (min-width: 768px){.offset-md-1{margin-left:8.3333333333% !important}.offset-md-2{margin-left:16.6666666667% !important}.offset-md-3{margin-left:25% !important}.offset-md-4{margin-left:33.3333333333% !important}.offset-md-5{margin-left:41.6666666667% !important}.offset-md-6{margin-left:50% !important}.offset-md-7{margin-left:58.3333333333% !important}.offset-md-8{margin-left:66.6666666667% !important}.offset-md-9{margin-left:75% !important}.offset-md-10{margin-left:83.3333333333% !important}.offset-md-11{margin-left:91.6666666667% !important}}@media (min-width: 1012px){.offset-lg-1{margin-left:8.3333333333% !important}.offset-lg-2{margin-left:16.6666666667% !important}.offset-lg-3{margin-left:25% !important}.offset-lg-4{margin-left:33.3333333333% !important}.offset-lg-5{margin-left:41.6666666667% !important}.offset-lg-6{margin-left:50% !important}.offset-lg-7{margin-left:58.3333333333% !important}.offset-lg-8{margin-left:66.6666666667% !important}.offset-lg-9{margin-left:75% !important}.offset-lg-10{margin-left:83.3333333333% !important}.offset-lg-11{margin-left:91.6666666667% !important}}@media (min-width: 1280px){.offset-xl-1{margin-left:8.3333333333% !important}.offset-xl-2{margin-left:16.6666666667% !important}.offset-xl-3{margin-left:25% !important}.offset-xl-4{margin-left:33.3333333333% !important}.offset-xl-5{margin-left:41.6666666667% !important}.offset-xl-6{margin-left:50% !important}.offset-xl-7{margin-left:58.3333333333% !important}.offset-xl-8{margin-left:66.6666666667% !important}.offset-xl-9{margin-left:75% !important}.offset-xl-10{margin-left:83.3333333333% !important}.offset-xl-11{margin-left:91.6666666667% !important}}.markdown-body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body::before{display:table;content:""}.markdown-body::after{display:table;clear:both;content:""}.markdown-body>*:first-child{margin-top:0 !important}.markdown-body>*:last-child{margin-bottom:0 !important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:#cb2431}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:none}.markdown-body p,.markdown-body blockquote,.markdown-body ul,.markdown-body ol,.markdown-body dl,.markdown-body table,.markdown-body pre{margin-top:0;margin-bottom:16px}.markdown-body hr{height:.25em;padding:0;margin:24px 0;background-color:#e1e4e8;border:0}.markdown-body blockquote{padding:0 1em;color:#6a737d;border-left:0.25em solid #dfe2e5}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:solid 1px #c6cbd1;border-bottom-color:#959da5;border-radius:3px;box-shadow:inset 0 -1px 0 #959da5}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#1b1f23;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 tt,.markdown-body h1 code,.markdown-body h2 tt,.markdown-body h2 code,.markdown-body h3 tt,.markdown-body h3 code,.markdown-body h4 tt,.markdown-body h4 code,.markdown-body h5 tt,.markdown-body h5 code,.markdown-body h6 tt,.markdown-body h6 code{font-size:inherit}.markdown-body h1{padding-bottom:0.3em;font-size:2em;border-bottom:1px solid #eaecef}.markdown-body h2{padding-bottom:0.3em;font-size:1.5em;border-bottom:1px solid #eaecef}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:0.875em}.markdown-body h6{font-size:0.85em;color:#6a737d}.markdown-body ul,.markdown-body ol{padding-left:2em}.markdown-body ul.no-list,.markdown-body ol.no-list{padding:0;list-style-type:none}.markdown-body ul ul,.markdown-body ul ol,.markdown-body ol ol,.markdown-body ol ul{margin-top:0;margin-bottom:0}.markdown-body li{word-wrap:break-all}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:600}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table{display:block;width:100%;overflow:auto}.markdown-body table th{font-weight:600}.markdown-body table th,.markdown-body table td{padding:6px 13px;border:1px solid #dfe2e5}.markdown-body table tr{background-color:#fff;border-top:1px solid #c6cbd1}.markdown-body table tr:nth-child(2n){background-color:#f6f8fa}.markdown-body table img{background-color:transparent}.markdown-body img{max-width:100%;box-sizing:content-box;background-color:#fff}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #dfe2e5}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:#24292e}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:0.2em 0.4em;margin:0;font-size:85%;background-color:rgba(27,31,35,0.05);border-radius:3px}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body pre{word-wrap:normal}.markdown-body pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:transparent;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f6f8fa;border-radius:3px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:600;background:#f6f8fa;border-top:0}.highlight table td{padding:5px}.highlight table pre{margin:0}.highlight .cm{color:#999988;font-style:italic}.highlight .cp{color:#999999;font-weight:bold}.highlight .c1{color:#999988;font-style:italic}.highlight .cs{color:#999999;font-weight:bold;font-style:italic}.highlight .c,.highlight .cd{color:#999988;font-style:italic}.highlight .err{color:#a61717;background-color:#e3d2d2}.highlight .gd{color:#000000;background-color:#ffdddd}.highlight .ge{color:#000000;font-style:italic}.highlight .gr{color:#aa0000}.highlight .gh{color:#999999}.highlight .gi{color:#000000;background-color:#ddffdd}.highlight .go{color:#888888}.highlight .gp{color:#555555}.highlight .gs{font-weight:bold}.highlight .gu{color:#aaaaaa}.highlight .gt{color:#aa0000}.highlight .kc{color:#000000;font-weight:bold}.highlight .kd{color:#000000;font-weight:bold}.highlight .kn{color:#000000;font-weight:bold}.highlight .kp{color:#000000;font-weight:bold}.highlight .kr{color:#000000;font-weight:bold}.highlight .kt{color:#445588;font-weight:bold}.highlight .k,.highlight .kv{color:#000000;font-weight:bold}.highlight .mf{color:#009999}.highlight .mh{color:#009999}.highlight .il{color:#009999}.highlight .mi{color:#009999}.highlight .mo{color:#009999}.highlight .m,.highlight .mb,.highlight .mx{color:#009999}.highlight .sb{color:#d14}.highlight .sc{color:#d14}.highlight .sd{color:#d14}.highlight .s2{color:#d14}.highlight .se{color:#d14}.highlight .sh{color:#d14}.highlight .si{color:#d14}.highlight .sx{color:#d14}.highlight .sr{color:#009926}.highlight .s1{color:#d14}.highlight .ss{color:#990073}.highlight .s{color:#d14}.highlight .na{color:#008080}.highlight .bp{color:#999999}.highlight .nb{color:#0086B3}.highlight .nc{color:#445588;font-weight:bold}.highlight .no{color:#008080}.highlight .nd{color:#3c5d5d;font-weight:bold}.highlight .ni{color:#800080}.highlight .ne{color:#990000;font-weight:bold}.highlight .nf{color:#990000;font-weight:bold}.highlight .nl{color:#990000;font-weight:bold}.highlight .nn{color:#555555}.highlight .nt{color:#000080}.highlight .vc{color:#008080}.highlight .vg{color:#008080}.highlight .vi{color:#008080}.highlight .nv{color:#008080}.highlight .ow{color:#000000;font-weight:bold}.highlight .o{color:#000000;font-weight:bold}.highlight .w{color:#bbbbbb}.highlight{background-color:#f8f8f8} diff --git a/assets/images/2016/07/201011_Fig_1.png b/assets/images/2016/07/201011_Fig_1.png new file mode 100644 index 0000000..3a9df6a Binary files /dev/null and b/assets/images/2016/07/201011_Fig_1.png differ diff --git a/assets/images/2016/07/201011_Fig_10.png b/assets/images/2016/07/201011_Fig_10.png new file mode 100644 index 0000000..28055c7 Binary files /dev/null and b/assets/images/2016/07/201011_Fig_10.png differ diff --git a/assets/images/2016/07/201011_Fig_2.png b/assets/images/2016/07/201011_Fig_2.png new file mode 100644 index 0000000..8edb5d9 Binary files /dev/null and b/assets/images/2016/07/201011_Fig_2.png differ diff --git a/assets/images/2016/07/201011_Fig_3.png b/assets/images/2016/07/201011_Fig_3.png new file mode 100644 index 0000000..b8bcd1b Binary files /dev/null and b/assets/images/2016/07/201011_Fig_3.png differ diff --git a/assets/images/2016/07/201011_Fig_4.png b/assets/images/2016/07/201011_Fig_4.png new file mode 100644 index 0000000..1d0683f Binary files /dev/null and b/assets/images/2016/07/201011_Fig_4.png differ diff --git a/assets/images/2016/07/201011_Fig_5.png b/assets/images/2016/07/201011_Fig_5.png new file mode 100644 index 0000000..579f140 Binary files /dev/null and b/assets/images/2016/07/201011_Fig_5.png differ diff --git a/assets/images/2016/07/201011_Fig_6.png b/assets/images/2016/07/201011_Fig_6.png new file mode 100644 index 0000000..17d7139 Binary files /dev/null and b/assets/images/2016/07/201011_Fig_6.png differ diff --git a/assets/images/2016/07/201011_Fig_7.png b/assets/images/2016/07/201011_Fig_7.png new file mode 100644 index 0000000..98333d4 Binary files /dev/null and b/assets/images/2016/07/201011_Fig_7.png differ diff --git a/assets/images/2016/07/201011_Fig_8.png b/assets/images/2016/07/201011_Fig_8.png new file mode 100644 index 0000000..e5d151f Binary files /dev/null and b/assets/images/2016/07/201011_Fig_8.png differ diff --git a/assets/images/2016/07/201011_Fig_9.png b/assets/images/2016/07/201011_Fig_9.png new file mode 100644 index 0000000..093455c Binary files /dev/null and b/assets/images/2016/07/201011_Fig_9.png differ diff --git a/assets/images/2016/07/20110417_Fig_1.jpg b/assets/images/2016/07/20110417_Fig_1.jpg new file mode 100644 index 0000000..52d85fc Binary files /dev/null and b/assets/images/2016/07/20110417_Fig_1.jpg differ diff --git a/assets/images/2016/07/20110417_Fig_2.jpg b/assets/images/2016/07/20110417_Fig_2.jpg new file mode 100644 index 0000000..d0d55e7 Binary files /dev/null and b/assets/images/2016/07/20110417_Fig_2.jpg differ diff --git a/assets/images/2016/07/20110417_Fig_3.jpg b/assets/images/2016/07/20110417_Fig_3.jpg new file mode 100644 index 0000000..10c4558 Binary files /dev/null and b/assets/images/2016/07/20110417_Fig_3.jpg differ diff --git a/assets/images/2016/07/20110429_Fig_1.png b/assets/images/2016/07/20110429_Fig_1.png new file mode 100644 index 0000000..9b29517 Binary files /dev/null and b/assets/images/2016/07/20110429_Fig_1.png differ diff --git a/assets/images/2016/07/20110429_Fig_2.png b/assets/images/2016/07/20110429_Fig_2.png new file mode 100644 index 0000000..c113720 Binary files /dev/null and b/assets/images/2016/07/20110429_Fig_2.png differ diff --git a/assets/images/2016/07/20110429_Fig_3.png b/assets/images/2016/07/20110429_Fig_3.png new file mode 100644 index 0000000..9d7d11b Binary files /dev/null and b/assets/images/2016/07/20110429_Fig_3.png differ diff --git a/assets/images/2016/07/20110429_Fig_4.png b/assets/images/2016/07/20110429_Fig_4.png new file mode 100644 index 0000000..c47cb8c Binary files /dev/null and b/assets/images/2016/07/20110429_Fig_4.png differ diff --git a/assets/images/2016/07/20110429_Fig_5.png b/assets/images/2016/07/20110429_Fig_5.png new file mode 100644 index 0000000..cf56b2b Binary files /dev/null and b/assets/images/2016/07/20110429_Fig_5.png differ diff --git a/assets/images/2016/07/20110507_Fig_1.png b/assets/images/2016/07/20110507_Fig_1.png new file mode 100644 index 0000000..0582cf9 Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_1.png differ diff --git a/assets/images/2016/07/20110507_Fig_10.png b/assets/images/2016/07/20110507_Fig_10.png new file mode 100644 index 0000000..0f259eb Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_10.png differ diff --git a/assets/images/2016/07/20110507_Fig_11.png b/assets/images/2016/07/20110507_Fig_11.png new file mode 100644 index 0000000..747bb69 Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_11.png differ diff --git a/assets/images/2016/07/20110507_Fig_12.png b/assets/images/2016/07/20110507_Fig_12.png new file mode 100644 index 0000000..76ea7e4 Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_12.png differ diff --git a/assets/images/2016/07/20110507_Fig_13.png b/assets/images/2016/07/20110507_Fig_13.png new file mode 100644 index 0000000..b0f4c6a Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_13.png differ diff --git a/assets/images/2016/07/20110507_Fig_2.png b/assets/images/2016/07/20110507_Fig_2.png new file mode 100644 index 0000000..4a6a23c Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_2.png differ diff --git a/assets/images/2016/07/20110507_Fig_3.png b/assets/images/2016/07/20110507_Fig_3.png new file mode 100644 index 0000000..cc931f8 Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_3.png differ diff --git a/assets/images/2016/07/20110507_Fig_4.png b/assets/images/2016/07/20110507_Fig_4.png new file mode 100644 index 0000000..83698f9 Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_4.png differ diff --git a/assets/images/2016/07/20110507_Fig_5.png b/assets/images/2016/07/20110507_Fig_5.png new file mode 100644 index 0000000..49b0122 Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_5.png differ diff --git a/assets/images/2016/07/20110507_Fig_6.png b/assets/images/2016/07/20110507_Fig_6.png new file mode 100644 index 0000000..e336660 Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_6.png differ diff --git a/assets/images/2016/07/20110507_Fig_7.png b/assets/images/2016/07/20110507_Fig_7.png new file mode 100644 index 0000000..8b6e0b8 Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_7.png differ diff --git a/assets/images/2016/07/20110507_Fig_8.png b/assets/images/2016/07/20110507_Fig_8.png new file mode 100644 index 0000000..3d2c63c Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_8.png differ diff --git a/assets/images/2016/07/20110507_Fig_9.png b/assets/images/2016/07/20110507_Fig_9.png new file mode 100644 index 0000000..ee2be9e Binary files /dev/null and b/assets/images/2016/07/20110507_Fig_9.png differ diff --git a/assets/images/2016/07/20110604_Fig_1.png b/assets/images/2016/07/20110604_Fig_1.png new file mode 100644 index 0000000..383584e Binary files /dev/null and b/assets/images/2016/07/20110604_Fig_1.png differ diff --git a/assets/images/2016/07/20110604_Fig_2.png b/assets/images/2016/07/20110604_Fig_2.png new file mode 100644 index 0000000..20d5a59 Binary files /dev/null and b/assets/images/2016/07/20110604_Fig_2.png differ diff --git a/assets/images/2016/07/20110604_Fig_3.png b/assets/images/2016/07/20110604_Fig_3.png new file mode 100644 index 0000000..6f87147 Binary files /dev/null and b/assets/images/2016/07/20110604_Fig_3.png differ diff --git a/assets/images/2016/07/20110604_Fig_4.png b/assets/images/2016/07/20110604_Fig_4.png new file mode 100644 index 0000000..67627b1 Binary files /dev/null and b/assets/images/2016/07/20110604_Fig_4.png differ diff --git a/assets/images/2016/07/20110604_Fig_5.png b/assets/images/2016/07/20110604_Fig_5.png new file mode 100644 index 0000000..b2df3f1 Binary files /dev/null and b/assets/images/2016/07/20110604_Fig_5.png differ diff --git a/assets/images/2016/07/20110604_Fig_6.png b/assets/images/2016/07/20110604_Fig_6.png new file mode 100644 index 0000000..468e1b8 Binary files /dev/null and b/assets/images/2016/07/20110604_Fig_6.png differ diff --git a/assets/images/2016/07/20110604_Fig_7.png b/assets/images/2016/07/20110604_Fig_7.png new file mode 100644 index 0000000..0aeacdc Binary files /dev/null and b/assets/images/2016/07/20110604_Fig_7.png differ diff --git a/assets/images/2016/07/20110620_Fig_1.png b/assets/images/2016/07/20110620_Fig_1.png new file mode 100644 index 0000000..cb02af9 Binary files /dev/null and b/assets/images/2016/07/20110620_Fig_1.png differ diff --git a/assets/images/2016/07/20110620_Fig_2.png b/assets/images/2016/07/20110620_Fig_2.png new file mode 100644 index 0000000..bb5597c Binary files /dev/null and b/assets/images/2016/07/20110620_Fig_2.png differ diff --git a/assets/images/2016/07/20111104_Fig_1.png b/assets/images/2016/07/20111104_Fig_1.png new file mode 100644 index 0000000..9494d56 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_1.png differ diff --git a/assets/images/2016/07/20111104_Fig_10.png b/assets/images/2016/07/20111104_Fig_10.png new file mode 100644 index 0000000..71cae8d Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_10.png differ diff --git a/assets/images/2016/07/20111104_Fig_11.png b/assets/images/2016/07/20111104_Fig_11.png new file mode 100644 index 0000000..7644788 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_11.png differ diff --git a/assets/images/2016/07/20111104_Fig_12.png b/assets/images/2016/07/20111104_Fig_12.png new file mode 100644 index 0000000..e921c5f Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_12.png differ diff --git a/assets/images/2016/07/20111104_Fig_13.png b/assets/images/2016/07/20111104_Fig_13.png new file mode 100644 index 0000000..fff5247 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_13.png differ diff --git a/assets/images/2016/07/20111104_Fig_14.png b/assets/images/2016/07/20111104_Fig_14.png new file mode 100644 index 0000000..4deb828 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_14.png differ diff --git a/assets/images/2016/07/20111104_Fig_15.png b/assets/images/2016/07/20111104_Fig_15.png new file mode 100644 index 0000000..2ad61ad Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_15.png differ diff --git a/assets/images/2016/07/20111104_Fig_16.png b/assets/images/2016/07/20111104_Fig_16.png new file mode 100644 index 0000000..69ff58e Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_16.png differ diff --git a/assets/images/2016/07/20111104_Fig_17.png b/assets/images/2016/07/20111104_Fig_17.png new file mode 100644 index 0000000..6447e73 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_17.png differ diff --git a/assets/images/2016/07/20111104_Fig_2.png b/assets/images/2016/07/20111104_Fig_2.png new file mode 100644 index 0000000..ca3fc77 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_2.png differ diff --git a/assets/images/2016/07/20111104_Fig_3.png b/assets/images/2016/07/20111104_Fig_3.png new file mode 100644 index 0000000..b23cfee Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_3.png differ diff --git a/assets/images/2016/07/20111104_Fig_4.png b/assets/images/2016/07/20111104_Fig_4.png new file mode 100644 index 0000000..4fcff96 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_4.png differ diff --git a/assets/images/2016/07/20111104_Fig_5.png b/assets/images/2016/07/20111104_Fig_5.png new file mode 100644 index 0000000..e548239 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_5.png differ diff --git a/assets/images/2016/07/20111104_Fig_6.png b/assets/images/2016/07/20111104_Fig_6.png new file mode 100644 index 0000000..00eb627 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_6.png differ diff --git a/assets/images/2016/07/20111104_Fig_7.png b/assets/images/2016/07/20111104_Fig_7.png new file mode 100644 index 0000000..a06b679 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_7.png differ diff --git a/assets/images/2016/07/20111104_Fig_8.png b/assets/images/2016/07/20111104_Fig_8.png new file mode 100644 index 0000000..b839c57 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_8.png differ diff --git a/assets/images/2016/07/20111104_Fig_9.png b/assets/images/2016/07/20111104_Fig_9.png new file mode 100644 index 0000000..6f70fc8 Binary files /dev/null and b/assets/images/2016/07/20111104_Fig_9.png differ diff --git a/assets/images/2016/07/20111106_Fig_1.png b/assets/images/2016/07/20111106_Fig_1.png new file mode 100644 index 0000000..36eb9d7 Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_1.png differ diff --git a/assets/images/2016/07/20111106_Fig_10.png b/assets/images/2016/07/20111106_Fig_10.png new file mode 100644 index 0000000..ece4cbe Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_10.png differ diff --git a/assets/images/2016/07/20111106_Fig_11.png b/assets/images/2016/07/20111106_Fig_11.png new file mode 100644 index 0000000..b0ac90f Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_11.png differ diff --git a/assets/images/2016/07/20111106_Fig_12.png b/assets/images/2016/07/20111106_Fig_12.png new file mode 100644 index 0000000..c1976d7 Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_12.png differ diff --git a/assets/images/2016/07/20111106_Fig_2.png b/assets/images/2016/07/20111106_Fig_2.png new file mode 100644 index 0000000..f4a95ec Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_2.png differ diff --git a/assets/images/2016/07/20111106_Fig_3.png b/assets/images/2016/07/20111106_Fig_3.png new file mode 100644 index 0000000..ee12635 Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_3.png differ diff --git a/assets/images/2016/07/20111106_Fig_4.png b/assets/images/2016/07/20111106_Fig_4.png new file mode 100644 index 0000000..dd39b24 Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_4.png differ diff --git a/assets/images/2016/07/20111106_Fig_5.png b/assets/images/2016/07/20111106_Fig_5.png new file mode 100644 index 0000000..78da8b5 Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_5.png differ diff --git a/assets/images/2016/07/20111106_Fig_6.png b/assets/images/2016/07/20111106_Fig_6.png new file mode 100644 index 0000000..c100118 Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_6.png differ diff --git a/assets/images/2016/07/20111106_Fig_7.png b/assets/images/2016/07/20111106_Fig_7.png new file mode 100644 index 0000000..47577c1 Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_7.png differ diff --git a/assets/images/2016/07/20111106_Fig_8.png b/assets/images/2016/07/20111106_Fig_8.png new file mode 100644 index 0000000..66fcae1 Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_8.png differ diff --git a/assets/images/2016/07/20111106_Fig_9.png b/assets/images/2016/07/20111106_Fig_9.png new file mode 100644 index 0000000..d3401cc Binary files /dev/null and b/assets/images/2016/07/20111106_Fig_9.png differ diff --git a/assets/images/2016/07/2011_Fig_11.jpg b/assets/images/2016/07/2011_Fig_11.jpg new file mode 100644 index 0000000..0355038 Binary files /dev/null and b/assets/images/2016/07/2011_Fig_11.jpg differ diff --git a/assets/images/2016/07/20120220_Fig_1.png b/assets/images/2016/07/20120220_Fig_1.png new file mode 100644 index 0000000..4c9531b Binary files /dev/null and b/assets/images/2016/07/20120220_Fig_1.png differ diff --git a/assets/images/2016/07/20120220_Fig_2.png b/assets/images/2016/07/20120220_Fig_2.png new file mode 100644 index 0000000..d94ac6d Binary files /dev/null and b/assets/images/2016/07/20120220_Fig_2.png differ diff --git a/assets/images/2016/07/20120220_Fig_3.png b/assets/images/2016/07/20120220_Fig_3.png new file mode 100644 index 0000000..b198ed5 Binary files /dev/null and b/assets/images/2016/07/20120220_Fig_3.png differ diff --git a/assets/images/2016/07/20120220_Fig_4.png b/assets/images/2016/07/20120220_Fig_4.png new file mode 100644 index 0000000..bd32772 Binary files /dev/null and b/assets/images/2016/07/20120220_Fig_4.png differ diff --git a/assets/images/2016/07/20120220_Fig_5.png b/assets/images/2016/07/20120220_Fig_5.png new file mode 100644 index 0000000..57b65b7 Binary files /dev/null and b/assets/images/2016/07/20120220_Fig_5.png differ diff --git a/assets/images/2016/07/20120220_Fig_6.png b/assets/images/2016/07/20120220_Fig_6.png new file mode 100644 index 0000000..5983d6b Binary files /dev/null and b/assets/images/2016/07/20120220_Fig_6.png differ diff --git a/assets/images/2016/07/20120220_Fig_7.png b/assets/images/2016/07/20120220_Fig_7.png new file mode 100644 index 0000000..ebe8bdf Binary files /dev/null and b/assets/images/2016/07/20120220_Fig_7.png differ diff --git a/assets/images/2016/07/20120220_Fig_8.png b/assets/images/2016/07/20120220_Fig_8.png new file mode 100644 index 0000000..acf1620 Binary files /dev/null and b/assets/images/2016/07/20120220_Fig_8.png differ diff --git a/assets/images/2016/07/20120316_Fig_1.png b/assets/images/2016/07/20120316_Fig_1.png new file mode 100644 index 0000000..781c5e3 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_1.png differ diff --git a/assets/images/2016/07/20120316_Fig_10.png b/assets/images/2016/07/20120316_Fig_10.png new file mode 100644 index 0000000..581ca29 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_10.png differ diff --git a/assets/images/2016/07/20120316_Fig_11.png b/assets/images/2016/07/20120316_Fig_11.png new file mode 100644 index 0000000..5b9ce47 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_11.png differ diff --git a/assets/images/2016/07/20120316_Fig_12.png b/assets/images/2016/07/20120316_Fig_12.png new file mode 100644 index 0000000..f1688e1 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_12.png differ diff --git a/assets/images/2016/07/20120316_Fig_13.png b/assets/images/2016/07/20120316_Fig_13.png new file mode 100644 index 0000000..d17948d Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_13.png differ diff --git a/assets/images/2016/07/20120316_Fig_14.png b/assets/images/2016/07/20120316_Fig_14.png new file mode 100644 index 0000000..98c7457 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_14.png differ diff --git a/assets/images/2016/07/20120316_Fig_15.png b/assets/images/2016/07/20120316_Fig_15.png new file mode 100644 index 0000000..c7a5974 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_15.png differ diff --git a/assets/images/2016/07/20120316_Fig_16.png b/assets/images/2016/07/20120316_Fig_16.png new file mode 100644 index 0000000..bc6270c Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_16.png differ diff --git a/assets/images/2016/07/20120316_Fig_2.png b/assets/images/2016/07/20120316_Fig_2.png new file mode 100644 index 0000000..d365c57 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_2.png differ diff --git a/assets/images/2016/07/20120316_Fig_3.png b/assets/images/2016/07/20120316_Fig_3.png new file mode 100644 index 0000000..f25fa22 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_3.png differ diff --git a/assets/images/2016/07/20120316_Fig_4.png b/assets/images/2016/07/20120316_Fig_4.png new file mode 100644 index 0000000..61e8242 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_4.png differ diff --git a/assets/images/2016/07/20120316_Fig_5.png b/assets/images/2016/07/20120316_Fig_5.png new file mode 100644 index 0000000..17da14f Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_5.png differ diff --git a/assets/images/2016/07/20120316_Fig_6.png b/assets/images/2016/07/20120316_Fig_6.png new file mode 100644 index 0000000..ec62b47 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_6.png differ diff --git a/assets/images/2016/07/20120316_Fig_7.png b/assets/images/2016/07/20120316_Fig_7.png new file mode 100644 index 0000000..fcdc2ee Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_7.png differ diff --git a/assets/images/2016/07/20120316_Fig_8.png b/assets/images/2016/07/20120316_Fig_8.png new file mode 100644 index 0000000..aa52031 Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_8.png differ diff --git a/assets/images/2016/07/20120316_Fig_9.png b/assets/images/2016/07/20120316_Fig_9.png new file mode 100644 index 0000000..bb157aa Binary files /dev/null and b/assets/images/2016/07/20120316_Fig_9.png differ diff --git a/assets/images/2016/07/20121012_Fig_1.png b/assets/images/2016/07/20121012_Fig_1.png new file mode 100644 index 0000000..4e75c8d Binary files /dev/null and b/assets/images/2016/07/20121012_Fig_1.png differ diff --git a/assets/images/2016/07/20121012_Fig_2.png b/assets/images/2016/07/20121012_Fig_2.png new file mode 100644 index 0000000..72afdce Binary files /dev/null and b/assets/images/2016/07/20121012_Fig_2.png differ diff --git a/assets/images/2016/07/20121012_Fig_3.png b/assets/images/2016/07/20121012_Fig_3.png new file mode 100644 index 0000000..e46e994 Binary files /dev/null and b/assets/images/2016/07/20121012_Fig_3.png differ diff --git a/assets/images/2016/07/20121018_Fig_1.png b/assets/images/2016/07/20121018_Fig_1.png new file mode 100644 index 0000000..5476dcd Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_1.png differ diff --git a/assets/images/2016/07/20121018_Fig_10.png b/assets/images/2016/07/20121018_Fig_10.png new file mode 100644 index 0000000..37a6f32 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_10.png differ diff --git a/assets/images/2016/07/20121018_Fig_11.png b/assets/images/2016/07/20121018_Fig_11.png new file mode 100644 index 0000000..ddb6c7d Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_11.png differ diff --git a/assets/images/2016/07/20121018_Fig_12.png b/assets/images/2016/07/20121018_Fig_12.png new file mode 100644 index 0000000..7e3e34d Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_12.png differ diff --git a/assets/images/2016/07/20121018_Fig_13.png b/assets/images/2016/07/20121018_Fig_13.png new file mode 100644 index 0000000..58c43e4 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_13.png differ diff --git a/assets/images/2016/07/20121018_Fig_14.png b/assets/images/2016/07/20121018_Fig_14.png new file mode 100644 index 0000000..aeb3192 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_14.png differ diff --git a/assets/images/2016/07/20121018_Fig_14a.png b/assets/images/2016/07/20121018_Fig_14a.png new file mode 100644 index 0000000..0f27720 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_14a.png differ diff --git a/assets/images/2016/07/20121018_Fig_15.png b/assets/images/2016/07/20121018_Fig_15.png new file mode 100644 index 0000000..5f9e38e Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_15.png differ diff --git a/assets/images/2016/07/20121018_Fig_16.png b/assets/images/2016/07/20121018_Fig_16.png new file mode 100644 index 0000000..a263f1b Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_16.png differ diff --git a/assets/images/2016/07/20121018_Fig_17.png b/assets/images/2016/07/20121018_Fig_17.png new file mode 100644 index 0000000..75a25a5 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_17.png differ diff --git a/assets/images/2016/07/20121018_Fig_18.png b/assets/images/2016/07/20121018_Fig_18.png new file mode 100644 index 0000000..14f26c3 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_18.png differ diff --git a/assets/images/2016/07/20121018_Fig_19.png b/assets/images/2016/07/20121018_Fig_19.png new file mode 100644 index 0000000..4315412 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_19.png differ diff --git a/assets/images/2016/07/20121018_Fig_2.png b/assets/images/2016/07/20121018_Fig_2.png new file mode 100644 index 0000000..23240bd Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_2.png differ diff --git a/assets/images/2016/07/20121018_Fig_20.png b/assets/images/2016/07/20121018_Fig_20.png new file mode 100644 index 0000000..d3634ed Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_20.png differ diff --git a/assets/images/2016/07/20121018_Fig_21.png b/assets/images/2016/07/20121018_Fig_21.png new file mode 100644 index 0000000..272a1d6 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_21.png differ diff --git a/assets/images/2016/07/20121018_Fig_22.png b/assets/images/2016/07/20121018_Fig_22.png new file mode 100644 index 0000000..e65547e Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_22.png differ diff --git a/assets/images/2016/07/20121018_Fig_23.png b/assets/images/2016/07/20121018_Fig_23.png new file mode 100644 index 0000000..73dea7d Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_23.png differ diff --git a/assets/images/2016/07/20121018_Fig_24.png b/assets/images/2016/07/20121018_Fig_24.png new file mode 100644 index 0000000..d015709 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_24.png differ diff --git a/assets/images/2016/07/20121018_Fig_25.png b/assets/images/2016/07/20121018_Fig_25.png new file mode 100644 index 0000000..294f1c6 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_25.png differ diff --git a/assets/images/2016/07/20121018_Fig_26.png b/assets/images/2016/07/20121018_Fig_26.png new file mode 100644 index 0000000..d52a4d3 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_26.png differ diff --git a/assets/images/2016/07/20121018_Fig_27.png b/assets/images/2016/07/20121018_Fig_27.png new file mode 100644 index 0000000..be5c086 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_27.png differ diff --git a/assets/images/2016/07/20121018_Fig_28.png b/assets/images/2016/07/20121018_Fig_28.png new file mode 100644 index 0000000..fb529ca Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_28.png differ diff --git a/assets/images/2016/07/20121018_Fig_29.png b/assets/images/2016/07/20121018_Fig_29.png new file mode 100644 index 0000000..056640c Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_29.png differ diff --git a/assets/images/2016/07/20121018_Fig_3.png b/assets/images/2016/07/20121018_Fig_3.png new file mode 100644 index 0000000..7ec710c Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_3.png differ diff --git a/assets/images/2016/07/20121018_Fig_4.png b/assets/images/2016/07/20121018_Fig_4.png new file mode 100644 index 0000000..c962c1c Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_4.png differ diff --git a/assets/images/2016/07/20121018_Fig_5.png b/assets/images/2016/07/20121018_Fig_5.png new file mode 100644 index 0000000..a1fe629 Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_5.png differ diff --git a/assets/images/2016/07/20121018_Fig_6.png b/assets/images/2016/07/20121018_Fig_6.png new file mode 100644 index 0000000..7da94bc Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_6.png differ diff --git a/assets/images/2016/07/20121018_Fig_7.png b/assets/images/2016/07/20121018_Fig_7.png new file mode 100644 index 0000000..458081b Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_7.png differ diff --git a/assets/images/2016/07/20121018_Fig_8.png b/assets/images/2016/07/20121018_Fig_8.png new file mode 100644 index 0000000..22350fe Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_8.png differ diff --git a/assets/images/2016/07/20121018_Fig_9.png b/assets/images/2016/07/20121018_Fig_9.png new file mode 100644 index 0000000..e1bf2ed Binary files /dev/null and b/assets/images/2016/07/20121018_Fig_9.png differ diff --git a/assets/images/2016/07/20130101_Fig_1.png b/assets/images/2016/07/20130101_Fig_1.png new file mode 100644 index 0000000..c55d990 Binary files /dev/null and b/assets/images/2016/07/20130101_Fig_1.png differ diff --git a/assets/images/2016/07/20130101_Fig_2.png b/assets/images/2016/07/20130101_Fig_2.png new file mode 100644 index 0000000..ba0fa40 Binary files /dev/null and b/assets/images/2016/07/20130101_Fig_2.png differ diff --git a/assets/images/2016/07/20130101_Fig_3.png b/assets/images/2016/07/20130101_Fig_3.png new file mode 100644 index 0000000..ce62a5b Binary files /dev/null and b/assets/images/2016/07/20130101_Fig_3.png differ diff --git a/assets/images/2016/07/20130101_Fig_4.png b/assets/images/2016/07/20130101_Fig_4.png new file mode 100644 index 0000000..dcd844b Binary files /dev/null and b/assets/images/2016/07/20130101_Fig_4.png differ diff --git a/assets/images/2016/07/20130101_Fig_5.png b/assets/images/2016/07/20130101_Fig_5.png new file mode 100644 index 0000000..f06b625 Binary files /dev/null and b/assets/images/2016/07/20130101_Fig_5.png differ diff --git a/assets/images/2016/07/20130101_Fig_6.png b/assets/images/2016/07/20130101_Fig_6.png new file mode 100644 index 0000000..f0a5423 Binary files /dev/null and b/assets/images/2016/07/20130101_Fig_6.png differ diff --git a/assets/images/2016/07/20130118_Fig_1.png b/assets/images/2016/07/20130118_Fig_1.png new file mode 100644 index 0000000..e82c31c Binary files /dev/null and b/assets/images/2016/07/20130118_Fig_1.png differ diff --git a/assets/images/2016/07/20130118_Fig_2.png b/assets/images/2016/07/20130118_Fig_2.png new file mode 100644 index 0000000..918c8c2 Binary files /dev/null and b/assets/images/2016/07/20130118_Fig_2.png differ diff --git a/assets/images/2016/07/20130118_Fig_3.png b/assets/images/2016/07/20130118_Fig_3.png new file mode 100644 index 0000000..154d02c Binary files /dev/null and b/assets/images/2016/07/20130118_Fig_3.png differ diff --git a/assets/images/2016/07/20130118_Fig_4.png b/assets/images/2016/07/20130118_Fig_4.png new file mode 100644 index 0000000..ad7348d Binary files /dev/null and b/assets/images/2016/07/20130118_Fig_4.png differ diff --git a/assets/images/2016/07/20130317_Fig_1.png b/assets/images/2016/07/20130317_Fig_1.png new file mode 100644 index 0000000..7b719fa Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_1.png differ diff --git a/assets/images/2016/07/20130317_Fig_10.png b/assets/images/2016/07/20130317_Fig_10.png new file mode 100644 index 0000000..257d2d7 Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_10.png differ diff --git a/assets/images/2016/07/20130317_Fig_11.png b/assets/images/2016/07/20130317_Fig_11.png new file mode 100644 index 0000000..74aba20 Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_11.png differ diff --git a/assets/images/2016/07/20130317_Fig_12.png b/assets/images/2016/07/20130317_Fig_12.png new file mode 100644 index 0000000..6d8aa30 Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_12.png differ diff --git a/assets/images/2016/07/20130317_Fig_2.png b/assets/images/2016/07/20130317_Fig_2.png new file mode 100644 index 0000000..0796511 Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_2.png differ diff --git a/assets/images/2016/07/20130317_Fig_3.png b/assets/images/2016/07/20130317_Fig_3.png new file mode 100644 index 0000000..a495993 Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_3.png differ diff --git a/assets/images/2016/07/20130317_Fig_4.png b/assets/images/2016/07/20130317_Fig_4.png new file mode 100644 index 0000000..377ca8b Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_4.png differ diff --git a/assets/images/2016/07/20130317_Fig_5.png b/assets/images/2016/07/20130317_Fig_5.png new file mode 100644 index 0000000..05ae0b1 Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_5.png differ diff --git a/assets/images/2016/07/20130317_Fig_6.png b/assets/images/2016/07/20130317_Fig_6.png new file mode 100644 index 0000000..d524588 Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_6.png differ diff --git a/assets/images/2016/07/20130317_Fig_7.png b/assets/images/2016/07/20130317_Fig_7.png new file mode 100644 index 0000000..ef5e88d Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_7.png differ diff --git a/assets/images/2016/07/20130317_Fig_8.png b/assets/images/2016/07/20130317_Fig_8.png new file mode 100644 index 0000000..9416e6a Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_8.png differ diff --git a/assets/images/2016/07/20130317_Fig_9.png b/assets/images/2016/07/20130317_Fig_9.png new file mode 100644 index 0000000..176fceb Binary files /dev/null and b/assets/images/2016/07/20130317_Fig_9.png differ diff --git a/assets/images/2016/07/20160718_Fig_1.PNG b/assets/images/2016/07/20160718_Fig_1.PNG new file mode 100644 index 0000000..42d35bf Binary files /dev/null and b/assets/images/2016/07/20160718_Fig_1.PNG differ diff --git a/assets/images/2016/07/20160718_Fig_2.PNG b/assets/images/2016/07/20160718_Fig_2.PNG new file mode 100644 index 0000000..56b8d6b Binary files /dev/null and b/assets/images/2016/07/20160718_Fig_2.PNG differ diff --git a/assets/images/2016/07/20160718_Fig_3.PNG b/assets/images/2016/07/20160718_Fig_3.PNG new file mode 100644 index 0000000..fdd5a98 Binary files /dev/null and b/assets/images/2016/07/20160718_Fig_3.PNG differ diff --git a/assets/images/2016/07/20160718_Fig_4.PNG b/assets/images/2016/07/20160718_Fig_4.PNG new file mode 100644 index 0000000..337795c Binary files /dev/null and b/assets/images/2016/07/20160718_Fig_4.PNG differ diff --git a/assets/images/2016/07/Figure-1-1.png b/assets/images/2016/07/Figure-1-1.png new file mode 100644 index 0000000..5d34967 Binary files /dev/null and b/assets/images/2016/07/Figure-1-1.png differ diff --git a/assets/images/2016/07/Figure-2.png b/assets/images/2016/07/Figure-2.png new file mode 100644 index 0000000..6d1afdd Binary files /dev/null and b/assets/images/2016/07/Figure-2.png differ diff --git a/assets/images/2016/07/Figure-3.png b/assets/images/2016/07/Figure-3.png new file mode 100644 index 0000000..77dd65a Binary files /dev/null and b/assets/images/2016/07/Figure-3.png differ diff --git a/assets/images/2016/07/Figure-4.png b/assets/images/2016/07/Figure-4.png new file mode 100644 index 0000000..b2f6522 Binary files /dev/null and b/assets/images/2016/07/Figure-4.png differ diff --git a/assets/images/2016/07/Figure-5.png b/assets/images/2016/07/Figure-5.png new file mode 100644 index 0000000..2dbcc23 Binary files /dev/null and b/assets/images/2016/07/Figure-5.png differ diff --git a/assets/images/2016/07/Maitreya.png b/assets/images/2016/07/Maitreya.png new file mode 100644 index 0000000..e450c85 Binary files /dev/null and b/assets/images/2016/07/Maitreya.png differ diff --git a/assets/images/2016/07/Workday-Time-Diff-mono.svg b/assets/images/2016/07/Workday-Time-Diff-mono.svg new file mode 100644 index 0000000..f7aea8c --- /dev/null +++ b/assets/images/2016/07/Workday-Time-Diff-mono.svg @@ -0,0 +1 @@ +Get the parameter values from calling function- assigneddatetime in 'YYYY-MM-DD hh:mm' format,- closeddatetime in 'YYYY-MM-DD hh:mm' format,- starttime in 'hh:mm' format,- endtime in 'hh:mm' formatSet @starttime = starttimeSet @endtime = endtimeSet @assigneddate = assigneddatetimeSet @closeddate = closeddatetimeSet @timecount = 0Set @timevar1 = @assigneddateSet @nextdate = @assigneddateSet @timevar2 = null1.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hoursSelect time_to_sec(timediff(@endtime,@starttime))/3600into @maxhoursadaySet @checkstart = nullSet @checkend = nullSelect CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@starttime),CONCAT(SUBSTRING_INDEX(@closeddate, ' ', 1), ' ',@endtime) into @checkstart, @checkendyes@assigneddate > @checkstart@closeddate<@checkendyesnoSet @assigneddate = @assigneddateSet @closeddate = @closeddateSet @assigneddate = @assigneddateSet @closeddate = @checkendyes@closeddate<@checkendnoSET @assigneddate = @checkstartSet @closeddate = @closeddateSET @assigneddate = @checkstartSet @closeddate = @checkendSELECT DATEDIFF(@closeddate, @assigneddate)INTO @fixcountSet @count = @fixcountfixcount>0?yesif not, avoid while loop asboth assigned and closed dateare same.calculate weekday for nextdateselect weekday(@nextdate) into @weekdaySet the holiday flag toanything other than zeroif the value in nextdatefor this loop is a holidaySelect sum(if(date_format(holiday_date,'%Y-%m-%d') =substring_index(@nextdate,' ',1),1,0)) fromholiday_table where Country_codes = 'ALL'or instr(Country_codes,@param_country)>0 into@holidayflag@weekday<5? and @holidayflag=0yesit is a weekend so get nextdateWeekday_Calculationsyes@count = @fixcount?Set @timevar1 = @assigneddateSELECT CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1),' ',@endtime)INTO @timevar2yes@count = 0?noSelect concat(substring_index(@closeddate,' ',1),' ',@starttime)into @timevar1Set @timevar2 = @closeddateif neither of previous conditionsare true,then nextdate is neitherclosedate nor assigneddate so justuse the date with normal start andend timeSelect concat(@nextdate,' ',@starttime)into @timevar1SELECT CONCAT(@nextdate, ' ', @endtime)INTO @timevar21.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hours4.if the difference was negative, GREATEST willignore result in favour of 05.If result is greater than max working hours,LEAST will ignore result in favour of maxhoursdaySELECT LEAST(Greatest((TIME_TO_SEC(TIMEDIFF(@timevar2, @timevar1))) / 3600),0), @maxhoursaday)INTO @timecounttempSet @timecount = @timecounttemp + @timecountSet @timevar1 = @nextdateSELECTADDDATE(SUBSTRING_INDEX(@timevar1, ' ', 1),1)INTO @nextdateSet @count = @count - 1truecount>=0?false1.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hours4.if the difference was negative, GREATEST willignore result in favour of 05.If result is greater than max working hours,LEAST will ignore result in favour of maxhoursdaySELECT Least(Greatest(((TIME_TO_SEC(TIMEDIFF(@closeddate, @assigneddate)))/ 3600),0),@maxhoursaday) INTO @timecountreturn timecount*60 \ No newline at end of file diff --git a/assets/images/2016/07/Workday-Time-Diff.svg b/assets/images/2016/07/Workday-Time-Diff.svg new file mode 100644 index 0000000..26f0dd4 --- /dev/null +++ b/assets/images/2016/07/Workday-Time-Diff.svg @@ -0,0 +1 @@ +Get the parameter values from calling function- assigneddatetime in 'YYYY-MM-DD hh:mm' format,- closeddatetime in 'YYYY-MM-DD hh:mm' format,- starttime in 'hh:mm' format,- endtime in 'hh:mm' formatSet @starttime = starttimeSet @endtime = endtimeSet @assigneddate = assigneddatetimeSet @closeddate = closeddatetimeSet @timecount = 0Set @timevar1 = @assigneddateSet @nextdate = @assigneddateSet @timevar2 = null1.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hoursSelect time_to_sec(timediff(@endtime,@starttime))/3600into @maxhoursadaySet @checkstart = nullSet @checkend = nullSelect CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@starttime),CONCAT(SUBSTRING_INDEX(@closeddate, ' ', 1), ' ',@endtime) into @checkstart, @checkendyes@assigneddate > @checkstart@closeddate<@checkendyesnoSet @assigneddate = @assigneddateSet @closeddate = @closeddateSet @assigneddate = @assigneddateSet @closeddate = @checkendyes@closeddate<@checkendnoSET @assigneddate = @checkstartSet @closeddate = @closeddateSET @assigneddate = @checkstartSet @closeddate = @checkendSELECT DATEDIFF(@closeddate, @assigneddate)INTO @fixcountSet @count = @fixcountfixcount>0?yesif not, avoid while loop asboth assigned and closed dateare same.calculate weekday for nextdateselect weekday(@nextdate) into @weekdaySet the holiday flag toanything other than zeroif the value in nextdatefor this loop is a holidaySelect sum(if(date_format(holiday_date,'%Y-%m-%d') =substring_index(@nextdate,' ',1),1,0)) fromholiday_table where Country_codes = 'ALL'or instr(Country_codes,@param_country)>0 into@holidayflag@weekday<5? and @holidayflag=0yesit is a weekend so get nextdateWeekday_Calculationsyes@count = @fixcount?Set @timevar1 = @assigneddateSELECT CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1),' ',@endtime)INTO @timevar2yes@count = 0?noSelect concat(substring_index(@closeddate,' ',1),' ',@starttime)into @timevar1Set @timevar2 = @closeddateif neither of previous conditionsare true,then nextdate is neitherclosedate nor assigneddate so justuse the date with normal start andend timeSelect concat(@nextdate,' ',@starttime)into @timevar1SELECT CONCAT(@nextdate, ' ', @endtime)INTO @timevar21.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hours4.if the difference was negative, GREATEST willignore result in favour of 05.If result is greater than max working hours,LEAST will ignore result in favour of maxhoursdaySELECT LEAST(Greatest((TIME_TO_SEC(TIMEDIFF(@timevar2, @timevar1))) / 3600),0), @maxhoursaday)INTO @timecounttempSet @timecount = @timecounttemp + @timecountSet @timevar1 = @nextdateSELECTADDDATE(SUBSTRING_INDEX(@timevar1, ' ', 1),1)INTO @nextdateSet @count = @count - 1truecount>=0?false1.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hours4.if the difference was negative, GREATEST willignore result in favour of 05.If result is greater than max working hours,LEAST will ignore result in favour of maxhoursdaySELECT Least(Greatest(((TIME_TO_SEC(TIMEDIFF(@closeddate, @assigneddate)))/ 3600),0),@maxhoursaday) INTO @timecountreturn timecount*60 \ No newline at end of file diff --git a/assets/images/2016/07/advanced-dns-3-6-2016-171.png b/assets/images/2016/07/advanced-dns-3-6-2016-171.png new file mode 100644 index 0000000..c09aef0 Binary files /dev/null and b/assets/images/2016/07/advanced-dns-3-6-2016-171.png differ diff --git a/assets/images/2016/07/profile.PNG b/assets/images/2016/07/profile.PNG new file mode 100644 index 0000000..1e1f3b6 Binary files /dev/null and b/assets/images/2016/07/profile.PNG differ diff --git a/assets/images/2016/09/ComputerFrustration.jpg b/assets/images/2016/09/ComputerFrustration.jpg new file mode 100644 index 0000000..1666742 Binary files /dev/null and b/assets/images/2016/09/ComputerFrustration.jpg differ diff --git a/assets/images/2017/02/Administration-Command.PNG b/assets/images/2017/02/Administration-Command.PNG new file mode 100644 index 0000000..4dfed14 Binary files /dev/null and b/assets/images/2017/02/Administration-Command.PNG differ diff --git a/assets/images/2017/02/Administration-Management.PNG b/assets/images/2017/02/Administration-Management.PNG new file mode 100644 index 0000000..5dca7c6 Binary files /dev/null and b/assets/images/2017/02/Administration-Management.PNG differ diff --git a/assets/images/2017/02/CuteMarkEd-2017-02-12-20-02-02.png b/assets/images/2017/02/CuteMarkEd-2017-02-12-20-02-02.png new file mode 100644 index 0000000..cad95c1 Binary files /dev/null and b/assets/images/2017/02/CuteMarkEd-2017-02-12-20-02-02.png differ diff --git a/assets/images/2017/02/DDNS.PNG b/assets/images/2017/02/DDNS.PNG new file mode 100644 index 0000000..708af42 Binary files /dev/null and b/assets/images/2017/02/DDNS.PNG differ diff --git a/assets/images/2017/02/DateGantt.PNG b/assets/images/2017/02/DateGantt.PNG new file mode 100644 index 0000000..068c092 Binary files /dev/null and b/assets/images/2017/02/DateGantt.PNG differ diff --git a/assets/images/2017/02/GanttChartCuteMarkEd-1.PNG b/assets/images/2017/02/GanttChartCuteMarkEd-1.PNG new file mode 100644 index 0000000..1b60f6a Binary files /dev/null and b/assets/images/2017/02/GanttChartCuteMarkEd-1.PNG differ diff --git a/assets/images/2017/02/GanttChartCuteMarkEd-2.PNG b/assets/images/2017/02/GanttChartCuteMarkEd-2.PNG new file mode 100644 index 0000000..46d6de5 Binary files /dev/null and b/assets/images/2017/02/GanttChartCuteMarkEd-2.PNG differ diff --git a/assets/images/2017/02/GanttChartCuteMarkEd.PNG b/assets/images/2017/02/GanttChartCuteMarkEd.PNG new file mode 100644 index 0000000..1b60f6a Binary files /dev/null and b/assets/images/2017/02/GanttChartCuteMarkEd.PNG differ diff --git a/assets/images/2017/02/NAT-QOS-PortForwarding.PNG b/assets/images/2017/02/NAT-QOS-PortForwarding.PNG new file mode 100644 index 0000000..bfde33d Binary files /dev/null and b/assets/images/2017/02/NAT-QOS-PortForwarding.PNG differ diff --git a/assets/images/2017/02/NAT-QOS-PortRangeForwarding.PNG b/assets/images/2017/02/NAT-QOS-PortRangeForwarding.PNG new file mode 100644 index 0000000..c41ee4b Binary files /dev/null and b/assets/images/2017/02/NAT-QOS-PortRangeForwarding.PNG differ diff --git a/assets/images/2017/02/Services-Services.PNG b/assets/images/2017/02/Services-Services.PNG new file mode 100644 index 0000000..e4f2f47 Binary files /dev/null and b/assets/images/2017/02/Services-Services.PNG differ diff --git a/assets/images/2017/02/SetupBasicSetup_1.PNG b/assets/images/2017/02/SetupBasicSetup_1.PNG new file mode 100644 index 0000000..5dcb070 Binary files /dev/null and b/assets/images/2017/02/SetupBasicSetup_1.PNG differ diff --git a/assets/images/2017/02/SetupBasicSetup_2.PNG b/assets/images/2017/02/SetupBasicSetup_2.PNG new file mode 100644 index 0000000..8757139 Binary files /dev/null and b/assets/images/2017/02/SetupBasicSetup_2.PNG differ diff --git a/assets/images/2017/02/Wireless-Basic-Settings.PNG b/assets/images/2017/02/Wireless-Basic-Settings.PNG new file mode 100644 index 0000000..81e9637 Binary files /dev/null and b/assets/images/2017/02/Wireless-Basic-Settings.PNG differ diff --git a/assets/images/2017/02/Wireless-Wireless-Security.PNG b/assets/images/2017/02/Wireless-Wireless-Security.PNG new file mode 100644 index 0000000..1a623c6 Binary files /dev/null and b/assets/images/2017/02/Wireless-Wireless-Security.PNG differ diff --git a/assets/images/2017/10/Screenshot-from-2017-10-17-23-32-43.png b/assets/images/2017/10/Screenshot-from-2017-10-17-23-32-43.png new file mode 100644 index 0000000..43b0145 Binary files /dev/null and b/assets/images/2017/10/Screenshot-from-2017-10-17-23-32-43.png differ diff --git a/assets/images/2017/10/finish-install-process.PNG b/assets/images/2017/10/finish-install-process.PNG new file mode 100644 index 0000000..29ff0b8 Binary files /dev/null and b/assets/images/2017/10/finish-install-process.PNG differ diff --git a/assets/images/2017/10/nginx-not-found.PNG b/assets/images/2017/10/nginx-not-found.PNG new file mode 100644 index 0000000..35507d6 Binary files /dev/null and b/assets/images/2017/10/nginx-not-found.PNG differ diff --git a/assets/images/2017/11/Menu_001.png b/assets/images/2017/11/Menu_001.png new file mode 100644 index 0000000..389a2e9 Binary files /dev/null and b/assets/images/2017/11/Menu_001.png differ diff --git a/assets/images/2018/01/20171219_163816.jpg b/assets/images/2018/01/20171219_163816.jpg new file mode 100644 index 0000000..54cc8fc Binary files /dev/null and b/assets/images/2018/01/20171219_163816.jpg differ diff --git a/assets/images/2018/01/Workday-Time-Diff-mono.svg b/assets/images/2018/01/Workday-Time-Diff-mono.svg new file mode 100644 index 0000000..1708067 --- /dev/null +++ b/assets/images/2018/01/Workday-Time-Diff-mono.svg @@ -0,0 +1,169 @@ +Get the parameter values from calling function- assigneddatetime in 'YYYY-MM-DD hh:mm' format,- closeddatetime in 'YYYY-MM-DD hh:mm' format,- starttime in 'hh:mm' format,- endtime in 'hh:mm' formatSet @starttime = starttimeSet @endtime = endtimeSet @assigneddate = assigneddatetimeSet @closeddate = closeddatetimeSet @timecount = 0Set @timevar1 = @assigneddateSet @nextdate = @assigneddateSet @timevar2 = null1.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hoursSelect time_to_sec(timediff(@endtime,@starttime))/3600into @maxhoursadaySet @checkstart = nullSet @checkend = nullSelect CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@starttime),CONCAT(SUBSTRING_INDEX(@closeddate, ' ', 1), ' ',@endtime) into @checkstart, @checkendyes@assigneddate > @checkstart@closeddate<@checkendyesnoSet @assigneddate = @assigneddateSet @closeddate = @closeddateSet @assigneddate = @assigneddateSet @closeddate = @checkendyes@closeddate<@checkendnoSET @assigneddate = @checkstartSet @closeddate = @closeddateSET @assigneddate = @checkstartSet @closeddate = @checkendSELECT DATEDIFF(@closeddate, @assigneddate)INTO @fixcountSet @count = @fixcountfixcount>0?yesif not, avoid while loop asboth assigned and closed dateare same.calculate weekday for nextdateselect weekday(@nextdate) into @weekdaySet the holiday flag toanything other than zeroif the value in nextdatefor this loop is a holidaySelect sum(if(date_format(holiday_date,'%Y-%m-%d') =substring_index(@nextdate,' ',1),1,0)) fromholiday_table where Country_codes = 'ALL'or instr(Country_codes,@param_country)>0 into@holidayflagWeekday_Calculationsyes@count = @fixcount?Set @timevar1 = @assigneddateSELECT CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1),' ',@endtime)INTO @timevar2yes@count = 0?noSelect concat(substring_index(@closeddate,' ',1),' ',@starttime)into @timevar1Set @timevar2 = @closeddateif neither of previous conditionsare true,then nextdate is neitherclosedate nor assigneddate so justuse the date with normal start andend timeSelect concat(@nextdate,' ',@starttime)into @timevar1SELECT CONCAT(@nextdate, ' ', @endtime)INTO @timevar21.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hours4.if the difference was negative, GREATEST willignore result in favour of 05.If result is greater than max working hours,LEAST will ignore result in favour of maxhoursdaySELECT LEAST(Greatest((TIME_TO_SEC(TIMEDIFF(@timevar2, @timevar1))) / 3600),0), @maxhoursaday)INTO @timecounttempSet @timecount = @timecounttemp + @timecountyes@weekday<5? and @holidayflag=0it is a weekend so get nextdateSet @timevar1 = @nextdateSELECTADDDATE(SUBSTRING_INDEX(@timevar1, ' ', 1),1)INTO @nextdateSet @count = @count - 1truecount>=0?falsecalculate weekday for assigneddateselect weekday(@assigneddate) into @weekdaySet the holiday flag toanything other than zeroif the value in nextdatefor this loop is a holidaySelect sum(if(date_format(holiday_date,'%Y-%m-%d') =substring_index(@assigneddate,' ',1),1,0)) fromholiday_table where Country_codes = 'ALL'or instr(Country_codes,@param_country)>0 into@holidayflag@weekday<5? and @holidayflag=0yesif assigned and closed over holiday / weekend, set @timecount to 01.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hours4.if the difference was negative, GREATEST willignore result in favour of 05.If result is greater than max working hours,LEAST will ignore result in favour of maxhoursdaySELECT Least(Greatest(((TIME_TO_SEC(TIMEDIFF(@closeddate, @assigneddate)))/ 3600),0),@maxhoursaday) INTO @timecountSet @timecount = 0return timecount*60 \ No newline at end of file diff --git a/assets/images/2018/01/Workday-Time-Diff.svg b/assets/images/2018/01/Workday-Time-Diff.svg new file mode 100644 index 0000000..1f1532c --- /dev/null +++ b/assets/images/2018/01/Workday-Time-Diff.svg @@ -0,0 +1,168 @@ +Get the parameter values from calling function- assigneddatetime in 'YYYY-MM-DD hh:mm' format,- closeddatetime in 'YYYY-MM-DD hh:mm' format,- starttime in 'hh:mm' format,- endtime in 'hh:mm' formatSet @starttime = starttimeSet @endtime = endtimeSet @assigneddate = assigneddatetimeSet @closeddate = closeddatetimeSet @timecount = 0Set @timevar1 = @assigneddateSet @nextdate = @assigneddateSet @timevar2 = null1.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hoursSelect time_to_sec(timediff(@endtime,@starttime))/3600into @maxhoursadaySet @checkstart = nullSet @checkend = nullSelect CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@starttime),CONCAT(SUBSTRING_INDEX(@closeddate, ' ', 1), ' ',@endtime) into @checkstart, @checkendyes@assigneddate > @checkstart@closeddate<@checkendyesnoSet @assigneddate = @assigneddateSet @closeddate = @closeddateSet @assigneddate = @assigneddateSet @closeddate = @checkendyes@closeddate<@checkendnoSET @assigneddate = @checkstartSet @closeddate = @closeddateSET @assigneddate = @checkstartSet @closeddate = @checkendSELECT DATEDIFF(@closeddate, @assigneddate)INTO @fixcountSet @count = @fixcountfixcount>0?yesif not, avoid while loop asboth assigned and closed dateare same.calculate weekday for nextdateselect weekday(@nextdate) into @weekdaySet the holiday flag toanything other than zeroif the value in nextdatefor this loop is a holidaySelect sum(if(date_format(holiday_date,'%Y-%m-%d') =substring_index(@nextdate,' ',1),1,0)) fromholiday_table where Country_codes = 'ALL'or instr(Country_codes,@param_country)>0 into@holidayflagWeekday_Calculationsyes@count = @fixcount?Set @timevar1 = @assigneddateSELECT CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1),' ',@endtime)INTO @timevar2yes@count = 0?noSelect concat(substring_index(@closeddate,' ',1),' ',@starttime)into @timevar1Set @timevar2 = @closeddateif neither of previous conditionsare true,then nextdate is neitherclosedate nor assigneddate so justuse the date with normal start andend timeSelect concat(@nextdate,' ',@starttime)into @timevar1SELECT CONCAT(@nextdate, ' ', @endtime)INTO @timevar21.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hours4.if the difference was negative, GREATEST willignore result in favour of 05.If result is greater than max working hours,LEAST will ignore result in favour of maxhoursdaySELECT LEAST(Greatest((TIME_TO_SEC(TIMEDIFF(@timevar2, @timevar1))) / 3600),0), @maxhoursaday)INTO @timecounttempSet @timecount = @timecounttemp + @timecountyes@weekday<5? and @holidayflag=0it is a weekend so get nextdateSet @timevar1 = @nextdateSELECTADDDATE(SUBSTRING_INDEX(@timevar1, ' ', 1),1)INTO @nextdateSet @count = @count - 1truecount>=0?falsecalculate weekday for assigneddateselect weekday(@assigneddate) into @weekdaySet the holiday flag toanything other than zeroif the value in nextdatefor this loop is a holidaySelect sum(if(date_format(holiday_date,'%Y-%m-%d') =substring_index(@assigneddate,' ',1),1,0)) fromholiday_table where Country_codes = 'ALL'or instr(Country_codes,@param_country)>0 into@holidayflag@weekday<5? and @holidayflag=0yesif assigned and closed over holiday / weekend, set @timecount to 01.TIMEDIFF calculates difference between two times2.Time_to_sec function converts data into seconds3.Dividing by 3600 converts the time into hours4.if the difference was negative, GREATEST willignore result in favour of 05.If result is greater than max working hours,LEAST will ignore result in favour of maxhoursdaySELECT Least(Greatest(((TIME_TO_SEC(TIMEDIFF(@closeddate, @assigneddate)))/ 3600),0),@maxhoursaday) INTO @timecountSet @timecount = 0return timecount*60 \ No newline at end of file diff --git a/assets/images/2018/06/Gajim_2018-06-15_17-10-11.png b/assets/images/2018/06/Gajim_2018-06-15_17-10-11.png new file mode 100644 index 0000000..16bbb1b Binary files /dev/null and b/assets/images/2018/06/Gajim_2018-06-15_17-10-11.png differ diff --git a/assets/images/2018/06/PUTTY_2018-06-15_12-08-21.png b/assets/images/2018/06/PUTTY_2018-06-15_12-08-21.png new file mode 100644 index 0000000..938dd5c Binary files /dev/null and b/assets/images/2018/06/PUTTY_2018-06-15_12-08-21.png differ diff --git a/assets/images/2018/06/PUTTY_2018-06-15_12-19-30.png b/assets/images/2018/06/PUTTY_2018-06-15_12-19-30.png new file mode 100644 index 0000000..5480d0d Binary files /dev/null and b/assets/images/2018/06/PUTTY_2018-06-15_12-19-30.png differ diff --git a/assets/images/2018/06/PUTTY_2018-06-15_12-49-42.png b/assets/images/2018/06/PUTTY_2018-06-15_12-49-42.png new file mode 100644 index 0000000..41ad195 Binary files /dev/null and b/assets/images/2018/06/PUTTY_2018-06-15_12-49-42.png differ diff --git a/assets/images/2018/06/PUTTY_2018-06-15_17-23-18.png b/assets/images/2018/06/PUTTY_2018-06-15_17-23-18.png new file mode 100644 index 0000000..93e1751 Binary files /dev/null and b/assets/images/2018/06/PUTTY_2018-06-15_17-23-18.png differ diff --git a/assets/images/2018/06/chrome_2018-06-15_11-34-57.png b/assets/images/2018/06/chrome_2018-06-15_11-34-57.png new file mode 100644 index 0000000..22ba6b3 Binary files /dev/null and b/assets/images/2018/06/chrome_2018-06-15_11-34-57.png differ diff --git a/assets/images/2018/06/chrome_2018-06-15_11-43-31.png b/assets/images/2018/06/chrome_2018-06-15_11-43-31.png new file mode 100644 index 0000000..abd0c28 Binary files /dev/null and b/assets/images/2018/06/chrome_2018-06-15_11-43-31.png differ diff --git a/assets/images/2018/06/chrome_2018-06-15_11-48-32.png b/assets/images/2018/06/chrome_2018-06-15_11-48-32.png new file mode 100644 index 0000000..7bccb88 Binary files /dev/null and b/assets/images/2018/06/chrome_2018-06-15_11-48-32.png differ diff --git a/assets/images/2018/06/chrome_2018-06-15_11-53-35.png b/assets/images/2018/06/chrome_2018-06-15_11-53-35.png new file mode 100644 index 0000000..f9ade97 Binary files /dev/null and b/assets/images/2018/06/chrome_2018-06-15_11-53-35.png differ diff --git a/assets/images/2018/06/chrome_2018-06-15_17-02-32.png b/assets/images/2018/06/chrome_2018-06-15_17-02-32.png new file mode 100644 index 0000000..73f5a1a Binary files /dev/null and b/assets/images/2018/06/chrome_2018-06-15_17-02-32.png differ diff --git a/assets/images/2018/09/DSC_0469.JPG b/assets/images/2018/09/DSC_0469.JPG new file mode 100644 index 0000000..4af7579 Binary files /dev/null and b/assets/images/2018/09/DSC_0469.JPG differ diff --git a/assets/images/2020/05/2020-05-1919-17-07.png b/assets/images/2020/05/2020-05-1919-17-07.png new file mode 100644 index 0000000..e2b53d4 Binary files /dev/null and b/assets/images/2020/05/2020-05-1919-17-07.png differ diff --git a/assets/images/2020/05/2020-05-1919-17-07_o.png b/assets/images/2020/05/2020-05-1919-17-07_o.png new file mode 100644 index 0000000..543ea0a Binary files /dev/null and b/assets/images/2020/05/2020-05-1919-17-07_o.png differ diff --git a/assets/images/2020/05/2020-05-1919-46-13-1.png b/assets/images/2020/05/2020-05-1919-46-13-1.png new file mode 100644 index 0000000..06b9bde Binary files /dev/null and b/assets/images/2020/05/2020-05-1919-46-13-1.png differ diff --git a/assets/images/2020/05/2020-05-1919-46-13_o-1.png b/assets/images/2020/05/2020-05-1919-46-13_o-1.png new file mode 100644 index 0000000..5b85667 Binary files /dev/null and b/assets/images/2020/05/2020-05-1919-46-13_o-1.png differ diff --git a/assets/images/2020/05/2020-05-1920-34-14.png b/assets/images/2020/05/2020-05-1920-34-14.png new file mode 100644 index 0000000..563dd92 Binary files /dev/null and b/assets/images/2020/05/2020-05-1920-34-14.png differ diff --git a/assets/images/2020/05/2020-05-1920-34-14_o.png b/assets/images/2020/05/2020-05-1920-34-14_o.png new file mode 100644 index 0000000..563dd92 Binary files /dev/null and b/assets/images/2020/05/2020-05-1920-34-14_o.png differ diff --git a/assets/images/2020/05/2020-05-1920-35-14.png b/assets/images/2020/05/2020-05-1920-35-14.png new file mode 100644 index 0000000..8e2ee89 Binary files /dev/null and b/assets/images/2020/05/2020-05-1920-35-14.png differ diff --git a/assets/images/2020/05/2020-05-1920-35-14_o.png b/assets/images/2020/05/2020-05-1920-35-14_o.png new file mode 100644 index 0000000..8e2ee89 Binary files /dev/null and b/assets/images/2020/05/2020-05-1920-35-14_o.png differ diff --git a/assets/images/2021/03/00aec12e25c74fed8e124e788850090e.png b/assets/images/2021/03/00aec12e25c74fed8e124e788850090e.png new file mode 100644 index 0000000..1e2f339 Binary files /dev/null and b/assets/images/2021/03/00aec12e25c74fed8e124e788850090e.png differ diff --git a/assets/images/2021/03/00aec12e25c74fed8e124e788850090e_o.png b/assets/images/2021/03/00aec12e25c74fed8e124e788850090e_o.png new file mode 100644 index 0000000..1e2f339 Binary files /dev/null and b/assets/images/2021/03/00aec12e25c74fed8e124e788850090e_o.png differ diff --git a/assets/images/2021/03/053fbb025cc74ebc880f729d3dc3b6b2.png b/assets/images/2021/03/053fbb025cc74ebc880f729d3dc3b6b2.png new file mode 100644 index 0000000..14c8418 Binary files /dev/null and b/assets/images/2021/03/053fbb025cc74ebc880f729d3dc3b6b2.png differ diff --git a/assets/images/2021/03/053fbb025cc74ebc880f729d3dc3b6b2_o.png b/assets/images/2021/03/053fbb025cc74ebc880f729d3dc3b6b2_o.png new file mode 100644 index 0000000..dd19765 Binary files /dev/null and b/assets/images/2021/03/053fbb025cc74ebc880f729d3dc3b6b2_o.png differ diff --git a/assets/images/2021/03/2707fa8797d94ed08f1d6c44fc04ef85.png b/assets/images/2021/03/2707fa8797d94ed08f1d6c44fc04ef85.png new file mode 100644 index 0000000..2a83de8 Binary files /dev/null and b/assets/images/2021/03/2707fa8797d94ed08f1d6c44fc04ef85.png differ diff --git a/assets/images/2021/03/2707fa8797d94ed08f1d6c44fc04ef85_o.png b/assets/images/2021/03/2707fa8797d94ed08f1d6c44fc04ef85_o.png new file mode 100644 index 0000000..5542b77 Binary files /dev/null and b/assets/images/2021/03/2707fa8797d94ed08f1d6c44fc04ef85_o.png differ diff --git a/assets/images/2021/03/4aa44c106c724c16b27129ccc3acfb3a.png b/assets/images/2021/03/4aa44c106c724c16b27129ccc3acfb3a.png new file mode 100644 index 0000000..325a3e7 Binary files /dev/null and b/assets/images/2021/03/4aa44c106c724c16b27129ccc3acfb3a.png differ diff --git a/assets/images/2021/03/4aa44c106c724c16b27129ccc3acfb3a_o.png b/assets/images/2021/03/4aa44c106c724c16b27129ccc3acfb3a_o.png new file mode 100644 index 0000000..325a3e7 Binary files /dev/null and b/assets/images/2021/03/4aa44c106c724c16b27129ccc3acfb3a_o.png differ diff --git a/assets/images/2021/03/4c718c71ade84cb7a2c9eeb3596e32c1.png b/assets/images/2021/03/4c718c71ade84cb7a2c9eeb3596e32c1.png new file mode 100644 index 0000000..50cee92 Binary files /dev/null and b/assets/images/2021/03/4c718c71ade84cb7a2c9eeb3596e32c1.png differ diff --git a/assets/images/2021/03/4c718c71ade84cb7a2c9eeb3596e32c1_o.png b/assets/images/2021/03/4c718c71ade84cb7a2c9eeb3596e32c1_o.png new file mode 100644 index 0000000..47b6cca Binary files /dev/null and b/assets/images/2021/03/4c718c71ade84cb7a2c9eeb3596e32c1_o.png differ diff --git a/assets/images/2021/03/5229542f7a364b07bbed0356ec8f3565.png b/assets/images/2021/03/5229542f7a364b07bbed0356ec8f3565.png new file mode 100644 index 0000000..dbfe6fb Binary files /dev/null and b/assets/images/2021/03/5229542f7a364b07bbed0356ec8f3565.png differ diff --git a/assets/images/2021/03/5229542f7a364b07bbed0356ec8f3565_o.png b/assets/images/2021/03/5229542f7a364b07bbed0356ec8f3565_o.png new file mode 100644 index 0000000..6c66df2 Binary files /dev/null and b/assets/images/2021/03/5229542f7a364b07bbed0356ec8f3565_o.png differ diff --git a/assets/images/2021/03/64ed999845d644239615c3b0e2aab454.png b/assets/images/2021/03/64ed999845d644239615c3b0e2aab454.png new file mode 100644 index 0000000..e216f27 Binary files /dev/null and b/assets/images/2021/03/64ed999845d644239615c3b0e2aab454.png differ diff --git a/assets/images/2021/03/64ed999845d644239615c3b0e2aab454_o.png b/assets/images/2021/03/64ed999845d644239615c3b0e2aab454_o.png new file mode 100644 index 0000000..296a0ee Binary files /dev/null and b/assets/images/2021/03/64ed999845d644239615c3b0e2aab454_o.png differ diff --git a/assets/images/2021/03/84da158d66534dcc97a7a8bb60cba58e.png b/assets/images/2021/03/84da158d66534dcc97a7a8bb60cba58e.png new file mode 100644 index 0000000..ba66990 Binary files /dev/null and b/assets/images/2021/03/84da158d66534dcc97a7a8bb60cba58e.png differ diff --git a/assets/images/2021/03/84da158d66534dcc97a7a8bb60cba58e_o.png b/assets/images/2021/03/84da158d66534dcc97a7a8bb60cba58e_o.png new file mode 100644 index 0000000..b137146 Binary files /dev/null and b/assets/images/2021/03/84da158d66534dcc97a7a8bb60cba58e_o.png differ diff --git a/assets/images/2021/03/8c623ef74cdc41d8a6ba5fd05e30b32d.png b/assets/images/2021/03/8c623ef74cdc41d8a6ba5fd05e30b32d.png new file mode 100644 index 0000000..765521a Binary files /dev/null and b/assets/images/2021/03/8c623ef74cdc41d8a6ba5fd05e30b32d.png differ diff --git a/assets/images/2021/03/8c623ef74cdc41d8a6ba5fd05e30b32d_o.png b/assets/images/2021/03/8c623ef74cdc41d8a6ba5fd05e30b32d_o.png new file mode 100644 index 0000000..91f7c59 Binary files /dev/null and b/assets/images/2021/03/8c623ef74cdc41d8a6ba5fd05e30b32d_o.png differ diff --git a/assets/images/2021/03/8f671fb4efa542d08bdf60c1ead2756d.png b/assets/images/2021/03/8f671fb4efa542d08bdf60c1ead2756d.png new file mode 100644 index 0000000..a3680a2 Binary files /dev/null and b/assets/images/2021/03/8f671fb4efa542d08bdf60c1ead2756d.png differ diff --git a/assets/images/2021/03/8f671fb4efa542d08bdf60c1ead2756d_o.png b/assets/images/2021/03/8f671fb4efa542d08bdf60c1ead2756d_o.png new file mode 100644 index 0000000..2e5897e Binary files /dev/null and b/assets/images/2021/03/8f671fb4efa542d08bdf60c1ead2756d_o.png differ diff --git a/assets/images/2021/03/ProfilePix.jpg b/assets/images/2021/03/ProfilePix.jpg new file mode 100644 index 0000000..27d5c7b Binary files /dev/null and b/assets/images/2021/03/ProfilePix.jpg differ diff --git a/assets/images/2021/03/ProfilePix_o.jpg b/assets/images/2021/03/ProfilePix_o.jpg new file mode 100644 index 0000000..27d5c7b Binary files /dev/null and b/assets/images/2021/03/ProfilePix_o.jpg differ diff --git a/assets/images/2021/03/adfd44c4c0cf4e91aeeb8792970dbf67.png b/assets/images/2021/03/adfd44c4c0cf4e91aeeb8792970dbf67.png new file mode 100644 index 0000000..952aa3c Binary files /dev/null and b/assets/images/2021/03/adfd44c4c0cf4e91aeeb8792970dbf67.png differ diff --git a/assets/images/2021/03/adfd44c4c0cf4e91aeeb8792970dbf67_o.png b/assets/images/2021/03/adfd44c4c0cf4e91aeeb8792970dbf67_o.png new file mode 100644 index 0000000..65c1be5 Binary files /dev/null and b/assets/images/2021/03/adfd44c4c0cf4e91aeeb8792970dbf67_o.png differ diff --git a/assets/images/2021/03/cc9cdbfe94474be4b87515de72d9f98c.png b/assets/images/2021/03/cc9cdbfe94474be4b87515de72d9f98c.png new file mode 100644 index 0000000..0526618 Binary files /dev/null and b/assets/images/2021/03/cc9cdbfe94474be4b87515de72d9f98c.png differ diff --git a/assets/images/2021/03/cc9cdbfe94474be4b87515de72d9f98c_o.png b/assets/images/2021/03/cc9cdbfe94474be4b87515de72d9f98c_o.png new file mode 100644 index 0000000..7b06bf2 Binary files /dev/null and b/assets/images/2021/03/cc9cdbfe94474be4b87515de72d9f98c_o.png differ diff --git a/assets/images/2021/03/d417b24684524a05985e737b656b55a9.png b/assets/images/2021/03/d417b24684524a05985e737b656b55a9.png new file mode 100644 index 0000000..22338ce Binary files /dev/null and b/assets/images/2021/03/d417b24684524a05985e737b656b55a9.png differ diff --git a/assets/images/2021/03/d417b24684524a05985e737b656b55a9_o.png b/assets/images/2021/03/d417b24684524a05985e737b656b55a9_o.png new file mode 100644 index 0000000..b13a03c Binary files /dev/null and b/assets/images/2021/03/d417b24684524a05985e737b656b55a9_o.png differ diff --git a/assets/images/2021/03/tidepool.png b/assets/images/2021/03/tidepool.png new file mode 100644 index 0000000..c74b578 Binary files /dev/null and b/assets/images/2021/03/tidepool.png differ diff --git a/assets/images/2021/03/tidepool_o.png b/assets/images/2021/03/tidepool_o.png new file mode 100644 index 0000000..6a3bca7 Binary files /dev/null and b/assets/images/2021/03/tidepool_o.png differ diff --git a/assets/images/2023/01/1.png b/assets/images/2023/01/1.png new file mode 100644 index 0000000..a2bf0bd Binary files /dev/null and b/assets/images/2023/01/1.png differ diff --git a/assets/images/2023/01/2.png b/assets/images/2023/01/2.png new file mode 100644 index 0000000..98fd530 Binary files /dev/null and b/assets/images/2023/01/2.png differ diff --git a/assets/images/2023/01/3.png b/assets/images/2023/01/3.png new file mode 100644 index 0000000..4a42de5 Binary files /dev/null and b/assets/images/2023/01/3.png differ diff --git a/assets/images/2023/01/4.png b/assets/images/2023/01/4.png new file mode 100644 index 0000000..34a8c72 Binary files /dev/null and b/assets/images/2023/01/4.png differ diff --git a/assets/images/2023/01/5.png b/assets/images/2023/01/5.png new file mode 100644 index 0000000..0d2bf7b Binary files /dev/null and b/assets/images/2023/01/5.png differ diff --git a/assets/images/2023/01/6.png b/assets/images/2023/01/6.png new file mode 100644 index 0000000..63003f7 Binary files /dev/null and b/assets/images/2023/01/6.png differ diff --git a/assets/images/2023/01/7.png b/assets/images/2023/01/7.png new file mode 100644 index 0000000..41ddf80 Binary files /dev/null and b/assets/images/2023/01/7.png differ diff --git a/assets/images/profile_pix/profile_1.jpg b/assets/images/profile_pix/profile_1.jpg new file mode 100644 index 0000000..e84077b Binary files /dev/null and b/assets/images/profile_pix/profile_1.jpg differ diff --git a/assets/js/clipboardrouge.js b/assets/js/clipboardrouge.js new file mode 100644 index 0000000..2eb6be9 --- /dev/null +++ b/assets/js/clipboardrouge.js @@ -0,0 +1,48 @@ +/* Simple copy to clipboard action + * --- + * This script adds a simple "copy to clipboard" button to code elements located within
 elements. 
+ * Requires jQuery. Can be used with the Jekyll Minimal Mistakes theme by adding it to the 'after_footer_scripts'.
+ */
+function sleep(ms) {
+  return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+async function onClickEffect(btn, style) {
+  btn.removeClass("btn-light");
+  btn.addClass(style);
+  await sleep(250);
+  btn.removeClass(style);
+  btn.addClass("btn-light");
+}
+
+$(document).ready(function() {
+  // Create butons
+  $(".page__content pre > code").each(function() {
+      $(this).parent().prepend(
+          $(document.createElement('button')).prop({
+              type: 'button',
+              innerHTML: '',
+          })
+          .attr('title', 'Copy to clipboard')
+          .addClass('btn')
+          .addClass('btn--primary')
+          .css('position', 'absolute')
+          .css('right', '1em')
+          // Click listener
+          .on('click', function() {
+              let codeElement = $(this).parent().children('code').first();
+
+              if (!codeElement) {
+                  throw new Error("Unexpected error! No corresponding code block was found for this button.");
+              }
+
+              // Blink effect
+              onClickEffect($(this), "btn--success")
+
+              // Copy to clipoard function
+              navigator.clipboard.writeText($(codeElement).text()).then(() => true, () => true);
+              return true;
+          })
+      );
+  });
+});
\ No newline at end of file
diff --git a/assets/js/lunr/lunr-en.js b/assets/js/lunr/lunr-en.js
new file mode 100644
index 0000000..d1400a7
--- /dev/null
+++ b/assets/js/lunr/lunr-en.js
@@ -0,0 +1,69 @@
+var idx = lunr(function () {
+  this.field('title')
+  this.field('excerpt')
+  this.field('categories')
+  this.field('tags')
+  this.ref('id')
+
+  this.pipeline.remove(lunr.trimmer)
+
+  for (var item in store) {
+    this.add({
+      title: store[item].title,
+      excerpt: store[item].excerpt,
+      categories: store[item].categories,
+      tags: store[item].tags,
+      id: item
+    })
+  }
+});
+
+$(document).ready(function() {
+  $('input#search').on('keyup', function () {
+    var resultdiv = $('#results');
+    var query = $(this).val().toLowerCase();
+    var result =
+      idx.query(function (q) {
+        query.split(lunr.tokenizer.separator).forEach(function (term) {
+          q.term(term, { boost: 100 })
+          if(query.lastIndexOf(" ") != query.length-1){
+            q.term(term, {  usePipeline: false, wildcard: lunr.Query.wildcard.TRAILING, boost: 10 })
+          }
+          if (term != ""){
+            q.term(term, {  usePipeline: false, editDistance: 1, boost: 1 })
+          }
+        })
+      });
+    resultdiv.empty();
+    resultdiv.prepend('

'+result.length+' Result(s) found

'); + for (var item in result) { + var ref = result[item].ref; + if(store[ref].teaser){ + var searchitem = + '
'+ + '
'+ + '

'+ + ''+store[ref].title+''+ + '

'+ + '
'+ + ''+ + '
'+ + '

'+store[ref].excerpt.split(" ").splice(0,20).join(" ")+'...

'+ + '
'+ + '
'; + } + else{ + var searchitem = + '
'+ + '
'+ + '

'+ + ''+store[ref].title+''+ + '

'+ + '

'+store[ref].excerpt.split(" ").splice(0,20).join(" ")+'...

'+ + '
'+ + '
'; + } + resultdiv.append(searchitem); + } + }); +}); diff --git a/assets/js/lunr/lunr-gr.js b/assets/js/lunr/lunr-gr.js new file mode 100644 index 0000000..e829362 --- /dev/null +++ b/assets/js/lunr/lunr-gr.js @@ -0,0 +1,522 @@ +step1list = new Array(); +step1list["ΦΑΓΙΑ"] = "ΦΑ"; +step1list["ΦΑΓΙΟΥ"] = "ΦΑ"; +step1list["ΦΑΓΙΩΝ"] = "ΦΑ"; +step1list["ΣΚΑΓΙΑ"] = "ΣΚΑ"; +step1list["ΣΚΑΓΙΟΥ"] = "ΣΚΑ"; +step1list["ΣΚΑΓΙΩΝ"] = "ΣΚΑ"; +step1list["ΟΛΟΓΙΟΥ"] = "ΟΛΟ"; +step1list["ΟΛΟΓΙΑ"] = "ΟΛΟ"; +step1list["ΟΛΟΓΙΩΝ"] = "ΟΛΟ"; +step1list["ΣΟΓΙΟΥ"] = "ΣΟ"; +step1list["ΣΟΓΙΑ"] = "ΣΟ"; +step1list["ΣΟΓΙΩΝ"] = "ΣΟ"; +step1list["ΤΑΤΟΓΙΑ"] = "ΤΑΤΟ"; +step1list["ΤΑΤΟΓΙΟΥ"] = "ΤΑΤΟ"; +step1list["ΤΑΤΟΓΙΩΝ"] = "ΤΑΤΟ"; +step1list["ΚΡΕΑΣ"] = "ΚΡΕ"; +step1list["ΚΡΕΑΤΟΣ"] = "ΚΡΕ"; +step1list["ΚΡΕΑΤΑ"] = "ΚΡΕ"; +step1list["ΚΡΕΑΤΩΝ"] = "ΚΡΕ"; +step1list["ΠΕΡΑΣ"] = "ΠΕΡ"; +step1list["ΠΕΡΑΤΟΣ"] = "ΠΕΡ"; +step1list["ΠΕΡΑΤΑ"] = "ΠΕΡ"; +step1list["ΠΕΡΑΤΩΝ"] = "ΠΕΡ"; +step1list["ΤΕΡΑΣ"] = "ΤΕΡ"; +step1list["ΤΕΡΑΤΟΣ"] = "ΤΕΡ"; +step1list["ΤΕΡΑΤΑ"] = "ΤΕΡ"; +step1list["ΤΕΡΑΤΩΝ"] = "ΤΕΡ"; +step1list["ΦΩΣ"] = "ΦΩ"; +step1list["ΦΩΤΟΣ"] = "ΦΩ"; +step1list["ΦΩΤΑ"] = "ΦΩ"; +step1list["ΦΩΤΩΝ"] = "ΦΩ"; +step1list["ΚΑΘΕΣΤΩΣ"] = "ΚΑΘΕΣΤ"; +step1list["ΚΑΘΕΣΤΩΤΟΣ"] = "ΚΑΘΕΣΤ"; +step1list["ΚΑΘΕΣΤΩΤΑ"] = "ΚΑΘΕΣΤ"; +step1list["ΚΑΘΕΣΤΩΤΩΝ"] = "ΚΑΘΕΣΤ"; +step1list["ΓΕΓΟΝΟΣ"] = "ΓΕΓΟΝ"; +step1list["ΓΕΓΟΝΟΤΟΣ"] = "ΓΕΓΟΝ"; +step1list["ΓΕΓΟΝΟΤΑ"] = "ΓΕΓΟΝ"; +step1list["ΓΕΓΟΝΟΤΩΝ"] = "ΓΕΓΟΝ"; + +v = "[ΑΕΗΙΟΥΩ]"; +v2 = "[ΑΕΗΙΟΩ]" + +function stemWord(w) { + var stem; + var suffix; + var firstch; + var origword = w; + test1 = new Boolean(true); + + if(w.length < 4) { + return w; + } + + var re; + var re2; + var re3; + var re4; + + re = /(.*)(ΦΑΓΙΑ|ΦΑΓΙΟΥ|ΦΑΓΙΩΝ|ΣΚΑΓΙΑ|ΣΚΑΓΙΟΥ|ΣΚΑΓΙΩΝ|ΟΛΟΓΙΟΥ|ΟΛΟΓΙΑ|ΟΛΟΓΙΩΝ|ΣΟΓΙΟΥ|ΣΟΓΙΑ|ΣΟΓΙΩΝ|ΤΑΤΟΓΙΑ|ΤΑΤΟΓΙΟΥ|ΤΑΤΟΓΙΩΝ|ΚΡΕΑΣ|ΚΡΕΑΤΟΣ|ΚΡΕΑΤΑ|ΚΡΕΑΤΩΝ|ΠΕΡΑΣ|ΠΕΡΑΤΟΣ|ΠΕΡΑΤΑ|ΠΕΡΑΤΩΝ|ΤΕΡΑΣ|ΤΕΡΑΤΟΣ|ΤΕΡΑΤΑ|ΤΕΡΑΤΩΝ|ΦΩΣ|ΦΩΤΟΣ|ΦΩΤΑ|ΦΩΤΩΝ|ΚΑΘΕΣΤΩΣ|ΚΑΘΕΣΤΩΤΟΣ|ΚΑΘΕΣΤΩΤΑ|ΚΑΘΕΣΤΩΤΩΝ|ΓΕΓΟΝΟΣ|ΓΕΓΟΝΟΤΟΣ|ΓΕΓΟΝΟΤΑ|ΓΕΓΟΝΟΤΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + w = stem + step1list[suffix]; + test1 = false; + } + + re = /^(.+?)(ΑΔΕΣ|ΑΔΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + + reg1 = /(ΟΚ|ΜΑΜ|ΜΑΝ|ΜΠΑΜΠ|ΠΑΤΕΡ|ΓΙΑΓΙ|ΝΤΑΝΤ|ΚΥΡ|ΘΕΙ|ΠΕΘΕΡ)$/; + + if(!(reg1.test(w))) { + w = w + "ΑΔ"; + } + } + + re2 = /^(.+?)(ΕΔΕΣ|ΕΔΩΝ)$/; + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + + exept2 = /(ΟΠ|ΙΠ|ΕΜΠ|ΥΠ|ΓΗΠ|ΔΑΠ|ΚΡΑΣΠ|ΜΙΛ)$/; + + if(exept2.test(w)) { + w = w + "ΕΔ"; + } + } + + re3 = /^(.+?)(ΟΥΔΕΣ|ΟΥΔΩΝ)$/; + + if(re3.test(w)) { + var fp = re3.exec(w); + stem = fp[1]; + w = stem; + + exept3 = /(ΑΡΚ|ΚΑΛΙΑΚ|ΠΕΤΑΛ|ΛΙΧ|ΠΛΕΞ|ΣΚ|Σ|ΦΛ|ΦΡ|ΒΕΛ|ΛΟΥΛ|ΧΝ|ΣΠ|ΤΡΑΓ|ΦΕ)$/; + + if(exept3.test(w)) { + w = w + "ΟΥΔ"; + } + } + + re4 = /^(.+?)(ΕΩΣ|ΕΩΝ)$/; + + if(re4.test(w)) { + var fp = re4.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept4 = /^(Θ|Δ|ΕΛ|ΓΑΛ|Ν|Π|ΙΔ|ΠΑΡ)$/; + + if(exept4.test(w)) { + w = w + "Ε"; + } + } + + re = /^(.+?)(ΙΑ|ΙΟΥ|ΙΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + re2 = new RegExp(v + "$"); + test1 = false; + + if(re2.test(w)) { + w = stem + "Ι"; + } + } + + re = /^(.+?)(ΙΚΑ|ΙΚΟ|ΙΚΟΥ|ΙΚΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re2 = new RegExp(v + "$"); + exept5 = /^(ΑΛ|ΑΔ|ΕΝΔ|ΑΜΑΝ|ΑΜΜΟΧΑΛ|ΗΘ|ΑΝΗΘ|ΑΝΤΙΔ|ΦΥΣ|ΒΡΩΜ|ΓΕΡ|ΕΞΩΔ|ΚΑΛΠ|ΚΑΛΛΙΝ|ΚΑΤΑΔ|ΜΟΥΛ|ΜΠΑΝ|ΜΠΑΓΙΑΤ|ΜΠΟΛ|ΜΠΟΣ|ΝΙΤ|ΞΙΚ|ΣΥΝΟΜΗΛ|ΠΕΤΣ|ΠΙΤΣ|ΠΙΚΑΝΤ|ΠΛΙΑΤΣ|ΠΟΣΤΕΛΝ|ΠΡΩΤΟΔ|ΣΕΡΤ|ΣΥΝΑΔ|ΤΣΑΜ|ΥΠΟΔ|ΦΙΛΟΝ|ΦΥΛΟΔ|ΧΑΣ)$/; + + if((exept5.test(w)) || (re2.test(w))) { + w = w + "ΙΚ"; + } + } + + re = /^(.+?)(ΑΜΕ)$/; + re2 = /^(.+?)(ΑΓΑΜΕ|ΗΣΑΜΕ|ΟΥΣΑΜΕ|ΗΚΑΜΕ|ΗΘΗΚΑΜΕ)$/; + if(w == "ΑΓΑΜΕ") { + w = "ΑΓΑΜ"; + } + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + } + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept6 = /^(ΑΝΑΠ|ΑΠΟΘ|ΑΠΟΚ|ΑΠΟΣΤ|ΒΟΥΒ|ΞΕΘ|ΟΥΛ|ΠΕΘ|ΠΙΚΡ|ΠΟΤ|ΣΙΧ|Χ)$/; + + if(exept6.test(w)) { + w = w + "ΑΜ"; + } + } + + re2 = /^(.+?)(ΑΝΕ)$/; + re3 = /^(.+?)(ΑΓΑΝΕ|ΗΣΑΝΕ|ΟΥΣΑΝΕ|ΙΟΝΤΑΝΕ|ΙΟΤΑΝΕ|ΙΟΥΝΤΑΝΕ|ΟΝΤΑΝΕ|ΟΤΑΝΕ|ΟΥΝΤΑΝΕ|ΗΚΑΝΕ|ΗΘΗΚΑΝΕ)$/; + + if(re3.test(w)) { + var fp = re3.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re3 = /^(ΤΡ|ΤΣ)$/; + + if(re3.test(w)) { + w = w + "ΑΓΑΝ"; + } + } + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re2 = new RegExp(v2 + "$"); + exept7 = /^(ΒΕΤΕΡ|ΒΟΥΛΚ|ΒΡΑΧΜ|Γ|ΔΡΑΔΟΥΜ|Θ|ΚΑΛΠΟΥΖ|ΚΑΣΤΕΛ|ΚΟΡΜΟΡ|ΛΑΟΠΛ|ΜΩΑΜΕΘ|Μ|ΜΟΥΣΟΥΛΜ|Ν|ΟΥΛ|Π|ΠΕΛΕΚ|ΠΛ|ΠΟΛΙΣ|ΠΟΡΤΟΛ|ΣΑΡΑΚΑΤΣ|ΣΟΥΛΤ|ΤΣΑΡΛΑΤ|ΟΡΦ|ΤΣΙΓΓ|ΤΣΟΠ|ΦΩΤΟΣΤΕΦ|Χ|ΨΥΧΟΠΛ|ΑΓ|ΟΡΦ|ΓΑΛ|ΓΕΡ|ΔΕΚ|ΔΙΠΛ|ΑΜΕΡΙΚΑΝ|ΟΥΡ|ΠΙΘ|ΠΟΥΡΙΤ|Σ|ΖΩΝΤ|ΙΚ|ΚΑΣΤ|ΚΟΠ|ΛΙΧ|ΛΟΥΘΗΡ|ΜΑΙΝΤ|ΜΕΛ|ΣΙΓ|ΣΠ|ΣΤΕΓ|ΤΡΑΓ|ΤΣΑΓ|Φ|ΕΡ|ΑΔΑΠ|ΑΘΙΓΓ|ΑΜΗΧ|ΑΝΙΚ|ΑΝΟΡΓ|ΑΠΗΓ|ΑΠΙΘ|ΑΤΣΙΓΓ|ΒΑΣ|ΒΑΣΚ|ΒΑΘΥΓΑΛ|ΒΙΟΜΗΧ|ΒΡΑΧΥΚ|ΔΙΑΤ|ΔΙΑΦ|ΕΝΟΡΓ|ΘΥΣ|ΚΑΠΝΟΒΙΟΜΗΧ|ΚΑΤΑΓΑΛ|ΚΛΙΒ|ΚΟΙΛΑΡΦ|ΛΙΒ|ΜΕΓΛΟΒΙΟΜΗΧ|ΜΙΚΡΟΒΙΟΜΗΧ|ΝΤΑΒ|ΞΗΡΟΚΛΙΒ|ΟΛΙΓΟΔΑΜ|ΟΛΟΓΑΛ|ΠΕΝΤΑΡΦ|ΠΕΡΗΦ|ΠΕΡΙΤΡ|ΠΛΑΤ|ΠΟΛΥΔΑΠ|ΠΟΛΥΜΗΧ|ΣΤΕΦ|ΤΑΒ|ΤΕΤ|ΥΠΕΡΗΦ|ΥΠΟΚΟΠ|ΧΑΜΗΛΟΔΑΠ|ΨΗΛΟΤΑΒ)$/; + + if((re2.test(w)) || (exept7.test(w))) { + w = w + "ΑΝ"; + } + } + + re3 = /^(.+?)(ΕΤΕ)$/; + re4 = /^(.+?)(ΗΣΕΤΕ)$/; + + if(re4.test(w)) { + var fp = re4.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + } + + if(re3.test(w)) { + var fp = re3.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re3 = new RegExp(v2 + "$"); + exept8 = /(ΟΔ|ΑΙΡ|ΦΟΡ|ΤΑΘ|ΔΙΑΘ|ΣΧ|ΕΝΔ|ΕΥΡ|ΤΙΘ|ΥΠΕΡΘ|ΡΑΘ|ΕΝΘ|ΡΟΘ|ΣΘ|ΠΥΡ|ΑΙΝ|ΣΥΝΔ|ΣΥΝ|ΣΥΝΘ|ΧΩΡ|ΠΟΝ|ΒΡ|ΚΑΘ|ΕΥΘ|ΕΚΘ|ΝΕΤ|ΡΟΝ|ΑΡΚ|ΒΑΡ|ΒΟΛ|ΩΦΕΛ)$/; + exept9 = /^(ΑΒΑΡ|ΒΕΝ|ΕΝΑΡ|ΑΒΡ|ΑΔ|ΑΘ|ΑΝ|ΑΠΛ|ΒΑΡΟΝ|ΝΤΡ|ΣΚ|ΚΟΠ|ΜΠΟΡ|ΝΙΦ|ΠΑΓ|ΠΑΡΑΚΑΛ|ΣΕΡΠ|ΣΚΕΛ|ΣΥΡΦ|ΤΟΚ|Υ|Δ|ΕΜ|ΘΑΡΡ|Θ)$/; + + if((re3.test(w)) || (exept8.test(w)) || (exept9.test(w))) { + w = w + "ΕΤ"; + } + } + + re = /^(.+?)(ΟΝΤΑΣ|ΩΝΤΑΣ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept10 = /^(ΑΡΧ)$/; + exept11 = /(ΚΡΕ)$/; + if(exept10.test(w)) { + w = w + "ΟΝΤ"; + } + if(exept11.test(w)) { + w = w + "ΩΝΤ"; + } + } + + re = /^(.+?)(ΟΜΑΣΤΕ|ΙΟΜΑΣΤΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept11 = /^(ΟΝ)$/; + + if(exept11.test(w)) { + w = w + "ΟΜΑΣΤ"; + } + } + + re = /^(.+?)(ΕΣΤΕ)$/; + re2 = /^(.+?)(ΙΕΣΤΕ)$/; + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + re2 = /^(Π|ΑΠ|ΣΥΜΠ|ΑΣΥΜΠ|ΑΚΑΤΑΠ|ΑΜΕΤΑΜΦ)$/; + + if(re2.test(w)) { + w = w + "ΙΕΣΤ"; + } + } + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept12 = /^(ΑΛ|ΑΡ|ΕΚΤΕΛ|Ζ|Μ|Ξ|ΠΑΡΑΚΑΛ|ΑΡ|ΠΡΟ|ΝΙΣ)$/; + + if(exept12.test(w)) { + w = w + "ΕΣΤ"; + } + } + + re = /^(.+?)(ΗΚΑ|ΗΚΕΣ|ΗΚΕ)$/; + re2 = /^(.+?)(ΗΘΗΚΑ|ΗΘΗΚΕΣ|ΗΘΗΚΕ)$/; + + if(re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + } + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept13 = /(ΣΚΩΛ|ΣΚΟΥΛ|ΝΑΡΘ|ΣΦ|ΟΘ|ΠΙΘ)$/; + exept14 = /^(ΔΙΑΘ|Θ|ΠΑΡΑΚΑΤΑΘ|ΠΡΟΣΘ|ΣΥΝΘ|)$/; + + if((exept13.test(w)) || (exept14.test(w))) { + w = w + "ΗΚ"; + } + } + + re = /^(.+?)(ΟΥΣΑ|ΟΥΣΕΣ|ΟΥΣΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept15 = /^(ΦΑΡΜΑΚ|ΧΑΔ|ΑΓΚ|ΑΝΑΡΡ|ΒΡΟΜ|ΕΚΛΙΠ|ΛΑΜΠΙΔ|ΛΕΧ|Μ|ΠΑΤ|Ρ|Λ|ΜΕΔ|ΜΕΣΑΖ|ΥΠΟΤΕΙΝ|ΑΜ|ΑΙΘ|ΑΝΗΚ|ΔΕΣΠΟΖ|ΕΝΔΙΑΦΕΡ|ΔΕ|ΔΕΥΤΕΡΕΥ|ΚΑΘΑΡΕΥ|ΠΛΕ|ΤΣΑ)$/; + exept16 = /(ΠΟΔΑΡ|ΒΛΕΠ|ΠΑΝΤΑΧ|ΦΡΥΔ|ΜΑΝΤΙΛ|ΜΑΛΛ|ΚΥΜΑΤ|ΛΑΧ|ΛΗΓ|ΦΑΓ|ΟΜ|ΠΡΩΤ)$/; + + if((exept15.test(w)) || (exept16.test(w))) { + w = w + "ΟΥΣ"; + } + } + + re = /^(.+?)(ΑΓΑ|ΑΓΕΣ|ΑΓΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept17 = /^(ΨΟΦ|ΝΑΥΛΟΧ)$/; + exept20 = /(ΚΟΛΛ)$/; + exept18 = /^(ΑΒΑΣΤ|ΠΟΛΥΦ|ΑΔΗΦ|ΠΑΜΦ|Ρ|ΑΣΠ|ΑΦ|ΑΜΑΛ|ΑΜΑΛΛΙ|ΑΝΥΣΤ|ΑΠΕΡ|ΑΣΠΑΡ|ΑΧΑΡ|ΔΕΡΒΕΝ|ΔΡΟΣΟΠ|ΞΕΦ|ΝΕΟΠ|ΝΟΜΟΤ|ΟΛΟΠ|ΟΜΟΤ|ΠΡΟΣΤ|ΠΡΟΣΩΠΟΠ|ΣΥΜΠ|ΣΥΝΤ|Τ|ΥΠΟΤ|ΧΑΡ|ΑΕΙΠ|ΑΙΜΟΣΤ|ΑΝΥΠ|ΑΠΟΤ|ΑΡΤΙΠ|ΔΙΑΤ|ΕΝ|ΕΠΙΤ|ΚΡΟΚΑΛΟΠ|ΣΙΔΗΡΟΠ|Λ|ΝΑΥ|ΟΥΛΑΜ|ΟΥΡ|Π|ΤΡ|Μ)$/; + exept19 = /(ΟΦ|ΠΕΛ|ΧΟΡΤ|ΛΛ|ΣΦ|ΡΠ|ΦΡ|ΠΡ|ΛΟΧ|ΣΜΗΝ)$/; + + if(((exept18.test(w)) || (exept19.test(w))) && !((exept17.test(w)) || (exept20.test(w)))) { + w = w + "ΑΓ"; + } + } + + re = /^(.+?)(ΗΣΕ|ΗΣΟΥ|ΗΣΑ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept21 = /^(Ν|ΧΕΡΣΟΝ|ΔΩΔΕΚΑΝ|ΕΡΗΜΟΝ|ΜΕΓΑΛΟΝ|ΕΠΤΑΝ)$/; + + if(exept21.test(w)) { + w = w + "ΗΣ"; + } + } + + re = /^(.+?)(ΗΣΤΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept22 = /^(ΑΣΒ|ΣΒ|ΑΧΡ|ΧΡ|ΑΠΛ|ΑΕΙΜΝ|ΔΥΣΧΡ|ΕΥΧΡ|ΚΟΙΝΟΧΡ|ΠΑΛΙΜΨ)$/; + + if(exept22.test(w)) { + w = w + "ΗΣΤ"; + } + } + + re = /^(.+?)(ΟΥΝΕ|ΗΣΟΥΝΕ|ΗΘΟΥΝΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept23 = /^(Ν|Ρ|ΣΠΙ|ΣΤΡΑΒΟΜΟΥΤΣ|ΚΑΚΟΜΟΥΤΣ|ΕΞΩΝ)$/; + + if(exept23.test(w)) { + w = w + "ΟΥΝ"; + } + } + + re = /^(.+?)(ΟΥΜΕ|ΗΣΟΥΜΕ|ΗΘΟΥΜΕ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + test1 = false; + + exept24 = /^(ΠΑΡΑΣΟΥΣ|Φ|Χ|ΩΡΙΟΠΛ|ΑΖ|ΑΛΛΟΣΟΥΣ|ΑΣΟΥΣ)$/; + + if(exept24.test(w)) { + w = w + "ΟΥΜ"; + } + } + + re = /^(.+?)(ΜΑΤΑ|ΜΑΤΩΝ|ΜΑΤΟΣ)$/; + re2 = /^(.+?)(Α|ΑΓΑΤΕ|ΑΓΑΝ|ΑΕΙ|ΑΜΑΙ|ΑΝ|ΑΣ|ΑΣΑΙ|ΑΤΑΙ|ΑΩ|Ε|ΕΙ|ΕΙΣ|ΕΙΤΕ|ΕΣΑΙ|ΕΣ|ΕΤΑΙ|Ι|ΙΕΜΑΙ|ΙΕΜΑΣΤΕ|ΙΕΤΑΙ|ΙΕΣΑΙ|ΙΕΣΑΣΤΕ|ΙΟΜΑΣΤΑΝ|ΙΟΜΟΥΝ|ΙΟΜΟΥΝΑ|ΙΟΝΤΑΝ|ΙΟΝΤΟΥΣΑΝ|ΙΟΣΑΣΤΑΝ|ΙΟΣΑΣΤΕ|ΙΟΣΟΥΝ|ΙΟΣΟΥΝΑ|ΙΟΤΑΝ|ΙΟΥΜΑ|ΙΟΥΜΑΣΤΕ|ΙΟΥΝΤΑΙ|ΙΟΥΝΤΑΝ|Η|ΗΔΕΣ|ΗΔΩΝ|ΗΘΕΙ|ΗΘΕΙΣ|ΗΘΕΙΤΕ|ΗΘΗΚΑΤΕ|ΗΘΗΚΑΝ|ΗΘΟΥΝ|ΗΘΩ|ΗΚΑΤΕ|ΗΚΑΝ|ΗΣ|ΗΣΑΝ|ΗΣΑΤΕ|ΗΣΕΙ|ΗΣΕΣ|ΗΣΟΥΝ|ΗΣΩ|Ο|ΟΙ|ΟΜΑΙ|ΟΜΑΣΤΑΝ|ΟΜΟΥΝ|ΟΜΟΥΝΑ|ΟΝΤΑΙ|ΟΝΤΑΝ|ΟΝΤΟΥΣΑΝ|ΟΣ|ΟΣΑΣΤΑΝ|ΟΣΑΣΤΕ|ΟΣΟΥΝ|ΟΣΟΥΝΑ|ΟΤΑΝ|ΟΥ|ΟΥΜΑΙ|ΟΥΜΑΣΤΕ|ΟΥΝ|ΟΥΝΤΑΙ|ΟΥΝΤΑΝ|ΟΥΣ|ΟΥΣΑΝ|ΟΥΣΑΤΕ|Υ|ΥΣ|Ω|ΩΝ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem + "ΜΑ"; + } + + if((re2.test(w)) && (test1)) { + var fp = re2.exec(w); + stem = fp[1]; + w = stem; + + } + + re = /^(.+?)(ΕΣΤΕΡ|ΕΣΤΑΤ|ΟΤΕΡ|ΟΤΑΤ|ΥΤΕΡ|ΥΤΑΤ|ΩΤΕΡ|ΩΤΑΤ)$/; + + if(re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem; + } + + return w; +}; + +var greekStemmer = function (token) { + return token.update(function (word) { + return stemWord(word); + }) +} + +var idx = lunr(function () { + this.field('title') + this.field('excerpt') + this.field('categories') + this.field('tags') + this.ref('id') + + this.pipeline.remove(lunr.trimmer) + this.pipeline.add(greekStemmer) + this.pipeline.remove(lunr.stemmer) + + for (var item in store) { + this.add({ + title: store[item].title, + excerpt: store[item].excerpt, + categories: store[item].categories, + tags: store[item].tags, + id: item + }) + } +}); + +$(document).ready(function() { + $('input#search').on('keyup', function () { + var resultdiv = $('#results'); + var query = $(this).val().toLowerCase(); + var result = + idx.query(function (q) { + query.split(lunr.tokenizer.separator).forEach(function (term) { + q.term(term, { boost: 100 }) + if(query.lastIndexOf(" ") != query.length-1){ + q.term(term, { usePipeline: false, wildcard: lunr.Query.wildcard.TRAILING, boost: 10 }) + } + if (term != ""){ + q.term(term, { usePipeline: false, editDistance: 1, boost: 1 }) + } + }) + }); + resultdiv.empty(); + resultdiv.prepend('

'+result.length+' Result(s) found

'); + for (var item in result) { + var ref = result[item].ref; + if(store[ref].teaser){ + var searchitem = + '
'+ + '
'+ + '

'+ + ''+store[ref].title+''+ + '

'+ + '
'+ + ''+ + '
'+ + '

'+store[ref].excerpt.split(" ").splice(0,20).join(" ")+'...

'+ + '
'+ + '
'; + } + else{ + var searchitem = + '
'+ + '
'+ + '

'+ + ''+store[ref].title+''+ + '

'+ + '

'+store[ref].excerpt.split(" ").splice(0,20).join(" ")+'...

'+ + '
'+ + '
'; + } + resultdiv.append(searchitem); + } + }); +}); diff --git a/assets/js/lunr/lunr-store.js b/assets/js/lunr/lunr-store.js new file mode 100644 index 0000000..66334f2 --- /dev/null +++ b/assets/js/lunr/lunr-store.js @@ -0,0 +1,403 @@ +var store = [{ + "title": "AAO A150L Step by Step Installation Process for Linux4one", + "excerpt":"Linux4one is the only Linux distro other than pre-installed Linpus that worked out of the box on my and two other Acer aspire one A150L which belonged to my friends. With the GUI same as that for Ubuntu Netbook Remix, it is a fantastic OS to have on your machine. The only downside I felt was that the icons looked little washed out compared to those on actual UNR but that is not so much of an issue. Everything pretty much worked out of the box which matters the most. I did find the splash screen at the time of start-up a little childish but again these are not major issues. However if you would rather have a more professional look and feel, Ubuntu may be your choice. Refer to other post on step by step process for installing Ubuntu Netbook Remix but be forewarned that not everything works as well on UNR as of now, at least not for me. I am still working and finding out more on how these can work but given the fact that my knowledge of Linux is zero it can take some time. I have compiled these tutorials solely by hunting on net and felt this will be my way of giving back to community. I hope you find it useful. If you do find this useful, please don’t forget to put a comment. A thanks will be enough to keep me motivated. Without further ado, the process is as below. You will need: Two 2GB card with a USB Card Reader or a 2GB USB stick formatted to FAT32 One 1GB card with a USB Card Reader or a 1GB USB stick formatted to FAT32 One PC / Laptop with optical drive (DVD Drive) and windows installed as this tutorial uses a Windows based laptop. Ensure that you AAO is having a constant AC supply and you are not relying on battery. Lot of patience 1. Create a recovery USB for Acer Aspire One A150L: (Optional) This should be quite straight forward. Insert the recovery DVD that came with your Acer Aspire One A150. Connect the 2GB Card / USB stick and start the process. On different laptops this may mean pressing a different function key (F1 to F12) but generally it will be F2, F8 or F12. So once your laptop shows the first screen after switching on, at that time press the relevant key to enter the boot set-up, now goto boot and change the order of boot to select DVD as first preference. Now press F10 to save and exit. Your laptop will now boot using the recovery DVD and should help you create the recovery USB. It is best to once test if the recovery USB is working or not. You can do so by now plugging your recovery DVD into AAO and starting it, press F12 and change the boot order to make USB disk the first option. If the recovery USB is created properly it will start the recovery process. If this does not happen, you are among the unfortunate few who received a faulty DVD as I have seen many posts around these. In such situations, you will find this blogpost very handy: http://macles.blogspot.com/2008/12/acer-aspire-one-recovery-dvd.html macles.blogspot.com has a wealth of information on how to customise Linpus Linux and everything on this site is explained very nicely so incase you want to stick to Linpus Linus, it is worth to bookmark this site. Anyway, now one way or the other once your recovery USB is ready, we have minimised the risk of not being able to restore to factory conditions in case you do not like the end result of what we have set out to achieve. 2. Update BIOS Version to latest V.3309 (Optional) You will be able to download the latest BIOS V.3309 from ACER website. The ftp link to the site is ftp://ftp.acer-euro.com/netbook/aspire_one_110/bios/ and http link is http://support.acer-euro.com/drivers/notebook/as_one_150.html Once you have downloaded the latest version, you will need to make a bootable USB. I used Unetbootin which will be used later on for creating installatioon USB for Linux4one as well so you will need to now download it from here. After downloading, connect your 1 GB Card / USB stick and run the unetbootin-windows-357.exe file and select the screen as shown below: With this setup achieved, click OK. Once this is completed, extract all the files from BIOS zip file into the root of USB Drive. Remember it is important to have it in Root. Now connect this USB drive to your Acer Aspire One, reboot it and press F12 to select the bootable device.System will show an error message around missing CD ROm. Ignore it. The AAO150 screen will now show A prompt A>. Type C: in front of A> and press enter. This should change your drive to C>. Now type 3309.BAT. This should start the flashing of BIOS to V.3309. Once completed AAO150 will reboot. This is it. Your BIOS should now be updated to latest version. 3. Create a installation USB for Linux4one v1.1: While the latest version of Linux4one is 1.5 and is available for download I was unable to make a successful installation USB from available iso. Therefore, I downloaded the V1.1 from this link: http://downloads.quellicheilpc.com/linux4one/1.1/index.html I don’t know how long this version will be hosted there so I will at a later point in time try and upload this iso to some other filehost as well. Once you have downloaded the iso, plug your 2GB card / USB and start Unetbootin. Select the Disk image radio button as shown in figure and select the path for linux4one iso image that you have downloaded.Select the USB Drive and click OK. This will take some time but once done it will ask for a restart. Do NOT restart and cancel it. Your installation USB is now ready. 4. Install Linux4one Official Guide Now plug the Installation USB made above and plug it into your AAO150 and boot it. Press F12 and select the boot device. Once the system starts booting throgh USB you will be presented with the options, select Option \"Install Linux4One\". Now the only place which turns out confusing could be when during installation it asks for partitioning. I did not want dual boot so I picked up option 2 which installs on whole hard drive.If you are not a power user, I don’t recommend having dual boot for AAO150 but that has to be your call. Right, this will ensure that your Linux4one V1.1 is now installed. At this stage, you will reboot, configure location keyboard etc etc and restart once again. Check that all is working as expected so far. Only thing that I found not responding well was microphone. It was not working for Skype and hence the next step. 5. Download update script for Linux4one v1.5 Official Guide Download the script from here onto your AAO150L using mozilla browser. Now goto Accessories and click on \"Terminal\". As mozilla downloads directly to \"Desktop\" directory you will need to change your directore accordingly. The easiest way will be to click on the \"Desktop\" link in right hand side navigator on your main screen. This will open the browser, now note the path to Desktop. In my case it was /home/Ankit/Desktop/ so I gave the following commands: cd /home/Ankit/Desktop/ tar -xjvf linux4one-1.5.tar.bz2 cd linux4one-1.5 sudo ./aggiornamento.sh After this it will start installing and ask several questions that you just type Y and press enter. At one point it will also ask do you want to uninstall mozilla and you answer Y as anyway we will install it again in next step. Right now with V1.5 the browser provided is IceCat which is same as mozilla anyway. This will take about good 15 to 20 minutes but at the end you would have got latest version of Linux4one installed. 6. Install Mozilla (Optional): With Linux4one installed, more than half of the work is already done but with last step mozilla was uninstalled and if you are used to Mozilla and would want to install it back it is quite straight forward. Just goto Administration and select Synaptic Package Manager. It will then ask you to enter your password to perform administrative tasks. You must now enter the password that you gave during installation. It will now open a window. In Search field enter \"mozilla\" and it will show several options listed. If you scroll you will find an entry \"firefox\" under package. When you select that you can read the description that will be something like \"Safe and easy web browser from Mozilla\". Select the checkbox next to this entry and click \"Mark for Installation\". It will show you dependencies, just say yes to everything and then it will download and install Mozilla Browser for you. 7. Install and configure Skype Goto www.skype.com and download the skype version for Ubuntu 7.04 - 8.04. Mozilla browser will download it to Desktop folder. Now once the download is complete right click on the downloaded file and click on \"Open with \"GDebi Package Installer\". You will be asked for password, enter the password you gave during installation of Linux4one. After that Skype installation will begin. There will be some dialogue boxes on dependencies for which you just click Yes / OK. Once skype is installed it will appear as an icon in your desktop under Internet. If you want it under favourite, right click the icon and select add to favourite. This can be done for all installed softwares. Now open skype, login and at left side bottom corner click on skype settings, select Options. Now in Options select \"Video Devices\" and click on Test to check that Webcam is set correct. Next click on \"Sound Devices\" and click on Make a sound Test and Make a test call. If all is configured fine you should hear a voice and a test call will be made and in test call you will be asked to speak and what you speak will be played back that you should be able to hear. It did not work for me at first but when I selected HDA Intel (hw:Intel,0) in all three it worked. However for my friends netbook I had to do more circus and if you are not able to resolve your mic issues, it is time to move to next step. 8. Modify Mic settings (Remember : you need to do this only if in previous step mic did not work.) Official Guide While there is some explanation on Official guide it took me some time to figure out how to get those windows to look like they do in the screenshots there. First goto Preferences and scroll down. You will see \"Sound\" icon. Click on this and change \"Sound Playback\" and \"Sound Capture\" to OSS. Next change the \"Device\" to \"Realtek ALC268 (OSS Mixer) Now close this window and click on Volume icon in right hand corner of screen and then click on Edit and select device. Then for HDA Intel (ALSA Mixer) select all options that are not selected - Capture, Capture 1, Beep, Input, Input 1. I have borrowed the image from link above to keep information at one place. Ensure that on last tab you select \"Mic\" and not \"Front Mic\". That is it. This should complete the mic settings. Now we need to check it in Skype. In official tutorial they suggest testing with recorder but that did not work for my friends AAO150 but skype did. I figure we anyway need it for skype so why bother with voice recorder at all. So over to next step then. 9. Test Skype Test skype the same way we did in step 7 and see if it is working. Click on \"Sound Devices\" and click on Make a sound Test and Make a test call. If all is configured fine you should hear a voice and a test call will be made and in test call you will be asked to speak and what you speak will be played back that you should be able to hear. If this does not work try different combinations from the dropdown one should definitely work. 10. Extra – Install Moneydance (Optional) OK now is one piece of software that is not used by everyone. Moneydance is a moneymanagement tool. It is a paid software that I use for managing my finances and have grown quite fond of it and dependent on it at the same time and I really wanted this installed on my netbook and it was one of the not so easy things to install but with following instructions it should be piece of cake really. Download linux version of Moneydance from this here. Mozilla will download it into your Desktop folder. (http://www.moneydance.com/download/2008/installers/Moneydance_linux_x86.tar.gz) Now goto Accessories -> Terminal. Type cd /home/YourUserName/Desktop/ and press enter Type tar xfvz Moneydance_linux_x86.tar.gz and press enter It will print following kind of stuff: Moneydance/ Moneydance/.install4j/ Moneydance/.install4j/i4jruntime.jar Moneydance/Moneydance Moneydance/.install4j/firstrun Moneydance/.install4j/i4jparams.conf Moneydance/.install4j/MessagesDefault Moneydance/.install4j/user.jar Moneydance/appsrc.jar Moneydance/jcommon-1.0.12.jar Moneydance/jfreechart-1.0.9.jar Moneydance/license.txt Moneydance/moneydance.jar Moneydance/moneydance_icon32.png Now it will present the command prompt again. Type cd Moneydance Now Type ./Moneydance It will display testing JVM in /usr and then open the Monedance software. This is it software is now installed. Unfortunately, this does not create any icon that you can directly click every-time and can only be run by clicking on Moneydance.sh file in Moneydance folder. So to make it easy do the following. Goto Prefrences -> Main Menu -> Favourites. Now click on "New Item". It will open a dialogue box 4 fields. In second field Enter "Moneydance". In third field click on Browse and navigate to Desktop->Moneydance->Moneydance.sh If you want to change the icon click on icon and select the one that you want. Click OK. This is it you are all done. Ensure that now you restart your Acer Aspire One 150 atleast three times before you start using as I noticed that it was only after three or four restarts that everything was well settled. 11. That's it friends....Now Enjoy. Please let me know if you are stuck somewhere and I might be able to help. ","categories": [], + "tags": [], + "url": "/aao-a150l-step-by-step-installation-process-for-linux4one/", + "teaser": null + },{ + "title": "Get Gmail as Push Email on Sony P990i", + "excerpt":"While I have moved on from P990i which is now in safe hands of my dear wife, I remember doing some good amount of web searching and still had no idea how to set up push-mail on P990i. I then thought I will configure a mail anyway and to my surprise the device is capable of enabling push-mail on it’s own as I found while fiddling along. Anyway I have configured push-mail for wifey dear and she was mighty impressed…:-) The steps I followed are as below: Main Menu -> Tools -> Control Panel -> Messaging -> Email Accounts -> New Fill Account Name as Gmail Your Name as ..well... your name..:) Email Address - Your gmail address In Connection Type select IMAP from the drop down list. Tick on Push email Now goto "Inbox" Tab. Type imap.gmail.com under "Incoming Server Address" Type your email address "xyz@gmail.com" under "Username" Enter your "password" under "Password" Depending on you data plan you might want to put something else but I have unlimited internet plan so I have selected "No Restriction" under "Download Restrictions" Limit number of Emails - I have selected 100, you can chose as per your needs. Receive using Group - Select the group of internet account that has your phone provider configured. (I am assuming that you have already configured internet settings on your phone. If not that is a separate topic but you can easily find information on user manual so at the moment let's consider it as out of scope for this entry.) Now goto "Outbox" Tab. Type "smtp.gmail.com" under "Outgoing Server Address" Tick the checkbox "Use SMTP Authentication" Tick the checkbox "Use Inbox Login details" Select the same internet group as above under "Send using Group" Now click on the arrow next to "Email account" on top of the screen and click on "Advanced" from the dropdown list Under "Incoming" tab Select "SSL" under the field "Secure connection" Type "993" under field "Incoming mail port" Next in "Outgoing" tab under "Secure Connection" filed select "TLS" Type "587" under field "Outgoing mail port" Tick the checkbox "Use MIME encoding" Click on "Save" Click on "Save" again Now the screen will show Gmail listed as your email account. On this screen again click on the arrow next to Email account on top of the screen and select "Always on Pushmail". Tick the checkbox next to "Always On" Select the timings and select the phone providers datasetting name under "Internet Account" and click "Save". This is it. You are now all set to receive your mails on the go… Have fun !!! ","categories": [], + "tags": [], + "url": "/get-gmail-as-push-email-on-sony-p990i/", + "teaser": null + },{ + "title": "Sony VAIO FE21H Webcam on skype", + "excerpt":"Alright friends not a huge tip but still I, being new fan of Ubuntu, installed it on my media laptop…Sony VAIO… one might ask what is media laptop…well I had a bit of problem with my Sony VAIO some 1.5 years back and a key got stuck and as it goes with any Sony product there was no cheap solution at hand… At that time for some reason it appeared like a motherboard issue and I just bought another Dell laptop…later I realised that it was a keyboard issue so I invested in a wireless keyboard and had two lappies at home just like that :)…I figured that with wireless keyboard VAIO laptop will make perfect companion to my 32\" Samsung and hence it got the name of media laptop. I use this laptop to browse without fear as I will never ever do any important work on it so am never worried of loosing..not that I ended up having any virus at all….and now with Linux I doubt if there will ever be any virus on this laptop….but anyway I have gone on a tangent… What I started to tell was a tip on how we can make VAIO’s inbuilt motion-eye webcam to behave and the tip goes as below… Incase your webcam is not giving good images on ur Sony VAIO, you may want to try this: Goto System->Preferences->Main Menu In Main Menu window click on Internet in left hand navigation under "Menu" and then click on Skype" in right hand under "Items". Now click on "Properties" button. It will open a Launcher Properties dialogue box. In third field "Command" replace "Skype" with this line: bash -c 'LD_PRELOAD=/usr/lib/libv4l/v4l1compat.so skype' That is it. You should now have a properly working motion-eye with skype. There is more info on this link: https://help.ubuntu.com/community/Webcam Ciao !!! ","categories": [], + "tags": [], + "url": "/sony-vaio-fe21h-webcam-on-skype/", + "teaser": null + },{ + "title": "Sony VAIO N-VIDIA setup to make S-Video work", + "excerpt":" UPDATE for 9.10 For Ubuntu 9.10 I was just not able to get it work and finally I figured it out. The problem is with the latest Nvidia driver, the one that ubuntu says recommended. In order to make your TV Out work, you must uninstall the latest release version and install the one just lower than that. (The working version for me was 173.) Then follow these steps: sudo nvidia-xconfig sudo reboot sudo nvidia-settings Then follow the steps shown below. If you are lucky you wont need to refer Section A of this post at all. I did not have to do that !!! Cheers, Ankit UPDATE for 9.10 S-Video did not work out of the box on ubuntu Sony Vaio combination but it was not difficult to have it sorted once the following steps were taken. I am giving the steps to set-up TV-Out assuming your n-vidia file is working fine, if not follow steps in Section A before completing steps below. Step 1: Connect laptop and TV through S-Video Cable. Step 2. System->Administration->Nvidia X-Server Settings Step 3: Click on X-Server Display Configuration and then click on "Detect Displays". If you have connected fine, the screen should be something like this: http://4.bp.blogspot.com/_-KsGlJDRxDw/SrFWWRqfjhI/AAAAAAAAAHY/Ze7d7J7dKT8/s400/Screenshot-NVIDIA+X+Server+Settings-1.png Step 4: Click on the TV icon, following screen will appear. http://2.bp.blogspot.com/_-KsGlJDRxDw/SrFY6ndgpeI/AAAAAAAAAHg/Tkskm1BYebM/s400/Screenshot-NVIDIA+X+Server+Settings-2.png Step 5: Click on "Configure" and then select "Twinview" http://1.bp.blogspot.com/_-KsGlJDRxDw/SrFY7MnhQzI/AAAAAAAAAHo/hw7mVXTa4PI/s400/Screenshot-NVIDIA+X+Server+Settings-3.png Step 6: Now I found that keeping TV on left ensured that while using dailymotion full screen it will come on TV but keeping on right it came on laptop monitor so you may want to depending on your preference change the location to left as shown below. http://4.bp.blogspot.com/_-KsGlJDRxDw/SrFY7bt7sCI/AAAAAAAAAHw/pGeyp1_GfeU/s400/Screenshot-NVIDIA+X+Server+Settings-4.png This is it.You should now be able to watch streaming video on your TV…:) Section A: I found the most useful instructions below, but I did have to make few tweaks. Source: http://ubuntuforums.org/showthread.php?t=98456 Right, I will walk step by step based on the guide from the source mentioned above and mention the changes / tweaks I did to make it work on my laptop. I am sure it should work with same conf on other VAIO as well. Step 1: Aplication-> Accessories-> Terminal Step 2: Type sudo cp /etc/X11/xorg.conf /etc/X11/xorg.conf.backup Step 3: You will need to enter password at this stage. Enter the password. Step 4: Now Type: sudo gedit /etc/X11/xorg.conf Difference in old and new xorg.conf files: I used the following command on terminal to compare the old and new .conf files: diff -u /etc/X11/xorg.conf.backup /etc/X11/xorg.conf The numbers between @@ represent Row number and column number. - stands for old and + for new. @@ -61,9 +61,9 @@ Section \"Monitor\" Identifier \"Monitor0\" VendorName \"Unknown\" - ModelName \"TV-0\" - HorizSync 28.0 - 55.0 - VertRefresh 43.0 - 72.0 + ModelName \"Nvidia Default Flat Panel\" + HorizSync 29.0 - 49.0 + VertRefresh 0.0 - 60.0 EndSection Section \"Monitor\" @@ -110,12 +110,14 @@ # Removed Option \"TwinView\" \"0\" # Removed Option \"metamodes\" \"DFP: nvidia-auto-select +0+0\" +# Removed Option \"TwinView\" \"1\" +# Removed Option \"metamodes\" \"TV: nvidia-auto-select +0+0, DFP: nvidia-auto-select +1024+0\" Identifier \"Screen0\" Device \"Device0\" Monitor \"Monitor0\" DefaultDepth 24 - Option \"TwinView\" \"1\" - Option \"metamodes\" \"TV: nvidia-auto-select +0+0, DFP: nvidia-auto-select +1024+0\" + Option \"TwinView\" \"0\" + Option \"metamodes\" \"DFP: nvidia-auto-select +0+0\" SubSection \"Display\" Depth 24 EndSubSection Copy of my xorg.conf and xorg.conf.backup xorg.conf - CURRENT - WORKING VERSION Section \"ServerLayout\" Identifier \"Default Layout\" Screen 0 \"Screen0\" 0 0 InputDevice \"Keyboard0\" \"CoreKeyboard\" InputDevice \"Mouse0\" \"CorePointer\" EndSection Section \"Module\" Load \"glx\" EndSection Section \"ServerFlags\" Option \"Xinerama\" \"0\" EndSection Section \"InputDevice\" # generated from default Identifier \"Keyboard0\" Driver \"kbd\" EndSection Section \"InputDevice\" # generated from default Identifier \"Mouse0\" Driver \"mouse\" Option \"Protocol\" \"auto\" Option \"Device\" \"/dev/psaux\" Option \"Emulate3Buttons\" \"no\" Option \"ZAxisMapping\" \"4 5\" EndSection Section \"Monitor\" Identifier \"Configured Monitor\" EndSection Section \"Monitor\" Identifier \"Monitor0\" VendorName \"Unknown\" ModelName \"Nvidia Default Flat Panel\" HorizSync 29.0 - 49.0 VertRefresh 0.0 - 60.0 EndSection Section \"Monitor\" Identifier \"Monitor1\" VendorName \"Unknown\" ModelName \"TV-0\" HorizSync 28.0 - 55.0 VertRefresh 43.0 - 72.0 EndSection Section \"Device\" Identifier \"Configured Video Device\" Driver \"nvidia\" Option \"NoLogo\" \"True\" EndSection Section \"Device\" Identifier \"Device0\" Driver \"nvidia\" VendorName \"NVIDIA Corporation\" BoardName \"GeForce Go 7400\" EndSection Section \"Device\" Identifier \"Device1\" Driver \"nvidia\" VendorName \"NVIDIA Corporation\" BoardName \"GeForce Go 7400\" Option \"TVOutFormat\" \"SVIDEO\" Option \"TVStandard\" \"PAL-G\" Option \"ConnectedMonitor\" \"Monitor[1]\" BusID \"PCI:1:0:0\" Screen 1 EndSection Section \"Screen\" Identifier \"Default Screen\" Device \"Configured Video Device\" Monitor \"Configured Monitor\" DefaultDepth 24 EndSection Section \"Screen\" # Removed Option \"TwinView\" \"0\" # Removed Option \"metamodes\" \"DFP: nvidia-auto-select +0+0\" # Removed Option \"TwinView\" \"1\" # Removed Option \"metamodes\" \"TV: nvidia-auto-select +0+0, DFP: nvidia-auto-select +1024+0\" Identifier \"Screen0\" Device \"Device0\" Monitor \"Monitor0\" DefaultDepth 24 Option \"TwinView\" \"0\" Option \"metamodes\" \"DFP: nvidia-auto-select +0+0\" SubSection \"Display\" Depth 24 EndSubSection EndSection Section \"Screen\" Identifier \"Screen1\" Device \"Device1\" Monitor \"Monitor1\" DefaultDepth 24 Option \"TwinView\" \"0\" Option \"metamodes\" \"TV: nvidia-auto-select +0+0\" SubSection \"Display\" Depth 24 EndSubSection EndSection xorg.conf.backup - OLD COPY Section \"ServerLayout\" Identifier \"Default Layout\" Screen 0 \"Screen0\" 0 0 InputDevice \"Keyboard0\" \"CoreKeyboard\" InputDevice \"Mouse0\" \"CorePointer\" EndSection Section \"Module\" Load \"glx\" EndSection Section \"ServerFlags\" Option \"Xinerama\" \"0\" EndSection Section \"InputDevice\" # generated from default Identifier \"Keyboard0\" Driver \"kbd\" EndSection Section \"InputDevice\" # generated from default Identifier \"Mouse0\" Driver \"mouse\" Option \"Protocol\" \"auto\" Option \"Device\" \"/dev/psaux\" Option \"Emulate3Buttons\" \"no\" Option \"ZAxisMapping\" \"4 5\" EndSection Section \"Monitor\" Identifier \"Configured Monitor\" EndSection Section \"Monitor\" Identifier \"Monitor0\" VendorName \"Unknown\" ModelName \"TV-0\" HorizSync 28.0 - 55.0 VertRefresh 43.0 - 72.0 EndSection Section \"Monitor\" Identifier \"Monitor1\" VendorName \"Unknown\" ModelName \"TV-0\" HorizSync 28.0 - 55.0 VertRefresh 43.0 - 72.0 EndSection Section \"Device\" Identifier \"Configured Video Device\" Driver \"nvidia\" Option \"NoLogo\" \"True\" EndSection Section \"Device\" Identifier \"Device0\" Driver \"nvidia\" VendorName \"NVIDIA Corporation\" BoardName \"GeForce Go 7400\" EndSection Section \"Device\" Identifier \"Device1\" Driver \"nvidia\" VendorName \"NVIDIA Corporation\" BoardName \"GeForce Go 7400\" Option \"TVOutFormat\" \"SVIDEO\" Option \"TVStandard\" \"PAL-G\" Option \"ConnectedMonitor\" \"Monitor[1]\" BusID \"PCI:1:0:0\" Screen 1 EndSection Section \"Screen\" Identifier \"Default Screen\" Device \"Configured Video Device\" Monitor \"Configured Monitor\" DefaultDepth 24 EndSection Section \"Screen\" # Removed Option \"TwinView\" \"0\" # Removed Option \"metamodes\" \"DFP: nvidia-auto-select +0+0\" Identifier \"Screen0\" Device \"Device0\" Monitor \"Monitor0\" DefaultDepth 24 Option \"TwinView\" \"1\" Option \"metamodes\" \"TV: nvidia-auto-select +0+0, DFP: nvidia-auto-select +1024+0\" SubSection \"Display\" Depth 24 EndSubSection EndSection Section \"Screen\" Identifier \"Screen1\" Device \"Device1\" Monitor \"Monitor1\" DefaultDepth 24 Option \"TwinView\" \"0\" Option \"metamodes\" \"TV: nvidia-auto-select +0+0\" SubSection \"Display\" Depth 24 EndSubSection EndSection ","categories": [], + "tags": [], + "url": "/sony-vaio-n-vidia-setup-to-make-s-video-work/", + "teaser": null + },{ + "title": "O2 XDA Serra - Official HTC upgrade with TF3D of Touch Pro 2", + "excerpt":"Ever since I laid my hands on O2 XDA Serra aka HTC Raphael aka HTC Touch Pro , I have always loved the device despite it’s limitations on battery life and have found some really useful apps during my association with windows mobile (for last five years or so) which found their way onto Serra as well. While I have known for a while that we can flash ROMS, I was perhaps over protective when it came to Serra and never really went beyond doing a HardSPL for device but last Sunday when I was fiddling with the facebook application and SMS registration, I realised that each time I sent an SMS and then tried to open the internet explorer or opera they will just crash demanding a soft reset. I could instantly find the problem to be Kaspersky Anti-Virus(KAV) failure. It is not possible to remove KAV without a hard reset at least not to my knowledge. (A hard reset is what brings your device back to factory conditions). Now since a Hard Reset would mean lot of work in terms of reinstalling all my beloved apps etc, I figured I might as well see if there is some other ROM I could use. This is what triggered my quest. What started as fixing the issue caused with a Kaspersky Anti Virus failure rendering crashes to internet browsers on my HTC Touch Pro ended in my pleasantly getting a complete device makeover and in the process making my gadget work way better than it was working before. Lovely GUI, great Facebook integration, faster boot time and most important of all a functional GPS, yes you read it right a functional GPS. The problem that plagued almost all handsets provided by O2. I must start by saying that if it worked for me it must work for you but if it does not please don’t hold me responsible. Another thing you must know is doing a HardSPL strips your device of the warranty. There is a way of putting back the StockSPL for warranty reasons but I have not tried it but then it does not sound too complicated. You may also find views on forums that suggest installing a cooked ROM is better but as I said I am too much in love with the device to install flaky ROMs I actually opted for the official update ROM released by HTC and then gave it the touchflo3d of touch pro 2 and after some tweaks here and there which I will all list down, I am completely (I stress the word completely) satisfied with the final results. Ok so now over to the process: What are we trying to achieve? To install the manufacturer provided ROM To get the handset a good look and feel. Ensure that GPS functions To get phone address book integrated with face-book so you can get your contacts pictures from their FB profile onto your device. To get Weather for the local city that may not be present in the default list. Gmail sync to ensure Gmail is configured as push mail on your device. The Google calendar sync to your on device outlook calendar. What do we need? A windows PC / laptop. O2 XDA Serra / HTC Raphael / HTC Touch Pro USB cable to connect XDA to PC / laptop At least a 1 GB Mini SD Card. Considering you have XDA, you may want to get a bigger capacity memory card (of up to 8GB or 16GB…). I have a 8GB one. Access to internet. A good data plan that will ensure good use of the effort you are about to put in. Downloaded cabs and ROM. You can either download all in bulk from here (http://www.mediafire.com/?l12vaj688qwh85w) (I have uploaded them for ease as a bundle) or follow the links and download individually under each section. and as I always ask, lot of patience. Steps: HardSPL Install HTC ROM Install touchflo3d Install Dialler Install .net 3.5 and weather database editor Configure Device Configure Email Step 1: HardSPL Now to start, we will first need to apply HardSPL to the device. I do not know what the words SPL stand for but what I have come to understand after reading for hours on XDA-developer forums is that if you want to install ROM that is not provided by the vendor who sold you the device (O2 in my case), you will need to apply this HardSPL or you may either not be able to install the new ROM or may even be at risk of bricking your device. HardSPL is not complicated and all credit goes to developers at xda-developer community. You will have official instructions if you follow this link < http://forum.xda-developers.com/showthread.php?t=410150>. For the sake of completeness, I am anyway including instructions here. </hr>text borrowed from XDA-developer website<hr></hr> Instructions: download Hard-SPL package from attachment, extract to an empty folder. make sure it's launched from a local drive (not through network drive, etc.) you must Have Phone Synced with PC in Windows Mobile! run RaphaelHardSPL-Unsigned_190_1_3.exe, follow steps in the RUU, check device for prompts after PC shows loading bar. it should go to black screen now. SPL flashes, device automatically reboots, job done. to confirm you got it installed, go into bootloader mode (tricolour screen!) and verify the screen shows 1.90.OliNex. NOTE 1: you will not see the SPL version during normal boot, that is the OS version, not SPL! to enter bootloader mode to see version: with the device turned on, press and hold the volume down button, then press the reset button with the stylus tip, then release the volume down button when bootloader tricolour screen appears. NOTE 2: anyone having problems with the device entering SSPL automatically, please copy SSPLManual.exe from second attachment to the device and run it. then once the screen is black, run RUU manually. i.e. you run the RUU on the PC, if it isn’t obvious. NOTE 3: this is unsigned Hard-SPL. no limitations on flashing ROMs or radio packages. also, this has overwrite protection, if someone needs to revert to stock SPL for warranty reasons, we will soon post a stock SPL downgrade package. NOTE 4: do not use this RUU for anything other than SPL flashing (i.e. hardspl or stock spl restore)!!! if you want to flash some other rom, then use customruu from: http://forum.xda-developers.com/showthread.php?t=410761 Step by step how to return to stock spl - for warranty reasons only! be sure to first restore stock OS, and stock radio. stock SPL is always to be done last!! download the stock spl package from this post. also download the original hardspl from the attachments in this post. run the hardspl exe but do not click anything in RUU yet. just let the hardspl EXE extract the files for flashing. the SPL you want to revert to is a .NBH file, put the NBH in the extracted hardspl package, overwriting the original NBH file in it! continue with the RUU (or if needed, run SSPL-Manual.exe manually, then run RUU when it goes to black screen), it goes to 100%, reboots, done. +6. to verify, volume down + reset, see version number on tricolour screen, should now just say 1.90.0000. </hr>text borrowed from XDA-developer website<hr></hr> Step 2: Install HTC ROM Straight forward really. Connect your phone to the PC through the USB cable. Ensure that a connection is established with pc through active sync. Now double click on the ROM update utility (RUU) (RUU_Raphael_HTC_Europe_5.05.401.1_R2_Radio_Signed_Raphael_52.58.25.30_1.11.25.01_Ship) and it will guide you through the process. If you want to directly download from HTC the link is http://www.htc.com/uk/SupportDownload.aspx?p_id=140&cat=2&dl_id=501 You will have to use the non-O2 serial number. The one I used was HT833K016924. (Now if all went well what follows is the non-risky bit. To be honest I don’t even understand why we have to do this but then that’s the process and I was not about to experiment on my lovely instrument based on my limited knowledge led beliefs so I did this nonetheless.) Once completed it will restart XDA. Once it reaches the screen where it asks to configure Stylus, do a Hard Reset by pressing the Volume down key and the enter key (Round centre key) simultaneously and pressing the reset key with your stylus. (Reset Key is a small hole at the bottom of your handset next to the charging slot.) Once the system switches off release the reset key but continue to hold Volume Down key and enter key. It will now show a screen cautioning you that if you continue it will result in loss of data and if you wish to continue press Vol Up key. Press the Vol up key now. I don’t distinctly remember but I think it asked me twice to press Vol Up which I did. Basically just follow the instructions. That should sort out the new ROM installation. Now at this stage, you would have got rid of the O2 splash screen which has given way to elegant Touch Pro splash screen. This itself was an extreme form of happiness as I was not a huge fan of the O2 splash screen anyway. Ok you may want to take a moment with the new screen and play around. If you are happy and are not overly worried about facebook integration, I will recommend that you skip to Step 5. If however you want FB integration and would want a more Touch Pro 2 kind of interface there is still some work to be done. Moving on, then to Step 3. Step 3: Install touchflo3d </hr>text borrowed from XDA-developer website<hr></hr> http://forum.xda-developers.com/showthread.php?t=542113 Instructions 1) Disable “TouchFLO 3D” from your Today items. Soft reset. 2) Go to System Settings, “Power”, and uncheck all of the Options on the “Advanced” tab. 3) Install the “Gen.Y_Manila_R1_5.cab” (install may take 10 minutes or more). DO NOT RESTART. 4) Install Language Pack cab 0409 Gen.Y_Manila_R1_5. DO NOT RESTART. 5) Install the HTC Scroll_1_0_1914_2726 cab file. NOW SOFT RESET. Done! </hr>text borrowed from XDA-developer website<hr></hr> I did not install anything in the optional installs but if you would want to, you can follow the instructions on the post by Captain Throwback on the link above. The version I downloaded was R1.5 which is included in the pack. Step 4: Install Dialler I downloaded the dialler from ppcgeek forums, many thanks to them. It is not available on the link provided in xda-developer post. If you want to download from there, you will have to register on their site and do a bit of googling. Alternatively, it is included in the pack above, just the non confusing cab file named - . Move it to your memory card on phone. Now from XDA go to the location where you saved it and click the cab to install it. It will take about 3 to 5 minutes. Step 5: Install .net 3.5 and weather database editor Copy the < NETCFv35.wm.armv4i> cab(.net 3.5) and < WeatherDatabaseEditor 1.1 Modified> cab(weather DB) onto the memory card on your device. Now first install .net 3.5 by clicking the cab file from the location where you copied it. To install weather DB follow the steps below: 5.1. Install Weather Database Editor 5.2. find your locCode on <a href=\"http://www.accuweather.com\">http://www.accuweather.com</a>. (Example: Accuweather URL for Northwich in UK is : <a href=\"http://www.accuweather.com/world-index-forecast.asp?partner=accuweather&traveler=0&loccode=EUR%7CUK%7CUK123%7CNorthwich\">http://www.accuweather.com/world-index-forecast.asp?partner=accuweather&traveler=0&loccode=EUR|UK|UK123|Northwich</a>, The locCode is EUR|UK|UK123|Northwich) 5.3. Now open the weather DB on XDA, select your country and then select a city that begins with the same alphabet as your city and the one you are unlikely to use. For this example lets say - Nuneaton. Click on Edit -> Edit City. 5.4. Now change the name of city to the one you want and in “Accu Weather Code” Field enter the locCode obtained in Step 5.2. For our example it would be - EUR|UK|UK123|Northwich 5.5. Click on File and exit. 5.6. Go to Weather tab and add your city and update the weather. NOTE: Steps 5.7 to 5.9 are not relevant to you if you directly jumped to this section from Step 2 and have not installed the new TF3D 5.7. Go to home page on XDA and click on watch, now click on “Add City” and add the local city. 5.8. Make this as your default city by clicking the radio button. 5.9 Now click on Menu-> Rearrange Cities and bring your local city to top of the list. This is it, we will now move to next step. Step 6: Configure Device Many people don’t care to update the owner details but I think it is important to have that updated and hence my first step when I am done with initial installations and all is to update owner details. This ensures that if phone is lost and lands in honest hands you still have a chance of getting it back. It did happen to me so it isn’t a fairy tale…J Click on Windows icon -> Settings-> All Settings ->Personal -> Owner Information and update these details. Ensure that the telephone number you enter is different than the one for this instrument else it pretty much defies the purpose of entering this information on the first place. Next give your XDA a name. Click on Windows icon -> Settings-> All Settings -> System -> About. -> Device ID. Update your device name and description. Personalise weather updates. Slide over to weather, add any other cities you are interested in and click update now. I recommend that you install registry editor through the cab file provided in my bundle named and a file Explorer named but these are completely optional Your basic configuration is now complete. Step 7: Configure Email If you use hotmail or live and you have a good data plan (mine is unlimited internet usage) it may be a good idea to configure windows live as that ensures push mail so you have instant delivery of your mail as soon as it arrives into your handset. To do this, Click on Windows icon -> All Programmes -> Windows Live. Enter details and configure. Ensure that you change sync settings from “Manual” to “As soon as it comes” and the time schedule according to your needs. Gmail has recently introduced Gmail Sync that can sync your email, calendar and address book. They have provided instructions but they were not exactly mapping to how it was shown on my device. I instead used the following route. Connect your phone to your PC / laptop through ActiveSync and open Device Centre. Now click on Setup device -> Sync Setup and in server name enter m.google.com. enter your username and password. The SSL checkbox is clicked by default so let it remain. If it is not checked, please select it in the checkbox. Save settings. Now under items to be synced, of the three : email, calendar and address book select whichever you want synced…(Remember if you set anything other than these three to be synced through exchange nothing will work on Google Sync). I did not want my address book sync with Gmail so I left it and for other two I changed the sync settings to Exchange server. This is it your email will now be delivered into outlook mailbox on your XDA. You may want to check in your Gmail settings that you have activated IMAP or else even though this setup is correct you will not get mails delivered. Congratulations on successful completion of the whole process. Now enjoy the new look XDA with enhanced capabilities!!! ","categories": [], + "tags": [], + "url": "/o2-xda-serra-official-htc-upgrade-with-tf3d-of-touch-pro-2/", + "teaser": null + },{ + "title": "KUBUNTU Blog Entry", + "excerpt":"This is how my love for Kubuntu has started….and growing by the minute… I was experimenting with different Linux distros and then I went ahead with Linux Mint – which is quite popular and is know as “ubuntu done right”. I liked it and started dwelling more and played around with themes and such before I figured it is becoming kind of prescriptive and thought will actually play with the a distro with KDE not too far from Ubuntu – Kubuntu and boy was I surprised with the difference … all to their own opinion but mine is KDE is one helluva beautiful thing….the stuff that is done here is simply amazing …. alright once I was done with dropping my jaw at every step I figured there is a bit of learning curve right from the word go and so before I start forgetting what all I have learnt I felt it will be safe jotting it all down and thus this post: OK, so lets start with all the steps involved: How to upgrade Ubuntu 9.10 to Kubuntu 9.10 Restoring the default panel at bottom of the screen? Problems with system audio, you-tube audio and how it all finally started working for me. Change the theme. Change the splash screen How I managed to change the Log-in Screen (KDM Screen) Finally, change boot splash screen Right so if any of this catches your fancy, please be my guest…:) How to upgrade Ubuntu 9.10 to Kubuntu 9.10: Plenty of information on the website and better presented at the one site than other. I liked the way it was explained here: http://www.psychocats.net/ubuntu/kde This site is also a good source of information in general so well worth a bookmark. Restoring the default panel at bottom of the screen? Once I finished installing Kubuntu as per the instruction from above link, I started playing around and ended up deleting the panel thus learning about how to get that back. In the process I learnt important lesson on making a backup of setting as soon as you have configured system to your first level of satisfaction as if you do not take this precaution you will have to start all over again. The steps I took were as below: Goto Applications tab, click on Settings → Terminal and open the terminal, Type following commands: sudo cp -R ~/.kde .kde_backup sudo rm -rfv .kde kquitapp plasma-desktop sudo restart This is it. Now once the system restarts, your panel will be in place. However you will have to put all your configuration again. So to avoid that in future, repeat first two steps and next time you end up in similar situation all you need to do is give this command sudo cp -R ~/.kde_backup .kde and you should be good to go. Problems with system audio, you-tube audio and how it all finally started working for me. When I opened a video on youtube, I was not getting any sound. After some googling with no results I tried this and again today after restoring panel the sound was gone and trying this made it work so I assume at-least for all Dell Inspiron 1525 this should work. Open the voice control by clicking on volume icon in system tray and clicking on Mixer as and then unmute the “Headphone”. After this I restarted the computer and logged into GNOME session, and played a youtube video. Sound was coming alright. I logged out from GNOME session and logged in again in KDE session and tried a video on youtube and it was working fine. Change the theme and wallpaper. To change the theme, it is quite simple. Just right click anywhere on desktop and select desktop settings, and under theme either select the available option or download from available options and install. For wallpaper also do the same thing. Change the splash screen Download the splash screen. I downloaded “Kcarbon” splash screen from http://kde-look.org/index.php?xcontentmode=35x45 as it was going nicely with my desktop theme and wallpaper. Once it was downloaded, I opened from Application Menu → Computer → System Settings → Appearance (under look and feel section) → Splash Screens to get to this screen and then click install, select location where you downloaded the tar file. How I managed to change the Log-in Screen (KDM Screen) UPDATE Open the "System Settings" KDE application. Choose the "Advanced" tab. Go to "Login Manager". Choose "Theme" tab. Click on the "Install new theme" button. Choose the compressed file and click ok. This one was rather flaky solution. I could not find anything that could change the initial background screen where we enter user credentials and password. This is called KDM Theme as I learnt while finding out how to do it. Several searches on google talked about something called KDM Theme Manager but they are all old mails and for new KDE 4+ it comes pre-installed and I did not know what to do. So I downloaded one of the KDM themes from http://kde-look.org and unzipped it into the following location as root. `kdesudo dolphin /usr/share/kde4/apps/kdm/themes/` Next, I renamed the folder “oxygen-air” as “oxygen-air_old” and the downloaded and unzipped folder as “oxygen-air”. This is it. When I restarted I had the brand new KDM Theme to my satisfaction.:) Finally, This is how I changed my boot splash screen: I tried many suggestions but only after going through the whole GRUB2 entries as explained on this link I got the result I was looking for. However, I did have to do a little bit of trial and error so I will put the steps that finally worked for me. These may also come handy if you do not want to know in depth about all GRUB 2 stuff. http://ubuntuforums.org/showthread.php?t=1195275&highlight=grub2 In GIMP, open the image you want to show as the background when GRUB shows choices to select which OS you want to load at start-up Now goto Image -> Scale and then enter height as 640, press tab. Now save the image as a .png file. in the terminal type kdesudo dolphin /usr/share/images/desktop-base Copy the image saved in step 3 and paste it in the desktop-base folder Now in the terminal type kdesudo kate /etc/grub.d/05_debian_theme This will open 05_debian_theme file with kate text editor. Go to line 16 and find the following line and edit the highlighted area, replacing it with the name of image copied into desktop-base folder in Step 5 for i in {/boot/grub,/usr/share/images/desktop-base}/moreblue-orbit-grub.{png,tga} ; do Save the file and in the terminal type: sudo update-grub2 sudo reboot ","categories": [], + "tags": [], + "url": "/kubuntu-blog-entry/", + "teaser": null + },{ + "title": "Configure BlogTK 1.0 for blogger", + "excerpt":"Right so I am happy with Blogilo, then why BlogTK? It so happened that I was trying to edit my last post and for some unknown reason I was getting error in updating through Blogilo. I was not able to update through Blogilo and I think it is because of the snapshots included in the post. Anyhoo…so I installed BlogTK and after entering the details I found it not connecting so I opened the gnome-blog and then when I was configuring its preference I saw the default entry in the field XML/RPC URL - http://www.blogger.com/api/RPC2 I opened BlogTK, Edit -> Accounts and Settings and then in Server URL entered - http://www.blogger.com/api/RPC2 Next entered username and password fields. Clicked Save and then OK. Now on main window File-> Connect and it showed the message connected to server at http://www.blogger.com/api/RPC2. I was then able to edit the post however the Edit post etc were not all that great and meanwhile I found that Blogilo is working fine so am back to Blogilo. Not a huge tip and am sure many might already know but it took me a while to work out the working configuration. However, if someone knows better way to connect, please do let me know as BlogTK sure wasn’t living up to the name and fame it has with this way of configuration so maybe there is a better way to do it. ","categories": [], + "tags": [], + "url": "/configure-blogtk-1-0-for-blogger/", + "teaser": null + },{ + "title": "Audio problem in Wine under KDE 4.4.1 - Solved", + "excerpt":"I was trying to install spotify on linux which used to work perfectly on Gnome and when I tried on KDE it was giving error box. I wrote winecfg on terminal and got following error: fixme:jack:JACK_drvLoad error loading the jack library libjack.so.0, please install this library to use jack After a bit of google I found the answer to be as simple as running this command on terminal: sudo apt-get install libjack0 After this audio was working fine. Hope this helps few others. ","categories": [], + "tags": [], + "url": "/audio-problem-in-wine-under-kde-4-4-1-solved/", + "teaser": null + },{ + "title": "Install Maitreya - Vedic Astrology Software on Ubuntu / Kbuntu", + "excerpt":"First I must thank the Maitreya developers for coming up with such a wonderful Vedic Astrology software that works on Linux. I am not aware of any other vedic astrology software that works on Linux. Next I have to say that the install instructions on the site (and in the tarball) are not written with a newbie in mind and I had to struggle a bit before I could get it to work. The compiling of source code is really similar for I guess all source codes in general on linux and in particular in Ubuntu / Kubuntu but thing is when you are new to the platform and probably have almost all required software in repositories you rarely come across a situation where you need to compile from a source code to install your favourite software. Anyway so without wasting time in chit-chat, below is the step by step installation of Maitreya 6 on Ubuntu / Kubuntu: 1. Download Maitreya 6 tarball. 2. Prepare the system for installing 3. Install wxWidgets 2.8 Dependencies 4. Configure and Build 5. Install 6. Create a desktop icon Download Maitreya 6 tarball - From your browser goto http://maitreya.svn.sourceforge.net/viewvc/maitreya/ and click on the link "Download GNU tarball". This will download the maitreya.tar.gz file on your system. Prepare the system for installing - I used the instructions from ubuntu help documentation that can be found here. However, I have documented the step that I used in sequence to achieve the objective. Open the terminal. Type - sudo apt-get install build-essential checkinstall You will be asked for the password. Enter it. Once system completes the above instruction and is ready to take next instruction then Type: sudo apt-get install cvs subversion git-core mercurial Next type: sudo chown yourusername /usr/local/src Please note that here you need to put the your user name so don't just copy paste. So if say user name is \"ankit\" , you will type sudo chown ankit /usr/local/src Next Type: sudo chmod u+rwx /usr/local/src Now the system is prepared for installing. Install wxWidgets 2.8 Dependencies - As mentioned on Maitreya site, the software depends on wxWidgets 2.8 and corresponding packages must be installed. On the site itself is given the code that needs to copied onto the terminal to achieve this. Either from the site or from below select the text. sudo apt-get install libwxbase2.8-0 libwxbase2.8-dev libwxgtk2.8-0 libwxgtk2.8-dev wx2.8-headers wx2.8-i18n Press Ctrl+C Goto Terminal window and press Ctrl+Shift+V or click on Edit from top menu and select Paste. Press enter. You may have to provide password. This should now install wxWidgets 2.8 Over to next step. Configure and Build - There are few things that should be done before we start to configure the downloaded source code. First goto the folder where maitreya.tar.gz file was downloaded in Step 1. Now right click and select "Extract Archive To". Select the location /usr/local/src This will extract a folder named maiterya into the location /usr/local/src The content downloaded may change in future but at the time of writing this article it has two folders maitreya5 and maitreya6. All next steps will assume that folders name maitreya6 is present in your download but if not you will just have to replace with whatever folder is inside the extracted maitreya folder. Goto your termincal window and type: cd /usr/local/src/maitreya/maitreya6/trunk Now type `./configure`. If it gives some bash error like permission denied or file does not exist etc try `sh ./confgure`. This is what worked from me though everywhere instructions were to just use without sh. It will take a bit of time but it should complete without any error and end with some instructions about using "make". Now type `make` on terminal. It should be done with no errors if all steps were followed correctly so far. Over to next step. Install - Again there were options around using sudo make install but the ubuntu help I have referred to (link above), suggested that checkinstall is better and it also creates a .deb file and I like the idea so that next time if I have to reinstall I don't need to do all this circus so I used the following and would recommend the same. So in terminal window type sudo checkinstall. When you run it it will ask some questions like doc-pak is not there, do you want to create?(Y) just type Y. Then it shows some options and asks if you want to edit. I typed 2 to edit name and entered Maitreya. It threw a warning that maitreya 6-1_i386 is not complaint with debian standard or something to that effect and I just pressed enter. After this it was all fine and finally a message was shown telling a debian package has been created and to remove use dpkg ... The software should now be installed and you can test it by clicking the file named Maitreya6 at Location - /usr/local/src/maitreya/maitreya6/trunk/src/gui. However if you want to access it easily from desktop there is just one more step. Create a desktop icon - To create a desktop icon goto the folder /usr/local/src/maitreya/maitreya6/trunk and copy the file maitreya6.desktop and paste in on desktop. This is it you have now installed the Maitreya software !!! ","categories": [], + "tags": [], + "url": "/install-maitreya-vedic-astrology-software-on-ubuntu-kbuntu/", + "teaser": null + },{ + "title": "Jagannath Hora on Linux - Play on Linux Magic", + "excerpt":"While I prefer Maitreya as it can run with Linux as native, quite a few of my friends have asked me if Jagannatha Hora will work on Linux and hence this post. Right, when I tried last time around mid Oct’09, JHora was not working using wine. However, recently things have changed. If you are not aware or have not used Play On Linux then you should try it out. It works. Anyway back to original topic, the steps to install Jagannatha Hora on Kubuntu 10.04 are as below. (I am mighty sure this will work on other linux machines but I have tried it only on Kubuntu 10.04 which is increasingly becoming my OS of choice.) Step 1: Install Play On Linux (POL) Step 2: Download Jagannatha Hora ( JH ) Step 3: Install JH using POL Install Play On Linux (POL) - You can install using Package Manager - Synaptic or KPackagekit but you will most likely get a little older version. I installed using Synaptic but then updated it by downloading directly from the site or follow this link to download the version 3.7.6. This is latest at the time of writing but it may be best to just get it from the website here. Once downloaded and installed, we move to a very simple and straightforward Step 2. Download Jagannatha Hora ( JH ) I am sure if you have come here searching on net, you don't need introduction to this software but in any case I admire the author of this software Mr. P. V. R Narsimha Rao for various reasons. One I consider him my Guru and as I am just a beginner to the world of astrology I find his to book the best reference one can get. I purchased it in 2006 when it was not easily available in UK but now the book itself is included in the software. Though the topic of this post does not require mention of book, I will take a moment to write few words on it. As the author is an engineer from IIT, his approach to subject was structured much like an engineering book. Very logical flow that takes you through the concepts in a way I have not found in any other book till date. It does not mean I don't like other authors. It just means that I recommend this to any beginner as it makes the concepts easy to grasp. OK, now back to topic, if you don't already know, you can get this software by following this link - http://www.vedicastrologer.org/jh/ Once you download the zip file, extract it in the same folder. Just remember where you extracted. The file you have extracted will be named - jh_full_install.exe. Install JH using POL - Open the Play on Linux software. In Kubuntu you will find it by clicking KLauncher -> Applications -> Games. Once it starts, click on "Install".This will open a new window. In this window, click on the line in lower left hand side corner - “Install a .pol or an unsupported application”. This will close the above window and open another window. Please note that if this window does not close, it appears that Play On Linux has hanged. In such case click on cross to close that window and it will show an error message suggesting that “PlayOnLinux” is not responding or something to that effect but you select to force close it and you will still land up on the next window. I found that this problem went away when I installed the latest version 3.7.6. a) In this window select “Manual installation” and click on “Next”. b) On the next window that appears, click “Forward”. c) You will be presented with another window with choices. Default choice being – “Install a program in a new prefix”. Keep it selected or if it is not selected, select it and click on “Forward”, d) Next will be presented a window asking name for wine prefix. Type “Jhora” and click “Forward” e) On the next window, there will be two check-boxes. Leave them un-ticked, do nothing and click Forward. f) Once prefix is created, you will be presented with this window. Browse to the the file extracted in step 2 and then press Forward. g) Now it should start installing and once complete, it will ask if you want it to be shown on desktop. I chose Yes but it will be up to personal preference. Play On Linux can also be used to install Spotify and 7zip, but more importantly you can see that an entry for Jhora has now been created. Finally on desktop you should be able to see the link for JH. You should now be able to use JH without any problems. Hope you found this post useful. -Ankit. ","categories": [], + "tags": [], + "url": "/jagannath-hora-on-linux-play-on-linux-magic/", + "teaser": null + },{ + "title": "Part 1 - Configure Epson S515W on Linux Mint / Ubuntu 10.04", + "excerpt":" UPDATE While this tutorial will get your printer up and running, you should also follow Part 2 to ensure that it continues to work even after you have restarted your printer. -Ankit Lately I have not done much experiments and hence a stable system. I did not require any changes and all was well and then recently I changed over to Linux Mint which by the way is a UBUNTU derivative, very slick and very cool. Now after this reinstall I was able to get back all my previous installs and everything the usual way but scanning using Epson S515W was not out of the box and below are the steps I followed to get it working: Step 1: Configure Printer Step 2: Test Print a page Step 3 - Install drivers and software for scanner Step 4 - Test Scanner Step 1: Configure Printer This really works quite easily in all Ubuntu installs. Goto System > Administration > Printer Now you can see my printer configured but when you will get this window, you wil only see Print_to_PDF. You should then Click on arrow next to ADD and select Printer. It will open the window below: Again under Network Printer it may not show anything initially but if your printer is on and configured to your wireless router it will identify in a moment or two. If it does not, click on Find Network Printer. Once your printer appears, click on it and then click on \"Forward\". Step 2: Print a test Page Open the Printer window again from System>Administration>Printer and double click on printer. It should open the following window: Click on \"Print Test Page\". If it prints the ubuntu test page correctly your printer is configured. Mine was so I will not go more in details of alternative option. If you do want to know more let me know and I will put a post. However if now you will try to scan you will find that scanner either is not identified or if it is it does not complete the scanning and printer gets hanged, so over to next steps then. Step 3 - Install drivers and software for scanner Goto the avasys site that provides downloads and navigate for your model or if you have the same as mine just click on the link below and then goto Downloads and select All In One Printers. On the next page scroll down all the way and select the radio button for your printer. http://avasys.jp/eng/ Now these installs need to happen in a sequence and we don’t need all deb files on the next page anyway so I will mention download and install in sequence as it should be done. From the section "Download for Epson Stylus NX510/NX515/SX510W/SX515W/TX550W data package" download the data package iscan-data_1.4.0-1_all.deb. Install this package now. From the section "Download for Epson Stylus NX510/NX515/SX510W/SX515W/TX550W core package" download iscan_2.26.0-3.ltdl7_i386.deb. Install this package now From the section "Download for Epson Stylus NX510/NX515/SX510W/SX515W/TX550W network plugin package" download iscan-network-nt_1.1.0-2_i386.deb Install this package now. Now on your printer(actual machine not on laptop / computer) goto Settings (Press the button with Wrench and Screwdriver symbol as shown in the image Printer Control). Using left arrow navigate to Network Settings (Computer and Printer icon) Click OK button. Using Down Arrow select "Confirm Settings" and click OK button. Press Down Arrow twice and it should show the Printer IP Address. Note it Down. It will be something like 192.168.1.60. Now back on your laptop open terminal and type gksudo nautilus. You will be asked your superuser password. Provide that. It will open the file explorer with admin privileges. Be extremely careful now, you do not want to delete anything in this privilege mode. In the file explorer, click on "File System" and browse to the file path etc>sane.d and open the file named epkowa.conf. (Screenshot below) In the file below usb and scsi add the line net 1865 so for our example this will be net 192.168.1.60 1865. (In gedit it was line 12 for me.) Save and close the file. Close all windows. Step 4 - Test Scanner Now open Menu>All Applications>Graphics>Image Scan! for Linux. This should open the software window. Now click on preview to see that a document preview is produced. If it does you are all set. If not you must check the configuration of epkowa.conf and try again. The preview of my 3.5 year olds drawing from scanner : and the final scan is shown below: ","categories": [], + "tags": [], + "url": "/part-1-configure-epson-s515w-on-linux-mint-ubuntu-10-04/", + "teaser": null + },{ + "title": "Call US and several other countries free using Android", + "excerpt":"That’s right…with smartphones all that was possible using computers is increasingly becoming feasible through phones. I just now configured my Nexus S to make free phone calls over internet to USA. How you ask? Well here we go: The site offering free calls to USA landlines and mobiles is www.voipstunt.com so I registered an account with this site. Now Android 2.3 has inbuilt SIP support so I directly configured instead of downloading any app from market but it can be as easily done using sipdroid or csipsimple but anyway since there is good dialer integration I opted for direct route. Goto Settings -> Call Settings and under Internet Call Settings click on "Accounts". Untick receive calls. Click on Add Account. Now in username enter the username with which you have registered on voipstunt website. Enter the password used for voipstunt account in password field. Enter sip.voipstunt.com in server. Untick the "Set as primary account" field. Press back button till you are back to call settings. Now under Internet Call Settings click on "Use Internet Calling" and select "Ask Each Time". This is it. All done. Try calling landline or mobile number of your US contact and before placing the call your phone will ask whether to place call over internet or cellular network. Choose internet and you are off to free calls. ","categories": [], + "tags": [], + "url": "/call-us-and-several-other-countries-free-using-android/", + "teaser": null + },{ + "title": "Google Voice + SIP2SIP + Ikall = Free international Calls to known contacts", + "excerpt":"Second post in succession on the topic but believe me when I get a new gadget I try getting all information and then once I have had the stuff working I have to make a post right away or I will forget and hence this post. This post also overrides the previous posts. Right then, let’s get down to business. What do we aim to do? We aim to make free international calls and there is nothing illegal in this set-up, not to my knowledge. How? Google has introduced Google Voice and allows free calls to all US numbers. This can be done using their google talk plugin in the gmail browser. Pre-Requisites: Gmail account Preferably a new one and not the one you use for your day to day usage. Ability to install X-Lite 4.0 which will require a machine running Windows PC directly or on virtualbox. Ability to install Hotspot Shield if residing outside of USA. An Android device at both source and destination of the call. Steps: Get a SIP number. If outside of USA, install hotspot shield on windows machine by going to this site - http://www.hotspotshield.com .I am not sure how safe this is from spyware and all security perspective. I am not really worried about it as I carry out these things on virtualmachine using virtualbox and there is no real threat to my actual machine which runs on Linux. So if Windows is your primary machine you do it on your own risk. Once installed, open the site www.sip2sip.info.Register on sip2sip using your new gmail account. Ensure that you select US Central as your region. You will get an email from sip2sip on the new gmail account giving your login details which will be something like: SIP address: 2233xxxxxx@sip2sip.info Password: abcdabcdabcd Open the email and click on the link provided for lgging in. Enter the login details provided and goto settings tab (3rd from left). In the first field under SIP Account, enter a new easy to remember password and click SAVE. This completes Step 1 and you have successfully created SIP number for yourself. Get a US phone number using ipkall. Go to the website http://www.ipkall.com and click on sign-up.On the sign-up page complete the following details using the email from sip2sip: SIP username: 2233xxxxxx Hostname or IP address: sip2sip@info Email Address: Preferably the email address you used to register at sip2sip Password: Prefereably the same password as what you changed on sip2sip in Step 1-5. Enter the human verification codes and click "Submit". You will receive a mail with in your email account with a new US number with text of something to following effect: Thank you for signing up. Your IPKall phone number is: 253-XXX-XXXX. SIP Phone Number: 2233xxxxxx SIP Proxy: sip2sip.info Email: abcdefg@gmail.com Password: qwerty This completes Step 2. You have now received a US phone number that is linked to your sip2sip account. Set-up Xlite / softphone to receive calls made to US phone number. In order to activate Google Voice account, it is important to be able to receive call on the new phone number that we have created in Step 2 so install Xlite v4.0 from here - http://download.cnet.com/X-Lite/3000-2349_4-10547103.html Once X-Lite is installed, open it and click on Softphone -> Account Settings. Now fill the following fields: Account Name: Fill your gmail username. User ID: 2233xxxxxx Domain: sip2sip.info Password: sip2sip password (qwerty for this example) Click OK at the bottom of the window. This completes Step 3. Activate Google Voice for this Gmail account. Log into the google voice account - https://www.google.com/voice. Provide a user pin to retrieve voicemails Now provide the US phone number obtained in Step 2-4 (253-XXX-XXXX) A window will be shown with two numbers and a button call now. Click on Call Now and you should receive call on Xlite phone. Accept the call on Xlite and enter the two numbers shown in google's window. You will get confirmation that the numbers are correct and will be asked to set-up voice mail greeting or hang-up. Hang-up now as you can set this up later. Google Voice account is now set-up. Configure Android to receive calls using Google Voice. If you are using nexus S, good news, it has inbuilt capability to get the SIP calls though this can be also be done using SIPDROID on other android devices. For Nexus S you can follow the steps below: Goto Settings -> Call Settings and under Internet Call Settings click on "Accounts". Untick receive calls. Click on Add Account. Now in username enter the 2233xxxxxx provided by sip2sip Enter the password used for sip2sip account in password field. (qwerty in this example) Enter sip2sip.info in server. Untick the "Set as primary account" field. Click on Optional Settings and in Outbound proxy address enter proxy.sipthor.net Press back button till you are back to call settings. Now under Internet Call Settings click on "Use Internet Calling" and select "Only for Internet Calls". For SIPDROID once you have downloaded and installed it from android market, follow the steps below: Open SIPDROID, and goto Settings ->SIP Account. In Authorization Username enter: 2233xxxxxx@sip2sip.info Enter the password used for sip2sip account in password field. (qwerty in this example) In Server or Proxy enter proxy.sipthor.net In Domain enter sip2sip.info Now save and exit. SIPDROID will register the VOIP and turn green. Now from gtalk plugin in the browser from some other gmail account try to call US phone number obtained in Step 2-4 (253-XXX-XXXX). Your phone should ring and so should the Xlite. If step 4 did not happen as expected, you need to review the configuration and once it does happen as expected, your set-up to recieve calls is completed. Configure Android to make calls using Google Voice. Download and install Google Voice Callback on android device - https://market.android.com/details?id=com.xinlu.gvdial&feature=search_result Provide the gmail credentials for the application. This will perhaps explain my recommendation for a new gmail account. You will be giving login credentials to a third party application but since it's a new account with no confidential info, it should really be safe. In the settings for when to use callback select "Ask Everytime" if you are outside of USA. This is it. Try calling one of your USA contact and this application should make a call back and you should be able to talk for free to your US friends. This is happy ending for those who don’t have sight of our aim - make free international calls. For free international calls though you have reached a point where you will have dependency on person you are calling. Following 3 options will be possible: If the person you are calling also has android device and they follow this tutorial they will have a US number which you can then store in your contacts against that person’s name and from thereon you both can call each other absolutely free. If your friend has a SIP enabled device, they can follow this tutorial and replace the set-up of Android device to setting up their own SIP device. If above two are not viable options, you can ask your friend to call you on your international number through googletalk plugin in their web browser. Unfortunately this takes away the flexibility of you being able to call them but given the constraints this may still be a good option to talk for free. Final words - I know it's a long post and looks complex but believe me if you do it right it takes roughly 20 minutes. Hope you find the post helpful. ","categories": [], + "tags": [], + "url": "/google-voice-sip2sip-ikall-free-international-calls-to-known-contacts/", + "teaser": null + },{ + "title": "Time for a bit of show-off", + "excerpt":"Hey Hey I made an Android app named “Sai Satcharitra”. It’s a very basic app and all it does is helps people read Sai Satcharitra on their Android Smartphones, something that can also be achieved by maybe converting the text ino a pdf, epub file etc. Anyhoo, all I wanted was to experiment with Android coding to see how much of that developer is still alive in this business project manager. It took me about 5 hours to complete it after understanding the nuances of Android SDK and refreshing my java coding skills which were quite minimal to start with ;-). I used to be more of a PL/SQL / Lotus Script / C++ kind of developer in my prime. So well long story short, I paid the amount to be registered as developer so I can publish this app on android market all the while thinking that if people do find it useful, I will assume this amount and effort is my way of giving something as Charity in name of Sai Baba. I published the app on Market on 26/03/2011 and then I was busy with all the work that spring brings with it. Then today when I checked my account I was happily surprised to see the stats. The app has been downloaded over 200 times with about 180 active installs. It felt very good to see that many people are finding my work useful.I was a bit disappointed that of the 200+ downloads only 2 have given feedback but hey, I wasn’t there fishing for reviews so as long as it helps someone, I am a happy dude. Check out some screenshots ","categories": [], + "tags": [], + "url": "/time-for-a-bit-of-show-off/", + "teaser": null + },{ + "title": "Logitech Ex100 - Wireless Keyboard and Mouse not working? Read on..", + "excerpt":"Today for some unknown reason suddenly my Logitech Wireless Keyboard and Mouse - Model Number EX100 stopped working and very stubbornly denied to work despite my repeated attempts on all obvious and logical steps - resets, battery change and such. Logitech Cordless Desktop EX100 (920-000879) After trawling through the internet for some time I figured that I am not alone in facing the issue but surely have been hit quite late by it. It appears Logitech was more than happy to replace it at one point. Anyway, further forum hopping lead me to a post that felt like a very unlikely solution but I gave it a shot and should I say more? It worked indeed. So this post is not only for world but for my future reference. With thanks to “Andromedia” on logitech forum, I present the solution that resurrected my dead keyboard and mouse. For Keyboard: Remove the batteries from the Keyboard. Type on the keyboard for about 10 to 30 seconds. Insert the batteries back into Keyboard. Press Left Alt+Left Ctrl+F12 all at the same time and hold for about a second. Now PRESS the Connect Button on Receiver. (DO NOT HOLD) Now PRESS the Connect Button on Keyboard. (DO NOT HOLD) Finally, Press “Esc” on Keyboard. For Mouse: PRESS the Connect Button on Receiver. (DO NOT HOLD) Now PRESS the Connect Button on Mouse. (DO NOT HOLD) That should do the trick. At least for me it did. :) ","categories": [], + "tags": [], + "url": "/logitech-ex100-wireless-keyboard-and-mouse-not-working-read-on/", + "teaser": null + },{ + "title": "MMC Convert media files on Linux", + "excerpt":"I have been using Mobile Media Converter for almost a year now and it is such a great tool. I think it is a must have at-least on Linux. It not only converts almost all popular media formats into other formats but also has an inbuilt youtube video downloader which comes very handy for converting all the free official(and unofficial) song videos into MP3. Let me spell it out, how it all works. 1. Download and Install MMC Well you download and install MMC from this site - http://www.miksoft.net/mobileMediaConverterDown.htm Scroll down to the part of screen that shows the following: Now follow the instructions to add Medibuntu repo if you haven’t already done so. 2. Download media: Open youtube and search for a song that you want to have on you mp3 player. Copy the link from browser and open MMC. In Linux Mint follow Menu - All Applications - Sound & Video, to open following window. Now clicking on Add YouTube video will open following window: Only in this window your link copied from youtube would be already populated. Click on Download and wait till it downloads your song. 3. Convert it to MP3 Once downloaded the video gets added into the queue as shown in the following screenshot: Now you can select the location where you want to store the mp3 and the conversion type as can be seen below. This also shows how many other formats this nice little piece of software can handle. Anyway, select MP3 Audio and click on Convert. That’s it all done. Now I don’t condone the download of any material with copyright, I do support use of anything that is legal. There are some really good official videos (like the one in this example) that can be converted into MP3 or even video formats to enjoy on a long journey. ","categories": [], + "tags": [], + "url": "/mmc-convert-media-files-on-linux/", + "teaser": null + },{ + "title": "Glympse", + "excerpt":"Every now and then we come across something that has a strong potential, something that is a great phenomenon in making and Glympse to me is that idea. The simple to use and highly effective application available on android market for free that allows you to share your position during the travel in real time. While it’s moderately useful for long journeys as was evident on my holiday travel this weekend when my host was able to monitor my progress to their house 250+ miles away, I think this application can find great and effective use in postal and transportation sector. It not only allows you to share your journey but also shows you when was your progress last viewed and who is viewing it right now.I provides you the control on who can have access to this real time view and for how long. You provide a time-frame for which you would prefer this monitoring to be available and then allows you to add more time in 15 minutes intervals. It integrates with phone contacts and has a very slick GUI. All in all very impressed with this application and the fact that it’s available for free is the icing on cake. If you have an android phone, do lot of travelling and need to keep calling for your whereabouts, guess this application can save you on unnecessary calls while letting you concentrate on your journey. Highly recommended application. ","categories": [], + "tags": [], + "url": "/glympse/", + "teaser": null + },{ + "title": "Part 2 - Configure Epson S515W on Linux Mint / Ubuntu 10.04", + "excerpt":"While the majority of settings are covered in my previous post here I found that each time I switched off my printer, it used to change it’s IP address and I had to repeat Steps 7 to 16 each time. My printing and scanning needs are pretty limited so I never bothered working out a solution until today and knowing what I now know, it was quite a simple and quick solution I should have done this on the first place. Anyway as they say better late than never. :) So to carry out the magical transformation to your printing experience follow the steps below: On your printer (actual machine not on laptop / computer) go to Settings (Press the button with Wrench and Screwdriver symbol as shown in picture). Using left arrow navigate to Network Settings (Computer and Printer icon) Click OK button. Using down arrow navigate to "General Setup" and click OK button. Printer will show the question "After changing the settings, network may be disconnected. Continue?" and present options Yes and No with No selected (highlighted in Yellow.) Using left arrow move to Yes and press OK. Printer will now show the screen for "Printer Name Setup", do not change anything. Just Click OK to proceed to next screen. Now printer will show screen for TCP/IP screen with two options Auto which is selected by default and highlighted in yellow and Manual. Here using down arrow key select Manual. Click OK button. The screen will show current IP address assigned to printer something like 192.168.1.66. Now I am assuming that you have already followed part 1 of this tutorial so on your laptop open the file epkowa.conf from /etc/sane.d. You can also open this file by typing the following command in terminal gedit /etc/sane.d/epkowa.conf Note down the IP address you entered last time 192.168.1.60 below usb and scsi. (Line 12 on my file.) Now on your printer change IP address to match the one on last step. To do so, first click on left arrow on printer to reach the last digit of displayed IP address and then adjust the number using up or down arrow. Once it is same as that on epkowa.conf file (192.168.1.60 in this example), click OK button. Now continue to click OK button on all remaining screens till you reach Network Settings screen (About 5 times) Press OK one final time and that's it you are done. If you are to now switch off and switch on the printer and test scan it should work without any issues. Hope you find this useful. ","categories": [], + "tags": [], + "url": "/part-2-configure-epson-s515w-on-linux-mint-ubuntu-10-04/", + "teaser": null + },{ + "title": "How to edit PDF in Linux - The easy way.", + "excerpt":"Recently someone asked this question to me and after some search on Google I came across mentions of PDFedit, Scribus, flpsed, Gimp, PDFMod and even the openoffice plugin for importing PDF and I tried all of these. PDFescape is probably a good solution but is only online and limited to 30 pages in it’s free avatar. If your aim is similar to the one stated below, these tools are way too complicated (PDFedit), limiting (PDFescape) or ineffective (rest of them). So, Let’s start with the usual what was my aim question. Aim: To be able to highlight and put his own annotations, highlights and comments in the lecture notes that he gets from his college lecturers in pdf and be able to print the amended notes from any platform. (Linux / Mac / Windows.) What Worked? What I gave my friend as his final solution in the end kills two birds with one stone - Use of \"Calibre\" to convert the pdf to rtf and then use the open office to edit the notes. Where is the second bird, you ask…well Calibre is a great tool, infact in my opinion the best tool for ebook management. Using Calibre will not only help in editing the notes from the pdf but enable us to have a copy of final notes for ready reference right on our smartphone. There you have the second bird. :) Now the steps involved are listed below: Steps: 1. Install and set-up Calibre 2. Import pdf into Calibre 3. Convert pdf to rtf using Calibre 4. Open rtf file and start editing 5. Convert final version of rtf to epub and transfer to your phone. Step 1 - Install and set-up Calibre: The current version of Calibre at the time of writing is 0.8 while the one in ubuntu repository is 0.6 so I recommend you should update after installing from repo as that will ensure all dependencies are satisfied and then you have the latest version as well. So first open your package manager (on Linux Mint: Menu - Package Manager). You will need to provide root password and then search for Calibre and install it. Now to update to latest version, open the terminal (on Linux Mint: Menu - Terminal) and type the following command: sudo python -c \"import urllib2; exec urllib2.urlopen('http://status.calibre-ebook.com/linux_installer').read(); main()\" You will be asked for your root password so provide that and terminal will show a question: Enter the installation directory for calibre [/opt]: Type /opt and press enter. Now type Calibre and it will fire up the welcome wizard. Follow these screen-shots: Select your language and Directory where you will want your ebooks to be stored. I keep it default. Select your ebook Device. I have selected Android as that is the smartphone I use for reading ebooks. That’s Calibre configured. Easy-Peasy !!! :) Step 2: Import pdf into Calibre Open Calibre (On Linux Mint: Menu - All Applications - Office - Calibre or just by typing calibre on terminal). Now click on \"Add Books\" and select \"Add books from single directory. It will open the dialogue box for you to select file. Go to the location where you have saved your pdf. In my case it was in /Downloads/Tutorials as you can see in next screenshot. Select the file and click open. Calibre will then import the pdf. While importing it shows the \"Adding\" screen as can be seen in screenshot below. Depending on size of the pdf it can take a while or be very quick. In this case as it’s a small lecture note it was hardly few seconds. Once completed, imported file will appear in your library as shown. Once completed, it’s time to move to next step. Step 3: Convert pdf to rtf using Calibre Select and right click on the pdf imported into Calibre and select Convert - Convert Individually. It will open the window to show options for conversion as shown below. In this window on right hand side corner select \"rtf\" from the drop-down against \"Output\". Now click OK. Once Converted it will show the rtf format in right pane as shown in red circle in following figure. Step 4: Open rtf file and start editing Click on rtf and it will open the file in open-office and you are ready to edit, highlight and do pretty much what you like. You may notice that some pictures are not coming on the page but all that is required is to do some formatting for openoffice which is quite straight forward. Double click on the picture and in most cases it will be fixed if you click on \"keep Ratio\" and changing the anchor to Page. Some changes to page layout from Portrait to landscape may also help. To do so go to Format and select Page. It will open the dialogue box as shown below where you can switch between Portrait and landscape. Step 5: Convert final version of rtf to epub and transfer to your phone (Optional) Once done save your work with a different filename and follow steps 2 and 3 to import the saved rtf file into calibre and convert to epub this time (Keep output to epub in step 3) and then transfer it to your smartphone through mail or by usb transfer. That’s it all done. It’s a long tutorial keeping in mind people who are new to Linux but carrying out the steps to convert doesn’t take more than 2 minutes so trying this will be well worth it. I must add that this may not be required if all you will ever need is to add notes and comments and highlight in pdf and then print from your own laptop / computer. In which case OKULAR is a far superior way of doing it and you must try it out. However, having said that I don’t know many college going students who have easy access to Printers at their hostel and in all likelihood their print-outs will be from college library in which case solution above should come handy. ","categories": [], + "tags": [], + "url": "/how-to-edit-pdf-in-linux-the-easy-way/", + "teaser": null + },{ + "title": "Install Open Workbench and JRE on Wine in Linux Mint", + "excerpt":"What is Open Workbench? Open Workbench is a Project Planning Software comparable to Microsoft Project. There are mixed views on whether it is truly open source or not but as it is considered a very good alternative for Microsoft Project and it is free to download, it is something I as a Project Manager would want to know about. After all some of my clients are going to be using this. :) Now then, as of writing this article Open Workbench is not available from the well publicised site www.openworkbench.org which seems to be down. Instead it is available for download fromhttp://www.itdesign.de/en/products-solutions/open-workbench.html As you will find that the installer is an exe and the product is made for Windows platform and requires Java Runtime Environment, we will need to either use Windows in virtualbox or Wine. I am going to cover below the steps for installing the software on Linux using Wine. Steps to install Open Workbench and JRE on Wine V1.2.2 in Linux Mint: Install JRE in wine Install Open Workbench in wine Configure wine to play well with Open Workbench Configure icon to launch Open Workbench What do we need? We will need to download the installer exe file from http://www.itdesign.de/en/products-solutions/open-workbench.html As the product requires Java Runtime Environment, we will need to download the JRE for windows. We will need access to system32 folder of a WindowsXP machine or in Virtualbox. Step 1: Install JRE in wine You can download the JRE file from http://www.oracle.com/technetwork/java/javase/downloads/index.html . Remember, we only need JRE not JDK so click the download button in second column. This will open another page for you to select which JRE package you want to download. Click on the radio-button to accept license agreement and then select Windows x86 Offline for download. I am assuming that your hardware is 32 bit, if not please select Windows x64 but it will not have the offline version and I have no way of checking if it will work or not; so please leave a comment either way as it will help others. a) Copy the downloaded (jre-6u25-windows-i586.exe) file into .wine/drive_C/Program Files. You can do this using GUI on Linux Mint using steps below: Goto MENU-ALL APPLICATIONS-WINE and click on BROWSE C: DRIVE Click on Program Files folder. Paste the copied exe file here. b) Right click on the exe file and select \"open with wine windows program loader\". c) Follow the installation wizard. Once completed move on to next step. Step 2: Install Open Workbench in wine a) Download the “Open Workbench” installer file(Open_Workbench_Setup_1.1.6.exe) and copy it into .wine/drive_C/Program Files. TIP: You can use Step 1 bullet \"a\" to reach drive_c and then click on windows folder and then on system32 folder. b) Right click on the exe file and select \"open with wine windows program loader\". TIP: You can use Step 1 bullet \"b\" to reach drive_c and then click on windows folder and then on system32 folder. c) Follow the installation wizard. Step 3: Configure wine to play well with Open Workbench Due to some bug identified in Wine V1.2 the odbc32.dll file does not work and gives the error “err:module:attach_process_dlls “odbc32.dll” failed to initialize, aborting” if you try to run file from Terminal. The following workaround is one suggested on Launchpad (https://bugs.launchpad.net/ubuntu/+source/wine1.2/+bug/572393) and does solve the problem. I have just tried to make it easier to follow the instructions by providing screenshots and explaining each step. On the WindowsXP machine / virtualbox goto following location – C:\\WINDOWS\\system32 Now copy odbc32.dll and odbcint.dll files onto the Linux machine at this location - .wine/drive_c/windows/system32 . TIP: You can use Step 1 bullet \"a\" to reach drive_c and then click on windows folder and then on system32 folder. Now goto wine configuration (On Linux Mint: MENU-ALL APPLICATIONS-WINE-CONFIGURE WINE) Click on Libraries tab. From the dropdown select odbc32 and click add. Click OK Step 4: Configure Open Workbench icon to launch the application Open terminal (MENU - TERMINAL) Type: wine ~/.wine/drive_c/Program\\ Files/Open\\ Workbench/bin/npWBench.exe Once Open Workbench is opened we are sure the command works. Select this command and right click and select copy. TIP: You can also press Ctrl+Shift+C to copy the selected text from terminal Close "Open Workbench" and goto icon in MENU-ALL APPLICATIONS-WINE. Right click the "Open Workbench" icon and click on "Edit Properties". In Command click on browse and navigate to following location: .wine/drive_c/Program Files/Open Workbench/bin/ and select the file npWBench.exe. Now click "OK" This is it. You are ready to use Open workbench on Linux using wine. ","categories": [], + "tags": [], + "url": "/install-open-workbench-and-jre-on-wine-in-linux-mint/", + "teaser": null + },{ + "title": "Visio Alternative on Linux - Business Process Model and Notation Tool", + "excerpt":"UPDATE: PENCIL is an opensource software which is a pretty good alternative as well with a smaller learning curve and is Opensource. I was recently looking for Visio alternatives on Linux and while Dia is good it lacks the oomph of Visio and hence not an easy sell for new and potential Linux converts. Now good news for those open to enter the wonderful world of Linux is that any OS need not have an alternative for it to be usable to you, all it needs is the ability to let you run the software you find useful. So this post isn’t about why Linux is better. It is about knowing that there is something better than the software you have got accustomed to. If someone is adamant on continuing with the software they have fallen in love with, there is always WINE / Virtualbox but if you have opened to the idea of new OS but you are taking baby steps software like yEd can help you take a giant leap. yEd is written in Java and can run on Windows, Mac and of-course Linux. Before we move into the steps of how to get it and use it let me bore you little with what are the other alternatives I explored. During my quest I came across two other useful candidates Activiti 5.6, ARIS Express 2.3. What went against these and in favour of yEd? Read on: Ease of installation First on the list is ARIS Express 2.3. Now this requires registration which is my first gripe. I could have lived with that but as claimed on their website the product is meant for Windwos and is reported to be working fine for Linux. What they forgot to mention is on linux using Wine or atleast that is what it did on my set-up. Now am not sure if it has something to do with my installing JRE in wine for using Open Workbench (see last post) or it is because the product works on similar lines as Open Workbench installer but eighther way it does not run natively on Linux and for that reason alone it is unreliable and looses points even to Activiti. For easiest installation, Activiti 5.6 can be added into Eclipse but that results in a bit complicated and not so user friendly user-experience. You will at-least need to have one Activiti project created in eclipse before you can draw using Activiti Diagram. (Check Screenshot) Above might be circumvented by installing standalone Activiti bit that involves configuring ANT and other stuff. While guide is there, it is not very straight forward and considering we want to win new users not scare them, it is best left as untouched topic. Ease of Use - While all the three products yEd, Activiti and ARIS are superior to Visio, in terms of ease of use I think it's yEd which wins hands down even to Visio. The one aspect of this software that won me over was it's ability to auto align the whole flowchart / diagram with just one click and it can do it in several layouts. OK, now that I have covered comparison, let’s get down to how we install it on our beloved Linux Mint. It’s really very simple. Step 1: Go to this website - http://www.yworks.com/en/products_yed_download.html Step 2: Download yEd for your platform. In our case for Linux Mint the 43 MB sh file in front of Linux. Step 3: a) Go to the folder where it is downloaded, select the yEd-3.7.sh file and right click on it. b) In the right click menu select Properties (last entry). This will open the yEd-3.7.sh properties box. c) On the Properties box, click on Permissions tab. d) Click the checkbox in front of Execute Allow executing file as program. e) Click Close. f) Double click on the yEd-3.7.sh file. It will display a dialogue box with four options Run in terminal, Display, Cancel, Run. Select Run in terminal g) yEd will now install, it may give one small error but it did not affect anything at all so ignore it if you get it. h) Follow the wizard and leave default options. i) Once done, you will have yEd successfully installed and can be found in MENU ALL APPLICATIONS-OTHER. j) Create a shortcut of yEd by dragging yEd from MENU ALL APPLICATIONS-OTHER onto desktop. You may need to make this dragged link executable by right clicking the link and following steps b,c and d. All Done !!! A quick tutorial from yWorks: Find more on http://www.yworks.com/en/products_yed_videos.html Having seen, how nice and easy it is to create the flowchart, let’s also see the best feature of the product that is one click alignment. ","categories": [], + "tags": [], + "url": "/visio-alternative-on-linux-business-process-model-and-notation-tool/", + "teaser": null + },{ + "title": "Access (files / folders) Directories from Linux Mint on Android", + "excerpt":" Let me clear the air before anyone mentions. Yes, I know it’s directories and not folders and yes I know many still call these folders and this may encourage the wrong usage but my aim is to help not educate but hopefully making this the first line in post will have some educational effect :). Right, so to start with, presented below is the problem statement: We want to share particular folder on our home WiFi network such that all members of the family can access it using their gadget - laptop / smartphone / netbook etc. Now like a true project manager, I will scope it down by making an assumption that any gadget that runs a variant of linux - Android included. It’s not to say that the solution will not work on iOS or Windows, it’s to say that these operating systems are not covered but might as well work with right application downloaded.However the solution offered will make no such claims…:p. Having said that, I will actually only be covering Android in this article as for Linux on desktop, it is fairly straight forward by going through \"Network\" places. Ok now to the exciting stuff: Step 1: Decide which Directory (Folder) you want to share. For purpose of this demonstration, let’s create a new folder called \"DemoShare\". Step 2: Decide whether you want it to be accessed by specific users or anyone and everyone connected to your home network a) If you would want the shared directory (folder) to be password protected go through all steps below. b) If you would want to give guest access to all users, skip to Step 4. Step 3: a) Create new User(s) - Let’s create a new user - androshare. This can be done by following the images below: Obviously you are free to be creative with the username. Just replace androshare with your username in subsequent commands. b) Create samba username(s)/password(s) Open the terminal and type the following command: sudo smbpasswd -a androshare. You will be asked for the root password and then new password for the username. Once you type in new password, you will be asked to retype the new password. Again as shown in image. c) Restart Samba Open the terminal and type the following command: sudo service smbd restart Step 4: Update Share Properties of the Directory(ies) to be shared Goto the folder \"DemoShare\" created in Step 1, right click on the folder and select properties. On the properties dialogue box click on Share tab. Click on share the folder checkbox. For scenario (a) of Step 2 click on checkbox - \"Allow users to create and delete files in this folder\" as shown in the figure below and click on Create Share. For scenario (b) of Step 2, mark the \"Guest access\" Checkbox too as shown in next figure before clicking the Create Share button.</tr> After the Create Share button is clicked following dialogue will appear. Select \"Add the permissions automatically\"</tr> </table> The folder icon will then change to show a share flag as shown below. Step 5: Download and install the file explorer app ES File explorer is what I use and recommend. It can be downloaded from market here - https://market.android.com/details?id=com.estrongs.android.pop Step 6: Configure the app to access the shared directory On your computer, open the terminal and type: ifconfig In the resulting information, locate wlan0 (last entry) and under that in second line you will find something like "inet addr: 192.168.1.74", Note this down. Open the app and goto LAN tab. Press the menu button and click on "New". Now click on "Server". This will open the "New/Edit Samba Server Screen". Complete it as shown with following information: | Field | Scenario A | Scenario B | |-----------|-----------------------|-----------------------| | Server | IP Address from above | IP Address from above | | Username | Androshare | BLANK | | Password | As given in Step 3a | BLANK | | Anonymous | BLANK | Select the checkbox | Click OK. Now if you click on the IP address, it should show the shared directory. As you can see in the screenshot at the beginning of this post, I have already shared my Calibre Library and it does make life really simple. I can download files and e-books without actually going to my laptop. That is all there is to it. Hope you find it useful. ","categories": [], + "tags": [], + "url": "/access-files-folders-directories-from-linux-mint-on-android/", + "teaser": null + },{ + "title": "OpenVPN on Linux Mint to access US sites", + "excerpt":" UPDATE: LOOKS LIKE HOSTIZZLE IS NOT WORKING ANYMORE. THE STEPS IN THIS GUIDE WILL STILL BE RELEVANT FOR SETTING UP OPEN VPN, JUST THAT YOU WILL NEED TO FIND SOME OTHER PROVIDER. I received a Google Music invite and as it is only available in US, I used OpenVPN on linux. Now this is a handy little trick to bypass the geographical constraints that are placed to block services on net. All this without opening the windows box to install dodgy looking services of Hotspot Shield which anyway does not work on Linux. Ever come across \"This video is not available in your country.\" on you tube or ever wanted to access Hulu only to be greeted by a similar message. Well then, if yes is your answer, openVPN is the solution to such sorrows. It also comes with some other benefits such as anonymous browsing but then highlighting benefits of VPN is not exactly the aim of this post. The aim of this post is: To configure a free VPN service that allows you to bypass country restrictions for US based sites. Keyword - US. As the site I am going to use only gives a US IP. Ofcourse if we remove the word free it opens up a range of possibilities which I will leave the readers to explore. :) Alright, let’s get on with the business: Step 1: Install OpenVPN Open Synaptics and type OpenVPN in \"Quick Search\". Then select all the packages marked green in screenshot below but not in your synaptic window and click on \"Apply\". Then type \"pptp\" in \"Quick Search\" and select all the packages marked green in the screenshot below but not in your synaptic window and click on \"Apply\". Step 2: Register with the free OpenVPN provider "Hostizzle" a) Open the website http://hostizzle.com/ and follow the screenshots below: Step 3: Set-up OpenVPN on Linux Mint a) Open Terminal and type the following command: gksudo nautilus /etc/openvpn/ You will be asked for root password and the File Explorer will be opened with root access. This is necessary as /opt directory only gives write access to root and as you wil see next step requires some files to be extracted in this directory. This extracted directory will be some long number. Please be extremely careful now, as you are accessing your file system as root. b) Now copy the zip file downloaded from Hostizzle in Step 2 to this directory and extract it here. Step 4: Configure network manager Open the Network Manager as shown in following screenshot: Menu > All Applications > Preferences > Network Connections This will open the following window: Now click on VPN tab and then on Import button as shown in next screenshot. It will open the file selection box. Navigate to File System/etc/openvpn/ and now open the long numbered directory that was extracted in Step 3(a) as shown in next figure. Here select the .ovpn file and click Open. Following dialogue box will open. Just accept defaults and click \"Apply\". Then Click on \"Close\" on Network Connections window. Step 5: Connect to the OpenVPN Restart your system and once it is connected to your home WiFi, click on Network Indicator in the panel and select \"VPN Connections\". This will list all available VPN connections as can be seen in the screenshot. Select the VPN name with big number which is what we have just configured. Once done if everything was done correctly, it should connect to VPN and you should be able to browse with an american IP address. Some key points to remember: Free service of Hostizzle is quite generous compared to so many other options I explored, yet it is worth knowing the restrictions: a) 100GB of bandwidth each month. b) US IP c) Limited to one month. You need to download new Keys and configure VPN using steps below each month. Now considering that it takes less than 5 minutes to complete these steps and that it is a free alternative with a generous bandwidth, I would say it is all worth it. Ofcourse, if you are not happy with the restrictions, there are some paid alternatives that can be implemented for as low as $4 per month from various providers, Hostizzle included. Go shopping !!! ","categories": [], + "tags": [], + "url": "/openvpn-on-linux-mint-to-access-us/", + "teaser": null + },{ + "title": "How to boot from USB when BIOS does not have the option.", + "excerpt":"I have an old Sony VAIO which is not in it’s best of health and has long been really a companion for my telly, faithfully streaming media from bbc iplayer, youtube, dailymotion and likes. Internet enabled TV arrived in my home long long back :). Now the thing with this laptop is that it’s kinda gimpy - inbuilt keyboard won’t work, battery is dead and it hangs on life with constant supply of energy from the AC source on the wall and the one thing that helps me load new OS on this machine - the optical reader - is temperamental and may or may not work and is moody in selecting which CD / DVD it will read and which it won’t. It does in particular like CD’s authored by Linux Format guys though. Writing is a skill it has forgotten long back and if it reads something, anything I am found celebrating. Anyway, I had ubuntu installed on this laptop for quite some time but as this laptop has one more flaw - the nvidia graphic card - and the latest update from ubuntu broke the nvidia drivers which aren’t all that well supported anyway, I was being forced to reformat the machine. So I decided to try a new distro named Bodhi Linux which is very cool and uses Enlightenment as desktop which is very very good and way better than Unity and some might argue even Gnome 3.2. However, in order to do so I had to cross the hurdle of burning a CD that my laptop’s Optical Drive will find intresting enough to read. I have checked and rechecked the BIOS of this laptop and there is no way to make BIOS understand that it can boot from USB. The only options it provides are Hard Drive, Floppy Disk, Optical Reader and Network Boot. None of these were particularly useful for reasons explained above and unfortunately I was hitting the wall. So I started looking for alternative ways to get Bodhi Linux installed. Alternative is what I found in Plop Boot Manager. Now open source enthusiasts at this point be aware, this nifty piece of software is not open source but it is so useful that this one minor flaw must be completely ignored. It gives you options to boot from USB in several ways - you can burn a CD and fool BIOS to boot from CD onto plop boot manager which in turn allows you to boot from device of your choice aka USB, it can also be put on floppy or the option that I have used - install on hard drive and configured through GRUB. So presented below without further ado is the guide to how you can install this on your hard drive but before I do so a quick thanks to several boards and posts I referred in the process of making this work: Steps: Please note that these instructions will work for machines that have one of these OS installed: Bodhi Linux and Ubuntu variants. For other linux versions it should be on similar lines Download the Plop Boot Manager (plpbt-5.0.14.zip file) on the machine where you want to achieve the result from http://www.plop.at/en/bootmanager/download.html Extract the zip file in folder where it was downloaded. In my case I have my browser setting set to download everything to "Download" folder. Now open the terminal and type following command relevant to your distro: For UBUNTU: gksu nautilus /boot For Bodhi: sudo pcmanfm /boot You will be presented with a dialogue box to enter password. Once you enter the password you will be presented the contents of boot folder as shown below. Now (a) Go to the extracted plpbt-5.0.14 from step 2, click on Linux folder (b) Then copy the files - "plpbt.bin" and "plpcfgbt" and paste them in the boot folder opened through step 3. Once the files are copied in boot folder, double click on folder named grub and there open the file named "grub.cfg" in texteditor. Once the file is opened, press \"Ctrl+f\" and search for string \"END /etc/grub.d/10_linux\". Now copy the text as highlighted in screen below and paste it in a new text-editor window. In the new text editor after pasting the four lines from above a) Edit the fourth line so it reads as below: linux16 /boot/plpbt.bin b) Complete the block in new editor to read as below, keeping the first three lines intact from what was copied in step 5 from grub.cfg file. In our example it will read as below: menuentry "Plop Bootmanager" { insmod ext2 set root='(hd0,6)' search --no-floppy --fs-uuid --set cb7a6eb7-b355-4d0f-865e-f7312880f887 linux16 /boot/plpbt.bin } Once again, this is important so remember the final structure will be achieved by following steps below. Copy Paste this in Line 1: menuentry "Plop Bootmanager" { Next three lines remain same as copied from step 5: insmod ext2 set root='(hd0,6)' search --no-floppy --fs-uuid --set cb7a6eb7-b355-4d0f-865e-f7312880f887 Then the fourth line will be edited to look as shown below. You can copy paste this in fourth line. linux16 /boot/plpbt.bin Fifth line will be closing bracket. } Now open another terminal window and type following command relevant to your distro: For UBUNTU: gksu nautilus /etc/grub.d For Bodhi: sudo pcmanfm /etc/grub.d You will be presented with a dialogue box to enter password. Once you enter the password you will be presented the contents of boot folder as shown below. Open the file 40_custom (highlighted in screenshot above) in texteditor - gedit on ubuntu or leafpad on Bodhi and paste the block from step 6(b) in this file and save it. #!/bin/sh exec tail -n +3 $0 # This file provides an easy way to add custom menu entries. Simply type the # menu entries you want to add after this comment. Be careful not to change # the 'exec tail' line above. menuentry "Plop Bootmanager" { insmod ext2 set root='(hd0,6)' search --no-floppy --fs-uuid --set cb7a6eb7-b355-4d0f-865e-f7312880f887 linux16 /boot/plpbt.bin } Very Important - Press "Enter" at-least twice after pasting to ensure there are atleast two new lines below closing bracket. Close all windows and open terminal once again and type following command: sudo update-grub Now open /boot/grub/grub.conf and you should find the following entry on it: ### BEGIN /etc/grub.d/40_custom ### # This file provides an easy way to add custom menu entries. Simply type the # menu entries you want to add after this comment. Be careful not to change # the 'exec tail' line above. menuentry "Plop Bootmanager" { insmod ext2 set root='(hd0,6)' search --no-floppy --fs-uuid --set cb7a6eb7-b355-4d0f-865e-f7312880f887 linux16 /boot/plpbt.bin } ### END /etc/grub.d/40_custom ### Now reboot your machine with your Live USB plugged in. If you already dual boot your grub will show on restart and will have an additional option in the end - "Plop Bootmanager". If however, you just have single OS ubuntu, press shift once the bios logo shows up and keep holding for getting the system to show Grub. Once Grub is shown it will have the additional \"Plop Bootmanager\" menu entry on grub. Select the \"Plop Bootmanager\" and press enter. Plop Bootmanager will show option to boot from USB. It was third option for me. Select and press enter. If your Live USB is working, you will be able to load OS from it. This is it. You can check the video below: Please do let me know in comments if you found this useful. If there are any other ways I will be keen to hear those too. ","categories": [], + "tags": [], + "url": "/how-to-boot-from-usb-when-bios-does-not-have-the-option/", + "teaser": null + },{ + "title": "StageVu / DivX video on Bodhi Linux", + "excerpt":"If you read my last post you will know I have been playing with Bodhi Linux lately. One of the selling point for this distro is it’s minimalistic approach. However, that also means it doesn’t come with some of the media packages that help stream all type of media - stagevu (DivX) included. Fortunately it does not require lot of effort to get them all working. At-least for me it was quite quick. The steps I followed are listed below: Install non-free codecs: http://appcenter.bodhilinux.com/software/showDesc/Non-Free_Codecs Install dvd codecs: http://appcenter.bodhilinux.com/software/showDesc/Dvd_Playback Install VLC Player: http://appcenter.bodhilinux.com/software/showDesc/VLC Install SMplayer: http://appcenter.bodhilinux.com/software/showDesc/SMPlayer Install totem, totem-mozilla, mozilla-plugin-vlc, mencoder using Synaptic Package Manager: You can find Synaptic Package Manager under Applications or simply by typing the following command in terminal: sudo synaptic Install divx4linux package: This can be downloaded from following link: http://www.mediafire.com/?71fs4c9cy8xy4q4 Double click on the downloaded file and it will launch the installer. Once installed, restart the system and then open media sites in firefox and check that the videos are working. For stagevu though divx4linux should work out of the box on firefox but if not click on \"Tools\" > \"Manage Content Plugin\" and select divx to play the plugin. Restart browser and that should be it. Let me know your experiences and tips in comments. ","categories": [], + "tags": [], + "url": "/stagevu-divx-video-on-bodhi-linux/", + "teaser": null + },{ + "title": "Exchange 2007 on Thunderbird using DAVMail", + "excerpt":"The start of this week was like a nightmare for me. Whole family was down with flu and I had the fever that is probably the highest ever of my entire life at 40.5 C (~ 106 F). Anyway, surviving that was easier compared to the aftermaths of this health problem that forced me to stay in bed and inadvertently deal with office mails at home on a non-IE browser with Lite version which is crap and makes you feel miserable enough to kill yourself. So the option I had was to either boot windows on a virtual machine or find a solution within linux. Obviously, I prefer the latter and was glad to work out a solution that can help me avoid booting windows. I have listed below the steps I followed to achieve this, though in all honesty the documentation is quite good on sourceforge site itself. It’s just that some of their screenshots are dated and in French (literally, no pun intended). Now there are several options floating around but this set-up works flawlessly for me and so I will obviously recommend this over other methods. It’s so easy to configure that there really is no reason not to give it a shot. Step 1. Install DAVMail You can get the latest version from sourcefourge by following the link below: http://davmail.sourceforge.net/download.html Select the download based on your distro. For Linux Mint 12 it will be Green Box and for non-debian based distros the red. Now double click on the downloaded .deb file and DAVMail will be installed taking care of any required dependencies. Step 2. Configure DAVMail Once installed, you can open DAVMail on Linux Mint 12 under Internet. You can copy all the settings from this screenshot, leave them with what is there by default or change if you need to. Most important part is the first field OWA (Exchange) URL. You must paste your exchange 2007 OWA URL here. Believe it or not, that’s all the configuration you need to do for DavMail. Step 3. Configure Thunderbird Email Client Now over to configuring the Thunderbird Email Client. Enter Details and Click on Continue. It will show next screen by itself. Follow instructions as per next screenshot. If you see following warning, tick the checkbox and Click on Create Account. After this you may be presented with a dialogue box to enter the username and password once again. If so provide details. Remember to try just abc.xyz as username for abc.xyz@officemail.com and only if that does not work and you are presented with the dialogue box again should you try the whole email id as username. It does take quite some time in first run as Thunderbird downloads all the mails. I was happy with the speed though YMMV. I have applied an MS Office based theme that you can find in add-ons directory. Link below: https://addons.mozilla.org/en-US/thunderbird/addon/ms-office-2003-jb-edition/ Finally my mailbox looks as below: Step 4. Configure Calendar On Linux Mint 12 Lightning the Thunderbird calendar client does not come pre-installed. So head over to Synaptic package manager and search for \"xul-ext-light\", select the shown items and install them. Once installed, restart thunderbird and you will see calendar icon and Task Pane. Either click on Calendar or just press Ctrl+Shift+c This will open Calendar view as shown below: Select \"New Calendar\". Select \"On the Network\" radio button and click on \"Next\" Select CalDAV radio button, and fill the location field with http://localhost:1081/users/ankit@officemail.com/calendar. Make sure you adjust the port as per your settings of Step 2. Give a name to this Calendar - say Office Cal for instance - and click Next. After this you may be presented with a dialogue box to enter the username and password once again. If so provide details. Here provide abc.xyz@officemail.com and if that does not work and you are presented with the dialogue box again try with just the abc.xyz as username. Once connected, your Calendar will be in sync as will be your tasks. Step 5: Configure Global Address Book Open Address Book and then select File > New > LDAP Directory. It will open the following box. Fill as shown below and make sure Port number is as advised in red text. Then Click \"OK\" That is it. Test and see if you are now able to update your calendar, tasks, emails etc. and if all has gone as explained above you should be good to go. ","categories": [], + "tags": [], + "url": "/exchange-2007-on-thunderbird-using-davmail/", + "teaser": null + },{ + "title": "Top 10 Android Apps that I use !!!!", + "excerpt":"I have been using Android Phones and Tablets for quite some time now and have monitored my use for apps that I use on a daily basis and those I would not even come to know if they were removed without my knowledge. So clearly there is a personal perspective on the list below: Gmail The most obvious and the most heavily used app. No introduction required. ** What I like best? ** Clean Interface ** Push Notification ** What can improve? *** Ability to zoom messages. AppyGeek / AppyGeek for Tablet I use this app on a daily basis to get my daily dose of news in technical world. ** What I like best? ** It pulls news based on keywords you can select. So my feed includes news on tags such as “Linux”, “Smartphones”, “Galaxy Tab”, “Nexus”, “Raspberry Pi” …you get the gist. ** What can improve? ** Ability to access to stories in offline mode. Guardian Anywhere Fine, I admit wholeheartedly, I am strong left wing supporter. Now with that out of the way, I prefer this app over the official Guardian app as this downloads content overnight. ** What I like best? ** Offline Access to stories. ** You can schedule the time for download, I prefer 4:00 AM so I have all fresh news first thing in the morning. ** “Picks” tab which learns my taste over time and provides stories to match it. ** What can improve? ** Ability to copy a small section of text from the story. Financisto is an opensource app to manage your budget and track your expenses. It’s very comprehensive and one of the best app available on any mobile platform in my opinion. It does require initial set-up and perhaps something I will write about in another post soon but generally I find using it a very easy affair. ** What I like best? ** Complete Package equivalent to any desktop finance management software. ** What can improve? ** App can do with a basic list of category to reduce the burden of creating list of categories from scratch. *** Improved Documentation QuickPic It is a photo gallery which have got accustomed to as initially the stock gallery app on Android was not very good. However, recent gallery app has seen some very good enhancements yet I still feel more at home using QuickPic. ** What I like best? ** Ability to hide folders that have downloaded photos from other apps and are not taken by phone camera. ** Ability to share a photo right from within the app through gmail, email, bluetooth, G+, FB etc. ** What can improve? *** I don’t think there is much to improve. It’s a pretty well rounded app in my opinion. Android Assistant It’s a android system utility for monitoring system tasks, killing apps etc. All these are not what make it interesting though as there are several apps out there doing the same stuff. What I like most is listed below: ** What I like best? ** With latest version, it prompts to move any newly installed/updated app to SD card if it is possible. ** It shows the total start-up time and allows you to select apps that should be silent at start-up. ** It allows for batch uninstall, which comes quite handy when on a system clean-up spree. ** What can improve? ** Can do with a more glossy makeover. ES File Explorer File Manager The primary function of this app is to allow user to browse the file system but it really has loads of features under the hood. ** What I like best? ** Simplicity. ** Cloud Connection to support likes of Dropbox, Google Drive, Ubuntu One etc. ** FTP ** Everything really. ** What can improve? *** This app covers the area of accessing files, network and so on. It has a lots to offer but I don’t think it’s used to it’s full potential by many. I think this app can do with a strong tutorial for each of it’s feature. Profile Scheduler: This is better app than Smart Profile and I have found that it’s ability to switch profile based on WiFi saves me a complex set-up that would have been based on time but even so the time based set-up is much easier and straight forward and it does allow priority over rules which makes things super easy. It’s not a drain on battery and I am very satisfied with this app. Nothing to improve really. Smart Profile This app is a profile management tool that helps in switching to a profile based on user defined time slots. I have FOUR profiles that I switch between Sleep (Completely Silent Mode every day from 23:45), Work (Vibrate mode Monday to Friday from 09:00), Normal (Monday to Friday from 17:00), 0600 Weekends + Mornings (Normal Profile everyday from 06:00). How it works then is Starting Monday morning 06:00 phone goes on normal mode, then at 09:00 turns to vibrate mode, then at 17:00 switches to Normal profile and in night at 23:45 goes to sleep mode. This continues till Saturday morning but as “Work Profile” is scheduled only from Monday to Friday so from Saturday 06:00 onwards phone continues on normal mode whole day till 23:45 in night when it switches to sleep mode, only to get back to normal mode on Sunday at 06:00. Then again remains on normal mode whole of Sunday and in night 23:45 goes back to sleep mode. Then the cycle starts again. ** What I like best? ** Free yet effective. ** What can improve? ** As you can see from my explanation above, it is a bit complex to set-up. There was an app called audioguru which stopped working on ICS forcing me to switch but it had a simpler interface. That said in free version of audioguru there was no ability to schedule weekend so this is much better at the lovely price tag of “free”. :-) HulloMail It’s a visual voicemail app that works with most of the providers in the UK for their pay-monthly customers. ** What I like best? ** Ability to select the voicemail I want to listen to first. ** Replay the important part of voicemail ** Store VM as mp3 for future refrence. ** What can improve? ** I don’t want much else from this app in it’s free version. So it’s alright as is. Go SMS Pro Go SMS Pro is a SMS client which is better than the stock client as it provides many useful additional features ** What I like best? ** Ability to schedule SMS. ** Ability to send SMS in batches / groups ** Cool skins ** Go Chat is another useful feature as long as your mates are using it too. ** What can improve? *** They are improving it all the time but I can’t think of any complaints at the moment. ","categories": [], + "tags": [], + "url": "/top-10-android-apps-that-i-use/", + "teaser": null + },{ + "title": "Flash Samsung Galaxy S (GT -I9000) with Cyanogenmod 9 on Linux", + "excerpt":"I had an old Samsung Galaxy S which was still on stock ROM hence it only ever got to gingerbread and then Samsung just decided not to upgrade and I upgraded the phone so this little gadget was till yesterday destined to live with the old gingerbread. Then yesterday, I just decided to play around with it and started reading so I know what are my options. Now wiki guide on Cyanogenmod site is quite nicely written but there were one or two steps here and there which had me confused for a little while so here is my usual step-by-step guide on how to go about it. UPDATE: Uploaded all the files to mediafire as requested in one of the comments below. These can be downloaded from following link: http://www.mediafire.com/?ims1bxp6b8yp8 Step 1: a) Download Heimdall Suite 1.3.2 Command-line Binary for your OS from here ; for Linux Mint you can use the Ubuntu link and install the downloaded \"heimdall_1.3.2_i386.deb\" file. b) Download hardcore’s Kernel with the ClockworkMod Recovery 2.5 here. This will download a file named \"hardcore-speedmod.tar\". I am assuming that it will be saved in \"Downloads\" directory but if you have a different location, please replace \"Downloads\" with appropriate directory. Step 2: Just to avoid any confusion, make a new directory in Downloads and name it \"Galaxy_S\". Step 3: Copy the downloaded file (from 1-b) \"hardcore-speedmod.tar\" into the new directory \"Galaxy_S\" Step 4: Now right-click on \"hardcore-speedmod.tar\" and select extract here as shown. This will extract the file zImage into the directory Galaxy_S. Step 5: Connect microUSB cable to your computer but not the phone. Step 6: Power off the Samsung Galaxy S. Step 7: Connect the microUSB cable to Samsung Galaxy S. Step 8: Boot the phone in download mode by holding \"HOME+Volume Down+POWER\" buttons. Step 9: Open the terminal and type following commands: cd Downloads/Galaxy_S sudo heimdall flash --kernel zImage A blue transfer bar will appear on the phone showing the kernel being transferred. Once completed, the device will reboot automatically. Step 10: Disconnect the phone from microUSB cable, switch it on and connect to your computer using the microUSB cable as mass storage. You may need to go to phone settings and change USB connection settings to be able to connect the phone as mass storage. Step 11: Download the latest Cyanogenmod ROM from here. Step 12: Follow this link to land at above page and then download the latest version of Google Apps. Step 13: You will now have following two zip files in your \"Downloads\" directory: a) cm-9.1.0-galaxysmtd.zip from Step 11. b) gapps-ics-20120317-signed.zip from Step 12 Copy these two files into the root directory of the Samsung Galaxy S. Step 14: Now, disconnect the phone from microUSB and switch it off. Step 15: Boot the phone in Recovery mode by holding \"HOME+Volume Up+POWER\" buttons. You will be presented with various recovery options such as reboot phone etc. Step 17: Now use the Volume Up and Volume Down buttons to navigate options. Use Volume Down button to reach the option to Wipe data/factory reset and press POWER button to select this option. Step 18: Once done, Use Volume Down button to reach the option to wipe cache partition and press POWER button to select this option. Step 19: Next select \"Install zip from sdcard\" which will present another set of options where you should select \"Choose zip from sdcard\" Step 20: Now you will see the list of files on your SD Cards root directory. Select the file \"cm-9.1.0-galaxysmtd.zip\" and on following screen of options select \"Yes\". Step 21: Once installed, select +++++Go Back+++++ and again select \"Install zip from sdcard\" which will present another set of options where you should select \"Choose zip from sdcard\" Step 22: This time select the file \"gapps-ics-20120317-signed.zip\" and on following screen of options select \"Yes\". Step 23: Once installed, select +++++Go Back+++++ to return to main menu. Step 24: On main menu select \"Reboot System now\" option. Step 25: If all has gone as above, your phone will restart and after a while you will see cyanogenmod flash screen. This screen will be there for a good 1 to 2 minutes. Don’t panic and dont mess. Let system do it’s work and in some time you would have given a fresh lease of life to your old dieing Samsung Galaxy S. ","categories": [], + "tags": [], + "url": "/flash-samsung-galaxy-s-gt-i9000-with-cyanogenmod-9-on-linux/", + "teaser": null + },{ + "title": "Prepare Linux Mint 13 for Android development", + "excerpt":"Few weeks back I updated to the latest Linux Mint offering \"Maya\" a.k.a Linux Mint 13. Now this is a LTS (Long Term Support) version and I wanted to be in a position to install everything right just so I can keep it for a longer duration and hence have been taking my time configuring stuff. Last time when I had set up system for Android Development I remember messing up a lot and ending up installing too many things here and there and in the process did learn how to do it properly. I did not document that as a blog as it was too fragmented an experience at that time but this time round I did it properly and everything (well, okay, almost everything) was perfect. There is lot of material on the web but again that is what led to a less than perfect install last time as it is all disjointed, making the sequence go wrong, using one way for one thing another way for second and ending us with a not so nice experience distracting you from what you want to do, start developing something on android platform or perhaps just get the adb set-up to flash your nexus phone. So presented below is a guide that will help you prepare your Linux Mint 13 or equivalent distro for you to start android development. You will notice that these notes do not require downloading \"android sdk\" separately and that is to save time and effort, trust me. So the high level steps are: Step 1: Download required files Step 2: Install Oracle Java (or what is called sun-java on Android webpages) Step 3: Install eclipse Step 4: Install ADT Plug-in for Eclipse Step 5: Final Configurations Step 6: Create Android Virtual Device Step 1: Download required files Download following files: Links to original file locations: Script to Update Java (Updated link to the latest version of script as highlighted in screenshot.) Java 7 SDK Java 7 Samples and Demo Java 7 API Docs Java 6 Samples and Demo Java 6 API Docs Java 6 SDK If you don’t want to move around different webpages and websites, I have also uploaded all these files on mediafire. http://www.mediafire.com/?4x3u3if9o7dy4 Step 2: Install Oracle Java You might ask, why do we need to do it this way? That’s because Oracle Java 6 SDK is a pre-requisite for installing Android SDK but it’s not available in Ubuntu or Linux Mint repository so it can’t be installed using synaptic or apt-get. Right then, I am assuming that all the files downloaded above are placed in \"Downloads\" directory. If not, please replace \"Downloads\" in all commands with whichever directory you have downloaded these file to. #1. Open terminal and type: cd Downloads #2. Make the downloaded .bin Java 6 file executable and run chmod a+x jdk-6u37-linux-i586.bin ./jdk-6u37-linux-i586.bin ###This will create a directory named \"jdk1.6.0_37\". #3. Untar the Java 7 SDK tar -xvf jdk-7u7-linux-i586.tar.gz ###This will create a directory named \"jdk1.7.0_07\". #4. Unzip the Java 6 api docs unzip jdk-6u30-apidocs.zip -d jdk1.6.0_37/ ###This command will unzip the apidocs zip file ###and place the contents in \"jdk1.6.0_37\" #5. Unzip the Java 7 api docs unzip jdk-7u6-apidocs.zip -d jdk1.7.0_07/ ###This command will unzip the apidocs zip file ###and place the contents in \"jdk1.7.0_07\" #6. Untar the Java 6 demos and samples tar -xvf jdk-6u37-linux-i586-demos.tar.gz ###This command will untar the demos and samples file ###and place the contents in \"jdk1.6.0_37\" #7. Untar the Java 7 demos and samples tar -xvf jdk-7u9-linux-i586-demos.tar.gz ###This will create a folder named jdk1.7.0_09. ###Copy the contents of this folder into jdk1.7.0_07. #8. Move the Java 6 to it's proper location sudo mv jdk1.6.0_37 /usr/lib/jvm ###You will be asked to provide root password. #9. Now move the Java 7 to it's proper location sudo mv jdk1.7.0_07 /usr/lib/jvm #10. Make the script to update java update-java-0.5b executable #then execute it by using following commands in terminal. chmod +x update-java-0.5b sudo ./update-java-0.5b You will be presented with following selection box: Once you click on OK you will be presented with following screen: Select the radio button and click on OK. Once Java 6 SDK is installed repeat step 10 and this time when you reach the selection window select Java 7 as shown below. Now Oracle Java 6 and Oracle Java 7 will both be installed on your system. To check this you can use the tool \"galternatives\". This can be installed by typing following command on the terminal window: sudo apt-get install galternatives followed by galternatives. This will open the \"G Alternatives\" window, scroll down to \"Java\" in left hand pane and click on it. You should see both versions installed and radio button for highest version selected as shown below: Step 3: Install eclipse Open Synaptic package manager and type eclipse, click on the check-box next to it and select "Mark for installation" then click on "Apply" as shown below. Once Eclipse is installed, we need to find out whether the necessary SWT (Standard Widget Toolkit) libraries link are set correctly or not. This is to avoid Eclipse throwing a tantrum and not starting because it is unable to find the SWT library. As you can see in the screenshot below it's a pretty quick thing: A) First type the following command to check if whether SWT directory exists or not: ls ~/.swt/lib/linux/x86/ If you see the same message as shown in screenshot above \"ls: cannot access /home//.swt/lib/linux/x86/: No such file or directory\", then it means we need to create the SWT directory so continue to sub-step B. If this command does not result in this message nor does it show a list of files (blue text in screenshot) then skip directly to sub-step C. If it does show the list of files in blue in above screenshot, you don’t need to do anything further and for you it’s time to move to next step. B) Type the following command in terminal to create the SWT directory: mkdir -p ~/.swt/lib/linux/x86/ C) If the swt directory exist, but nothing is listed (no blue text), then run the following command in terminal: ln -s /usr/lib/jni/libswt-* ~/.swt/lib/linux/x86/ Finally as shown in screenshot, type the command in sub-step A once again and you should see the list shown in blue on the screenshot: Time to move on to install ADT (Android Development Tools Plugin on eclipse. Step 4: Install ADT Plug-in for Eclipse OK we have so far installed Java, installed eclipse and now we are all set to install Android. To do so we will follow the screenshots below: When you start eclipse, you will be shown a splash screen, ask you to set workspace which I leave default and finally this window will open. As shown, click on the Workbench in right hand side corner. This will lead to following window. Here Click on Help > Install New Software. This will open following window. Click on \"Add\" button in red rectangle above. Following pop-up window will appear. In Name type \"ADT Plugin\" or whatever name you want to give. In Location type \"https://dl-ssl.google.com/android/eclipse/\" Then click OK. On following window it will first show pending but eventually it will look as below. Here select the first option “Developer Tools” and click OK. Click “NEXT”. Click “NEXT”. Let it run in foreground i.e. don’t do anything and just wait. Click “RESTART NOW”. You will be presented with following window on restart. Now leave the target location as is and leave everything as default and click “NEXT”. As shown above, selected the “ACCEPT ALL” radio button and click “INSTALL”. Now the android SDK is installed on the system along with other things. Close all the open windows just to clear any screen clutter. Now open the terminal and type following commands: cd android-sdks/tools followed by ./android . It will open the following window: 1. Select as shown above and optionally you can also select checkbox against Documentation and Samples. Once done click on "INSTALL x Packages". 2. On the next window, for which I did not take a screenshot but which is same as the one before previous screenshot of terminal, select radio button "ACCEPT ALL" and click on "INSTALL". 3. Now once you are back to this window select "Google APIs" and click on "Install 1 package". 4. On the package lis, click on "ACCEPT" radio button and click on "INSTALL". Android Development Tools and SDK is now installed. We just need to configure few things. Step 5: Final Configurations adb Environmental Variables Go to home folder Press Ctrl+H Locate the .bashrc file, if it does not exist, create one (In home folder, right click and select "Create New Document > Empty Document"). Open the file in gedit and add export PATH=${PATH}:/tools:/platform-tools at the end, if you created the file yourself just add this line and save. Set the PATH environment Go to home folder Press Ctrl+H Locate the .profile file Open the file in gedit and add the following at the end. # set PATH so it includes user's Android SDK if it exists if [ -d \"$HOME/android-sdks\" ] ; then PATH=\"$HOME/android-sdks:$HOME/android-sdks/tools:$PATH\" fi Set-up udev This step is only required if you want to use your android device for development purpose or if you want to flash a custom ROM. Open the terminal and type following: gksudo nautilus /etc/udev/rules.d Create new document like we did above- Right click on empty space and select "Create New Document" and then "Empty Document. Name this document as "51-android.rules" Open the document with Gedit and add following lines - assuming you are using "Nexus S" like me, if not see if your phone details are available on this link: http://wiki.cyanogenmod.com/wiki/Udev and if not follow the guideline under the heading "Manually create udev rules" on that link. Now make this 51-android.rules file executable by typing following command in the terminal window: sudo chmod a+r /etc/udev/rules.d/51-android.rules Step 6: Create Android Virtual Device Open eclipse and follow the screenshots below: Click on Window - AVD Manager to get to next screen except that in your screen there will be no entry. Click on “NEW” to create a new AVD. It will open next window. Fill details as above - You can chose name of your choice and change details as per your requirements. To Start the AVD, click on Start. Finally, you will be able to see the AVD as below: This is it. Restart and your system is now all set for developing wonderful Android Applications. ","categories": [], + "tags": [], + "url": "/prepare-linux-mint-13-for-android-development/", + "teaser": null + },{ + "title": "Root Nexus 4 on Linux Mint 13 and access all files on computer", + "excerpt":"Having a rooted phone and then going to one that does not have root access is like getting used to driving a luxury car but then being forced to drive a tractor. So with arrival of my shining new nexus 4 once the novelty worn of in 8 hours or so, I sat down and rooted the device. Now there are plenty of guides out there but not many specific to Linux just yet. One reason might just be the fact the Linux Users are really smart and know how to figure it out but what about users who are new …well at least for them I am sure this post will be useful and while we are at it, I felt I will install the touch version of CWM… Pre-requisite: For purpose of this tutorial, I will assume that adb set-up is in place using steps explained in my last post specifically up to Step 5. It is totally worth doing it before you proceed but if you would prefer a shorter route, please refer some guide on how to install just adb. Though I would not recommend any other approach. (Not because they will not work but because this approach will ensure clean system and something I have tested 4 times already to work and if you will follow this post, it makes sense to do it the way I did) Clockwork Recovery Mod (Touch Version) Super User App Fastboot This process will wipe out all user data and apps. Appropriate back-ups should be taken and restoring those is beyond the scope of this post. Bullets 2 and 3 can be downloaded directly from http://www.clockworkmod.com/rommanager and http://download.chainfire.eu/282/SuperSU/ or as before I have uploaded them in mediafire and can be downloaded from this link. I downloaded fastboot few months back from xda using this link and am not sure if it is still there or not so I have uploaded it to mediafire as well. Step 1: Prepare Nexus 4 and Linux Mint 13 Step 2: Unblock bootloader for Nexus 4 Step 3: Root Nexus 4 Step 4: Make Clockwork Mod Permanent Step 5: Mount and Un-Mount Nexus 4 to access files from Linux Mint Step 1: Prepare Nexus 4 and Linux Mint 13 1.1. Check if fastboot is already there in the the /android-sdks/platform-tools directory. If yes skip the next step. 1.2. Extract the fastboot from downloaded zip file and place it in the /android-sdks/platform-tools directory if it is not already there and make it executable (Right Click, Select properties, Go To permissions tab and select the checkbox in front of \"Execute\") 1.3. Copy the downloaded Clock Work Mod file (recovery-clockwork-touch-6.0.2.3-mako.img) in the /android-sdks/platform-tools directory. 1.4.Activate debug mode in nexus 4 - to do this go to settings > About Phone and then click 7 to 8 times on \"Build Number\". This will activate developer mode. 1.5. Now click back and got to {} Developer Options and Click the checkbox against USB debugging. 1.6. Set up udev on linux mint: a. Assuming that you have followed last post you would already have a \"51-android.rules\" file created. b. Open the file with Gedit using following command sudo gedit /etc/udev/rules.d/51-android.rules c. Add following lines: #LG - Nexus 4 SUBSYSTEM=="usb", ATTR{idVendor}=="1004", MODE="0666" SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="4ee1", MODE="0660", OWNER="ankit" #Normal nexus 4 SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="4ee2", MODE="0660", OWNER="ankit" #Debug & Recovery nexus 4 SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="4ee0", MODE="0660", OWNER="ankit" #Fastboot nexus 4 I got the Vendor ID and Product ID by connecting the phone in different states (USB debug checked, unchecked and also after the phone was connected and rebooted into bootloader using command \"adb reboot bootloader\") as per the guidance given here. d. Now Save the file, then chmod to all read using following command: sudo chmod +x /etc/udev/rules.d/51-android.rules Step 2: Unblock bootloader for Nexus 4 2.1. Plug your phone into the computer and type the following command in terminal: adb reboot bootloader 2.2. Once Nexus 4 has rebooted in recovery mode, type the following command in terminal: fastboot oem unlock If terminal displays the message \"waiting for device\" 51-android.rules file is not set correctly and you might need to check vendor ID and product id using lsusb command in different modes and update it with appropriate data. 2.3. Phone will display a long message and ask for confirmation to unlock bootloader. Select \"Yes\" by using the volume keys and use power to select it. 2.4. Now, using volume keys navigate to \"Recovery Mode\" and select it using \"Power\" key. 2.5. After a while Android with blue progress bar should appear and phone should reboot but if it does not and instead shows a screen with \"Android\" on it’s back with an exclamation mark on it’s tummy, don’t panic. Just press \"Power\" and \"Volume Up\" till it shows recovery menu and then select \"reboot\". The phone is now bootloader unlocked. Step 3: Root Nexus 4 OK now when the phone boots, it will be fresh with factory reset, no data or apps whatsoever other than those that are there by default and when the phone boots, it will ask all details like selecting country etc. Just enter quickly without bothering to enter gmail, wifi etc. Once done: 3.1. Install mtpfs from synaptics. 3.2. Plug the phone to computer. 3.3. Type the following commands: sudo mkdir /media/nexus4 followed by sudo chmod 755 /media/nexus4 3.4. Now mount the nexus 4 using following command so we can transfer files: sudo mtpfs -o allow_other /media/nexus4 3.5. Copy the downloaded file \"CWM-SuperSU-v0.98.zip\" on Nexus 4. 3.6. Type the following command to unmount nexus 4: sudo umount /media/nexus4 3.7. Reactivate the debug mode using Step 1.4 and Step 1.5 above. 3.8. On terminal type the command: adb reboot bootloader followed by: fastboot flash recovery /android-sdks/platform-tools/recovery-clockwork-touch-6.0.2.3-mako.img Make sure in above command you replace PATH_TO with the actual path to the file. An easy way can be to right click on \"recovery-clockwork-touch-6.0.2.3-mako.img\" file and click on properties thene select the path and paste in this command. 3.9. Once completed, on the phone navigate to \"Recover Mode\" using volume keys and select using power key. 3.10 The new recovery menu will be presented. Select \"install zip from sd card\" and \"choose zip from sd card.\" and select the file we had put in step 3.5 - \"CWM-SuperSU-v0.98.zip\". 3.11 Once done, go back to reboot and \"Reboot\" the phone. At this point your phone is rooted,however, you will get a message suggesting the recovery will not be permanent or something to that effect which is fine if you aren’t bothered about it but if you would rather want to keep this CWM we need to move to next step. Step 4: Make Clockwork Mod Permanent 4.1. On your phone install ES File Explorer. 4.2. Select Settings. 4.3. Select \"Root Settings\". 4.4. Select all checkboxes. You will be asked for superuser access, say Yes. 4.5. Now go back to main screen of ES File Explorer and select the third tab with an icon of Folder with up arrow and text Up. This should bring you to root. 4.6 Now using ES File Explorer navigate on your phone to /system/etc, find the file named \"install-recovery.sh\" and rename it to \"install-recovery.bak\" 4.7 Repeat Steps 3.7 to 3.11. Now your clockwork mod is permanent. Step 5: Mount and Un-Mount Nexus 4 to access files from Linux Mint Now, with the steps 3.1 to 3.6 we have laid the foundation for being able to connect Nexus 4 and transfer files using USB. Something earlier was possible using USB Mass storage mode but is not present jelly bean onwards. The commands in 3.4 and 3.6 are key to achieve this but rather than remembering these and typing each time, I have made a menu entries for each of these (Mount Nexus 4, Unmount Nexus 4) and after connecting phone via USB, I simply click on these, system asks root password and then connects nexus 4 as mass storage (see screenshot below): In order to get these you will follow the steps below: 5.1 Right Click on Menu and select \"edit Menu\". 5.2 Now click on \"New Menu\" and Enter a menu entry \"Phone\". 5.3. Select the checkbox next to the new menu entry \"Phone\" in middle pane. 5.4. Select new menu entry \"Phone\" in left pane. 5.5. Click on \"New Item\" in right Pane and in the dialogue box fill the fields as below and save: Type: Application in terminal Name: Mount Nexus 4 Command: sudo mtpfs -o allow_other /media/nexus4 5.6 Again click on \"New Item\" in right Pane and in the dialogue box fill the fields as below and save: Type: Application in terminal Name: Unmount Nexus 4 Command: sudo umount /media/nexus4 5.7 Make sure checkbox next to these new items is ticked. Save and Close. That’s it !!! All Done. Hope some will find this post useful. ","categories": [], + "tags": [], + "url": "/root-nexus-4-on-linux-mint-13-and-access-all-files-on-computer/", + "teaser": null + },{ + "title": "Conky on my desktop - step by step", + "excerpt":" My new friend Damjan recently mentioned that he liked the Conky on my desktop and asked for details as have few others so I figured a post on the topic will be useful. Many who have been playing with conky seem to believe it’s real easy stuff but I feel there are too many options and very little explanation which means it can be lot of messing around with different options and can take a while to get to a point where you have what you want. To make this conky appear exactly the way it is on my screen on your desktop follow steps 1 to 4 below and for changing time-zone settings it will be step 5: Step 1. Install Conky from Synaptic. Step 2. Install the fonts used in this conky. Step 3. Change the conky config file. Step 4. Enable settings to run conky at start-up. Step 5: Configure time-zones on the conky config. Step 1. Install Conky from Synaptic: Open the synaptic package manager and follow steps as shown in screenshot above. Actually I don’t think conky colors is required so if it does not appear in search result don’t bother. From memory, that was installed by me as part of experimenting. Step 2. Install the fonts used in this conky: Download and install the fonts \"pf_tempesta_five_condensed.ttf\" and \"ZegoeUI-U.ttf\" from web or I have uploaded these to mediafire and can be download from this link. Step 3. Change the conky config file: Download my conky configuration in the file named ".conkykrc" from this mediafire link. Open "Home" directory and press "Ctrl+h" to show hidden files. Locate the ".conkykrc" file and delete it. Now paste the "conkykrc" file downloaded in step 1 to home folder. Step 4. Enable settings to run conky at start-up: 4.1. Open \"Startup Application\" from Menu > Preferences. 4.2. Click on \"Add\" button and fill the fields as shown above. 4.3. Restart the computer and it’s all done. Step 5: Configure time-zones on the conky config: OK, so you have conky running as it is for me but you might want different time-zones than the one I have in which case you will need to modify the .conkykrc file in \"Home\" directory. Open "Home" directory and press "Ctrl+h" to show hidden files. Locate the ".conkykrc" file and open it in text editor of your choice. I use gedit. Now Open Terminal and Type "tzselect" and press enter. A list of continents will be displayed, select the one of your interest by typing the number against it and press enter. (I selected "Americas" so I typed "2") Then you will be presented a list of countries. Again select the one of your interest by typing the number against it and press enter. ( I selected "North America" so I typed "49") Finally, it will display time-zones in that country. As above, select the one of your interest by typing the number against it and press enter. ( I selected "Mountain Time" so I typed "18") It will confirm selection and then mention TZ being used. For my selection it displayed "Therefore TZ='America/Denver' will be used." This text after "TZ=" is what is important for us. Copy this and go back to .conkykrc file. Under "TIMEZONE" say you want to replace Nashville with Denver then you locate Nashville (Line 83, Col 64) and replace the text with the city of your choice, in this case "Denver". Now to reflect the appropriate time-zone, replace the existing time-zone "America/Chicago" with the one from step 10. In this example with "America/Denver'". That’s it. All Done !!! Hope you find it useful. Feel free to ask questions on settings here and if it’s something I was stuck with I might be able to help. ","categories": [], + "tags": [], + "url": "/conky-on-my-desktop-step-by-step/", + "teaser": null + },{ + "title": "Python, ERIC RAD IDE and QT designer", + "excerpt":"Right, I have decided to play around a little with the most loved language of open source a. k. a. Python. How do we do that? By coding? Aha and how do I code for Python and be productive given I am a semi-part-time-hobbyist kinda programmer…I am heavily inclined to use an IDE…and an IDE that actually has some GUI editor…so began my search to find out what are my options in the exciting open source world. I quickly worked out that IDE and RAD IDE are two different things. RAD IDE is what I am after and well there is nothing straight forward and best bet was to use QT with PySide or PyQT and there is an argument on each side for PyQT vs PySide. Anyhoo, PySide is not compatible yet with QT5 and QT4.8.x is not available for download from QT official downloads. Whatever happened to providing an archive. QT itself is a world in itself with lot of modules and plugins etc and I was feeling lost. I diverted efforts to find what else and stumbled upon ERIC IDE for Python and from here things started tying up to a point where I could logically assimilate the available information to start preparing my system for my kind of Python Development. However, this information isn’t as readily available as it should be and most mentioned on trusty old Internet Google search is combination of PySide and the non existent QT4 which had it not been for Linux package managers, might as well be a lost cause. All it involves is downloading Eric from repository which satisfied most of the dependencies and then I downloaded qt4 designer and included qt4 creator just for good measure. So there now we have the tool and all I need is setting it up and getting started. Following links will come handy I suppose. http://eric-ide.python-projects.org/tutorials/MiniBrowser/ http://anh.cs.luc.edu/python/hands-on/handsonHtml/handson.html#x1-310001.9.3 Here’s to some exciting learning experience. ","categories": [], + "tags": [], + "url": "/python-eric-rad-ide-and-qt-designer/", + "teaser": null + },{ + "title": "Linux Mint on Android through VNC and Jump", + "excerpt":"Today “Jump” was available for free on Amazon as the app of the day and since it’s nearly 7 quids on google play store, I downloaded. For windows and Mac users they have a pretty straight forward set-up but as usual for Linux it means some work but in the end it leaves you with a set-up you can trust and feel secure about. There are three things that need to be set-up for this to work: 1. Linux Mint machine should be set-up for x11vnc and ssh servers 2. Router firewall should be configured to allow inbound traffic on specific ports. 3. Jump or an equivalent VNC viewer should be configured on the android device. 1. Linux Mint machine should be set-up for x11vnc and ssh servers 1.1: Install X11VNC by typing following command in terminal: sudo apt-get install x11vnc 1.2: Create a password for VNC using following command in terminal and providing a password and answering yes for the prompt to store password in a file: x11vnc -storepasswd 1.3: Now to ensure that X11VNC starts at boot go to menu and type start, click on startup application as shown in the screenshot below: Then in the window that this will open click on “Add” and enter a “Name” and in “Command” field enter x11vnc -forever -xkb -usepw -display :0 as shown below. VNC set-up on machine is complete. 1.4 Now install openssh-server using following command on terminal: sudo apt-get install openssh-server 1.5 We will need to change some parameters in ssh configuration for making it secure as by default it allows root login but I dont want that for remote access and would advice most regular users to do so as well. So we will first make a backup of existing configuration file using the command below: sudo cp /etc/ssh/sshd_config ~ 1.6 Now, we will edit the actual config file using following command: gksudo gedit /etc/ssh/sshd_config 1.7 Once the file is open change the parameter \"PermitRootLogin\" to \"no\". It’s on line 27 for me. 1.8 Now the default port for ssh is 22 but I recommend changing it to something else such as 5432. To do so change the parameter “Port” from 22 to whatever port you want to put. In this example it will be 5432. For me “Port” parameter is on line 5. 1.9 Save the changes and close gedit. 1.10 Now we will restart the ssh server using following command in terminal sudo restart ssh For Arch, you can use the command: sudo systemctl start sshd followed by sudo systemctl enable sshd.service to ensure ssh daemon is enabled at startup. 1.11 Restart the machine and machine set-up is done. 2. Router firewall should be configured to allow inbound traffic on specific ports. This may involve different step from those given below depending on the router in use. Following steps are meant for configuring the sky router. However principle is same. We will be creating specific service definition and port on router and then create a firewall rule that allows inbound traffic and directs it to Linux machine we configured above. 2.1 Type following command on terminal: ifconfig 2.2 this will list lot of numbers, what we are interested in is the number just after “inet addr:” under wlan0. It will be something like 192.168.0.10. 2.3 Open sky router config through browser using 192.168.0.1 and click on “Security”. You will need to enter router username and password. 2.4 Then click on “Services” and then click on “Add Custom Services”. 2.5 Enter as shown in Figure 4 and Start Port as 5900, Finish Port as 5900 and click on “Apply”. 5900 is default port for display 0 in VNC. If you have changed it like me you will need to enter that port. To change port you will need to use “x11vnc -forever -xkb -usepw -autoport nnnn -display :0” option in step 1.3. This is not required for security but in case you have two different machines then this approach will come handy. 2.6 Now click on “Add Custom Services” again and this time enter as shown in next screenshot. Start Port and End Port should be same as entered in step 1.8, so for this example it will be 5432. Then click on “Apply”. 2.7 Now we need to set the firewall for these services. To do so, click on “Firewall Rules” then click on “Add” under inbound services. 2.8 Configure fields as shown in next screen-shot below and click on “Apply”: 2.9 Now we will do same for SSH, so again click on “Add” under inbound services and configure fields as shown in screen-shot below and click on “Apply”: 2.10 Click on “Apply” under “Inbound Services”. 2.11 In browser on the router management page, click on “Advanced” > “Remote Management” and on this screen make note of the IP address (number after http:// in red box in next screen-grab) shown under “Remote Management Address”. 2.12 Go to https://www.dlinkddns.com/signin and create an account. Refer this page for the how-to (http://www.dlinkddns.com/howto) and you will need to use the IP from step 2.11 above as the host. At the end of it you will have a hostname like “yourname.dlinkddns.com”, username and password for logging in to dlinkddns site. 2.13 Once this is done, go to the browser with sky router management and click on “Advanced”>“Dynamic DNS” and fill as shown in screen-shot below: Host Name: Hostname from Step 2.12 (yourname.dlinkddns.com in this example) User Name: D-Link site username Password: D-Link site password 2.14 Once above information is filled, click on “Apply” and then click on “Show Status”. It should open a separate window and showing the message “request successful”. Sky Router is now configured. 3. Jump or an equivalent VNC viewer should be configured on the android device. 3.1 On the android device open Jump and click on the “+” sign in right hand corner. 3.2 In the “Address” Field enter the hostname from 2.12 (yourname.dlinkddns.com in this example) and select connection type as “VNC” and click save. 3.4 Change the “Authentication Method” to “VNC Password” 3.5 Tap on “SSH Tunnel”, click on “Enabled” checkbox. 3.6 In Username enter the username used to log into the machine configured above in Step 1. 3.7 In Host Name, use the the hostname from 2.12 (yourname.dlinkddns.com in this example) 3.8 Change the port to one used in 1.8. So in this case 5432. 3.9 Password can be left empty and when asked during connection provide the one used to log on to the machine with this username. 3.10 Press back button and click on entry. You will be shown a SSH key notification, say ok. Then you will be asked for a password, provide the password you use to log onto your machine with the username provided in 3.6. 3.11 Then you will be asked for the VNC password, provide the password from step 1.2. You will now be able to view your desktop on your android machine. All Done !!! ","categories": [], + "tags": [], + "url": "/linux-mint-on-android-through-vnc-and-jump/", + "teaser": null + },{ + "title": "Arch Linux - nearly a year on....", + "excerpt":"I have been dwelling in the world of Arch Linux for just under a year now and must admit the experience is nothing less than liberating. Granted that the barrier to entry was big when I dived in, as a pure Arch install takes some reading and learning but the rewards are worth the pain. While it may seem easier to stick with dear old .deb files in the end AUR has an equally large collection if not more and the fact that it’s a rolling release means that the initial set-up pain is very well negated by another fact that unless like me you are forced to change your hard disk or something that sinister; there is no reason to do a reinstall which is often the case with Debian based distros should you chose to upgrade to their latest offering. I have read and heard words such as “Arch is not for faint hearted” or “Arch is for motivated newbie” and so on but in my opinion there are two types of Linux users: Those who have an itch to tinker (someone like me) Those who don’t care as long as they can get on with their day to day business (someone like my wife) It’s the type 1 who is intended audience of this post because type 2 can’t be bothered. Now if you have an itch to tinker there will come a point where you will find that all existing distros have a limit on what you can tinker without messing something to the point where you feel like you have to give another distro a try and before you know it a reinstall is on hand, which ofcourse means type 2 user has to deal with the changes a new distro will bring and this in turn will make them frown and thereby increase the risk of them secretly cursing the day you - the type 1 user - encountered the world of Linux. Now Arch is very stable and still gives you the freedom to tinker, learn and do all great things rather than worry about the cosmetics and allow your tinker angel to quench it’s thirst by allowing you to focus your learning potential on something that just doesn’t scratch the surface so you can feel good about yourself but actually allows you to learn a great deal in the process. The helpful Arch community usually has the answers for nearly every problem I encountered while trying to achieve my goals. I do not think that for a Type 1 user - newbie or not - Arch is hard or unfriendly. There is lot more satisfaction once you have your desktop exactly the way you wanted. Getting applications is as easy as using software centre in other distros and now that I have been using it, I find using “yaourt” (Arch equivalent of Synaptics) much better and the fact that repositories are maintained on servers in nearly all locations, it does seem to get new updates to your favourite applications rather quickly too. I will substantiate this claim with following examples: Getting android sdk configured on debian based distros is a right pain in you know where but with Arch it’s what we like to call a doddle in Great Britain. Getting intellij IDE on other distro repositories is not an option but in Arch - you got it, it’s a doddle. Why is that? It’s because Type 1 users when they switch to Arch they soon realise that they now have time on their hands which earlier used to disappear in distro hopping. So what do they do with this time? They search for software they want to use and if it isn’t in the repo then with their new found confidence they don’t shy away from building it from source and when they have done that they want to showcase their work and they can do so in AUR short for Arch User Repository - though with all that is already there this showcasing will only be possible if you genuinely need a very unique software not used by any Arch user - highly unlike scenario if you were to go by my experience . You will find nearly all software you can think of in AUR and since it’s being maintained by someone who needed it, chances are he / she will keep it updated for his / her use thereby ensuring you get the latest and greatest as it happens. What a wonderful model it is indeed. Now if you are still not geared up to go all the way to start from scratch on Arch but want to get a flavour, why not start with the new kid on the block - Manjaro Linux? Manjaro linux is an Arch based distro, with a rolling release schedule and will still give you the access to AUR. It takes away the pain of setting up your system on command line and does the hard work for you so you can get the benefits of Arch but not the pain. Seems like a win win especially for those who are repelled by the very thought of terminal based system configuration. Manjaro Linux offers all leading desktop environments - Gnome, KDE, Openbox but their best offering is with XFCE. XFCE is very good as well what with it being light weight and not making your Type 2 users feel that the system has been rendered useless. The default configuration of Manjaro XFCE flavour does come with a very ready to use set-up yet enough opportunity to make it and configure just the way you like it. In addition a type 1 user will be pleasantly surprised that using XFCE does wonders to satisfy that itch to tinker. The login manager used with XFCE is MDM but can be easily replaced to the slick lovely light and beautiful SLiM and by installing slimlock you can also get rid of the ugly default screen lock that comes bundled with XFCE. Explore some more and key bindings on this desktop are much more satisfying and easier to manage than on Gnome or KDE - IMHO. Manjaro also comes with GUI based synaptics equivalent called “packer” but like I said once you are used to “yaourt” interface everything else GUI based just seems pale in comparison becaue it does not give the same satisfaction of installing stuff. The one situation where Manjaro might actually be a better choice than Arch is when you are dealing with a potential convert. Offer them Arch with yaourt and they might be very sceptical but offer Manjaro with packer and they just might convert. Then again for this scenario perhaps Linux Mint or Zorin OS may be even better choice. Point is Arch should enter into anyone’s life where they make the choice but not when someone else is making that choice on their behalf as they may or may not develop the habit of tinkering. When and if they do, they will need to decide on their own to move to this platform but if you have been calling yourself a linux newbie and have found yourself reviewing new distros or worse distro hopping and by extension forum hopping, tailoring your linux install and generally enthusistic when it comes to all things Linux, I think it is time to get your feets wet and hands dirty. Get Arch experience in it’s purest form or by using Manjaro, you will not regret it. ","categories": [], + "tags": [], + "url": "/arch-linux-nearly-a-year-on/", + "teaser": null + },{ + "title": "MySQL Stored Procedure to return JSON for google charts on BIRT", + "excerpt":"My requirement was to get the data in a format that google chart can use to draw the chart I want. Now gogle chart accepts data in json format where all column names separated with comma are in first square bracket set followed by values in rest of the square bracket sets and each square bracket set is separated by comma as well. Having searched on good old google, there did not appear to be any quick way of doing it without getting hands dirty with likes of php and as I was to plug this into a BIRT report where a simple html would do the trick, I really just needed the data-set to be returned in format that google-chart understood. I figured it can be easily done using a MySQL stored procedure and can be a repeatable process which resulted in creation of stored procedure presented below. The code CREATE DEFINER=`root`@`localhost` PROCEDURE `json_builder_multiple_string`(IN `var1` varchar(10000), IN `tab_name` text, IN `int_col_as_str` int) \tLANGUAGE SQL \tNOT DETERMINISTIC \tCONTAINS SQL \tSQL SECURITY DEFINER \tCOMMENT '' BEGIN /* - This procedure will take columns and tablename as parameter. - Third parameter is to tell the procedure how many columns are to be returned as string - surrounded by quotes - Third parameter must always be less than the total columns being requested. - Rest of the columns must have numerical values as they won't be surrounded with quotes - Procedure returns 0 for each null value. ​ SAMPLE PROCEDURE CALL CALL `json_builder_multiple_string`('Year,Month,Region,Sales,Expenses', 'myjsonexample',4); ​ SAMPLE QUERY GENERATED: SELECT CONCAT('[\\'Year\\',\\'Month\\',\\'Region\\',\\'Sales\\',\\'Expenses\\'],', GROUP_CONCAT('[', CONCAT_WS(',', CONCAT('\\'', IFNULL(`Year`, 0), '\\',\\'', IFNULL(`Month`, 0), '\\',\\'', IFNULL(`Region`, 0), '\\',\\'', IFNULL(`Sales`, 0), '\\''), IFNULL(`Expenses`, 0)) SEPARATOR '],'), ']') AS data_set FROM myjsonexample ​ SAMPLE OUTPUT: ['Year','Month','Region','Sales','Expenses'],['2004','JAN','NW','1000',400],['2005','Feb','SW','1170',460],['2006','Mar','NE','2000',1210],['2007','Apr','SE','650',540],['2008','May','EC','0',0] ​ SAMPLE TABLE USED: Drop Table if exists `myjsonexample`; CREATE TABLE `myjsonexample` ( `Year` int(11) NOT NULL, `Month` varchar(25), `Region` varchar(25), `Sales` int(11) , `Expenses` int(11) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `myjsonexample` (`Year`, `Month`, `Region`, `Sales`, `Expenses`) VALUES (2004,'JAN','NW',1000,400), (2005,'Feb','SW',1170,460), (2006,'Mar','NE',2000,1210), (2007,'Apr','SE',650,540), (2008,'May','EC',null,null); */ SET SESSION group_concat_max_len = 1000000; Set @stmt1 = null; select concat('Select concat(\\'',concat('[\\\\\\'',replace(replace(var1,'_',' '),',','\\\\\\',\\\\\\''),'\\\\\\'],'),'\\'', ', group_concat(\\'[\\', concat_ws(\\',\\', CONCAT(\\'\\\\\\'\\', ifnull(`',replace(substring_index(var1,',',int_col_as_str),',','`,0),\\'\\\\\\',\\\\\\'\\',ifnull(`'), '`,0), \\'\\\\\\'\\'),', (concat('ifnull(`', replace( SUBSTRING(var1,LENGTH(SUBSTRING_INDEX(var1, ',', int_col_as_str)) + 2,LENGTH(var1)), ',', '`,0),ifnull(`'), '`,0)') ) ,') SEPARATOR \\'],\\'),\\']\\') as data_set from ', tab_name) into @stmt1; Prepare stmt2 from @stmt1; Execute stmt2; END</code></pre> <h1 id=\"thealternativecode\">The alternative code</h1> Another variation of above stored procedure where the output is returned in an output parameter can be created simply by adding two more lines to above code after Execute stmt2 and also including the output parameter in first line. Complete code below: <pre class=\"language-sql line-numbers\"><code>CREATE DEFINER=`root`@`localhost` PROCEDURE `json_builder_outparam`(IN `var1` varchar(10000), IN `tab_name` text, OUT `varout` text) \tLANGUAGE SQL \tNOT DETERMINISTIC \tCONTAINS SQL \tSQL SECURITY DEFINER \tCOMMENT '' BEGIN /* - This procedure will take columns and tablename as parameter. - Third parameter is to tell the procedure how many columns are to be returned as string - surrounded by quotes - Third parameter must always be less than the total columns being requested. - Rest of the columns must have numerical values as they won't be surrounded with quotes - Procedure returns 0 for each null value. ​ SAMPLE PROCEDURE CALL CALL `json_builder_multiple_string`('Year,Month,Region,Sales,Expenses', 'myjsonexample',4); ​ SAMPLE QUERY GENERATED: SELECT CONCAT('[\\'Year\\',\\'Month\\',\\'Region\\',\\'Sales\\',\\'Expenses\\'],', GROUP_CONCAT('[', CONCAT_WS(',', CONCAT('\\'', IFNULL(`Year`, 0), '\\',\\'', IFNULL(`Month`, 0), '\\',\\'', IFNULL(`Region`, 0), '\\',\\'', IFNULL(`Sales`, 0), '\\''), IFNULL(`Expenses`, 0)) SEPARATOR '],'), ']') AS data_set FROM myjsonexample ​ SAMPLE OUTPUT: ['Year','Month','Region','Sales','Expenses'],['2004','JAN','NW','1000',400],['2005','Feb','SW','1170',460],['2006','Mar','NE','2000',1210],['2007','Apr','SE','650',540],['2008','May','EC','0',0] ​ SAMPLE TABLE USED: Drop Table if exists `myjsonexample`; CREATE TABLE `myjsonexample` ( `Year` int(11) NOT NULL, `Month` varchar(25), `Region` varchar(25), `Sales` int(11) , `Expenses` int(11) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `myjsonexample` (`Year`, `Month`, `Region`, `Sales`, `Expenses`) VALUES (2004,'JAN','NW',1000,400), (2005,'Feb','SW',1170,460), (2006,'Mar','NE',2000,1210), (2007,'Apr','SE',650,540), (2008,'May','EC',null,null); */ SET SESSION group_concat_max_len = 1000000; Set @stmt1 = null; select concat('Select concat(\\'',concat('[\\\\\\'',replace(replace(var1,'_',' '),',','\\\\\\',\\\\\\''),'\\\\\\'],'),'\\'', ', group_concat(\\'[\\', concat_ws(\\',\\', CONCAT(\\'\\\\\\'\\', ifnull(`',replace(substring_index(var1,',',int_col_as_str),',','`,0),\\'\\\\\\',\\\\\\'\\',ifnull(`'), '`,0), \\'\\\\\\'\\'),', (concat('ifnull(`', replace( SUBSTRING(var1,LENGTH(SUBSTRING_INDEX(var1, ',', int_col_as_str)) + 2,LENGTH(var1)), ',', '`,0),ifnull(`'), '`,0)') ) ,') SEPARATOR \\'],\\'),\\']\\') as data_set from ', tab_name) into @stmt1; Prepare stmt2 from @stmt1; Execute stmt2; Set varout = @varouttemp; Select varout; END The BIRT usage explained My sample BIRT reports are available for download HERE. Enabling the google chart on BIRT couldn’t be simpler. Create a new report Add a text field, open it and change it to HTML as shown in the image below Copy the html[1] (modify as required - I used jsfiddle for checking the code and making changes) and paste in to the text field. Run Once the BIRT preview is working fine, create a dataset by calling the stored-procedure above and bind it to this text field. To bind a dataset, in "Report Design" perspective, click on the field and then in "Property Editor" select "Binding" as shown below: Then right click on the text field and click on edit and then click on the "Fx" symbol next to where field was changed to HTML on top of the window. This will open javascript editor. On javascript editor select the static dataset and replace it by selecting the dataset like so: That should change the HTML to dynamically get the data from json builder. <html> <head> <script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\"></script> <script type=\"text/javascript\" src=\"https://www.google.com/jsapi\"></script> <script type=\"text/javascript\"> google.charts.load('current', { 'packages': ['geochart', 'corechart', 'table', 'controls'] }); google.charts.setOnLoadCallback(drawRegionsMap); function drawRegionsMap() { \t\t\t\t// Create our data table. \t var data = google.visualization.arrayToDataTable([ ['Country', 'Period', 'RMPV', 'TARGET', 'Average'], ['Germany', 'Nov-2015', 75, 120, 97.5], ['Germany', 'Dec-2015', 55, 65, 60], ['Canada', 'Nov-2015', 200, 85,42.5], ['Canada', 'Dec-2015', 55, 65, 60], ['IN', 'Nov-2015', 328, 30, 179], ['IN', 'Dec-2015', 55, 65, 60], ['France', 'Dec-2015', 30, 100, 65], ['France', 'Nov-2015', 75, 120, 97.5], ['Brazil', 'Dec-2015', 50, 100, 75], ['Brazil', 'Nov-2015', 200, 85, 142.5], ['United States', 'Dec-2015', 55, 65, 60], ['United States', 'Nov-2015', 328, 30, 179] ]); var data1 = google.visualization.data.group(data,[0], [{'column': 4, 'aggregation': google.visualization.data.avg, 'type': 'number'}] ); var options = { colorAxis: { // values: [0,30,90,150], colors: ['#F40EF4','#FCFC2A','#7BFC2A', '#FC4A2A'] }, backgroundColor: '#F1F7FD', datalessRegionColor: '#8B8E91', defaultColor: '#f5f5f5' }; var chart = new google.visualization.GeoChart(document.getElementById('regions_div')); function selectHandler() { var selectedItem = chart.getSelection()[0]; if (selectedItem) { var message = data1.getValue(selectedItem.row, 0); // alert(message) //if(message=='Canada'){ var display_value = [message]; //}; }; /* Start of testing*/ // Create a dashboard. var dashboard = new google.visualization.Dashboard( document.getElementById('dashboard_div')); // Create a range slider, passing some options var donutRangeSlider = new google.visualization.ControlWrapper({ 'controlType': 'NumberRangeFilter', 'containerId': 'filter_div', 'options': { //'filterColumnLabel': 'M602' 'filterColumnIndex': [2] } }); // Create a range slider, passing some options var donutRangeSlider1 = new google.visualization.ControlWrapper({ 'controlType': 'NumberRangeFilter', 'containerId': 'filter_div1', 'options': { //'filterColumnLabel': 'M525' 'filterColumnIndex': [3] } }); var categoryPicker = new google.visualization.ControlWrapper({ controlType: 'CategoryFilter', containerId: 'control3', state: { selectedValues: display_value }, options: { //filterColumnLabel: 'Location Type', filterColumnIndex: [0], ui: { labelStacking: 'vertical', allowTyping: false, allowMultiple: false, allowNone: false } } }); // Create a column chart, passing some options var ColumnChart = new google.visualization.ChartWrapper({ 'chartType': 'ColumnChart', 'containerId': 'chart1', 'options': { 'width': '900px', 'legend': 'bottom', 'vAxes': { 0: { title: 'Average Utilisation/Target Utilisation' }, 1: { title: 'RMPV Utilisation' } }, 'series': { 1: { targetAxisIndex: 1 } } }, view: { columns: [1, 2, 3, 4] } }); // Create a table chart, passing some options var tableChart = new google.visualization.ChartWrapper({ 'chartType': 'Table', 'containerId': 'chart2', }); var pie = new google.visualization.ChartWrapper({ chartType: 'PieChart', containerId: 'chart3', options: { legend: 'bottom', title: 'Average Utilisation per month', pieSliceText: 'value' }, // Instruct the piechart to use colums 0 (Name) and 3 (Donuts Eaten) // from the 'data' DataTable. view: { columns: [1, 4] } }); // Establish dependencies, declaring that 'filter' drives 'pieChart', // so that the pie chart will only display entries that are let through // given the chosen slider range. dashboard.bind([donutRangeSlider, donutRangeSlider1, categoryPicker], [ColumnChart, tableChart, pie]); // Draw the dashboard. dashboard.draw(data); //redraw the main chart so selection doesn't retun empty \t\t\t\t\tchart.draw(data1, options); // End of testing*/ }; google.visualization.events.addListener(chart, 'select', selectHandler); chart.draw(data1, options); } \t </script> </head> <body> <div id=\"regions_div\" style=\"width: 900px; height: 500px;\"></div> \t <!--Div that will hold the dashboard--> <div id=\"dashboard_div\"> <!--Divs that will hold each control and chart--> <table> <tr> <td> <div id=\"control3\" align=\"center\"></div> </td> </tr> <tr> <td> <div id=\"filter_div\"></div> </td> <td> <div id=\"filter_div1\"></div> </td> </tr> </table> <div id=\"chart1\"></div> <div id=\"chart3\"></div> <div id=\"chart2\" align=\"center\"></div> </div> </body> </html> HTML to be pasted in text field: ↩︎ ","categories": [], + "tags": [], + "url": "/mysql-stored-procedure-to-return-json-for-google-charts/", + "teaser": null + },{ + "title": "Crossover", + "excerpt":"There are technologies that I can use in my personal life, that I am proud of and those that I play around with but reality is that as a Senior Project Manager, there aren’t very many times when I get the opportunity to influence the choice of software to be used. As a principle, my personal choices are driven by opensource and professional choices are driven by client needs. However, listed below are opensource technologies that crossed over from my personal to professional realm. Three open-source technologies that jumped from my hobbyist curiosity to my professional life PlantUML What is it? PlantUML is a great tool to create flow and activity diagrams. It uses plain text to create nice looking flow diagrams which are really quick to prepare. Postioning and all the other stuff is just taken care of by the algorithm. How do I use it? As such there is no install required. For Chrome, there is an extension which can be installed or from any browser using [PlantText] (http://www.planttext.com). I use it directly on the text editor at hand and create the flow I am after while taking notes on the process. It just works. SpagoBI What is it? SpagoBI is a fully working Business Intelligence Suite which just works. How do I use it? As part of one of my projects. I had the freedom to pick any MI solution that was cost effective, robust and fit for purpose. SpagoBI ticks all the boxes so I took the opportunity to bring in this opensource solution. It has very nice GUI and end user experience is great which meant it was an instant hit. It is live and am a user as well. Talend What is it? Talend - does not really need an introduction. It is a well established name and is what techies call ETL - Enterprise Transport Layer. To explain with an analogy, if different databases were people speaking different languages, then Talend is the translator who can not only explain what other party is speaking but can also perform some important jobs like getting the groceries, throwing garbage and arranging school pickups. How do I use it? Again as part of the aforementioned project, Talend is what was used to interact with an obscure offering from intuit called Quickbase as well as with MSSQL and mariaDB (another opensource tech we all love). The biggest benefit that I derived apart from the regular data integration came from the fact that it is also able to place the dataset directly into a spreadsheet (Excel or LibreCalc). Now, having all charts and formulae already in place as a template on spreadsheet kind of allows for report creation on a technology that these recipients are already working with and so they don’t have to learn any new technology. Instead of learning and struggling with something new, they are happy to get the information in a format they all accept and are comfortable with and can get on with their day job and make informed decisions based on what the report says. Icing on the cake, the emailing for these spreadsheets, once they were agreed as final versions, is also scheduled so the reports can be delivered in the format end-user likes without any human intervention. What more can a project manager ask. :D ","categories": [], + "tags": [], + "url": "/crossover/", + "teaser": null + },{ + "title": "Seafile Server behind nginx on Fedora 24 Security Lab Spin", + "excerpt":"I have recently been intrigued by the idea of replacing the likes of “Dropbox” and “Google Drive” with a cloud set-up of my own. I had “Owncloud” set-up for nearly a year but was not happy with it. There were minor niggles aside form speed and thumbnails and then “Owncloud” had a recent split leading to creation of “Nextcloud”. While “Nextcloud” is the one that is more aligned to the general principles of community driven software, it is new and is still plagued with owncloud issues as it is essentially same stuff in new packaging at the moment. In the meantime, every now and then I was reading all the good stuff people had to say about “Seafile” and so I wanted to give it a try. Now for the past year and a half I have also been using Fedora Security Lab spin on my home server and I just wanted to get the Seafile set-up on it so I did a few “duckduckgo” searches on the net and finally had the steps to achieve the objective. Obviously it all worked and my Seafile server is live and kicking, hence the post. :) So in nutshell my objective was to: Install seafile-server-5 behind nginx on Fedora 24 Security Lab spin all on a 32 bit 12 year old laptop. The steps I followed are listed below with detailed notes of what I did. I do not claim these to be perfect but this is what worked for me. If you know that something can be done better, please do let me know in the comments. 1. Install required software to support seafile with nginx: python python-imaging MySQL-python python-setuptools nginx mariadb mariadb-server policycoreutils-python setroubleshoot All this can be done with one single command: sudo dnf install python python-imaging MySQL-python python-setuptools nginx mariadb mariadb-server policycoreutils-python setroubleshoot 2. Start mariadb-server and set up basic security settings. Now as we need to run all commands with “sudo”, it is actually easier to just go root. In other words, type “su” on the terminal and provide your password so you are logged in as root on the terminal. When you are logged in as root the $ changes to # at command prompt. All commands below are on root so if you are not logged in as root, you will need to run these with “sudo”. #Start mariadb: [root@localhost /] systemctl start mariadb #Initiate the mariadb secure installation: [root@localhost /] mysql_secure_installation Above command will trigger a set of questions around port, username, password etc. You can of course change these. If you do so, you must keep note of it and change your specific details for database in subsequent steps but if you leave it default you will basically have a user: root, with a password of your choice (we will use “sqlpasswd” for this example) and a database server running at port 3306. 3. Set up databases and privileges for \"seafile" Type following: [root@localhost /] mysql -p You will be prompted to enter a password. Provide the password you have set-up for maria-db server in step above. So for our example it will be: sqlpasswd Now on mysql prompt type following commands one after one along with semi-colon: create database `ccnet-db` character set = 'utf8'; create database `seafile-db` character set = 'utf8'; create database `seahub-db` character set = 'utf8' Now we will create a user named “seafile” for mysql with a password ‘seafilepwd’. You must replace this password with one of your own. To do so use the command below and change seafilepwd with a password of your choice. create user 'seafile'@'localhost' identified by 'seafilepwd'; Once seafile user is created, we need to grant permissions to this user on the three databases we created above. To do so, use the following one by one on mysql command prompt: GRANT ALL PRIVILEGES ON `ccnet-db`.* to `seafile`@localhost; GRANT ALL PRIVILEGES ON `seafile-db`.* to `seafile`@localhost; GRANT ALL PRIVILEGES ON `seahub-db`.* to `seafile`@localhost; Finally we will make sure that mariadb-server starts every-time the system is started. To do this simply copy the command below: [root@localhost /] systemctl enable mariadb 4. Create directories to download and extract seafile server: 1 2 3 4 #Create a directory called seafile: [root@localhost /] mkdir /opt/seafile Now change directory to the newly created path [root@localhost /] cd /opt/seafile Download the latest seafile-server relevant to your machine architecture (32 bit, 64 bit etc). As I am using a 32 bit laptop, I used the link meant for that. TIP: You can get the relevant link by first opening the seafile site in firefox (https://www.seafile.com/en/download/) and scrolling down all the way to “Server” section. Then under linux section you will see link for 64 bit and 32 bit versions. Right click on one you need and click on “Copy Link” Location&quot;. The copied text is what you need to paste after wget in command below. [root@localhost /] wget https://bintray.com/artifact/download/seafile-org/seafile/seafile-server_5.1.3_i386.tar.gz Now to extract the downloaded file, type following command. [root@localhost /] tar -xzf seafile-server_5.1.3_i386.tar.gz TIP: If downloaded version is different, the filename “seafile-server_5.1.3_i386.tar.gz” will be different in the link you would have got from previous step. Use the file name right at the end of the link you copied. OK, now we will create a directory named “installed” and move the downloaded file in there. [root@localhost /] mkdir installed [root@localhost /] mv seafile-server_5.1.3_i386.tar.gz installed Checkpoint At this point, if you give the tree command, your directory structure would look as shown below [root@localhost seafile] tree -L 2 . |---installed |----- seafile-server_5.1.3_i386.tar.gz |---seafile-server-5.1.3 |----- check_init_admin.py |----- reset-admin.sh |----- runtime |----- seaf-fsck.sh |----- seaf-fuse.sh |----- seaf-gc.sh |----- seafile |----- seafile.sh |----- seahub |----- seahub.sh |----- setup-seafile-mysql.py |----- setup-seafile-mysql.sh |----- setup-seafile.sh |----- upgrade 5. Configure the seafile-server #Change directory to seafile-server where the execution scripts are residing. [root@localhost seafile] cd seafile-server-5.1.3 #Now run the execution script: [root@localhost seafile-server-5.1.3] ./setup-seafile-mysql.sh Running this script will initiate the seafile server set-up for mysql. You will need to provide answers to some questions. I am providing those below where you need to deviate from default or need to provide specifc information: Name of the server. Provide a servername like \"my_seafile_cloud\" or \"cloudy_lemon\" ...you get the gist. IP or Domain of Server: TIP: Now let’s take a bit of time to understand what should go here. For my usage I wanted to use dyndns so I can access this server from outside of my home network. So I had to configure my dyndns url to a port on my home router such that router understood that incoming traffic to that particular port must be transferred to this machine where the server is hosted. Then on this machine, I have forwarded the traffic coming to a specific port again to whichever server I want to access thus having my unique URL for each service I am interested in. I will go into in a bit more detail later but for now, just ensure you are providing 1 the internal IP as well as dyndns IP along with the port you have configured on your router to reach this machine in particular where you are configuring the server. So if internal IP of this machine is 192.168.1.24 and my dyndns url is banana.dyndns.com and I have forwarded port 9994 on my router to this machine and on machine incoming traffic to 9994 is forwarded to the port relevant to seahub then I will provide following entries as answer to this question: 192.168.1.24:9994, banana.dyndns.com Choose a way to initiatlise seafile databases: Now remember we have already done this in step 2. So for this question we will need to give option 2. so just type 2 and press enter. Host of mysql server: Unless you changed this in step 2, leave it as default (localhost) Port for mysql server: as above leave it default (3306) unless you changed in Step 2. User for seafile: seafile Password for user \"seafile": Use the password provided in Step 3. \"seafilepwd\" is what we provided for this example. Database name for ccnet: ccnet-db Database name for seafile: seafile-db Database name for seahub: seahub-db Once this wizard has completed configuring, you will get a confirmation that will tell you that seafile is now listening on port 8082 and seahub on port 8000 unless you changed these while providing answers to the wizard. 6. Add the user and provide right access Use following commands to go up one directory, add a user “seafile” and provide right privileges. [root@localhost seafile-server-5.1.3] cd .. [root@localhost seafile] adduser seafile [root@localhost seafile] chown -R seafile . 7. Generate SSL Certificate: #Change directory to /etc/ssl [root@localhost seafile] cd /etc/ssl #Generate private and public ssl certificates [root@localhost ssl] openssl genrsa -out seafile_privkey.pem 2048 [root@localhost ssl] openssl req -new -x509 -key seafile_privkey.pem -out seafile_cacert.pem -days 1095 8. Create the nginx config file for seafile Use following command to create seafile.conf file: [root@localhost ssl] nano /etc/nginx/conf.d/seafile.conf Copy Paste the following but do make relevant changes where required for servername etc: ######################################################## server { listen 80; server_name *banana.dyndns.com*; ---&gt; Must change this as per your set-up. Notice no port 9994 here. rewrite ^ https://$http_host$request_uri? permanent; # force redirect http to https. } server { listen 443; ssl on; ssl_certificate /etc/ssl/seafile_cacert.pem; ssl_certificate_key /etc/ssl/seafile_privkey.pem; server_name *banana.dyndns.com*; ---&gt;Must change this as per your set-up. Notice no port 9994 here. proxy_set_header X-Forwarded-For $remote_addr; add_header Strict-Transport-Security \"max-age=31536000; includeSubdomains&amp;quot;; server_tokens off; location / { fastcgi_pass 127.0.0.1:8000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_script_name; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; fastcgi_param HTTPS on; fastcgi_param HTTP_SCHEME https; access_log /var/log/nginx/seahub.access.log; error_log /var/log/nginx/seahub.error.log; } location /seafhttp { rewrite ^/seafhttp(.*)$ $1 break; proxy_pass http://127.0.0.1:8082; client_max_body_size 0; proxy_connect_timeout 36000s; proxy_read_timeout 36000s; proxy_send_timeout 36000s; } location /media { root /opt/seafile/seafile-server-latest/seahub; } ######################################################## 9. Apply config changes to seafile #Change to conf directory at /opt/seafile. [root@localhost ssl] cd /opt/seafile/conf #Now open the ccnet.conf file: [root@localhost conf] nano ccnet.conf Amend the file as per below: ######################################################## [General] USER_NAME = my_seafile_cloud #**Must change this as per your setup.** ID = NAME = my_seafile_cloud ####&lt;--------- Must change this as per your setup. SERVICE_URL = https://192.168.1.24:9994,https://banana.dyndns.com:9994 #**Must change this as per your setup.** #**Also note that here we have provided the 9994 port ** #**- the one we used as port forward on router ** #**to send traffic to this machine.** [Client] PORT = 13419 [Database] ENGINE = mysql HOST = 127.0.0.1 PORT = 3306 USER = seafile PASSWD = sqlpasswd #**This would be same as what was provided in Step 2.** DB = ccnet-db CONNECTION_CHARSET = utf8 ######################################################## Open the seahub_settings.py file [root@localhost conf] nano seahub_settings.py Add the line FILE_SERVER_ROOT= 'https://banana.dyndns.com:9994/seafhttp' replacing banana.dyndns.com:9994 with url to reach your machine. ######################################################## DATABASES = { \t'default': \t{ \t'ENGINE': 'django.db.backends.mysql', \t'NAME': 'seahub-db', \t'USER': 'seafile', \t'PASSWORD': 'sqlpasswd', #This will be same as password created in Step 2. \t'HOST': '127.0.0.1', \t'PORT': '3306' \t} } FILE_SERVER_ROOT= 'https://banana.dyndns.com:9994/seafhttp' #**Must change this as per your setup.** #**Also note that here we have provided the 9994 port ** #**- the one we used as port forward on router ** #**to send traffic to this machine.** 10. Now create the seafile and seahub services and enable them so they run at system startup: Create a seafile.service file: [root@localhost conf] nano /etc/systemd/system/seafile.service Paste the below into the file: ######################################################## [Unit] Description=Seafile After=network.target mariadb.service [Service] Type=oneshot ExecStart=/opt/seafile/seafile-server-latest/seafile.sh start ExecStop=/opt/seafile/seafile-server-latest/seafile.sh stop RemainAfterExit=yes User=seafile Group=seafile [Install] WantedBy=multi-user.target ######################################################### #Now reload the systemd and enable seafile to start at system start-up. [root@localhost conf] systemctl daemon-reload [root@localhost conf] systemctl enable seafile #Create a seahub.service file: [root@localhost conf] nano /etc/systemd/system/seahub.service Paste the following in the file and save. ########################################################## [Unit] Description=Seafile hub After=network.target seafile.service [Service] ExecStart=/opt/seafile/seafile-server-latest/seahub.sh start-fastcgi ExecStop=/opt/seafile/seafile-server-latest/seahub.sh stop User=seafile Group=seafile Type=oneshot RemainAfterExit=yes [Install] WantedBy=multi-user.target ########################################################## Now enable seahub to start at system start-up and start seafile and seahub services. [root@localhost conf] systemctl enable seahub [root@localhost conf] systemctl start seafile [root@localhost conf] systemctl start seahub Start nginx service and enable to run at system start [root@localhost conf] systemctl enable nginx.service [root@localhost conf] systemctl start nginx.service 12. Fedora specific steps: Now at this point there were few final things specific to Fedora that needed attention. I was ending up getting error 500 and what not and after a bit of searching on duckduckgo, I tried steps listed in this section that fixed the issue. I am not entirely sure what this does but what I understood is selinux was not allowing some access between nginx and seafile which this resolved. How? Well, you tell me :D. I had to install policycoreutils-python and setroubleshoot packages for these commands to work, so I have included them in Step 1 anyway. [root@localhost conf] cat /var/log/audit/audit.log | grep nginx | grep denied | audit2allow -M mynginx [root@localhost conf] semodule -i mynginx.pp [root@localhost conf] setsebool httpd_can_network_connect 1 13. Re-set admin user and password for seafile: #Change directory to seafile [root@localhost conf] cd /opt/seafile/seafile-server-5.1.3/ #Now run the reset-admin [root@localhost seafile-server-5.1.3] ./reset-admin.sh 14. Firewall configuration and port forwarding: Notes below are relevant to system running Fedora Security Lab spin with XFCE but general principle will be same. Application->Administration->Firewall will open firewall gui. You will be asked for password and upon entering you will be presented with Zones and Services. In Services enable https by clicking the checkbox against “https”. Now click on “Ports” and click on “Add”. Then add port 8082 in “Port / Port Range” and select “TCP” as “Protocol”. Then add port 8082 in “Port / Port Range” and select “UDP” as “Protocol”. Then add port 8000 in “Port / Port Range” and select “TCP” as “Protocol”. Then add port 8000 in “Port / Port Range” and select “UDP” as “Protocol”. Then add port 443 in “Port / Port Range” and select “TCP” as “Protocol”. Now click on Port Forward tab and click on “Add”. Then fill field as shown below: Protocol: tcp Port / Port Range: 9994 # This is the port you have used on your router for port forwarding to this machine. Under Destination section, tick the checkbox for \"Local Forwarding\" Port / Port Forwarding field: 443 # If you have used above configuration, SSL is enabled at 443. # If you have changed it, mention the relevant port. TIP: Once you have made these changes don’t forget to save this from run time to permanent or these changes will be lost when you restart the machine. [Options-> Runtime to Permanent] 15. Finally, restart nginx: Use the following command to restart nginx services. [root@localhost seafile-server-5.1.3]# systemctl restart nginx.service This is it. All Done !!! If you now type your URL: (https://banana.dyndns.com:9994 from this example) on a browser, you will be presented with login page where you should use the admin login details you have created in Step 13 above. ","categories": [], + "tags": [], + "url": "/seafile-server-behind-nginx-on-fedora-24-security-lab-spin/", + "teaser": null + },{ + "title": "Ghost on Fedora 24", + "excerpt":"To install Ghost as my blogging platform, I had to go through a number of hoops and one of them was to get the nodejs working and what not. I figured this might as well be worth documenting in case I have to do this all over again. It might also be helpful for some other inquisitive minds. :) The most useful reference I found was the post on rosehosting website specific to CentOS 7[1] It would have all gone well too; had it not been for the nodejs related issues which resulted in me finding the other helpful pointers from various forums. Anyway, the steps I took to get this all working are: Step 1: Install nodejs and npm Step 2: Install dependencies Step 3: Install npm modules Step 4: Tell Ghost your blog URL Step 5: Start Ghost and nginx These are detailed in my notes below - keeping it, where I can, true to the post I have referred above: Step 1: Install nodejs and npm On Fedora 24 node.js package already includes npm and if you try installing npm separately it will throw an error so just install node.js and npm will be installed along with it. sudo dnf distro-sync sudo dnf install nodejs Step 2: Install dependencies sudo dnf install php php-fpm php-cli php-mysql php-curl php-gd #Create a directory for the website: mkdir /var/www/html/[sitefolder. eg: blog, myblog, banana] #Change to the newly created directory: cd /var/www/html/[sitefolder. eg: blog, myblog, banana] #Set access permissions for this directory chown -R /var/www/html/[sitefolder. eg: blog, myblog, banana] #Download latest version of Ghost: curl -L https://ghost.org/zip/ghost-latest.zip -o ghost.zip #Unzip the downloaded file unzip ghost.zip #Finally check the directory structure tree -L 2 #OUTPUT of above command should look like as shown below: . ├── config.example.js ├── config.js ├── content │   ├── apps │   ├── data │   ├── images │   └── themes ├── core │   ├── built │   ├── index.js │   ├── server │   └── shared ├── ghost.zip ├── Gruntfile.js ├── index.js ├── LICENSE ├── npm-shrinkwrap.json ├── package.json ├── PRIVACY.md └── README.md Step 3: Install npm modules While installing/initiating npm modules, there were several errors that system was throwing. They were in two categories: Access Related Dependencies Related Access Related - I was getting EACCES error and solution given on on npmjs.com under Option 2 is what sorted the access issues [1:1]. mkdir ~/.npm-global npm config set prefix '~/.npm-global' nano ~/.profile Add the line export PATH=~/.npm-global/bin:$PATH in the opened file. <pre class=”line-numbers language-bash”style=”counter-reset: linenumber 3;”>source ~/.profile</pre> Dependencies Related - Some forum hopping later I just followed the advice on Ghost support[1:2] and installed the dependencies. Steps below: #install required dependencies: npm install -g node-gyp sudo dnf install gcc gcc-c++ Once above dependencies are installed following code should just work. NOTE: Make sure you are in the directory you created in step2. #Install PM2 a process manager to control Node.js applications #It will help in keeping specified Node.js applications alive forever: npm install pm2 -g #Install npm install --production #Start Ghost with pm2 and create a name for the pm2 NODE_ENV=production pm2 start index.js --name \"Ghost\" Step 4: Tell Ghost your blog URL A very simple change is required to config.js file as shown below: #Copy the sample config file cp config.example.js config.js nano config.js File that opens will have following javascript: // # Ghost Configuration // Setup your Ghost install for various [environments](http://support.ghost.org$ // Ghost runs in `development` mode by default. Full documentation can be found$ var path = require('path'), config; config = { // ### Production // When running Ghost in the wild, use the production environment. // Configure your URL and mail settings here production: { url: 'http://your.blog.com', It’s the line number 14 in above code block where you need to replace http://your.blog.com with actual url of your blog. Step 4: Configure Nginx NGINX install and configuration is something I covered in my post for installing Seafile on Fedora 24[1:3]. So I already had a running nginx. I just needed to create a reverse proxy for Ghost on the existing nginx server. Open the hostfile using following command: sudo nano /etc/hosts Now in the hosts file add the localhost alias for blog - in this example it is your.localhost.com. 127.0.0.1 localhost.localdomain localhost your.seafile.com your.blog.com Open the file using following command. NOTE: Replace `yourblog.conf` with your actual blog's conf file name. nano /etc/nginx/conf.d/yourblog.conf On the file that opens copy and paste the following code. NOTE: Replace `your.blog.com` on line number 6 below with alias for localhost for this blog you added to the host file above. upstream ghost { server 127.0.0.1:2368; } server { listen 80; server_name your.blog.com; access_log /var/log/nginx/ghost.access.log; error_log /var/log/nginx/ghost.error.log; proxy_buffers 16 64k; proxy_buffer_size 128k; location / { proxy_pass http://ghost; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; proxy_redirect off; 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; } } Step 5: Start Ghost and nginx After all the above steps are completed issue following commands to restart ghost and nginx. sudo systemctl restart nginx.service pm2 restart Ghost All Done !!! References: GHOST_URL/seafile-server-behind-nginx-on-fedora-24-security-lab-spin/ ↩︎ ↩︎ ↩︎ ↩︎ ","categories": [], + "tags": [], + "url": "/ghost-on-fedora-24/", + "teaser": null + },{ + "title": "The complete walkthrough of my blogger to ghost migration", + "excerpt":"The 7 Year Itch It can’t possibly be a coincidence that this is the 7th year since I started blogging on blogger and therefore it is very likely to be a strong case of the 7 year itch syndrome but whichever way you look at it, divorce was inevitable given blogger had just stopped inspiring me. I have been fiddling with different blogging platforms while getting accused of neglecting my sweet and loving family…😢. Ghost caught my fancy three weeks back. The last post was the beginning of our courtship and this post tells the tale of how a casual fling turned into marital commitment. 😂 To start a fresh blog, choosing any platform is easy and straight forward but to move from one platform to another is - umm… lets just say a very involved process - rewarding but involved. Love can move mountains!!! A complete migration from blogger to WordPress would have been way simpler. I know this as I have done it in past and it appeared like moving to Ghost would require migrating to a WordPress instance anyway. There was - I must admit - a temptation to call WordPress the home but that wouldn’t have made a great love-story now - would it? However, the much publicised WordPress route to Ghost migration did not work for me and eventually after a lot of manual copying, pasting, cleaning, pruning, hiding, reading and learning later, the self-hosted blog is all complete. My first baby with my new found love - Ghost - all ready and set to meet the world at large !!! 😃 So story below will take you through ups and downs of a love affair that I hope will last for a life-time (or for a very long foreseeable future at the very least…😍). Install Ghost Install a theme Configure the theme Enable commenting on the blog with DISQUS Migrate content from blogger Redirect Traffic from old blog Migrate comments from old blog Enable Search for your blog Enable Social Links Install Ghost This is covered in my last post. Once it was installed, I took some time exploring and learning Markdown. Last post was my first one using Markdown and it was a very pleasing experience indeed. That nice experience paved way as well as helped me finalise the decision to go the whole nine yards. Install a theme There are some very beautiful themes available on Ghost Marketplace. I have used the theme called scrawl and then tweaked it a bit. Once I found the theme I liked, I downloaded the zip file, unzipped it and deleted the zip file like so. #Download curl -L https://github.com/ktweeden/scrawl/archive/master.zip -o master.zip #unzip unzip -o master.zip #Delete zip file rm master.zip #restart Ghost pm2 restart Ghost Theme is now installed. Configure the theme The first thing I wanted to configure on my new theme was the code block. Prismjs is the way to go and it is already included in the theme I downloaded but the line numbering was not there. After reading a bit on PrismJS website, I understood that core css file from prism did not have this and also I wanted the dark theme so I downloaded the "okadia" theme css along with line number plugin. I then replaced the content of /var/www/html/site-name/content/themes/scrawl-master/assets/css/prism.css with the content in downloaded CSS. Next thing I wanted to change was the header background colour and also the link colour. While it was very close to what I wanted, my actual liking is for the colour #F2C20F and a bit darker link colour #B710EF. To do this I edited the _global_styles.scss like so: #change directory cd /var/www/html/site-name/content/themes/scrawl-master/sass/partials /#Edit _global_styles.scss nano _global_styles.scss Now change the colour of $primary-colour and also add $link-colour. After this your /* Colour */ section will look as shown below: /* Colours */ $primary-colour: #F2C20F; $secondary-colour: #254E70; $tertiary-colour: #FF4B3E; $font-colour: #011627; $background-colour: #EFEFEF; $link-colour: #B710EF; Then I changed directory using cd /var/www/html/site-name/content/themes/scrawl-master/assets/css and opened index.css and post.css where I changed the background-color property to #F2C20F as shown below: nano index.css /** MIXINS **/ .blog-title-background { background-color: #F2C20F; width: 100%; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } nano post.css /**line 86/434 (19%), col 22/29**/ .blog-title-background { background-color: #F2C20F; width: 100%; } Enable commenting on the blog with DISQUS Using DISQUS to enable comments is extremely simple especially on scrawl - the theme I have used and is as explained under the DISQUS section of the theme website: To enable commenting: I created an account on Disqus. Then I created a "channel" for this blog in DISQUS using steps below: Open DISQUS and click on "Get Started" to sign-up Once the sign-up was completed, email verified etc, I went to channels link on home page of disqus and created a channel say thetestchannel Copied the following code into the blog header code injection: <script>window.__themeCfg.disqusUsername = 'thetestchannel';</script> thetestchannel in code above must be replaced with the name of the channel created on DISQUS. Migrate content from blogger Now, this was the most painful of all the things I had to do - the moving of mountain if you like - because the automated solution using Blogger2Ghost just wouldn’t work for me. So I basically copy pasted most of the content from blogger over to Ghost and placed the various screenshots manually. If my posts were in 3 digits, I would have persevered and tried contacting someone for help but as it was relatively less content, I just went ahead and did it manually and am glad I did so because that way I was able to do a bit of clean-up too. Redirect Traffic from old blog There are a huge number of posts on www for 301 redirect and what not but I felt, it is only fair to let the redirects land on old blog and users be told there of the new destination so like last step, I manually updated the posts on blogger to just let the reader know that the post they are looking for has moved to new location and link for that post on new site. Not most elegant and efficient approach but I am happy that way. If there are huge entries and one still has to go the manual route, it will save a lot of pain if the permanent link for new posts on Ghost is same as that on old blog and this can be achieved by clicking on clog next to “Save Draft” on the post and changing the “Post URL” Migrate comments from old blog Migrating comments from blogger to DISQUS is very easy. I opened the blogger importing tool which can be accessed using a URL similar to this - `https://thetestchannel.disqus.com/admin/discussions/import/platform/blogger/` However, getting them reflected on new ghost instance has three approaches dependent on the route one takes for redirecting from old blog. As I chose no redirection as such, I had to go for a CSV mapping file between my old and new blog URL per post. Once again, for the number of posts I have this was not a challenge at all but I can’t imagine doing such a thing for a big content transfer and it may be worth paying attention to the note in step above if one has huge content to transfer as it will reduce the pain for comments transfer. Enable Search for your blog Ghost recommends Swifttype and the popular site Ghost for beginners provides guidance for Google CSE (Custom Search Engine). I did not like the idea of paying for Swifttype for my small site and Google Search Engine was taking it’s sweet time to crawl my site - unlike in past now we cant order / request a crawl and there was ofcourse Google Custom Search watermark which I am not all that fond of. I updated the theme I am using to include search feature. Enable Social Links This is, much like many other customisations I did, a very theme specific step and for the theme I have chosen it is fortunately very easy to achieve except for linkedin and google+ for which I had to add few lines of code. I achieved this using steps below: Adding already available social icons is very simple and most of the social links are actually available out of the box. So for Tumblr and Instagram all I had to do was open the admin panel of Ghost, click on Code Injection link and paste the following two lines replacing yourusername with my username on that platform. <script>window.__themeCfg.tumblrUsername = 'yourusername';</script> <script>window.__themeCfg.instagramUsername = 'yourusername';</script> For LinkedIn and Google+: Open the footer.hbs using nano /var/www/html/site-name/content/themes/scrawl-master/partials/footer.hbs and pasted the following code under div class footer-container around line 14. <a class=\"social-button linkedin hidden\"><i class=\"icon-linkedin\"></i></a> <a class=\"social-button gplus hidden\"><i class=\"icon-gplus\"></i></a> Then under scripts I added following code around line number 42 <ul> <li> case \"Linkedin\": return \"https://uk.linkedin.com/in/\" + username; </li> </ul> <ul> <li> case \"Gplus\":<br> return `<a href=\"https://plus.google.com/\">https://plus.google.com/</a>` + username;<br> </li> </ul> Finally under the function revealSocialLinks around line number 69, I pasted the following code: revealPlatform('Linkedin'); revealPlatform('Gplus'); Finally I added the following in the header part of the code injection in front end admin panel of Ghost instance; replacing yourusername with my username for the relevant platform. <script>window.__themeCfg.linkedinUsername = 'yourusername';</script> <script>window.__themeCfg.gplusUsername = 'yourusername';</script> My Learning Curve While there were many insights and learnings, the top 5 I think on the list of learning for me while migrating from blogger to ghost are: Clarity on using Nginx (refer last post.) - Never before have I played around with nginx but having read so many good things about it, I was keen to try and I can say it is indeed living up to all the praise I have read. It is fast and lightweight. Will I use it for production? Ofcourse I will. Will I favour it over Apache - hmm...Maybe... Maybe not. I think I will weigh my options based on what is it I want to achieve but to know about both nginx as well as Apache Server can't hurt. Better Idea of Networking Concepts - With Seafile and Ghost implementation in succession, I actually learnt about quite a few concepts on configuring firewall, DNS, reverse proxy, port forwarding to list a few but more importantly I read a lot about networking and this helped me understand the core concepts involved. Deeper understanding of Web Technologies and inner workings of Ghost - While trying to modify my blog's look and feel to my liking, I had to mess around with CSS, HTML. I have used the theme scrawl by ktweeden and modified it a bit. What attracted me most to it was the fact that it used a default colour theme similar to what I had in mind. I modified it but in doing so got a bit more understanding of how it all ties together - the default.hbs, index.hbs, post.hbs and the underlying handlebar scripts. Markdown - I had some basic idea before I started using Ghost but if I were to use Ghost full-time, I needed to know all there is to know about markdown and while there isn't a lot - it is damn straight forward and simple - it is a very good tool indeed. I am glad I learnt it. CSS plugins - Prismjs comes pre-loaded with scrawl theme but the default skin and plugins were not enough for me so I had to replace these. Now this might be very simple for people in the know. For me it was new and exciting and end result was so cool with numbers in code box and all that. I am obviously very happy with the end result and it did help me learn yet again how things interact within the Ghost platform. The Beginning !!! 😍😃😂 ","categories": [], + "tags": [], + "url": "/the-complete-walkthrough-of-my-blogger-to-ghost-migration/", + "teaser": null + },{ + "title": "MySQL function to calculate elapsed working time", + "excerpt":" I wrote this function to cater for a specific requirement and I don’t know if there are better ways of doing it but this saved tremendous amount of time and might have real time application elsewhere. Problem Statement: Find out the age of an incident in working minutes, given the following: Time and Date of when an incident was logged Time and date of when the same incident was closed Opening time of the site for which the incident was logged Closing Time of the site for which incident was logged Country of the site for which incident has been logged Assumption: It is assumed that opening and closing times are same on all working days and that all the sites are closed on holidays and weekends Function should take all the above five “given” as parameter and then calculate age of the incident. Example of problem Let’s say an incident was logged on “Friday 10th June 2016 at 12:00” for a site in the “UK” which opens between 09:00 to 16:00. This incident was then closed on “Tuesday 14th June 2016 at 14:00”. For the above incident function should calculate the age as 960 minutes = 16 hours = [4 hours on Friday (12:00 to 16:00) + 7 hours on Monday (09:00 to 16:00) + 5 hours on Tuesday (09:00 to 14:00)] Pre-requisites: A holiday table for the “Country” of the site for which incident is being provided should already be created on the database with the name holiday_table. Created using code below: 1 2 3 4 5 6 7 8 9 10 11 CREATE TABLE `holiday_table` ( \t`holiday_table_id` INT(11) NOT NULL, \t`holiday_date` DATETIME NULL DEFAULT NULL, \t`week_day` VARCHAR(12) NULL DEFAULT NULL, \t`holiday_name` VARCHAR(45) NULL DEFAULT NULL, \t`Country_codes` VARCHAR(45) NOT NULL DEFAULT 'ALL', \tPRIMARY KEY (`holiday_table_id`) ) COLLATE='utf8_general_ci' ENGINE=InnoDB ; Sample sql-insert Data for holiday_table: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (2, '2016-03-25 00:00:00', 'Friday', 'Good Friday', 'ALL'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (3, '2016-03-28 00:00:00', 'Monday', 'Easter Monday', 'ALL'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (4, '2016-05-02 00:00:00', 'Monday', 'Early May bank holiday', 'ALL'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (5, '2016-05-30 00:00:00', 'Monday', 'Spring bank holiday', 'ALL'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (6, '2016-08-29 00:00:00', 'Monday', 'Summer bank holiday', 'ALL'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (7, '2016-12-26 00:00:00', 'Monday', 'Boxing Day', 'ALL'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (8, '2016-12-27 00:00:00', 'Tuesday', 'Christmas Day (substitute day)', 'ALL'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (9, '2016-01-01 00:00:00', 'Friday', 'New Year’s Day', 'SG'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (10, '2016-02-08 00:00:00', 'Monday', 'Chinese New Year', 'SG'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (11, '2016-02-09 00:00:00', 'Tuesday', 'Chinese New Year', 'SG'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (12, '2016-05-21 00:00:00', 'Saturday', 'Vesak Day', 'SG'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (13, '2016-07-06 00:00:00', 'Wednesday', 'Hari Raya Puasa', 'SG'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (14, '2016-08-09 00:00:00', 'Tuesday', 'National Day', 'SG'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (15, '2016-09-12 00:00:00', 'Monday', 'Hari Raya Haji', 'SG'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (16, '2016-10-29 00:00:00', 'Saturday', 'Deepavali', 'SG'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (17, '2016-01-01 00:00:00', 'Friday', 'New Year’s Day', 'IN'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (18, '2016-01-26 00:00:00', 'Tuesday', 'Republic Day', 'IN'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (19, '2016-07-06 00:00:00', 'Wednesday', 'Idul Fitr', 'IN'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (20, '2016-08-15 00:00:00', 'Monday', 'Independence Day', 'IN'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (21, '2016-10-11 00:00:00', 'Tuesday', 'Dussehra/Durga Puja', 'IN'); INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (22, '2016-10-31 00:00:00', 'Monday', 'Diwali Privilege Holiday/Gobardhan Puja', 'IN'); Function code and explanation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 CREATE DEFINER=`root`@`localhost` FUNCTION `workday_time_diff_holiday_table`( `param_country` varchar(10), `assigneddatetime` varchar(20), `closeddatetime` varchar(20), `starttime` varchar(20), `endtime` varchar(20) ) \tRETURNS int(11) \tLANGUAGE SQL \tNOT DETERMINISTIC \tCONTAINS SQL \tSQL SECURITY DEFINER \tCOMMENT '' BEGIN Set @starttime = starttime; Set @endtime = endtime; Select time_to_sec(timediff(@endtime,@starttime))/3600 into @maxhoursaday; Set @assigneddate = assigneddatetime; Set @closeddate = closeddatetime; Set @timecount = 0; Set @timevar1 = @assigneddate; Set @nextdate = @assigneddate; Set @timevar2 = null; Set @param_country = param_country; ############ /*Check if the assigned time was before the starttime or closed time was after the endtime provided*/ ############# Set @checkstart = null; Set @checkend = null; Select CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@starttime), CONCAT(SUBSTRING_INDEX(@closeddate, ' ', 1), ' ',@endtime) into @checkstart, @checkend; if (@assigneddate > @checkstart) then \t\tif (@closeddate<@checkend) then \t\t\tSet @assigneddate = @assigneddate; Set @closeddate = @closeddate; \t\telse \t\t\tSet @assigneddate = @assigneddate; \t\t\tSet @closeddate = @checkend; \t\tend if; else \t\tif (@closeddate<@checkend) then \t\t\tSET @assigneddate = @checkstart; Set @closeddate = @closeddate; \t\telse \t\t\tSET @assigneddate = @checkstart; Set @closeddate = @checkend; \t\tend if; end if; #################### /*After above check, the assigneddate and closeddate variables will be reset in accordance with the checks.*/ #################################### SELECT DATEDIFF(@closeddate, @assigneddate) INTO @fixcount; # check the difference between assigned date and closed date. Set @count = @fixcount; # allocate the difference between closed date and assigned date to a counter If @fixcount > 0 then # true if line 57 resulted in more than 1 then run the while loop on next line \twhile @count>=0 do # run the while loop until the count which is right now difference between closed and assigned becomes zero \t\tselect weekday(@nextdate) into @weekday; # Assign the weekday value to @weekday. Weekday returns o for Monday, 2 for Tuesday ...5 for Saturday and 6 for Sunday /*Check if the date stored in nextdate (which is assigneddate on first run of while loop and closeddate on last run) is a holiday and set the holiday flag*/ Select sum(if(date_format(holiday_date,'%Y-%m-%d') = substring_index(@nextdate,' ',1),1,0)) from holiday_table where Country_codes = 'ALL' or instr(Country_codes,@param_country)>0 into @holidayflag; \t\tif ( @weekday<5 and @holidayflag=0) then #Proceed if the date in nextdate variable is neither weekend nor a holiday \t\t\tif (@count = @fixcount) then #Check if it is first run.ie. if nextdate is assigneddate \t\t\t\tSet @timevar1 = @assigneddate; #assign assigndate to variable timevar1 \t\t\t\tSELECT CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@endtime) INTO @timevar2;#get site closing time on assigned date and store it on to timevar2 \t\t\telseif (@count = 0) then #if the date in nextdate variable is closeddate then do the following otherwise proceed \t\t\t\tSelect concat(substring_index(@closeddate,' ',1),' ',@starttime) into @timevar1; # \t\t\t\tSet @timevar2 = @closeddate; \t\t\telse \t\t\t\tSelect concat(@nextdate,' ',@starttime) into @timevar1; \t\t\t\tSELECT CONCAT(@nextdate, ' ', @endtime) INTO @timevar2; \t\t\tend if; \tSELECT LEAST(Greatest(((TIME_TO_SEC(TIMEDIFF(@timevar2, @timevar1))) / 3600),0),@maxhoursaday) INTO @timecounttemp; \t\t\tSet @timecount = @timecounttemp + @timecount; \t\tend if; Set @timevar1 = @nextdate; SELECT ADDDATE(SUBSTRING_INDEX(@timevar1, ' ', 1),1) INTO @nextdate; \t\tSet @count = @count - 1; \tend while; else #check if the assigned date / closed date is a holiday or weekend select weekday(@assigneddate) into @weekday; # Assign the weekday value to @weekday. Weekday returns o for Monday, 2 for Tuesday ...5 for Saturday and 6 for Sunday Select sum(if(date_format(holiday_date,'%Y-%m-%d') = substring_index(@assigneddate,' ',1),1,0)) from holiday_table where Country_codes = 'ALL' or instr(Country_codes,@param_country)>0 into @holidayflag; #Check if the date stored in assigneddate is a holiday and set the holiday flag if ( @weekday<5 and @holidayflag=0) then #Proceed if the date in assigneddate variable is neither weekend nor a holiday SELECT Least(Greatest(((TIME_TO_SEC(TIMEDIFF(@closeddate, @assigneddate))) / 3600),0),@maxhoursaday) INTO @timecount; else Set @timecount = 0; end if; end if; RETURN @timecount*60; END Calling the function Function will expect 5 parameters and with specific format as explained below: param_country - This is the country code as specified in holiday table assigneddatetime - This must be provided in the format %Y-%m-%d %H-%i-%s. So for our example it will be 2016-06-10 12:00:00 closeddatetime - This must be provided in the format %Y-%m-%d %H-%i-%s. So for our example it will be 2016-06-14 14:00:00 starttime - This must be in the format %H:%i. So for our example it will be 09:00 endtime - This must be in the format %H:%i. So for our example it will be 16:00 The call for this function will be as below: ## To get number of minutes Select `WORKDAY_TIME_DIFF_HOLIDAY_TABLE`('UK','2016-06-10 12:00:00','2016-06-14 14:00:00','09:00','16:00'); ## To get number of hours Select `WORKDAY_TIME_DIFF_HOLIDAY_TABLE`('UK','2016-06-10 12:00:00','2016-06-14 14:00:00','09:00','16:00')/60; ## To get in number of working days Select (`WORKDAY_TIME_DIFF_HOLIDAY_TABLE`('UK','2016-06-10 12:00:00','2016-06-14 14:00:00','09:00','16:00')/60)/(substring_index('16:00',':',1)-substring_index('09:00',':',1)); Complete Flowchart The plantuml code for this can be checked by copying the image link and decoding it on Plantuml Online Server ","categories": [], + "tags": [], + "url": "/mysql-function-to-calculate-elapsed-working-time/", + "teaser": null + },{ + "title": "DDCLIENT set-up on Fedora for Namecheap", + "excerpt":"Configure Namecheap Follow the Namecheap guide here NOTE: For a subdomain “oxygen.copper.com”, just replace @ with “oxygen” Set-up DDCLIENT #Install DDCLIENT on Fedora sudo dnf install ddclient #Edit the configuration file to update IP on your Dynamic DNS host sudo nano /etc/ddclient/ddclient.conf Press Ctrl+W and type the name of host for your Dynamic DNS. Mine is with Namecheap and I needed to configure for the subdomain hence following config reflects how to do it for Namecheap. For other hosts, you will need to refer their documentation. ## NameCheap (namecheap.com) use=web, web=dynamicdns.park-your-domain.com/getip protocol=namecheap, \\ server=dynamicdns.park-your-domain.com, \\ login=copper.com, \\ password=&lt;copy the password from namecheap advanced DN section&gt; \\ oxygen # myhost.namecheap.com The password to be provided above is what you will find on namecheap dashboard (Ref. Screenshot above). Log in to the namecheap account. Go to Advanced DNS Scroll down to Dynamic DNS section Copy the password Paste in ddclient config file Test DDCLIENT Before we schedule ddclient to run at boot, we need to test if it has been configured properly and is able to communicate with Namecheap by sudo ddclient -daemon=0 -debug -verbose -noquiet. If it is configured properly, you will see a message similar to this as part of the final output. SUCCESS: updating oxygen: good: IP address set to 92.117.273.56 If it is not what you see, and more importantly, if you do not see last line as “Success”, then there is something wrong with configuration and you must correct it before proceeding. If this test worked, we are ready to update the DDCLIENT service. Set up DDCLIENT to run at start-up When we install ddclient using dnf, a ddclient.service file is automatically created in the location /etc/systemd/system/ddclient.service with following content. [Unit] Description=A Perl Client Used To Update Dynamic DNS After=syslog.target network.target nss-lookup.target [Service] User=ddclient Group=ddclient Type=forking PIDFile=/var/run/ddclient/ddclient.pid EnvironmentFile=-/etc/sysconfig/ddclient ExecStartPre=/bin/touch /var/cache/ddclient/ddclient.cache ExecStart=/usr/sbin/ddclient $DDCLIENT_OPTIONS [Install] WantedBy=multi-user.target We will enable and start this service by issuing following commands: sudo systemctl enable ddclient.service sudo systemctl start ddclient.service One would think that enabling and starting this service is all you need to do but that is not usually the case. I was getting following error: /bin/touch: cannot touch `/var/cache/ddclient/ddclient.cache’: Permission denied A quick google search establishes that there seems to be some bug in how the ddclient cache file is created and how the permissions are set. After lot of searching, scratching head later, I did the following which fixed the issue. So if sudo systemctl start ddclient results in error, you may need to do the following: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #Go Root su #Create a directory for ddclient mkdir /var/run/ddclient #Chown the various directories for ddclient as user chown ddclient:ddclient /etc/ddclient.conf chown ddclient:ddclient /var/run/ddclient/ #change directory cd /var/run/ddclient #delete ddclient.cache if it exists rm ddclient.cache #change directory cd /etc/sysconfig #delete ddclients.cache rm ddclients.cache #create a blank ddclient.cache nano /var/run/ddclient/ddclient.cache #chown it for ddclient user chown ddclient:ddclient /var/run/ddclient/ddclient.cache #exit root exit #enable and start ddclient service sudo systemctl enable ddclient.service sudo systemctl start ddclient.service Done !!! Known Issue with DDCLIENT There is a known issue and I can confirm that I have seen on my logfile as recently as today. WARNING: cannot connect to dynamicdns.park-your-domain.com:80 socket: IO::Socket::INET: Bad hostname ‘dynamicdns.park-your-domain.com’ It isn’t major but it is there and restarting the service by issuing the command sudo systemctl restart ddclient.service fixes the problem. ","categories": [], + "tags": [], + "url": "/ddclient-on-fedora-2/", + "teaser": null + },{ + "title": "Tomcat 8.5.4 on Fedora behind Nginx", + "excerpt":"Install Oracle Java #install jdk wget --no-cookies --no-check-certificate --header \"Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie\" \"http://download.oracle.com/otn-pub/java/jdk/8u102-b14/jdk-8u102-linux-i586.rpm\" #install jre wget --no-cookies --no-check-certificate --header \"Cookie: gpw_e24=http://www.oracle.com/ oraclelicense=accept-securebackup-cookie\" \"http://download.oracle.com/otn-pub/java/jdk/8u102-b14/jre-8u102-linux-i586.rpm\" #enable firefox plugin alternatives --install /usr/lib/mozilla/plugins/libjavaplugin.so libjavaplugin.so /usr/java/jdk1.8.0_102/jre/lib/i386/libnpjp2.so 20000 URL for JDK and JRE is best obtained directly from oracle website - http://www.oracle.com/technetwork/java/javase/downloads/index.html Download Tomcat su mkdir /opt/tomcat/ && cd /opt/tomcat wget http://mirror.ox.ac.uk/sites/rsync.apache.org/tomcat/tomcat-8/v8.5.4/bin/apache-tomcat-8.5.4.zip wget https://www.apache.org/dist/tomcat/tomcat-8/v8.5.4/bin/apache-tomcat-8.5.4.zip.md5 Check MD5 cat apache-tomcat-8.5.4.zip.md5 md5sum apache-tomcat-8.5.4.zip unzip apache-tomcat-8.5.4.zip Create a TOMCAT Group and User then grant access groupadd tomcat useradd -M -s /bin/nologin -g tomcat -d /opt/tomcat tomcat cd /opt/tomcat chgrp -R tomcat conf chmod g+rwx conf chmod g+r conf/* chown -R tomcat bin/ webapps/ work/ temp/ logs/ Create Service for Tomcat nano /etc/systemd/system/tomcat.service # Systemd unit file for tomcat [Unit] Description=Apache Tomcat Web Application Container After=syslog.target network.target [Service] Type=forking ExecStart=/opt/tomcat/apache-tomcat-8.5.4/bin/startup.sh ExecStop=/opt/tomcat/apache-tomcat-8.5.4/bin/shutdown.sh User=tomcat Group=tomcat [Install] WantedBy=multi-user.target systemctl start tomcat.service systemctl enable tomcat.service Alternative start and stop cd apache-tomcat-8.5.4/bin chmod 700 /opt/tomcat/apache-tomcat-8.5.4/bin/*.sh ln -s /opt/tomcat/apache-tomcat-8.5.4/bin/startup.sh /usr/bin/tomcatup ln -s /opt/tomcat/apache-tomcat-8.5.4/bin/shutdown.sh /usr/bin/tomcatdown tomcatup tomcatdown Change port nano /opt/tomcat/apache-tomcat-8.5.4/conf/server.xml Around line 69 is the connector tag where the port=8080 is specified. For this example lets change it to 8081. After change the connector tag in server.xml will look as below: <Connector port=\"8081\" protocol=\"HTTP/1.1\" connectionTimeout=\"20000\" redirectPort=\"8443\" >; Add tomcat Users Open tomcat-users.xml and add new users before tag nano /opt/tomcat/apache-tomcat-8.5.4/conf/tomcat-users.xml sample user: <role rolename=\"admin-gui\"/><br> <user username=\"admin\" password=\"some admin password\" roles=\"admin-gui\"/><br> <role rolename=\"manager-gui\"/><br> <user username=\"jhondoe\" password=\"some password\" roles=\"manager-gui\"/> Test touch /opt/tomcat/apache-tomcat-8.5.4/webapps/ROOT/testankit.jsp nano /opt/tomcat/apache-tomcat-8.5.4/webapps/ROOT/testankit.jsp #restart tomcat systemctl restart tomcat.service Open the browser and enter http://localhost:8080 (or whatever port you have configured Tomcat on.) Configure Nginx Reverse Proxy for Tomcat Configure the dynamic DNS. Steps will be as per my previous post. For purpose of this step I will be assuming you created a DDNS named tomcat.yoursite.com Update /etc/hosts to include tomcat.yoursite.com sudo nano /etc/hosts #Make an entry in your hosts 127.0.0.1 localhost.localdomain localhost your.seafile.com your.blog.com tomcat.yoursite.com Now create nginx conf file using sudo nano /etc/nginx/conf.d/tomcat.conf as shown below: upstream tomcat { server 127.0.0.1:8081; } server { listen 80; server_name tomcat.yoursite.com; access_log /var/log/nginx/tomcat.access.log; error_log /var/log/nginx/tomcat.error.log; proxy_buffers 16 64k; proxy_buffer_size 128k; location / { proxy_pass http://tomcat; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; proxy_redirect off; 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; } } Finally reload and restart services sudo systemctl daemon-reload sudo systemctl start nginx.service sudo systemctl start tomcat.service ","categories": [], + "tags": [], + "url": "/tomcat-on-fedora-behind-nginx/", + "teaser": null + },{ + "title": "Update Ghost on Fedora", + "excerpt":"While the guidance on Ghost website is very clear, I did get issues that required steps in troubleshooting. Something to do with lodash and npm version 2 stuff (node_modules/knex requires lodash@'^3.7.0') that I read on one of the forums specifically the comment from ErisDS on 13/06. Anyway, reading this I deleted node_modules followed by npm install and it worked. All commands in order as I did are listed below. If my previous posts were used to create the blog nothing here will require sudo or root privileges. As before all this was done on Fedora 24 Linux OS and following commands will need to be changed where it mentions yoursite and username. If the path is different then obviously entire path needs to be replaced. #Copy the entire site as backup. It will be a verbose copy an all access rights will be preserved. cp -avr /var/www/html/yoursite /home/<username>/ #Now in the site directory create a directory ghostlatest mkdir /var/www/html/yoursite/ghostlatest #change directory to ghostlatest cd /var/www/html/yoursite/ghostlatest #now download the latest ghost zip file curl -LOk https://ghost.org/zip/ghost-latest.zip #unzip the downloaded file unzip ghost-latest.zip #Stop your Ghost instance (assuming Ghost is the alias #created as per my previous post else replace with #whatever alias was used with pm2). pm2 stop Ghost #Change directory and delete old folders and files cd /var/www/html/yoursite rm -rf core rm -rf index.js rm -rf *.md rm -rf *.json rm -rf /var/www/html/yoursite/content/themes/casper #Remove node_modules because anyway the lodash issue will hit later on. rm -rf node_modules #Copy from ghost latest to site directory new folders cp -avr /var/www/html/yoursite/ghostlatest/core /var/www/html/yoursite cp -avr /var/www/html/yoursite/ghostlatest/index.js /var/www/html/yoursite cp -avr /var/www/html/yoursite/ghostlatest/*.md /var/www/html/yoursite cp -avr /var/www/html/yoursite/ghostlatest/*.json /var/www/html/yoursite #Optional if you haven't made customisation to default theme. cp -avr /var/www/html/yoursite/ghostlatest/content/themes/casper /var/www/html/yoursite/content/themes #Install Latest Version npm cache clean npm update npm install --production #Start to update dependencies npm start --production #Once above command is complete, stop the server and restart using pm2 Ctrl+C pm2 start Ghost ","categories": [], + "tags": [], + "url": "/update-ghost-on-fedora/", + "teaser": null + },{ + "title": "Fix for PHP Issues after upgrade to Ubuntu 16.04.1 (Xenial)", + "excerpt":"After updating from Ubuntu 14.04, the php and Apache stopped being friends and one of the WordPress site I maintain went all white and admin page was just showing php code. This is apparently because of a known issue in 16.04 with upgrade to php7 as shown on the ubuntu forum here. Using the guidance from this link and with some more of duckduckgo search later, I managed to resolve the problem thus: #1. Install aptitude if it is not already installed using sudo apt-get install aptitude #2. Removed php7 and unwanted php using sudo aptitude purge `dpkg -l | grep php| awk '{print $2}' |tr &quot;\\n&quot; &quot; &quot;` #3. Added old repo using sudo add-apt-repository ppa:ondrej/php #4. Updated repo sudo apt-get update #5. Installed php5.6 sudo apt-get install php5.6 sudo apt-get install php5.6-mbstring php5.6-mcrypt php5.6-mysql php5.6-xml php5.6-curl php5.6-gd php5.6-zip #6. Checked php version sudo php -v #7. Enabled mod_php sudo a2enmod php5 Ignored error message #8. Opened php5.6 conf sudo nano /etc/apache2/mods-enabled/php5.6.conf #9. Commented following lines <IfModule mod_userdir.c> <Directory /home/*/public_html> php_admin_flag engine Off </Directory> </IfModule> #10. Restarted the server sudo service apache2 restart ","categories": [], + "tags": [], + "url": "/fix-for-php-issues-after-upgrade-to-ubuntu-16-04-1-xenial/", + "teaser": null + },{ + "title": "Ethercalc", + "excerpt":"Ethercalc is good tool which can be selfhosted. It is fairly simple to do so. Though it will be available for anyone who has the URL because there is no inbuilt login mechanism. I did not dig into making it accessible with a login interface as I lost interest after I made it work on my server and played around a bit with it but it was simply because I got interested in other things and not because the tool isn’t fascinating enough. I am fairly certain this will not be overly complicated but for a simple selfhosted spreadsheet solution this is definitely worth playing around with. The steps I took are as below: #Ethercalc plays well wth redis as per their documentation. So Install and start &quot;redis&quot; sudo dnf install redis sudo systemctl start redis.service #Test if &quot;redis&quot; is working redis-cli ping #Enable redis to automatically start at the time of system start-up sudo systemctl enable redis.service #check if it runs ethercalc #Press Ctrl+C to exit #To run it forever use pm2 pm2 start ethercalc npm list -g --depth=0 #Change port to whichever port you want Ethercalc to run on by opening app.js #and changing port. nano /home/&lt;yourusername&gt;/.npm-global/lib/node_modules/ethercalc/app.js #change port and save #run with pm2 and alias as Ecalc pm2 start ethercalc --name &quot;Ecalc&quot; #check logs using pm2 pm2 logs Ecalc #Reverse proxy Ethercalc using nginx sudo nano /etc/nginx/conf.d/ecalc.conf sudo systemctl restart nginx.service ","categories": [], + "tags": [], + "url": "/ethercalc/", + "teaser": null + },{ + "title": "Windows 10 - a bucket load of pain", + "excerpt":" As Windows 10 is a commercial offering, one would think it will be working as expected and it does so long as like me one has come to expect pain from Microsoft in general and Windows in particular - because pain is what you get when you use Windows 10. When we work in office environment we are not as heavily exposed to the pain that accompanies Windows usage, mainly because everything is configured and dealt with through a central desktop provisioning team and there is usually a dedicated IT Help-desk in a big corporate. However, using Windows at home is not as smooth a sailing (I am really being liberal with usage of word smooth) and if you don’t trust me just try searching for issues that accompany upgrade to windows 10 and look at the range of issues. Now I, ofcourse, don’t blindly believe these search based opinions and have my own take based on my very own personal trauma. The story goes like so: Why - one may ask - do I even bother with windows at home if it is so painful? This is because as much as my daughter loves the penguin, the windows is what her teacher uses and so she does need to be aware of the necessary evil. So, there is this laptop I have which is exclusively for Windows and is only ever used by my daughter for her schoolwork. It isn’t some cheap stuff but a state of the art touch screen detachable monitor kinda laptop from HP with intel core i5 inside. However, as it is a detachable tablet, it has two HDD - 50 GB for monitor and 500 GB on keyboard that acts as extended memory. Now, it originally came with Windows 8 and then came Windows 10 followed by Windows 10 anniversary edition. When I upgraded from Windows 8 to Windows 10, I suddenly found that the whole of 50 gig was fully utilised making it impossible to do anything at all. Now a quick duckduckgo search later it was as simple as removing the \"previous installs\" which windows itself removes after a month or so they say. In my case the self-removal-a-month-later thing just did not happen and disk clean did not help either so I was basically left with no choice but to reformat the whole damn thing. This as I recollected from my old days with windows is quite a long process with lots of downloads and waits…and boy has that continued to be the same… Microsoft is nothing if not consistent. Finally after several hours of ordeal, the system was refreshed and my storage space reclaimed and a semi-functional laptop was there or thereabouts. I say semi-functional because unlike on Linux not all drivers are there and you have to individually download the drivers from HP website, install and then keep your fingers and toes crossed while you hope it works…finally sound was sorted but WiFi is still flaky. Now compare this to the seamless upgrade from one major version of Fedora 23 to Fedora 24…it did take a while to download and install but the actual effort involved was typing 4 lines on the terminal - that’s it. When I logged into Fedora 24 upgraded machine, everything just worked as before - no driver issues, no WiFi problems, nothing. RANT OVER ","categories": [], + "tags": [], + "url": "/the-pain-of-using-windows/", + "teaser": null + },{ + "title": "Grav - CMS with a difference", + "excerpt":"While I love Ghost as a blogging platform, it is not best placed for things other than blogs - after all that is the basic idea behind creation of this wonderful tool. As I wanted to host a static website using tools that don’t require a database or rely on php, I went searching on interwebs. I came across a lot of options and the most popular one appears to be Jekyll and it’s variants (Nikola and such) but they require a lot of terminal activity which won’t go well for regular end user responsible for maintaining content of the static website in question.So I continued looking and came across this wonderful project called Grav. Grav is super fast, very pretty and extremely easy to deploy and maintain. Additionally, it has very good documentation. The key features that I absolutely loved are as below: Easy installation - * Easier than Ghost / Wordpress IMHO * Good help and documentation Responsive themes and skeletons Built-in Markdown Support and then some Lot of useful plugins Browser based admin page Active development Easy upgrade and back-up My steps for installation: Download a skeleton you fancy Unzip the downloaded file into your server root and move it into a folder named grav: sudo unzip grav-skeleton-appi-1.0.0.zip -d /var/www/html/ cd /var/www/html/ sudo mv grav-skeleton-appi-1.0.0/ grav/ Fix permissions (replace username with your user on the server. This is important to ensure files can be modified both from browser as well as from terminal. So create a bash file by issuing command nano fixpermissions.sh and paste the following code in there. ##!/bin/sh chown &lt;username&gt;:www-data . chown -R &lt;username&gt;:www-data * find . -type f | xargs chmod 664 find ./bin -type f | xargs chmod 775 find . -type d | xargs chmod 775 find . -type d | xargs chmod +s umask 0002 Now make this bash file executable using chmod a+x fixpermissions.sh Finally run the bash file from within the grav directory: cd /var/www/html/grav/ sudo bash /<path_to_fixpermissions.sh>/fixpermissions.sh Open <server-ip-address>/grav from the browser and check your install is working Once it is confirmed to be working, install admin plugin from terminal like so: /bin/gpm install admin Open <server-ip-address>/grav/admin from the browser and create your admin login details Working on Grav is an absolutely pleasing experience and the swift turnaround for a static website is phenomenal. Suffice to say, I hope that this project goes from strength to strength. ","categories": [], + "tags": [], + "url": "/grav-cms-with-a-difference/", + "teaser": null + },{ + "title": "Note 7 to OnePlus 3", + "excerpt":"I was a super excited owner of Note 7 in September this year and then in few days the happiness started disappearing as the news of exploding Note 7 started appearing all over the internet. Then came the notice to exchange the Note 7 which I dutifully did and assured that nothing will now go wrong I started enjoying that gem of a device. Alas, it was not to be and the device was recalled for good. Being a user of Note devices for so long and yet unable to continue with my old Note 3, I decided to go for some interim device - you know until Samsung decides to get the act right and bring the next Note device to the market. After going through a number of possible options ranging from Note 7’s close cousin Galaxy S7 edge+ all the way to newly released Pixel, I actually settled for OnePlus 3. It was not a gamble actually as my wife is already using it and is quite pleased with it’s performance and the price is just right for this to qualify as the interim phone. Anyway, so I ordered and waited because there was a good 4 week wait period. The ordering and delivery all went as planned while in the meantime I was back with my trusty Note 3. OnePlus 3 arrived and I set it up to my taste and my oh my what an experience. Apart from Stylus it was lacking nothing and is very smooth, very light and camera quality is very good as well - not as good as Note 7 but definitely better than Note 3. I have now been with this device for just over a week and although I miss the S-Pen, I am very happy with the device in general and it’s battery life in particular. I have listed below the things that I like and the things I hope it had: Features I Loved: Battery life Superfast Charging with Dash Charger Display and inbuilt Night mode Ringer Switch Inbuilt Colour control for LED Notification Smooth minimal interface No bloatware Fingerprint Scanner doubles as Home button Gesture support for flash light Ability to swap Capacitive button functionality Dual Sim Card capability - Allows me to carry one phone for both work and personal numbers. Sure all of the above can be pretty similar on all high end handsets but what makes it unique for OnePlus 3 is the price point of £329. That price is unbeatable and the phone, in my humble opinion, must be the first choice for anyone who isn’t fussed about a stylus. There can be a comparison between camera results where Samsung outperforms in my experience but only slightly and still the photo quality from OnePlus 3 is definitely very good second only to Samsung and no other. Wishlist Functional Stylus (Frankly no other manufacturer is tapping on this market. God knows why?) Note 7 had a good integration of Fingerprint Scanner with browser to login to various sites. I miss that on OnePlus 3. External Memory Card slot. Here’s hoping that OnePlus 3 team will listen to my wishlist :). ","categories": [], + "tags": [], + "url": "/note-7-to-oneplus-3/", + "teaser": null + },{ + "title": "Swap File to create extra memory", + "excerpt":"While renewing my LetsEncrypt certificate, I found myself in a strange situation where the certbot won’t run asking me to update pip and then each time I tried updating pip it failed with the error error: command 'x86_64-linux-gnu-gcc' failed with exit status 4. It turns out that this happens due to low memory and with my digitalocean droplet being the cheapest one this was bound to happen sooner rather than later. Fortunately there is a way around it as explained below. Use of following commands will ensure that the swap file is created which in turn will help avoid the \"error: command ‘x86_64-linux-gnu-gcc’ failed with exit status 4\". Following commands will create a swap file: sudo dd if=/dev/zero of=/swapfile1 bs=1024 count=524288 sudo mkswap /swapfile1 sudo chown root:root /swapfile1 sudo chmod 0600 /swapfile1 sudo swapon /swapfile1 The swap file will now be activated but will be gone after the reboot. It can be reactivated using the last command (I hope so know as I have now tried it). Anyway, after creating the swapfile, you will be able to upgrade pip without the aforementioned error. :) Update: 02/03/2017 I ran into memory issues yet again and I thought instead of increasing the memory for swapfile1, what if I can create another swapfile. I tried this and it works. Infact I felt quite nice uncovering a concept of multiple swapfiles purely based on my whim ;). All I really had to do was repeat above code replacing swapfile1 with swapfile2 and I had two swapfiles working together increasing available memory for my server. sudo dd if=/dev/zero of=/swapfile2 bs=1024 count=524288 sudo mkswap /swapfile2 sudo chown root:root /swapfile2 sudo chmod 0600 /swapfile2 sudo swapon /swapfile2 Thing is after it worked I was a bit intrigued by the concept and read a bit more on swapon / swapoff and few useful commands are listed below: # To enable all swapfiles swapon -a # To disable all swapfiles swapoff -a # To see all available swapfiles swapon -s # To enable a particular swapfile swapon &lt;filename&gt; ","categories": [], + "tags": [], + "url": "/swap-file-to-create-extra-memory/", + "teaser": null + },{ + "title": "DD-WRT firmware on TP-LINK TL-WR841N v11", + "excerpt":"I have been with PlusNet for over two years now and am a happy camper as far as fiber optic broadband is concerned but as I am no longer on a broadband contract with PlusNet and had no intention of going on one, so the only way I could get a change to my ageing router was by purchasing a new one. Hence I started reading about my options and soon enough realised that an old router can be given new lease of life using DD-WRT. Equally soon-ish I also realised that the router from PlusNet - TG582n - is quite rubbish and does not play nice with any of the open source firmwares. So I figured that if I have to just play around a bit, I might as well start with something cheaper and cheaper is what I found in the TP-Link router TL-WR841n at just £16.00. You can’t get any cheaper than that in my opinion. OK, so now that we have established that I am cheap and my new router is cheap, let’s move on to interesting stuff. I had read that TP-Link router and specifically TL-WR841n plays nicely with DD-WRT but it was only after I had my new toy did I realise that these things also come in hardware version and while interwebs is filled with instructions on installing DD-WRT for upto v9, when it comes to v11 in Europe, it can be a bit tricky to proceed. There are some instructions in forums[1] but nothing that walks one end to end hence this post. Flash router with DD-WRT firmware There are two ways to ensure that the DD-WRT firmware gets flashed on the router: Apply Unlocked stock firmware - This is the one I used and to use this I downloaded the modified firmware from NeDark that he has provided in a post on the OpenWRT forum here Using TFTP server - This is considered to be a safe approach because you do not have to use any modded version of stock firmware to apply it. However, if like me you are anyway going to flash it with DD-WRT, I feel it's a bit of pain that can be avoided as it involves setting up TFTP server and making your router to connect to this server can take some time and effort. It is explained here. Right, so assuming you want to go with the first and easier approach, first you need to download the modded firmware from NeDark. He has uploaded it on his dropbox link and I have also uploaded a copy of this firmware here. I recommend that you download all the three files from my folder on Mediafire[2] using the link below but if you would much rather download directly from DD-WRT, then the links for rest of the two files that I used are also in references [3] and [4] below. Assuming that now you have all the three files safely downloaded to you computer we just follow the simple steps below: Connect the power supply to the TP-Link router and switch it on. Connect the ethernet cable in one of the yellow LAN slots of the router. Switch-off the wifi on your computer and connect the other end of the ethernet cable to the ethernet port of your computer. Once the ethernet connection on your laptop is established, open a browser and type 192.168.0.1 and press enter. You will be presented with TP-LINK admin interface. Login using the credentials username: admin; password: admin. On left hand navigation locate and click on System Tools and then on expanded menu click on Firmware Upgrade Now click on Browse button and select the file from NeDARK - wr841n(EU)_v11_150616.bin from your downloaded folder. Click on Upgrade button. It will take roughly 30 to 40 seconds and router will reboot. Refresh the browser screen and you will once again be presented with TP-LINK admin interface. Once again login using the credentials username: admin; password: admin. On left hand navigation locate and click on System Tools and then on expanded menu click on Firmware Upgrade Now click on Browse button and select the file factory-to-ddwrt.bin. Click on Upgrade button. It will take roughly 30 to 40 seconds and router will reboot. Disconnect the ethernet cable from Laptop and reboot the laptop - This isn't always required but just to be safe. Reconnect the ethernet cable to the laptop. Make sure that wifi is switched off on the laptop. Once the ethernet connection on your laptop is established, open a browser and type 192.168.1.1 and press enter. <-- Notice the different IP than what was used in step 4. If all has gone well until now you will be greeted with DD-WRT login interface and will actually be asked to change the password. After providing the password, navigate to tab Administration and then sub-tab named Firmware upgrade. Click on Browse and this time select tl-wr841nd-webflash.bin and Click upgrade. This will take 40 seconds or so and your router has now been liberated. This is it for flashing TP-LINK TL-WR841N v11 router with DD-WRT. I will be writing more about how to configure DD-WRT to work with Plusnet fiber optic broadband and to play nice with NEST so stay tuned if this interests you. I made fiber optic broadband work with no fuss but with NEST there were some issues basically down to auto setting changing to channel 13. Making NEST play nice with DD-WRT The issue was that NEST would suddenly drop internet connection and then not identify the SSID for my router. The SSID just won’t appear in the list of available wireless networks. NEST apparently does not like channel 13, possibly even Channel 11 and 12. Changing the channel to any of the single digit (1 - 9) works well. In addition giving a static IP for NEST MAC seems to have resolved any network drops whatsoever. Under Wireless Setting, I also changed the Beacon Interval to 211. Under Administration tab -> Management -> IP Filter Settings, I changed the TCP timeout to 1800 and UDP timeout to 3600. Configure router for PlusNet Fiber Optic Broadband For configuring PlusNet fiber optic broadband, the settings I used are as below: Setup -> Basic Setup Setup -> DDNS Wireless -> Basic Settings Wireless -> Wireless Security Administration -> Management Administration -> Commands Add the following in the startup commands # Fix lan port communication 841 v7, v9, v11 swconfig dev eth0 set enable_vlan 1 swconfig dev eth0 set apply as shown below: Above basic setting ensures that PlusNet fiber optic broadband works perfectly fine. REFERENCES: https://www.quora.com/Is-the-TP-Link-TL-WR841n-v11-router-supported-by-DD-WRT ↩︎ https://www.mediafire.com/folder/q56dcdecfh3v1/Router_Firmware ↩︎ http://download1.dd-wrt.com/dd-wrtv2/downloads/betas/2016/12-15-2016-r30949/tplink_tl-wr841ndv11/factory-to-ddwrt.bin ↩︎ http://download1.dd-wrt.com/dd-wrtv2/downloads/betas/2016/12-15-2016-r30949/tplink_tl-wr841ndv11/tl-wr841nd-webflash.bin ↩︎ ","categories": [], + "tags": [], + "url": "/dd-wrt-firmware-on-tp-link-tl-wr841n-v11/", + "teaser": null + },{ + "title": "Markdown and Gantt Charts", + "excerpt":"For a fairly long time, I have been looking for a simple markdown type of solution to be able to quickly draw Gantt charts but never came across what one would call a quick option. It has always been an involved process. To my simplistic mind, a simple solution would just be an option where I can type the action, a start date and an end date for the action line after line and it get’s displayed in the Gantt chart. The latest edition of Linux Format (Issue 221, Page 52) had mermaid as one of the hotpicks and highlighted the availability of Gantt charts which piqued my interest, I tried the solution and it is indeed what I was looking for. There are a few rough edges but it is a working solution and in normal circumstances will save considerable time and hence will become my tool of choice. There are few ways of using it and because it has been made to work with MarkDown syntax, it can work on a number of tools too. I found CuteMarkEd to be the easiest one to use but the various ways to use it are listed below and their usage explained in following sections: CuteMarkEd Install Mermaid CLI Install Markdown Plus Mermaid Live Editor Install CuteMarkEd CuteMarkEd is not available in debian repositories nor does it have a binary for debian. Although it does have an rpm binary. As I have been playing with Debian / Ubuntu based Elementary OS lately, instead of my regular Fedora install, I figured might as well compile from source code. Steps are as below and are also available on the github page for CuteMarkEd: #Prepare the environment for building from source code. sudo apt-get install build-essential checkinstall git #Get the code from github git clone --recursive https://github.com/cloose/CuteMarkEd.git #Install dependencies for CuteMarkEd sudo apt-get install libqt5webkit5-dev qttools5-dev-tools qt5-default discount libmarkdown2-dev libhunspell-dev #Compile and install CuteMarkEd qmake CuteMarkEd.pro cd CuteMarkEd/ qmake CuteMarkEd.pro make echo 'A Qt-based Markdown editor with live HTML preview and syntax highlighting of markdown document.' > description-pak sudo checkinstall --requires 'libqt5webkit5, libmarkdown2, libhunspell-1.3-0, discount' sudo ln -s /usr/lib/x86_64-linux-gnu/qt5/bin/cutemarked /usr/local/bin/ sudo mkdir -p /usr/local/share/icons sudo cp app/icons/scalable/cutemarked.svg /usr/local/share/icons/cutemarked.svg sudo apt-get install fcitx-libs-qt5 Now there is a bug in the tool which makes the Gantt charts appear as monochrome but fear not for there is a workaround for it.Once installed, open CuteMarkEd and add a snippet in the tool to ensure Gantt charts display properly. Steps below: Enable diagram support by clicking Extras->Options->Diagram Support Add the snippet for quick creation by clicking Extras->Options->Snippets->Add Then type ~~~gantt and in description something to the effect of Adds the codeblock for mermaid with the necessary javascript Now in the snippet box add the following code. To invoke the snippet simply type in editor ~~~gantt followed by pressing the Ctrl+Space keys. ~~~mermaid gantt dateFormat YYYY-MM-DD title <Name of the project> %%\t<Name of Activity>\t\t: crit if critical else empty,done, active or empty, reference name or empty, Start Date or dependency, End Date or Duration section Phase 1 Name Activity 1\t\t\t:\t done, des1, 2017-01-06, 2017-01-08 Activity 2 \t:\t active, des2, 2017-01-09, 2017-01-12 Activity 3 \t: \t des3, 2017-01-12, 5d Activity 4 \t: \t des4, after des3, 5d section Phase 2 Name Activity 5 \t\t\t: crit, done,\t\t2017-01-06, 24h Activity 6\t\t : crit, done, \t\tafter des1, 2d Activity 7\t\t : crit, active, \t\t 3d Activity 8\t\t\t: crit,\t\t\t \t 5d Activity 9\t\t\t:\t\t\t \t 2d Activity 10\t\t\t: \t\t\t \t 1d section Phase 3 Name Activity 11\t\t\t: \tactive, a1,\tafter des1, 3d Activity 12\t\t\t:\t\t\tafter a1 , 20h Activity 13\t\t\t:\t\t doc1, \tafter a1 , 48h section Phase 4 Name Activity 12\t\t\t:\t\t\tafter doc1, 3d Activity 15\t\t\t: \t \t\t\t 20h Activity 16\t\t\t: \t\t\t \t 48h ~~~ <script> mermaid.ganttConfig = { titleTopMargin:25, barHeight:20, barGap:4, topPadding:50, sidePadding:100, gridLineStartPadding:35, fontSize:11, numberSectionStyles:3, axisFormatter: [ // Within a day ['%I:%M', function (d) { return d.getHours(); }], // Monday a week ['w. %U', function (d) { return d.getDay() == 1; }], // Day within a week (not monday) ['%a %d', function (d) { return d.getDay() &amp;&amp; d.getDate() != 1; }], // within a month ['%b %d', function (d) { return d.getDate() != 1; }], // Month ['%m-%y', function (d) { return d.getMonth(); }] ] }; </script> This will then show in the preview pane following Gantt Chart and we can edit, add and modify the data as per the requirements: TIP: You can make as many snippets as you want and for the simple solution that I mentioned right at the top, I made one snippet called dategantt replacing the code between line 3 to line 29 of the snippet above \tdateFormat DD/MM/YY \ttitle Project Name Section Pre-condition Activity 1\t\t\t:21/12/16, 22/12/16 Activity 2 \t\t:21/12/16, 22/12/16 Activity 3 \t:16/01/17, 02/02/17 Activity 4 \t\t:01/02/17, 02/02/17 Section Kick-off Activity 5\t:01/02/17, 03/02/17 Activity 6\t:01/02/17, 03/02/17 Project Initiated\t\t:01/02/17, 03/02/17 Section Tech Design Technical Design\t\t:crit, active, T1, 06/02/17, 21/03/17 Section Delivery\t Activity 7\t\t:06/02/17, 10/02/17 Activity 8\t\t\t:10/02/17, 14/02/17 Order\t\t\t\t:15/02/17, 14/03/17 Deployment\t\t\t:crit, 15/03/17, 21/03/17 Activity 9\t\t\t:crit,T2, after T1, 23/03/17 Project Close-Down\t\t:after T2, 24/03/17 ``` Once done, just type dategantt followed by ctrl+space and you will have the framework to enter activities, start dates and end dates. Gantt Chart will be ready just like the one as cover image of this post right at the top !!! Install Mermaid CLI One of the drawbacks of the CuteMarkEd is that the pdf export there just does not work and you will need to rely on a screenshot. Now if the Gantt chart is really huge a screen shot wont cut it and mermaid does offer the ability to generate svg file for your effort. It is ofcourse a command line tool but is actually fairly easy to use once installed. #check if nodejs is installed. If command below results in nothing then you need to install nodejs nodejs -v # Instal nodejs sudo apt install nodejs #On Ubuntu there is some link issue where node is #referenced as nodejs which results in issues #while installing npm modules. This can be avoided #by creating a symlink using following command. sudo ln -s /usr/bin/nodejs /usr/bin/node #Install npm sudo apt install npm #In order to install npm modules without sudo, #As explained in one of [my previous posts](http://mgw.dumatics.com/ghost-on-fedora-24/#step3installnpmmodules), #it requires fixing the permissions using following steps. #This needs to be done only once so if you have #done this in past, then there is no need to repeat. mkdir ~/.npm-global npm config set prefix '~/.npm-global' nano ~/.profile source ~/.profile #Install Mermaid and phantomjs on which mermaid depends. npm install -g phantomjs npm install -g mermaid #Check if mermaid is installed by issuing the command mermaid --h #Generate a png using following command mermaid -p path/to/markdown file with .md or .mmd extension. Install Markdown-plus MarkdownPlus is a good markdown editor and can be used on their hosted solution or for offline use it can be installed as a local copy and accessed from the browser by pointing the browser to the index.html. The bug from CuteMarkEd is not present here. To access offline, you can clone it from github using following command. git clone --recursive https://github.com/tylingsoft/markdown-plus.git Then open the folder markdown-plus and right click on the index.html file and open it using your browser of choice. Alternatively, in the browser enter file:///home/path/to/markdown-plus//index.html. Mermaid Live Editor While Markdown Plus is quite a versatile browser based editor, I did not like the fact that there is no easy way to save the file you have created and all I could work out was that you will have to copy paste your text and save it using another text editor. I felt, for this reason that the Mermaid Live editor was better compared to this option and the live editor is quite basic to be honest and can’t be used offline. ","categories": [], + "tags": [], + "url": "/markdown-and-gantt-charts/", + "teaser": null + },{ + "title": "Rstudio Server Setup with SSL behind Apache proxy server", + "excerpt":"Install R using following commands: sudo apt-get install r-base libapparmor1 gdebi-core # Check that R is installed R #quit R q() Install Rstudio IDE server cd Downloads/ wget https://download2.rstudio.org/rstudio-server-1.0.136-amd64.deb sudo gdebi rstudio-server-1.0.136-amd64.deb At this point if all goes well you can check the status of rstudio server by issuing the command: sudo systemctl status rstudio-server.service The server is started automatically at port 8787 and can be accessed using <ip_address:8787> in browser of your choice, provided all firewall settings have been taken care of. However, when you open the Rstudio server you will be presented with a logon screen and while you can access this using the users for the machine it is hosted on, it will be prudent to create a lower privilege user as explained in next section. Add User to access the RStudio sudo adduser rstudio Set up SSL and reverse proxy for R-Studio Server Now important thing to note is that community version of Rstudio server does not come with SSL enabled but just to run it on a secure socket layer you don’t necessarily need the pro version. By following the steps below, your communication with the server will be on SSL. However to achieve the objective we need to accomplish following steps: Enable modules on Apache to help set up proxy Configure a proxy to control access to RStudio Server Use LetsEncrypt to enable SSL Restrict access to Rstudio server only through proxy Restart both Rstudio and Apache servers Step 1: Enable modules on Apache to help set up proxy There is guidance on how to do this on Rstudio Support. However, there was a bit of hair pulling and head scratching involved to get all the steps above work together so stick with me but keep that link in back pocket for variations or when you are stuck. With head scratching and hair pulling I mean that I encountered errors such as these - AH01102 error reading status line from remote server, Rstudio Proxy redirect changing the URL to localhost and many others which can be avoided by following steps as explained below. Anyway so we need to enable mod_proxy and mod_proxy_wstunnel modules. As Apache is already installed and mod_proxy already enabled I did not have to install the module itself, but if it needs to be done the commands are: sudo apt-get install libapache2-mod-proxy-html sudo apt-get install libxml2-dev Issuing the following commands should enable the relevant modules: sudo a2enmod proxy sudo a2enmod proxy_http sudo a2enmod proxy_wstunnel Step 2: Configure a proxy to control access to RStudio Server # Change directory to sites-available cd /etc/apache2/sites-available # create a rstudio conf file sudo nano rstudio.conf Paste the following in the conf file but make sure to change details relevant to your set-up for each entry (line numbers 2, 3, 4, 15 and 16 below): <VirtualHost *:80>; ServerAdmin user@yoursite.com ServerName yoursite.com ServerAlias whatever.yoursite.com #Specify path for Logs ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined RewriteEngine on #Rewrite the url supplied to ensure https is applied RewriteCond %{SERVER_NAME} =yoursite.com [OR] RewriteCond %{SERVER_NAME} =whatever.yoursite.com RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent] # Following lines should open rstudio directly from the url RewriteCond %{HTTP:Upgrade} =websocket RewriteRule /(.*) ws://localhost:8787/$1 [P,L] RewriteCond %{HTTP:Upgrade} !=websocket RewriteRule /(.*) http://localhost:8787/$1 [P,L] ProxyPass / http://localhost:8787/ ProxyPassReverse / http://localhost:8787/ </VirtualHost>; # vim: syntax=apache ts=4 sw=4 sts=4 sr noet Press Ctrl+x and save the file. TIP: If you just want reverse proxy and no SSL, you can just comment out line 15, 16 and 17 in above conf file and you are all set. If you do want to enable SSL, enabling the site with commands below won’t probably work just yet and subsequent steps will need to be completed. Now enable the new site by issuing the commands: sudo a2ensite rstudio.conf sudo service apache2 restart Step 3: Use LetsEncrypt to enable SSL Follow the instructions here for specific usecase but one way or the other using Certbot you will be able to obtain the LetsEncrypt SSL certificate and enable it on your server. Once certbot has completed doing it’s thing you would find an additional conf file in /etc/apache2/sites-available named rstudio-le-ssl.conf. It will be pretty much same content as in rstudio.conf with very minor changes. The first line will be listening on 443 instead of 80 and the ssl certificates will be included. Normally you would not need to tweak anything in the resultant file but just for reference the contenst of this file will look as below: <IfModule mod_ssl.c> <VirtualHost *:443> ServerAdmin user@yoursite.com ServerName yoursite.com ServerAlias whatever.yoursite.com ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined RewriteEngine on # Following lines should open rstudio directly from the url RewriteCond %{HTTP:Upgrade} =websocket RewriteRule /(.*) ws://localhost:8787/$1 [P,L] RewriteCond %{HTTP:Upgrade} !=websocket RewriteRule /(.*) http://localhost:8787/$1 [P,L] ProxyPass / http://localhost:8787/ ProxyPassReverse / http://localhost:8787/ SSLCertificateFile /etc/letsencrypt/live/whatever.yoursite.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/whatever.yoursite.com/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf </VirtualHost> # vim: syntax=apache ts=4 sw=4 sts=4 sr noet </IfModule> Restrict access to Rstudio server only through proxy Finally, we want to ensure that access to the Rstudio server is only through the proxy we configured and to do that we just need to specify this in the rstudio server configuration the attribute that tells it to only serve requests from localhost. sudo nano /etc/rstudio/rserver.conf Now on the opened file type www-address=127.0.0.1 and press Ctrl+x and save the file. Restart both Rstudio and Apache servers Finally issue the following commands to restart both the servers: sudo systemctl restart rstudio-server.service sudo service apache2 restart This is it. Now your new Rstudio server is ready to be used through secure socket layer. ","categories": [], + "tags": [], + "url": "/rstudio-server-setup-with-ssl-behind-apache-proxy-server/", + "teaser": null + },{ + "title": "Ghost V1.0 Upgrade on Apache stack, related quirks and fixes", + "excerpt":"Right then, the Ghost V1.0 was out a while back and they made Ghost 0.11.x an LTS so I was not in any rush to upgrade too. I have not had much time to sort this out for a while and two days back when I finally came around to check how to upgrade, my first moment of concern was that officially supported stack is for NGINX. I have moved my blog to the Apache Stack on DigitalOcean and while on my sandbox environment I still have NGINX, that is not a place I want to host my blog from. Anyhoo, I realised soon enough that while not officially supported it s easy to bypass the restrictions so I went ahead. The upgrade itself couldn’t have been simpler considering the major version bump. The answer to the question \"Was it worth it?\" is something we will have to wait and see although I am liking what I see except for the initial hiccups. EDITED AFTER THE POST: Boy oh boy - just after I finished this post I saw the latest version of Ghost V1.12 is out and it was such a painless process compared to past. Just a simple command ‘ghost update’ and job done. That itself makes this whole pain kind of worth it. OK so the steps I took are as below: Backup Download and Install Setup Wizard Configure Apache Restore Tweak Backup We will take the back-up from front end for all the posts and we will also backup on the server the entire directory where old instance of the blog is residing. To take backup of all the content and download it in a json file, open your ghost site on a browser, navidate to \"Settings\" and then click on \"Export\". Next for the backup of folder on the server itself. To do this issue the following commands on the terminal. #Updateand upgrade the OS repo sudo apt-get update sudo apt-get upgrade #Stop the ghost server pm2 stop Ghost &quot;assuming Ghost is the pm2 id for the site&quot; # Change directory to web-server root cd /var/www/html/&quot;path to your ghost directory say 'ghost' &quot; # Create a new directory for backup sudo mkdir old_ghost_bkp sudo mv ghost old_ghost_bkp # Recreate the ghost directory sudo mkdir ghost cd ghost # Give right privileges to the new directory sudo chown -R &lt;your username&gt;:www-data . Download and Install As we are already in the right directory lets get on with installing the latest version of Ghost using npm. sudo npm install -g ghost-cli #Make sure you are in the directory where new ghost is to be installed. #If you have followed all commands so far, you will already be in #required the directory ghost install It is at this point that you will have to deviate from official guide if you have Apache instead of NGINX. You will be prompted by the installer that it could not find NGINX and do you still want to continue. Default is \"No\" so make sure you enter \"Y\" and then press enter. For me rest of the install went smoothly. Setup Wizard Immediately after the install is complete, you will be presented with following questions: Please note that if you have configured SSL using LetsEncrypt as explained in previous posts on this blog then even if you are using https, the answer to blog url must be the with http and not https. For example: I gave http://mgw.dumatics.com and not https://mgw.dumatics.com </code></pre> 1 2 3 4 5 6 7 8 9 10 Enter your blog url: http://your.blog.url Enter your MySQL Hostname: localhost Enter your MySQL Username: root Enter your MySQL Password: &quot;your mysql root password&quot; Enter your Ghost database Name: \"a relevant name\" # for security reasons you may want to keep it different from your blog name\"; Do you wish to set up Nginx: no Do you wish to set uo Ghost MySQL User: yes Do you wish to set up Systemd: yes Do you want to start Ghost: yes Please do note that the response on line 6 above to \"Setup Nginx\" must be \"no\" After the questions are complete you will get a notification You can access you blog at http://your.blog.url. At this point, it is best to see which port is configured by ghost CLI for this installation. you can do so by checking the configuration file like so: nano config.production.json You can change the port if you like but if it is different than the port you originally had for old version of ghost you can either change it here or you need to change Apache conf file in next step. If you do decide to change the port here, then there should be no need to carry out the next step - Configure Apache. Configure Apache Assuming that the port in Ghost config file was 1234, there will be some changes that you will need to make in Apache conf files like so: cd /etc/apache2/sites-available/ sudo nano ghost.conf Now change the port on ProxyPass and ProxyPassReverse to be same as what is in the config.production.json file and save it by pressing Ctrl+x and y.- so for this example it will be changed to 1234 and change will look as below: ProxyRequests off ProxyPass / http://localhost:1234/ ProxyPassReverse / http:/localhost:1234/ Now open the ssl config file for the site using commands below and make the same changes as above. sudo nano ghost-le-ssl.conf TIP: If done using LetsEncrypt, it will be named something like ghost-le-ssl.conf. Once the changes are saved, disable and enable the configurations using following commands: sudo a2dissite ghost.conf sudo a2dissite ghost-le-ssl.conf sudo a2ensite ghost.conf sudo a2ensite ghost-le-ssl #Restart the server sudo service apache2 restart Now if you enter you blog url in a browser, you should be presented with vanilla Ghost site. If not, something in server set-up has not worked and you will need to troubleshoot it and fix - luckily for me all worked like a charm. Restore Right, so you are now on the browser looking at the Vanilla Ghost install. First thing you need to do now is create the user with same credentials you had on your old version of ghost. To do this you will first need to enter the admin url for ghost and follow the steps to create your user. Once you are into the admin interface, navigate to \"Settings\" -> \"Labs\" and click on \"Choose File\" button, select the json backup that was exported from your old version of the blog and then click on \"Import\" button. Now to restore the images from your old blog on the server issue following commands: cd /var/www/html/ghost/content sudo rm images sudo cp /var/www/html/old_ghost_bkp/ghost/content/images /var/www/html/ghost/content/ ### Make the new image directory is writable or image uploads will fail sudo chown -R ghost:ghost images/ ### Restart Ghost cd .. ghost restart The ghost CLI commands like stop, start and restart will require you to be in the directory where ghost is installed. While start and stop commands of ghost specifically ask for sudo credentials, restart command just keeps rotating and hence it is better to issue a command with sudo before you issue ghost restart. This is it. Your old blog is now fully restored. Tweak This section is a bit of a pain because there are quite a few things that break with this version. So if you have heavily used html, you will painstakingly need to go through posts and add a new line between markdown and html content for it to be parsed properly or else it will display quite wiered outputs on your blog. If you have used code blocks with syntax highlighting, another change is with older version you would have given three backticks followed by language-sql but now you just need to give three backticks followed by sql. If you have used line numbering using prism.js, it just wont work and you will need to apply changes to your theme the way I did. Without going too much in detail on that, you can get the copy of prism.js, prism-custom-line-number.js, prism.css, prism-line-number.css using the github links for my theme and place them in assets directory of your theme. Then make sure you include them in relevant files where your theme calls the javascripts. Once done, issue the command ghost restart and things should look pretty again. Happy Migrating !!! ","categories": [], + "tags": [], + "url": "/ghost-v1-0-upgrade-related-quirks-and-fixes/", + "teaser": null + },{ + "title": "Unprotect Sheets in Libre Calc, Excel", + "excerpt":"A friend of mine today had an issue. He had created a template for some really complex calculations and to ensure he does not mess up with the forumlae by mistake he had password protected sheets and cells. He had done this back in 2012 and now he wanted change something in there but he had forgotten the password so he asked me if I can help. Now I do not know much about how it could be done on windows or on excel but I knew a small trick on LibreOffice so I asked him to send me that excel file by email. I then took following steps: Opened the excel file Locked.xls in LibreCalc. Saved it as an Locked.ods file. From file browser, right click on newly saved Locked.ods file -> Open With -> Archive Manager as shown below. Now open the content.xml Find table:protected="true" and Replace All with table:protected="false" Save the xml file. Now open the Locked.ods in LibreCalc and save it as Unlocked.xlsx This should do the trick and unlock all password protected sheets and cells to be freely modified. ","categories": [], + "tags": [], + "url": "/unprotect-sheets-in-libre-calc-excel/", + "teaser": null + },{ + "title": "Ghost Upgrade errors and fixes (1.19.x)", + "excerpt":"I have found the recent ghost upgrades quite painless but there have been few hiccups for last two times so I kept a record of what helped and it is as listed below: General Update should be as simple as: cd /var/www/html/ghost/ ghost update If after this step, there are any errors or an indication to update ghost-cli, following command should be used. sudo npm install -g ghost-cli After this if there are issues accessing the blog over internet, we may need to do a bit of checks. Logical sequence is to first check that access for all folders is right and proper. If it needs to be updated, command to be used is: cd ghost sudo chown -R &lt;your username&gt;:www-data . cd content/ sudo chown -R ghost:ghost images/ If there are any errors when starting ghost, following command is indicated by the ghost-cli and it does help. ghost setup linux-user systemd If still accessing the blog is an issue following command will list the ghost log file: ghost log If there are database migration errors on log file, following commands may help: sudo npm install -g knex-migrator ghost setup migrate ghost setup migrate does work very well and must be remembered. If you have digitalocean setup such as mine, memory can be an issue you may want to restart the virtual machine and reload the swap files. sudo reboot now sudo swapon /swapfile1 sudo swapon /swapfile2 If update if failing with the message not enough memory try running the following: ghost update --no-check-mem If there are still issues accessing the blog with error 503, check the apache logs: sudo nano /var/log/apache2/error.log If issue is with accessing the upstream ghost server. Try changing the port on ghost config and updating the apache conf files. ##Change Directory to ghost install cd /var/www/html/ghost ##Stop the ghost server ghost stop ##Change port to another number nano config.production.json ##Change directory apache2 server cd /etc/apache2/sites-available/ ##Open the ghost.conf file and change the localhost port to same number that was changed in config.production.json sudo nano ghost.conf ##Open the ghost-le-ssl.conf file and change the localhost port to same number that was changed in config.production.json sudo nano ghost-le-ssl.conf ##Disable and enable the conf files on apache. sudo a2dissite ghost.conf ghost-le-ssl.conf sudo a2ensite ghost-le-ssl.conf ghost.conf sudo service apache2 reload ##Finally change directory to your ghost install and start ghost server. cd /var/www/html/ghost ghost start ","categories": [], + "tags": [], + "url": "/ghost-upgrade-errors-and-fixes-1-19-x/", + "teaser": null + },{ + "title": "MySQL Function to calculate closing WorkDay date given elapsed working time", + "excerpt":"On my post MySQL function to calculate elapsed working time I was asked in comments if the assumptions can be reversed such that given start date, starting time and closing time of site and the elapsed working hours, function should return the closed date. I was convinced that it will be possible to achieve this with minor tweaks to original logic and so I only concentrated on original code and how to tweak it to achieve the result. It is very likely that there might exist a more elegant solution but frankly I did not have a usecase I did find a usecase afterall - Clue was in redefining the problem description for this. Presented below is the function that with my minimal testing seems to give correct results. Feel free to try it and as always any feedback is welcome. :) Problem Description: Find out the WorkDay date by when a ticket must be closed given: Country of site for which incident was logged. Time and Date when Incident was logged. Elapsed working hours in decimal. (Ten and a half hours as 10.5 and so on) Opening time of the site for which the incident was logged. Closing Time of the site for which incident was logged. SLA Type - Is the output date to be based purely on number of hours or based on SLA in days (eg: Next Business Day). Assumption: It is assumed that opening and closing times are same on all working days and that all the sites are closed on holidays and weekends. Example of problem Let’s say an incident was logged on “Friday 23rd Feb 2018 at 15:00” for a site in the “UK” which opens between 08:00 to 16:00. Now if the SLA is purely based on hours i.e. Tickets must be closed withing 16 hours then for the above incident function should calculate the closing date as: 2018-02-27 15:00 = 1 hour on Friday, then skip Saturday and Sunday, 8 hours on Monday and 7 hours on Tuesday the 27th of Feb 2018. However, if the SLA is based on number of days, then 16 hours will translate to an SLA of next business day hence in this case function should return 2018-02-26 16:00 which is a Monday end of day and the next business day for this incident. The use case really is in creating a report that shows the target closed date based on SLA. Specifically Next Business Day SLA, which isn’t direct hours calculation and is really always the next working day but based on cut-off time when the call is logged. So a call logged on a working day 1 after cut-off will be counted as logged on working day 2 and hence will need to be closed by working day 3 whereas a call logged within cut-off of working day 1 will need to be closed by close of business working day 2. This is turn can allow for a comparison between Target Closed Date from this function and Actual Closed Date to show SLA failures where Actual Closed Date is greater than Target Closed Date. Pre-Requisites: Follow from previous post. Function code and explanation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 CREATE DEFINER=`ankit`@`%` FUNCTION `get_closed_date_given_start_time`( \t`param_country` VARCHAR(20), \t`assigneddatetime` VARCHAR(20), \t`param_elapsedhours` VARCHAR(10), \t`starttime` VARCHAR(20), \t`endtime` VARCHAR(20), \t`sla_type` VARCHAR(10) ) RETURNS TEXT LANGUAGE SQL NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER COMMENT '' BEGIN Set @starttime = starttime; Set @endtime = endtime; Select time_to_sec(timediff(@endtime,@starttime))/3600 into @maxhoursaday; Set @assigneddate = assigneddatetime; Set @timecount = param_elapsedhours; Set @timevar1 = @assigneddate; Set @nextdate = @assigneddate; Set @timevar2 = null; Set @param_country = param_country; ############ Check if the assigned time was before the starttime or closed time was after the endtime provided ############# Set @checkstart = null; Set @checkend = null; Set @slatype = sla_type; Select CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@starttime), CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@endtime) into @checkstart, @checkend; if (@slatype = 'H') then \tif (@assigneddate < @checkstart) then \t\tSET @nextdate = @checkstart; \tend if; else \tif (@assigneddate < @checkend) then \t\tSET @nextdate = @checkstart; \tend if; end if; Set @count = @timecount; while @count>0 do \tselect weekday(@nextdate) into @weekday; # Assign the weekday value to @weekday. Weekday returns o for Monday, 2 for Tuesday ...5 for Saturday and 6 for Sunday \t Select sum(if(date_format(holiday_date,'%Y-%m-%d') = substring_index(@nextdate,' ',1),1,0)) from holiday_table \t where Country_codes = 'ALL' or instr(Country_codes,@param_country)>0 \t into @holidayflag; #Check if the date stored in nextdate (which is assigneddate on first run of while loop) is a holiday and set the holiday flag \tif ( @weekday<5 and @holidayflag=0) then #Proceed if the date in nextdate variable is neither weekend nor a holiday \t\tif (@count = @timecount) then #Check if it is first run.ie. if nextdate is assigneddate \t\t\tSet @timevar1 = @nextdate; #assign assigndate to variable timevar1 \t\t\tSELECT CONCAT(SUBSTRING_INDEX(@nextdate, ' ', 1), ' ',@endtime) INTO @timevar2;#get site closing time on assigned date and store it on to timevar2 \t\telse \t\t\tSelect CONCAT(substring_index(@nextdate,' ',1),' ',@starttime) into @timevar1; \t\t\tSELECT CONCAT(substring_index(@nextdate,' ',1), ' ', @endtime) INTO @timevar2; \t\tend if; \t\tSELECT LEAST(Greatest(((TIME_TO_SEC(TIMEDIFF(@timevar2, @timevar1))) / 3600),0),@maxhoursaday) INTO @timecounttemp; \t\tSet @count = @count - @timecounttemp; \tend if; \t \tif @count > 0 then \t Select adddate(concat(substring_index(@nextdate,' ',1),' ',@starttime),1) INTO @nextdate; \t else \t Select SUBSTRING_INDEX(@nextdate, ' ', 1) INTO @nextdate; end if; end while; Set @finaldate = null; Select concat(@nextdate,' ',substring_index(addtime(@endtime,sec_to_time(3600*@count)),':',2)) INTO @finaldate; RETURN @finaldate; END Calling the function Function will expect 6 parameters and with specific format as explained below: param_country - This is the country code as specified in holiday table assigneddatetime - This must be provided in the format %Y-%m-%d %H-%i-%s. So for our example it will be 2018-02-23 15:00:00 param_elapsedhours - This must be provided in decimal hours format. Bear in mind that if SLA Type is not H, you need to understand the number of working hours in allowed number of days in SLA. So for 8 working hours in a day: Same day SLA will have 8 hours Next Business Day will have 16 hours(2*8) A 2 day SLA will have 24 hours and so on starttime - This must be in the format %H:%i. So for our example it will be 08:00 endtime - This must be in the format %H:%i. So for our example it will be 16:00 sla_type - This must be ‘H’ if the sla is based on hours or anything else (say ‘D’) if SLA is based on days. The call for this function will be as below: For hours SLA: Select get_closed_date_given_start_time('ALL','2018-02-23 15:00:00','16','08:00','16:00','H'); This will give an output of 2018-02-27 15:00 For Next Business Day SLA: Select get_closed_date_given_start_time('ALL','2018-02-23 15:00:00','16','08:00','16:00','D'); This will give an output of 2018-02-26 16:00 For just calculating next working day date given number of hours: Select get_closed_date_given_start_time('ALL','2018-02-23 15:00:00','10.5','08:00','16:00','H'); This will give an output of 2018-02-27 09:30 ","categories": [], + "tags": [], + "url": "/mysql-function-to-calculate-closing-workday-date-given-elapsed-working-time/", + "teaser": null + },{ + "title": "Home Networking", + "excerpt":"Network routing Router - Router is the device at home that connects all devices in your house to the internet. It does so by assigning IP address to each of your device but this IP address is only relevant for devices connected to this router. What this means is that if you use an IP address allocated by this router from a coffee shop and you are not connected to your home router then you will not be able to reach the device that IP address belonged to. For this reason these IP addresses are called internal IP addresses. Now what happens is when you try to access a website, your device sends the request to your router, router in turn sends it to the router of your ISP which in turn eventually connects to the server that is hosting the website. The information flows back through same route and your router finally gets the information and then passes it back to your device and it is able to do so because it identifies your device based on the Internal IP address it assigned to your device. By default pretty much all routers are configured to allocate these Internal IP Addresses using DHCP which is just fancy term for allocating a random number to the device. They do also provide the facility to reserve an IP address for a particular device if we tell it to do so - This is called "assigning a static IP on the router" in technical speak. This is important when we are trying to host service from home based server and is explained next Many ISP provided routers are locked in and it will serve you well to get a router and flash it with an open source firmware like DD-WRT. Internal LAN set-up - Ensure that on your internal LAN network you assign static IP address to device that will be running the server. If your router allows for LAN Domain to be configured then do so and allocate appropriate LAN Hostname as it makes it easier to access the server from within LAN than just by trying to remember the IP address. Port Forwarding - When the request comes from Internet, it will be with http, ftp, smtp those kind of headers. These headers operate on standard ports. However for security as well as application related issues, the ports on actual server within your home network can be significantly different. All this needs to be translated for communication to be completed and application to provide requested information. Port Forwarding at router level is simply us telling router which IP address to pass on the received request based on the port that request is trying to access. For example http requests are on port 80 so if we tell router that any requests coming on port 80 must be forwarded to a laptop in your house with internal ip address of 192.168.1.44 then port forwarding should be the way to do so. Internet to Home Use of Dynamic DNS Services Ensure that the router is informing your Dynamic DNS provider with latest IP address assigned by your ISP. Use of duckdns.org or dtdns.com etc Use of DNS-O-Matic to dynamically update the External IP address for your home router as soon as it is changed by the ISP. Allocate the Dynamic DNS to DNS name purchased from Domain Name registrar like Namecheap etc. Hosting a service from Home Server: Question we are first going to answer is - If we want to host on server from home. what do we need to do? 1. Ensure you are able to do changes to your router setting that allows you to: a. Create port forwarding rules b. Update the DynamicDNS each time your ISP changes External IP address for your router. The concept for these two topics are covered in previous sections but we will revisit in greater detail later. For now let’s start at something even more basic. 2. We need to decide the operating system and web server we want to use? The request from Internet when it reaches the router it is just dealing in numbers and not strings so it does not really know what information to send back unless it gets it back from the device where the information resides. In order for the device that has the information to provide it such that router can send it out, we need to have these web servers that do the job of translating the information into a format that the router and eventually the device can understand. So as an example if the request was on http protocol, it would mean it is for a web page and router knows that it is for port 80, and will forward it to the IP address you assigned - Router is assuming ofcourse that you know what you are doing. In other words it is your (human) responsibility to ensure that when this request lands on said IP address someone is there to receive it - that someone is the \"Web Server\" we are now talking about. Web Servers The two commonly used web-servers are: Apache Nginx There are other servers too for various other scenarios but the most popular options are the two above. QUESTION: Can we have both web servers running on same machine? ANSWER: Yes, the routing of information will be similar to the flow below: Internet -> Home Router -> Primary Server -> Other servers in home network accessed through reverse proxy and connected to world only through Primary Server. Operating System Assuming that we decide to go with Apache Server. You will need to install it on your laptop. This brings us to the first controversial topic of operating system that this laptop is using. It can be anything from Windows, Mac to a range of Linux Distros. As we are talking open source, lets assume it is will be a linux distro but there are so many to chose from so which one? A safe choice is to opt for Debian based distro as it is easy to start with. In that too Debian 9 (code named: Stretch) at the time of writing is latest stable version and is most recommended. I will assume for the purpose of this article that the reader will use Debian 9 (Stretch). I will also assume that reader is not aware of working on a Linux environment. Advanced users may find some of the information here obvious or boring as I explain few things at length. Prepare the laptop: Step 1 - Find out whether the laptop is 32 bit or 64 bit. Step 2 - Download a copy of the Debian 9 (Stretch) applicable to the specific version of your laptop (32 bit / 64 bit) Step 3 - Create a Live DVD / USB and test drive the OS Step 4 - Install it You will be asked to provide root password, username and user password. Make note of each of these as you will need them later. Step 5 - Update, Upgrade and Dist Upgrade Open the terminal of the laptop and type following commands: sudo apt update sudo apt upgrade sudo apt dist-upgrade ....More to come...Work in progress. ","categories": [], + "tags": [], + "url": "/home-networking/", + "teaser": null + },{ + "title": "Prosody behind Apache on Debian Stretch with Conversations", + "excerpt":"Genreal Guidance This how-to assumes that: User has access to a registered domain - domain.name. Any reference to domain.name throughout the tutorial therefore, must be replaced with your own domain name. If stuck anywhere, please do get in touch with prosody support chat - They are a very helpful lot and are very kind too. Network Setup: It is not mandatory to follow the flow of this how-to but if you do, please do it till the end before you try and access the IM server from any client to save yourself some pain. Obtain the IP address .… of the machine where your XMPP server will be installed using following command on the terminal ip -a address On the result shown, IP address against inet family is the one we are interested in as can be seen in screenshot below: Copy this ip address, we will need it in next step. Port Forwarding on router if hosting from home. You have to ensure that following ports are opened and directed to above IP address from your router. 5222, 5269, 5280, 5281 On DD-WRT this can be done as shown in screenshot below. This will, ofcourse, vary based on router you are using. Use the IP address from previous step to fill the IP address field. Basically this set-up is informing your router that if a request comes to you asking for one of the above mentioned ports direct that request to this IP address on the same port. Create a sub-domain record Please note that the official Prosody Documentation recommends using Server Record and even the experts on their chat support strongly recommend that approach. I did not have that option unfortunately - read I was being lazy / adventurous - anyway the C-Name set-up has worked fine for me and hence the guide follows this route. You have been warned !!! This step will vary based on the domain name registrar you use. I use Namecheap for which you could do it as shown in screenshot below: Ensure you have some sort of Dynamic DNS update set-up in place You can create an account here -> DNSOMATIC If you are going to use DNSOMATIC which allows you to update several places like so: For namecheap in order to get the value for password, login to Namecheap dashboard, go to Advanced DNS and scroll down to \"Dynamic DNS\" section and copy the value in front of the field named \"Dynamic DNS Password” as shown below: The setting to update DNSOMATIC on DD-WRT are as shown below: DYDNS Server:updates.dnsomatic.com Hostname: all.dnsomatic.com URL: https://updates.dnsomatic.com/nic/update?hostname= For all other Dynamic DNS set-ups ensure that your subdomain will always get updated with latest IP address to ensure continuity of service. Server Setup: Normally the XMPP clients on phone directly access port 5222 or 5269 for MUC (Multi User Chats aka Group Chats) but to make sure image uploads work and for being able to access IM from web we need to enable reverse proxy using Apache server. As this is fairly straight forward, lets get that out of the way. Apache with reverse Proxy enabled Make directory for subdomain: sudo mkdir /var/www/html/im Then create a server conf file and open for editing in nano using following command:\\ sudo nano /etc/apache2/sites-available/prosody.conf Paste the following in conf file: <VirtualHost *:80> ServerName im.domain.name ServerAlias im.domain.name DocumentRoot /var/www/html/im ErrorLog ${APACHE_LOG_DIR}/error_im.log CustomLog ${APACHE_LOG_DIR}/access_im.log combined ProxyPass /http-bind/ http://im.domain.name:5280/http-bind/ ProxyPassReverse /http-bind/ http://im.domain.name:5280/http-bind/ RewriteEngine on RewriteCond %{SERVER_NAME} =im.domain.name RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] </VirtualHost> Save the file with Ctrl+x, y and Enter. Finally enable site and reload server. sudo a2ensite prosody.conf sudo systemctl reload apache2 Enable SSL set-up using Let’s Encrypt {#enablesslsetupusingletsencrypt} ————————————– It is assumed at this point that certbot is already installed but if not, follow the tutorial here On terminal give the command sudo certbot Then follow the wizard and once completed check that an additional conf file named prosody-le-ssl.conf is created. If not something did not go well so check again and repeat but if yes move to next step. Ensure certbot is renewing automatically Debian stretch gets the service certbot.timer already on it which when enabled will try to autorenew twice a day ensuring a continuous certificate availability. To locate and edit the certbot.timerfollowing commands are helpful: locate certbot.timer nano /etc/systemd/system/timers.target.wants/certbot.timer The certbot.timer entry on my system is as below: Once edited, make sure it is enabled using following commands: sudo systemctl enable certbot.timer sudo systemctl start certbot.timer Prosody XMPP server Assumption - Apache 2.4 web server is already installed and functioning. Install Option 1 (Not Recommended): sudo nano /etc/apt/sources.list Then add this -> deb http://packages.prosody.im/debian stretch mainon the last line. Option 2 (Recommended): echo deb http://packages.prosody.im/debian \\$\\(lsb\\_release -sc) main | sudo tee -a /etc/apt/sources.list Above command is recommended because it takes the release of your installed version and adds it directly rather than the hard coded “stretch” in Option 1 which must be changes specific to the version of debian. Now add the key, update repository, upgrade for good measure and then install using the following commands: wget https://prosody.im/files/prosody-debian-packages.key -O- | sudo apt-key add sudo apt-get update sudo apt-get upgrade sudo apt-get install prosody Install plugins / modules Latest updated documentation is ofcourse on project website but at the time of writing these are the instructions that were followed: cd /usr/lib/prosody/ sudo hg clone https://hg.prosody.im/prosody-modules/ prosody-modules To update: cd /usr/lib/prosody/prosody-modules/ sudo hg pull --update Import SSL using certbot Issue following commands to get the SSL activated for Prosody: sudo certbot renew --deploy-hook \"prosodyctl --root cert import /etc/letsencrypt/live\" sudo prosodyctl --root cert import /etc/letsencrypt/live sudo prosodyctl cert generate localhost Configure The configuration file for prosody is very well documented and only needs minor tweaks. However, it is lot of commenting and to keep this readable, I have put my configuration here that can either be directly pasted or can be compared to update the default conf file. Either way, first you would need to open the config file: sudo nano /etc/prosody/prosody.cfg.lua Now the working config file for me is as shown below and can be compared to amend or can be directly copy pasted. Bear in mind that all references to domain.name must be replaced with your registered domain name. admins = {\"admin1@im.domain.name\"} plugin_paths = {\"/usr/lib/prosody/prosody-modules\" } consider_bosh_secure = true cross_domain_bosh = true modules_enabled = { -- Generally required \"roster\"; -- Allow users to have a roster. Recommended ;) \"saslauth\"; -- Authentication for clients and servers. Recommended if you want to log in. \"tls\"; -- Add support for secure TLS on c2s/s2s connections \"dialback\"; -- s2s dialback support \"disco\"; -- Service discovery -- Not essential, but recommended \"carbons\"; -- Keep multiple clients in sync \"pep\"; -- Enables users to publish the1ir mood, activity, playing music and more \"omemo_all_access\"; -- xep-0060 for enabling omemo access to non subscribers \"private\"; -- Private XML storage (for room bookmarks, etc.) \"blocklist\"; -- Allow users to block communications with other users \"vcard\"; -- Allow users to set vCards -- Nice to have \"version\"; -- Replies to server version requests \"uptime\"; -- Report how long server has been running \"time\"; -- Let others know the time here on this server \"ping\"; -- Replies to XMPP pings with pongs \"register\"; -- Allow users to register on this server using a client and change passwords \"mam\"; -- Store messages in an archive and allow users to access it --\"mam_muc\"; -- store group chat messages -- Admin interfaces \"admin_adhoc\"; -- Allows administration via an XMPP client that supports ad-hoc commands --\"admin_telnet\"; -- Opens telnet console interface on localhost port 5582 -- HTTP modules \"bosh\"; -- Enable BOSH clients, aka \"Jabber over HTTP\" \"websocket\"; -- XMPP over WebSockets \"http_files\"; -- Serve static files from a directory over HTTP \"http_upload\"; -- module to enable file upload in group chat -- Other specific functionality \"groups\"; -- Shared roster support \"proxy65\"; -- Enables a file transfer proxy service which clients behind NAT can use \"smacks\"; \"csi\"; \"cloud_notify\"; \"conversejs\"; } modules_disabled = { -- \"offline\"; -- Store offline messages -- \"c2s\"; -- Handle client connections -- \"s2s\"; -- Handle server-to-server connections -- \"posix\"; -- POSIX functionality, sends server to background, enables syslog, etc. } allow_registration = false c2s_require_encryption = false s2s_require_encryption = true s2s_secure_auth = true s2s_secure_domains = { \"domain.name\" } pidfile = \"/var/run/prosody/prosody.pid\" authentication = \"internal_hashed\" archive_expires_after = \"1w\" -- Remove archived messages after 1 week log = { info = \"/var/log/prosody/prosody.log\"; -- Change 'info' to 'debug' for verbose logging error = \"/var/log/prosody/prosody.err\"; -- \"*syslog\"; -- Uncomment this for logging to syslog -- \"*console\"; -- Log to the console, useful for debugging with daemonize=false } certificates = \"certs\" ----------- Virtual hosts ----------- -- You need to add a VirtualHost entry for each domain you wish Prosody to serve. -- Settings under each VirtualHost entry apply *only* to that host. VirtualHost \"localhost\" VirtualHost \"im.domain.name\" ------ Components ------ ---Set up a MUC (multi-user chat) room server on conference.example.com: Component \"conference.im.domain.name\" \"muc\" restrict_room_creation = \"local\" Once completed, save it and proceed to next steps. sudo service prosody start sudo service prosody stop sudo service prosody start -v sudo service prosody stop ## Enable prosody as a service so it restarts each time after system reboot sudo systemctl is-enabled prosody.service sudo service prosody restart Add User Use prosodyctl command to add user like so: sudo prosodyctl adduser admin1@im.domain.name and then provide password for the user. Ofcourse you must add as many users as you need and bear in mind the conf file above has registration switched-off so the users must be manually added using this step. Check the manpage using man prosodyctl to see how to update password for an existing user and to delete a user. Clients Setup: Conversations on Android Conversations is the best client for XMPP on Android and is available on playstore for a small amount or it can be installed from f-droid for free. The free version has some limitations with regards to push but I have been using it with no issues what-so-ever. OpenPGP using OpenKeyChain Install the OpenKeyChain app, create the key pair and trigger Conversations to start from OpenKeyChain. The other person must also have OpenPGP Key pair and you should have the public key of other person added on openkeychain instaled on your phone. Do bear in mind Conversations developer says it is an experimental feature. Although it did work perfectly fine for me while I tested with about 4 or 5 users. Conversejs.org on web Go to the web root we defined initially and create an index.html: sudo nano /var/www/html/im/index.html Then add following html code in there: Bear in mind that all references to domain.name must be replaced with your registered domain name. <!doctype html> <html class=\"no-js\" lang=\"en\"> <head> <meta charset=\"utf-8\"/> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/> <title>Converse</title> <link rel=\"shortcut icon\" type=\"image/ico\" href=\"css/images/favicon.ico\"/> <link type=\"text/css\" rel=\"stylesheet\" media=\"screen\" href=\"https://cdn.conversejs.org/3.3.4/css/converse.min.css\" /> <link type=\"text/css\" rel=\"stylesheet\" media=\"screen\" href=\"https://cdn.conversejs.org/3.3.4/css/mobile.min.css\" /> <script src=\"https://cdn.conversejs.org/3.3.4/dist/converse.min.js\"></script> </head> <body class=\"reset\"> <div class=\"content\"> <div class=\"inner-content\"> <h1 class=\"brand-heading\"><i class=\"icon-conversejs\"></i> Converse</h1> </div> </div> <script> converse.initialize({ authentication: 'login', auto_away: 300, auto_reconnect: true, bosh_service_url: 'https://im.domain.name/http-bind/', show_controlbox_by_default: true, message_archiving: 'always', allow_logout: true, allow_dragresize: true }); </script> </body> </html> Save the file with Ctrl+x, y and Enter. Once done restart the webserver as well as prosody for good measure: sudo systemctl restart apache2.service sudo prosodyctl restart ChatSecure on iOS Now I do not have an iphone but a few of my friends do. They were able to use ChatSecure which is also an opensource app. Unfortunately ChatSecure is unable to get the images on group chat from Conversations but I am hoping that developer of ChatSecure will fix this issue sooner rather than later. Desktop Client For desktop I found Gajim to be most polished and it can be downloaded for your OS from here All works great on it except for the http_upload which seems to fail for transferring images with an error “Unsecured”. I am not sure what the issue is a search results did not help much - I left it because I dont really need this feature from desktop. However if someone has any ideas on how to fix this, please do leave it in the comments. The plugins on Gajim that I have applied and activated can be seen below: Maintenance The service does generate a lot of logs and it will serve you well to delete generated logs daily using Cronjob like so sudo crontab -e Then add the following on the cronjob: ############## DELETE PROSODY LOG FILE ARCHIVES ########################### MAILTO=\"your@email.com\" 0 0 * * 0-6 rm /var/log/prosody/*.gz ","categories": [], + "tags": [], + "url": "/prosody-behind-apache-on-debian-stretch/", + "teaser": null + },{ + "title": "Metabase - A BI solution that just works", + "excerpt":"I like exploring new solutions and anything to do with data always piques my interest. I came across this nice tool through the list of free self hosted software. UPDATE: I am still getting introduced to the world of docker and while most of it just works few things confuse the hell out of me. Anyway what happened is there was an update for Metabase and I wanted to apply it on my container and in doing so I realised that some of this post can be changed to make it a better experience from get go because after update all my config and dashboards were gone and I had to reconfigure everything. It could be that my lack of knowledge meant I did something wrong and faced the issue but irrespective doing it as per updated post below will ensure you dont face the issue at all because I have now tested it twice to ensure it works well. I have also added the steps to update the metabase to latest docker image as it worked for me. The overall impression when I opened their site can be summed up with WOW!! Now they offer simple docker install supported officially by them and the guide to install docker image is simple enough but the few simple steps I followed are as below: Create an A-record for a subdomain for metabase Download the official metabse docker image from dockerhub Create an apache server conf file for metabase Run the metabase docker image Access metabase docker image using subdomain URL Configure your database on metabase Run Certbot to make the URL https Done!! Create A-record for subdomain Using the process for your Domain Name Regsitrar create an A-Record for a subdomain you would want to use for metabase. For this guide we will assume that the subdomain being created is called “metabase” so if your domain name is say yourdomain.com then the URL to access metabase will be metabase .yourdomain .com. It is important that if you use a different subdomain name, please replace metabase with the subdomain name you have chosen in all Apache conf file samples in sections below. More detailed steps are available on previous post HERE Download and Create Apache Conf docker pull metabase/metabase cd /etc/apache2/sites-available/ sudo nano metabase.conf Create a conf file similar to what is shown below: <VirtualHost *:80> ServerName metabase.yourdomain.com ServerAlias metabase.yourdomain.com ErrorLog ${APACHE_LOG_DIR}/error_metabase.log CustomLog ${APACHE_LOG_DIR}/access_metabase.log combined ProxyPreserveHost On ProxyPass / http://localhost:3000/ ProxyPassReverse / http:/localhost:3000/ ProxyPass \"/ws2/\" \"ws://localhost:3000/\" ProxyPass \"/wss2/\" \"wss://localhost:3000/\" </VirtualHost> Save this by pressing Ctrl+X and issue following commands to enable site and reload the apache server. sudo a2ensite metabase.conf sudo systemctl reload apache2 Run Metabase docker image docker run -d -p 3000:3000 --name metabase metabase/metabase If all goes well, you should have metabase already running. If you get an error to the effect saying port 3000 is already in use, you will perhaps have to run it on another port (say 12345) but as metabase container would have been created you will need to issue following commands. docker rm metabase docker run -d -p 12345:3000 --name metabase metabase/metabase In addition to avoid running the database through the container we must anyway move it to filesystem. Using commands below: mkdir /var/www/metabase This command will create metabase directory on the path /var/www/metabase but you can as easily chose another path as you wish. If you do, just remember to update it in next commands. docker ps This will show you the active docker containers. Copy the container ID in first column for the row where under Names you see “metabase” which is the container name we have given using previosu commands.Now copy the database from container to local filesystem. cd /var/www/metabase docker cp <container ID>:/metabase.db ./ Now Stop and rename the container docker stop metabase docker rename metabase metabase_old Finally run Metabase Docker Image while pointing it to your filesystem for database file. docker run -d -p 12345:3000 -v ~/metabase-data:/var/www/metabase -e \"MB_DB_FILE=/var/www/metabase/metabase.db\" --name metabase metabase/metabase The above docker command can be explained as below: docker run -d - This is telling system to start a container in detached mode. By design, containers started in detached mode exit when the root process used to run the container exits. ` -p 12345:3000` - This is telling docker to make the container map port 12345 to port 3000 which is the default port on which metabase starts its server. -v ~/metabase-data:/var/www/metabase- This part is telling docker to map the “metabase-data” directory in the container to “/var/www/metabase” on the filesystem. In other words, mounting the local volume at path “/var/www/metabase” onto the container in “metabase-data” directory. --name metabase metabase/metabase- Finally this part of command is telling docker to name the container “metabase” and to use the docker image “metabase/metabase” from dockerhub Do bear in mind if you change the port as explained above, you will also need to change the port in apache conf file in previous step. So your apache conf file in this case will look as shown below: <VirtualHost *:80> ServerName metabase.yourdomain.com ServerAlias metabase.yourdomain.com ErrorLog ${APACHE_LOG_DIR}/error_metabase.log CustomLog ${APACHE_LOG_DIR}/access_metabase.log combined ProxyPreserveHost On ProxyPass / http://localhost:12345/ ProxyPassReverse / http:/localhost:12345/ ProxyPass \"/ws2/\" \"ws://localhost:12345/\" ProxyPass \"/wss2/\" \"wss://localhost:12345/\" </VirtualHost> Access and configure on browser Access your metabase using the subdomain url - http:// metabase.yourdomain .com and it should walk you through the initial configuration. If you do not want to add a database and just play with the tool, they have kindly provided a sample database. It might be useful for you to create a “user” in your database that has “read only” access and then use that “user” when you configure metabase. Metabase will only ever need read access as it cannot be used to modify data in your database. Enable SSL Once you are happy with your configuration from step above, just run the certbot to enable https using letsencrypt certificate. On Debian this could be as simple as issuing the command sudo certbot Once above step is created you will have an additional conf file named metabase-le-ssl.conf created in the location /etc/apache2/sites-available. Check this and once it is there you are all set to access your new functional metabase instance on https. More detailed steps are available on previous post HERE Update version Updating on docker image is pretty straight forward and provided you have followed steps above should not result in you loosing all the effort once update has completed. #docker rm metabase_old docker stop metabase #docker rename metabase metabase_old docker pull metabase/metabase docker start metabase From my recent messing around, I have come to realise that so long as the first start of the docker image was done using environment variables pointing to correct database directory, there is no need to run the commented lines above and a simple stop, upgrade and start should work just fine. Enjoy!!! ","categories": [], + "tags": [], + "url": "/metabase-a-bi-solution-that-just-works/", + "teaser": null + },{ + "title": "Converting AAX (Audible) to mp3", + "excerpt":"Lately I have been a bit frustrated because while I subscribe to Audible services, Amazon and Google do not play nice with each other limiting me - the owner of books with rights to listen my purchase on any device - in my options on where I can listen. Now I am yet to find a good solution that can get my files playing on google home and not just Alexa, I guess first step was to free my audiobooks from Amazon jail. That as it turns out is rather simple to do.I must at this point mention OpenAudible which is one fine tool. However, their latest version (2.0.7 at the time of writing) added some limitations whereby you can get it to download the AAX files but not convert them to mp3 without purchasing their license. However,converting AAX to mp3 is accomplished using ffmpeg and I do not believe a nice GUI wrapper is what I need and hence I do not believe my money is well spent in getting a license just to achieve from a graphical user interface what I can achieve for free from the terminal with a single line.There are 3 steps in the conversion process: Get a copy of the AAX file you own from your audible account: This can be done in one of two ways as listed below: a. Install OpenAudible and follow instructions to download the AAX files. b. Copy it from the filesystem of your Android phone. Obtain the activation_bytes for your AAX file: a. If you installed OpenAudible, you can use the following steps: cd /opt/OpenAudible/bin/linux/ ffprobe /home/OpenAudible/aax/name_of_the_book.AAX b. This should give you a lot of output and a hash code. Let’s say the hashcode you get is a6ger35cf22u03c9x16743j06f6df50a00b811c3 c. Use this hashcode with following command to get the activation_bytes: ./rcrack path /opt/OpenAudible/bin/tables/ -h a6ger35cf22u03c9x16743j06f6df50a00b811c3 d. This should then output the activation_bytes in last line - something like hex:abc43opu as shown below: Above steps are required only once. Keep the activation_bytes code that you obtain above safe as it can be used for converting all your audiobooks. Now use the following commands to convert AAX file to mp3: #change directory to where the AAX files are saved. If using OpenAudible it will be in /home/<username>/OpenAudible/aax cd /home/<username>/OpenAudible/aax ffmpeg -activation_bytes xxxxxxxx -i 'name of the book.AAX' -map_metadata 0 -codec:a libmp3lame -qscale:a 6 /home/<username>/OpenAudible/mp3/name\\ of\\ the\\ book.mp3 Remember to change xxxxxxxx with the activation_bytes code you obtained in step 2 and the name of book as well as destination path and name of the book. Now ofcourse you can accomplish the first Step 2 above without OpenAudible. If you would want to do so, just follow the guidance on github. Hope this will be helpful for a few. In the meantime, I am yet to work out the best way to make google home to play my audiobooks when I so desire. If anyone has any tips they will be gratefully received.Have fun !!! ","categories": [], + "tags": [], + "url": "/converting-aax-audible-to-mp3/", + "teaser": null + },{ + "title": "Upgrading PHP version on Linux for Apache", + "excerpt":" Install the Apache module for specific php version sudo apt install libapache2-mod-php7.3 Copy the php.ini from previous version to newer version after making a backup of the original php.ini for new version. sudo cp /etc/php/7.3/apache2/php.ini php.ini.original sudo cp /etc/php/7.2/apache2/php.ini /etc/php/7.3/apache2/php.ini Install specific php modules for Apache and enable the php modules on new version of php. sudo apt install php7.3-curl php7.3-gd php7.3-gmp php7.3-intl php7.3-mbstring php7.3-simplexml php7.3-soap php7.3-wddx php7.3-xmlreader php7.3-xmlrpc php7.3-xmlwriter php7.3-xsl php7.3-zip php7.3-xml php7.3-mysql sudo phpenmod -v 7.3 pdo_mysql soap wddx xmlreader xmlrpc xsl zip intl gd dom curl mysqlnd gmp simplexml mysqli mbstring Disable old php version, enable new version and restart Apache server. sudo a2dismod php7.2 sudo a2enmod php7.3 sudo systemctl restart apache2 ","categories": [], + "tags": [], + "url": "/upgrading-php-version-on-linux-for-apache/", + "teaser": null + },{ + "title": "Tech for Diabetics", + "excerpt":"Background As a diabetic there are a number of things we are not able to control but one thing we can do is keep tabs on our data and while in past it would have meant meticulously noting down BG readings in a diary, today it is much much easier with all the apps and connected services. In this article, I aim to go through the list of technologies that I am aware of and use to keep on top of my diabetic data right from my phone. Blood Glucose Meter While there are a number of options available, I settled for CareSens Dual. The biggest advantage with this model is that it plays very nicely with xdrip+ app although lately I am relying more on Diabox so the point is kind of moot. Apart from that, one factor we must bear in mind while chosing the device is not just the cost of the device itself but also that of testing strips as it will be a recurring expense and this device has one of the most reasonably priced testing strips. Freestyle Libre 2 (FSL2) FSL2 became available in the UK only from Jan’21 and this or it’s previous version allow for a more regular check of the BG trends. FSL1 in my experience was a bit less accurate than FSL2. However these sensors along with the official app from manufacturer require manual scan, don’t give all datapoints and can not be calibrated which is again where xdrip+ / Diabox apps come into the picture. Freestyle Libre 1 (FSL1) with Transmitter (Miaomiao / Bubble) Before Jan’21, in the UK we only had the option of FSL1 and as this version does not have bluetooth communication; the possibility to calibrate and get automated data points required usage of a transmitter device such as Miaomiao or Bubble. I ordered a Miaomiao transmitter back then and I still can use it with FSL2 albiet with slightly modified approach. I do it because I have the device but with FSL2 a transmitter is not needed as explained later. Freestyle Libre App Now the fact is I dont really use this app for anything other than activating the sensor but it is for this reason alone an app you can not avoid or do without. In order to use the sensors to feed data directly and automatically to Diabox the sensor at the time of activation should not be paired through bluetooth to this app. Now there are several ways to achieve this. One is to install the app on a completely separate phone and turn off Bluetooth on that phone. Another is just do not give this app the “Location” permission which would then not allow app access to Bluetooth. Either scenario should work in theory but I use the safest approach of activating sensor through this app on a compltely different phone (that of my daughter actually :)) Diabox (Especially with FSL2) I have been using this app only for 10 days at the time of writing but it works really well with FSL2 and as it gets the data directly from the sensor it removes the additional cost of the transmitter and that makes this a very useful app indeed. The app is available for both iOS as well as Android but from what I gathered at some point Abbot Laboratories (Manufacturers of FSL sensor) complained about the app to Google and so google was obligated to takedown this app from playstore. App therefore is available as an apk file on github and the latest release for it can be downloaded from this GitHub Link The developer has a Diabox Dev website with lot more information on user interface and setup etc. but I will explain my setup below. Download the latest version of the app from GitHub Link Install the app - As it is not from playstore, the permission to install from unknown sources1 should be activated in Android Settings. Once installed, open the app. It will ask to scan the sensor. Once you scan, the phone will automatically pair the sensor with the Bluetooth of the Libre. Now to enable calibration tap on the sensor image as shown in screenshot below: Now tap 5 times on the text “Factory” in the last entry “Calibration Mode” as shown below: This will present a dialogbox “Sensor Magic Code”. Type “GODMODE” in the dialogbox field and press “OK”. This should activate the calibration mode and screen should look as shown below: Go back to homepage and you will now see a red coloured circular button with “+” symbol on it as you can see on the first screenshot above. Tapping on the add button will reveal an icon with a glucometer as shown below: Tapping on the glucometer icon will open a sliding scale to provide the calibration value. Add your calibration and click on Save button. Now, the app also allows uploading the data it collects to LibreView and Nighscout. I am not so keen on LibreView web interface so I never tried uploading on that service but have it syncing with Nightscout. For Nightscout sync, you will need to have a Nightscout instance up and running2 but assuming it is in place, the steps will be simple to follow: Click on “Settings” Click on “Integration” Enable “Nightscout Share Server Upload” Enter the URL for Nightscout instance, something like https://your_nightscout.herokuapp.com/api/v1/ Enter the password for your instance. Click on Connect Test If the test is a success, click on Save button. Nightscout Nightscout is a fairly detailed open source solution with loads and loads of functionalities but as a type 2 user I merely use it as a backup of my data. One key thing to note is that once you setup this server your BG data is available to anyone with access to your URL as there is no password block or similar which in my eyes is no big deal. What can anyone do with just my blood glucose data and how or why will they get access to an obscure URL anyway but if this makes you worried perhaps you can skip this part. If this is not of concern then I must say that while the setup guide2 looks very long and complex, it really isn’t. They have taken care to make it so very simple that its infact easier than even completing the registration form for some of the online services we have got accustomed to and it will just work. To set it up you can follow the official set-up and installation guide. Diabetes:M Diabetes-M is a very good app and creates some very useful and pretty reports that can be helpful during discussion with Diabetic Nurse / GP. One big advantage of using this app is that it is able to import data from Nightscout directly and keep it in sync every 15 minutes and then that data is also backed-up in Diabetes-M servers. The app also allows for automatic data sync with a number of popular Glucometers but that is a paid feature and I do not use it so I cannot comment on its reliability. For my usecase I anyway calibrate Diabox each time manually when I take the reading from finger prick and that is synched to Nightscout which is synched to Diabetes-M so the data is anyway there when I need it. Steps to sync with Nightscout are as below: Open your nightscout instance in a browser on a laptop / PC . Click on the site in sequence as shown below: On resulting screen - Administration panel, check if the role “readable” exists. If not create it by clicking on the button Add new Role and in “Permissions” type *.*.read as shown below. If it exists, go to the section “Subjects - People, Devices, etc” and click on “Add new Subject” and fill the resulting dialogbox like shown below and click Save: This will generate the Access Token for the app and it will be shown in the table in the section: Open the Diabetes:M app and click on the hamburger menu on top left corner of the screen. Then on the panel locate and tap on the Data Sync icon. On resulting screen under section “Link external sources” locate Nightscout and click on setting icon. Now fill the URL for your nightscout instance, something like https://your_nightscout.herokuapp.com Leave “Secret” empty and in in the field “Access token” type the access token shown generated in step 6 above from the browser and click Save. Direct links to appstore: Google Playstore Apple Appstore XDRIP+ xdrip+ is an opensource Android app (an iOS variant exists but I havent read much about it so have no knowledge to share) and as it is not available from playstore it can be obtained directly from developer site: https://jamorham.github.io/#xdrip-plus To use xdrip, you will also need to order a transmitter called miaomiao - It usually comes within 2 to 3 days - for me atleast it came in 2 days back in October when I started using FSL1. It is a bit of investment at about £162 but it pays for itself in long run. I ordered from their site3. Once you get the transmitter, the process will be like so: It will come with a USB charging cable so place it on charger. It should start blinking blue but if it does not there is a very small hole and you will need a safety pin to press and reset it. Insert the safety pin and keep it pressed for 10 seconds. Then place it on charge. Once the transmitter is charged it will display green light. Now there is also a strap they provide with the transmitter which you can use to place it over the libre sensor on your arm. Open xdrip app and follow the xdrip+ installation guide4. Once connected, it will ask you to calibrate the app with readings from two finger prick tests. You can do one and provide twice or you can do twice. I usually do it twice for each new sensor. Tidepool This is a not-for-profit site which xdrip+ can directly communicate with and push the readings to every 15 minutes thus creating a backup for your data in the cloud. As I still use the miaomiao transmitter paired with xdrip+ I have created a sync from xdrip+ to tidepool. Now the good thing with tidepool is that it shows glucometer readings in a separate table and while for Diabox I have to manually enter the finger prick test data, Caresens Dual is paired perfectly with xdrip and every single time I take a finger prick test, xdrip+ automatically fetches the reading from meter via Bluetooth and then that reading is fed into Tidepool which allows me to see my BG data at one place like so: Eufy Smartscales Eufy smartscale is very reasonably priced and I ordered it from Amazon. The scale is reliable, uses standard 3x “AAA” battery which last for years not months. The app interface is good too. One drawback perhaps is that data is only accessible through app on the phone and not through any website. It does sync weight with google fit so that can be accessed and shared across other services if one is so inclined but rest of it will need to be manually captured for any statistical fun. However, if I knew before what I know now I would have perhaps opted for a device from this list as then I could have had my data collected through the open source app openScale and would have had better freedom on how to move it around. Hope this list is useful. If there are any questions, please do not hesitate to ask. :) Reference on how to activate installation from unknown sources ↩ Guide to setup Nightscout Instance ↩ ↩2 Site to order MiaoMiao ↩ XDRIP+ installation guide ↩ ","categories": [], + "tags": [], + "url": "/tech-for-diabetics/", + "teaser": null + },{ + "title": "JupyterLite on Github Enterprise with Panel enabled", + "excerpt":"JupyterLite on Github Enterprise with Panel enabled Recently I found myself in a scenario where I had access to Github Enterprise but was limited to a windows machine. Now I wanted to host JupyterLite on Github Enterprise so it can be used by some other people on my team. Challenge was the “Actions” on this instance of Github enterprise were disabled and so I had to build Jupyterlite locally and push it on Github. I figured while at it, I might as well enable the “Panel” and few other dependencies. The steps that worked for me are documented here. This is ofcourse assuming that Python is already installed on the device. The final hosted JupyterLite can be seen in action here. The steps below created a Jupyterlite instance that allows use of Panel using the %pip install panel magic command, and if used on Chrome or its derivatives it also allows exploring local file system. Create and activate Jupyterlite Environment mkdir panel_lite cd .\\panel_lite\\ py -m venv panel_v .\\panel_v\\Scripts\\activate pip install --upgrade pip Install jupyterlite and other extensions pip install --pre jupyterlite pip install jupyter-bokeh ipython ipywidgets jupyterlab-drawio jupyterlab-markup jupyterlab-myst jupyterlab-pygments jupyterlite-p5-kernel jupyterlite-xeus-sqlite libarchive-c matplotlib matplotlib-inline matplotlib-venn myst-nb myst-parser nbconvert numpy openpyxl pandas pandocfilters pkginfo pyopenssl python-dateutil python-dotenv pyviz-comms pyxlsb scipy sql SQLAlchemy sqlparse tornado widgetsnbextension xlrd XlsxWriter zipp jupyterlab-filesystem-access Prepare build directory mkdir jupyterlite_panel cd .\\jupyterlite_panel\\ mkdir pypi cd .\\pypi\\ wget \"https://cdn.holoviz.org/panel/0.14.2/dist/wheels/bokeh-2.4.3-py3-none-any.whl\" -outfile \"bokeh-2.4.3-py3-none-any.whl\" wget \"https://cdn.holoviz.org/panel/0.14.2/dist/wheels/panel-0.14.2-py3-none-any.whl\" -outfile \"panel-0.14.2-py3-none-any.whl\" Make a record of all that you have installed. pip freeze > requirements.txt Including any existing notebooks If you have some existing notebooks or files you would want to include on the hosted version of Jupyterlite, you must create a directory and name it files Build Jupyterlite and serve locally to test jupyter lite build --output-dir ./dist cd .\\dist\\ py -m http.server 8000 deactivate The folder structure after build should look somewhat like below: panel_lite:. ├───.cache ├───dist │ ├───api │ ├───build │ ├───doc │ ├───extensions │ ├───files │ ├───kernelspecs │ ├───lab │ ├───pypi │ ├───repl │ ├───retro │ └───tree ├───files │ ├───data │ └───sample.ipynb ├───pypi └───panel_v Push it to Github Enterprise Github Desktop is the easiest way to do it. Open Github desktop and create new reporsitory: On Github desktop point the Local Path field to \\panel_lite\\jupyterlite_panel\\dist and do the initial commit and push to origin. Host Jupyterlite as Github pages Click on button View on Github on Github Desktop. Github Enterprise will open in browser to the created repository Click on Settings > Pages select Branch: master (or main) and root as source. Click Save and the message should be displayed in green box with the URL to access Jupyterlite. Finally open the URL to access JupyterLite ","categories": [], + "tags": [], + "url": "/jupyterlite-on-github-enterprise-with-panel-enabled/", + "teaser": null + },{ + "title": "Git Basics", + "excerpt":"Basics of Git Recently, I was asked to help people understand what is this whole business with GIT and I ran a few sessions on the topic specifically keeping in mind that my audience consisted of tech enthusiasts with minimal tech background. The content below is how I explained and as it went down well, I figured it may be helpful to someone else too; hence this post. :) Version Control System To understand the tool git lets first start by understanding what problem does it actually solve. At the most basic level git is a version control system. What this means is that it allows automated control of maintaining different versions of a digital asset such as a text file, CV, or indeed code files for a software. Now for simpler things like CV or text file one may not need something as sophisticated and complex as git but when it comes to maintaining assets for a software it is indeed a very useful tool. To understand how significant this is, lets put it in perspective of a typical business operations scenario completely unrelated to software development: Say there was an RFP response on which multiple people from Sales, Marketing, Pursuit and Technical team were helping prepare a response. Each team will complete one section of the response document relevant to their area. This will then all need to be integrated into the final response and reviewed by the Sales Lead. Any changes that Sales Lead identifies will need further changes which will then again need to be amended on the document. Even with change tracking enabled in MS Word, we all know how painful this entire exercise is. So much so that at times we try doing it together so all changes can be done once or we opt for the approach of making changes sequentially where one section is completed by one team before another can be started by another team. This when we have one document!!! Now imagine if we had 1000s of such documents - that is a real scenario for software developers as each software has multiple files that may need input from multiple people. So having an automated version control helps save time because it, at the most basic level, saves time on integrating changes carried out by different people or teams that were carried out in parallel. Now when it comes to version control there are two main ways in which it can be achieved: Centralised Version Control System: In our RFP example, the approach where everyone gathers together to carry out change on the main version will be a good simulation of centralised version control. What this means is that the changes to digital asset are done at one central location which is tightly controlled. This might as well be right approach for a smaller undertaking such as our RFP example but when it comes to software development it is not considered to be the best. Distributed Version Control System: This is loosely what would have been the approach in our RFP example if people were to carry out changes on their copy of document which will then be merged by Sales Lead to prepare the final response. Basically a distributed version control system works on the principle that each contributor to the change of digital asset will make change on their local copy and then submit the change for version maintenance to the version controller. Git as a system shines in this approach of distributed version control. Working Directory Directory is technically correct name for what are called folders in Windows Operating System and Working Directory is the directory in which the user is currently working. For example, let’s say I create a folder called “Project_1” in windows on path “C:/Documents” and save a python script in that folder, then the working directory will be “Project_1” and working directory path will be “C:/Documents/Project_1”. In order to version control this file, one can use git but this will include understanding the steps of how to go about it as explained in following sections. Initiatlise Now when let’s say the script file script.py is created by developer. It is not being tracked by git at this time. Now if we want it tracked by git, we need to first initiate git on the working directory. This is done using the command: git init but before typing this on command line, one needs to first get into the working directory on command line or terminal. In our example this will be done like so: cd C:/Documents/Project_1 git init This will create a .git directory (folder) inside the working directory. This is equivalent of git software saying it is now ready to start tracking but it will still need to know what exactly we want it to track. This is what git add does. Add To tell git what we want it to track, we have to use the command git add <filenames / foldernames>. However, if there are 100 script files in directory it will be very cumbersome to type every single filename and as a shortcut one can use dot notation for current working directory which is dot(.) and the command can be issued as: # To add all content of the working directory git add . # To add specific file say script.py git add script.py This tells git that we want all files and directories (folders) in the current working directory to be tracked. At this point, git is basically aware that it has to monitor changes on these files but it will not assume that every change we have made needs to be applied to maintained version so when we have added these files, we have basically also just said prepare these files for finalising their version - In other words we have asked git to stage. In order to finalise the versioning, we have to now tell this specifically to git which is done using commit. This will also be reflected in the tracking status which can be checked using status Status One can check current tracking status of various files and directories on the git initialised working directory by using the command: git status Commit So we have got git to stage the files and now we want it to label the latest changes as a new version and this is achieved using the command: git commit -m <Comment for the commit> This command ensures that git now makes the latest changes baseline and still keep record of previous changes. This is especially useful if in case we want to revert to a previous version for some reason - say we found that in latest version there were quite a few mistakes which will need to be rewworked and until then previous version is what we want to keep as baseline for anyone who wants to refer and make changes to. This reverting can be achieved using revert Revert Now reverting to a previous version requires that we know the commit id that git applied to previous commits and then identify the exact commit id to which we want git to revert to. This can be achieved by checking git logs, so the sequence to git revert will be like so: # Find the commit id to revert to git log # Copy the commit id of the version to revert to and issue following command git revert <commit id to revert to> Now at this point, all changes are being tracked on the local machine of the user, so how can this help with collboration. This is achieved by synchronising the latest version on local machine to a central server. This could be a server hosted by an organisation or it can be something public like Github, Gitlab and such. As GitHub is most frequently used in enterprise environment, lets see how that works. Github basically is a web based utility which acts as a remote location where we can push our finalised version to be saved in what is called a github repository for other collborators to then download (clone) as their local copy and work on. Once each poarty has made changes, the can each push their changes to the github with help of git and the version control will be applied automagically or in some instance with minimal manual intervention from the maintaining or one pushing the changes. There is lots going on in this last bit so lets break it down to digestable chunks of information in next session. ","categories": [], + "tags": [], + "url": "/git-basics/", + "teaser": null + },{ + "title": "Git Jargon", + "excerpt":"Git Jargon Before we delve into the topic of how collaboration happens using git, lets get some terminology out of the way. repo When talking to techy chaps, one will encounter the word repo very often. However, repo is just a shortform for the complete word repository. repository Now repository in git speak is basically specific to a project and is nothing but a fancy alias for the working directory where all the digital assets being tracked for the project are placed. root The term root when used with reference to a folder, directory, repo or repository is basically the directory in which all the digital assets of your project are stored. This directory may or may not contain other sub directories. remote remote is a way to refer to server. So if a techy were to say “I have updated File X on remote” - it will basically translate in plain English to something like this - “I updated the File X on my laptop and have then also taken required actions to get those updates reflected on the copy saved on the server.” push This refers to sending updates carried out (commited) on a local machine to the server (remote). Fairly frequently one may come across statements when checking for status updates on the lines of “I have pushed my changes to remote” which in normal English means “I made required changes on my local machine, committed them and then sent (pushed) them to the server (remote)” pull This refers to getting updates from the server (remote) on to the local machine. This is an activity typically carried out before one is planning to make any changes or updates to last known version. So if say a task is assigned to an individual for updating file , a reply can be “Alright, I will pull the latest from remote and make changes and will let you know once I have pushed my changes back.” which will translate to “Alright, I will get the latest copy from server, make the changes you have asked for and then commit those changes and send it back as updated version on the server” main This refers to the final / published version of project’s repository. branch This term is more of a concept really. You see branch is a mechanism to carry out changes without impacting the main version and therefore a branch is basically just a new or separate version of the main repository where one can make changes to their heart’s content without worrying about messing up with the last known final version. merge Once the experimental changes in a branch are completed and proven not be breaking any other digital assets, it is desirable indeed to get these changes reflected on the main version and this action of bringing digital assets in main to reflect same changes as that on the branch is called merge. modified In git speak, any changes (updates, additions, deletions) to a digital asset (including deletion of entire digital asset) then that digital asset will be identified as modified staged The modified digital assets are not automatically assumed to contribute a version change and that is only done when those changes are added explicity by issuing the command git add <filenaem / foldername> and at this point the changes are said to be staged. Recap With this in mind, the flow of GIT can now be explained as below: To work with Git, you first initialise it on a directory which will make it a Repository Once initialised, Git creates a hidden firectory called .git which is used to keep track of changes in the initialised directory. At this point, we need to make Git aware of the files in this repository which we want Git to track and in doing so we stage those digital assets. From this point on, any changes made to digital assets in project folder can be tracked. If a digital asset which is being tracked is changed, it gets the status of modified. Any digital asset with status must be staged before it can be passed on for version control. Once staged files must be committed so that the changes can be treated as the latest version snapshot. As each commit is tracked by Git, it is possible to revert to a previous version by first identifying the commit id one wants to revert to using git log and then providing that commit id through git revert <commit_id> ","categories": [], + "tags": [], + "url": "/git-jargon/", + "teaser": null + },{ + "title": "Python Function read excel / csv files from a given directory and its subdirectories", + "excerpt":"Code The code for the function is as shown below: import os import pandas as pd import numpy as np from datetime import datetime def extract_data(directory_path, columns_to_extract, date_columns=[], rows_to_skip=0, output_filename='output.csv'): \"\"\" Extracts data from all Excel and CSV files in the specified directory and its subdirectories that contain all the specified columns. :param directory_path: The path to the directory to search for Excel and CSV files. :param columns_to_extract: A list of column names to extract from each file. :param date_columns: A list of column names to parse as dates using pd.to_datetime(). :param output_filename: The path and filename to save the extracted data in a csv :return: A DataFrame containing the extracted data from all Excel and CSV files that contain the specified columns, or None if no files contain the specified columns. \"\"\" start_time = datetime.now() extracted_data = pd.DataFrame() files_with_no_columns = [] sheet_read = '' extracted_columns = columns_to_extract.copy() extracted_columns.extend(['File Name','SubDir','CreatedDate','LastModifiedDate']) for root, dirs, files in os.walk(directory_path): for file in files: print('Started reading ' + file + ' at: {}'.format(datetime.now()) + ' ...') if file.endswith('.xlsx') or file.endswith('.xls') or file.endswith('.csv'): file_path = os.path.join(root, file) columns_found_outer = False if file.endswith('.csv'): df = pd.read_csv(file_path,skiprows=rows_to_skip) for col in columns_to_extract: if col in df.columns: columns_found_outer = True #print(col + 'in csv true loop') else: columns_found_outer = False #print(col + 'in false loop') break else: dfs = pd.read_excel(file_path, sheet_name=None,skiprows=rows_to_skip) for key in dfs.keys(): columns_found = False for col in columns_to_extract: if col in dfs[key].columns: columns_found = True #print(col + 'in true loop') else: columns_found = False #print(col + 'in false loop') break if columns_found: columns_found_outer = True df = dfs[key] sheet_read = key break if columns_found_outer: if len(date_columns) > 0: for col in date_columns: if col in df.columns: df[col] = pd.to_datetime(df[col]) df['File Name'] = file df['SubDir'] = os.path.basename(root) createx = modx = os.path.getctime(file_path) xcreate = datetime.fromtimestamp(modx) df['CreatedDate'] = xcreate modx = os.path.getmtime(file_path) xmod = datetime.fromtimestamp(modx) df['LastModifiedDate'] = xmod extracted_data = pd.concat([extracted_data, df[extracted_columns]], ignore_index=True) #print(xmod) if file.endswith('.csv'): print(file + ' has been read') else: print(file + ' has been read and it was last modified on ' + xmod.strftime('%Y-%m-%d') + '. The name of the sheet that was read is: ' + sheet_read) else: files_with_no_columns.append(file_path) if len(files_with_no_columns) > 0: print(\"The following files do not contain the specified columns:\") for file_path in files_with_no_columns: print(file_path) if not extracted_data.empty: extracted_data = extracted_data.applymap(lambda s: s.upper() if type(s) == str else s).fillna('') extracted_data.to_csv(output_filename) print('Started at: {}'.format(start_time) + '. \\nEnded at: {}'.format(datetime.now()) + '. \\nTime elapsed (hh:mm:ss.ms) {}'.format(datetime.now() - start_time)) return extracted_data else: print(\"Specified columns do not exist in any file in the provided directory.\") print('Started at: {}'.format(start_time) + '. \\nEnded at: {}'.format(datetime.now()) + '. \\nTime elapsed (hh:mm:ss.ms) {}'.format(datetime.now() - start_time)) return None Explanation The function extract_data takes in the following parameters: directory_path: the path to the directory to search for Excel and CSV files columns_to_extract: a list of column names to extract from each file date_columns: a list of column names to parse as dates using pd.to_datetime() rows_to_skip: the number of rows to skip when reading each file output_filename: the path and filename to save the extracted data in a CSV file The function starts by creating an empty DataFrame called extracted_data. It also initializes variables files_with_no_columns and sheet_read to track files that do not contain the specified columns and to keep track of the current sheet being read when extracting data from Excel files. The list extracted_columns is created by copying the input columns_to_extract list and adding additional columns for the filename, subdirectory name, file creation date, and last modified date. The function then loops through all the files in the specified directory and its subdirectories using os.walk(). For each file, it checks if it has a “.xlsx”, “.xls”, or “.csv” extension. If the file is a CSV file, the function reads it into a DataFrame using pd.read_csv(), skipping the number of rows specified by rows_to_skip. It then checks if all the columns in columns_to_extract are present in the DataFrame. If so, it sets columns_found_outer to True and proceeds to the next step. If not, it sets columns_found_outer to False and moves on to the next file. If the file is an Excel file, the function reads all sheets in the file into a dictionary of DataFrames using pd.read_excel() and sheet_name=None. It then loops through all the sheets and all the columns in columns_to_extract, checking if each column is present in each sheet’s DataFrame. If all the columns are present in a sheet, it sets columns_found to True and proceeds to the next step. If not, it sets columns_found to False and moves on to the next sheet in the same Excel file. If at least one sheet contains all the specified columns, the function combines the DataFrames of all sheets into one using pd.concat(). If columns_found_outer is True, it extracts the filename, subdirectory name, file creation date, and last modified date using os.path.basename(), os.path.getctime(), and os.path.getmtime(), and adds them as new columns to the DataFrame. It then appends the DataFrame to the extracted_data DataFrame. If columns_found_outer is False, it increments the files_with_no_columns counter and prints a warning message. Finally, the function checks if any files contained the specified columns. If not, it returns None. Otherwise, it sorts the extracted_data DataFrame by filename and saves it to a CSV file using to_csv(). It then prints the number of files processed, the number of files that did not contain the specified columns, and the path to the output file. Sample Usage The function can be called in python as shown below: # set directory path directory_path = './Work' # Path for the directory where all the files containing data for extraction are to be searched # set the columns you want to extract columns_to_extract = ['Region','Country', 'Product Number','Quantity','Date of Sale'] date_col = ['Date of Sale'] # set the rows to skip skiprows = 0 #this basically will be number of rows in begining of the files which must be skipped to reach the header row of the data output_filename = 'Regional Sales Data.csv' #Call function df = extract_data(directory_path,columns_to_extract,date_col,skiprows,output_filename) Now, assuming there were 12 separate files for past 12 months inside the folder then so long as all those files, irrespective of whether they are csv or excel, have the columns Region,Country, Product Number,Quantity,Date of Sale; the function will read the files and extract the data and return it to the dataframe df. GUI Implementation A very basic GUI implementation of above function using PySimpleGUI with all codeis available here Usage The script can directly be copied to a Jupyter cell or can be run from terminal. Following command should ensure all dependencies are installed: py -m pip install pandas, numpy, pysimplegui Some things the GUI takes care of are: Allows selection of columns to be extracted from a sample .csv file Allows user to specify which of the selected columns should be parsed as date Gives a date based filename to output Shows colour coded log for which files were read in green and which were ignored in red background. Screenshots Some screenshots of the resulting app are as shown below: Empty Form Filled Form Displays extracted output Displays filename of the output and location where it is saved Colour coded log ","categories": [], + "tags": [], + "url": "/read-excel-csv-recursive/", + "teaser": null + },{ + "title": "Logseq Customisations for Project Management Template", + "excerpt":"Background While the overall planning of project timeline gets lot of attention in the world of software, the most important aspect of project management in my experience is maintaining and tracking Issues and Actions. This in normal project management practice is carried out through the use of an Actions Log and an Issues Log. In addition any medium complexity project invariably will have internal and external dependencies / constraints, risks which I tend to track on Constraints Log and Risks Log. Finally every project has decisions that I track on a Decisions Log. Additionally, any opportunities that I identify during the course of project that are not in the scope of my project I capture those too in an Opportunities Log Now all these logs have fairly standard fields so I created and started using an excel template and started calling it CARDIO Log short for log of Constraints, Actions, Risks, Decisions, Issues, Opportunities for a given project. This has worked well for me over the past 15 years or so but there are times where in order to maintain it meant cross linking an action to an issue or a risk and so on and more often than not it would become easier to just track actions in one of the other logs and that would make it a bit chaotic. That problem, however, is what I thought, can be resolved using Logseq especially after starting with the template by the Logseq community user Luhman and starting with his template and explanation provided here. Using logseq from browser when binary install is not possible When binary install is not possible, one can still use logseq from browser by navigating to the web app demo page from Chrome browser and then selecting a local directory where notes will be saved. It works perfectly fine except there is no option to install plugins. So inorder for issues table etc to work from broswer following steps will suffice: Option 1 Select the directory where your notes (or graphs in logseq speak) will be saved. Grant permission for local file access when asked by the browser. Now if you will press Ctrl+k it will open search box but the cusrsor goes to the omnibox (where you type the url for the page) and then you will have to either manually click into the logseq search or press esc key three times to be able to get the cursor back in the search box. It may be easier to just click on Search icon in left hand navigation at top of the screen (next to the hamburger menu). Alternatively configure the keyboard shortcut to Alt+k as that does not conflict with any other keyboard shortcut. To add custom shortcuts, you can navigate to the shortcuts page with g s. Press the blue button corresponding to a given shortcut and a modal should pop up. Press the keybinding you want and then press Save. Now search for logseq/custom.css and add the whole css from this gist. Then search for logseq/config.edn and add the whole edn from this gist If you were installing the binary, you can still copy the entire css and edn from above. If you do that, you can directly skip to the section - Create Templates. Option 2 Download the archive from my github repository here Unzip the downlaoded repository and delete the README.md file. Navigate to journals directory where there will be three files. Delete all these files. Navigate back and open the pages directory which will have 7 files. Except for content.md and templates.md, delete all other files. Now copy the logseq and pages directories into the directory where you want your notes saved. Open logseq from chrome and add the directory from step above where you copied the two folders. If you were installing the binary, you can follow option 2 and then there will be no need to read rest of this article :). Customisations Sample Project Log Preparation First we will ensure our actions log which will be generated using the Logseq’s inbuilt feature for To Do Lists displays fields that we want it to display. Press Ctrl+K and search for logseq/config.edn In the code block search for following lines: 1 2 3 4 5 6 7 ;; Advanced queries `:result-transform` function. ;; Transform the query result before displaying it. :query/result-transforms {:sort-by-priority (fn [result] (sort-by (fn [h] (get h :block/priority \"Z\")) result)) } Now replace these with following which is slightly reduced compared to the Source as I only needed “Deadline” for my purposes and I don’t use the “Scheduled” part of the Logseq task management feature: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 ;; Advanced queries `:result-transform` function. ;; Transform the query result before displaying it. :query/result-transforms {:sort-by-priority (fn [result] (sort-by (fn [h] (get h :block/priority \"Z\")) result)) :add-task-attrs (fn [result] (def months {1 \"January\" 2 \"February\" 3 \"March\" 4 \"April\" 5 \"May\" 6 \"June\" 7 \"July\" 8 \"August\" 9 \"September\" 10 \"October\" 11 \"November\" 12 \"December\"}) (def monthName (fn [dd] (get months (int dd)) )) \t ;; source: https://discuss.logseq.com/t/add-query-input-or-function-day-of-week/18361/12 (def days {0 \"Saturday\" 1 \"Sunday\" 2 \"Monday\" 3 \"Tuesday\" 4 \"Wednesday\" 5 \"Thursday\" 6 \"Friday\"}) (def weekDay (fn [date] (def month (quot (mod date 10000) 100)) (def month6 (quot (- month 8) 6)) (def year6 (+ (quot date 10000) month6)) (def yearnum (mod year6 100)) (def century (quot year6 100)) (def d (mod (+ (mod date 100) (quot (* 13 (inc (- month (* month6 12)))) 5) yearnum (quot yearnum 4) (quot century 4) (* 5 century)) 7)) (get days d) )) \t (def suffixes {0 \"th\" 1 \"st\" 2 \"nd\" 3 \"rd\" 4 \"th\" 5 \"th\" 6 \"th\" 7 \"th\" 8 \"th\" 9 \"th\"}) (def positionalSuffix (fn [dd] (if (or (= dd \"11\") (= dd \"12\") (= dd \"13\")) (get suffixes 0) (get suffixes (int (subs dd (count dd) 1))) ) )) \t (def token (fn [s] (str \"⟨\" s \"⟩\"))) (def format (-> (get (js->clj (call-api \"get_user_configs\")) \"preferredDateFormat\") (clojure.string/replace \"do\" (token \"1\")) (clojure.string/replace \"dd\" (token \"2\")) (clojure.string/replace \"d\" (token \"3\")) (clojure.string/replace \"EEEE\" (token \"4\")) (clojure.string/replace \"EEE\" (token \"5\")) (clojure.string/replace \"EE\" (token \"6\")) (clojure.string/replace \"E\" (token \"7\")) (clojure.string/replace \"MMMM\" (token \"8\")) (clojure.string/replace \"MMM\" (token \"9\")) (clojure.string/replace \"MM\" (token \"10\")) (clojure.string/replace \"M\" (token \"11\")) (clojure.string/replace \"yyyy\" (token \"12\")) (clojure.string/replace \"yy\" (token \"13\")) )) \t (def parseDate (fn [date] (if-not date nil (let [ regex (re-pattern \"(\\\\d{4})(\\\\d{2})(\\\\d{2})\") [_ yyyy mm dd] (re-matches regex (str date)) yy (subs yyyy 2 4) d (str (int dd)) do (str d (positionalSuffix dd)) mmmm (monthName mm) mmm (subs mmmm 0 3) m (str (int mm)) eeee (weekDay date) eee (subs eeee 0 3) ee (subs eeee 0 2) e eee ] (-> format (clojure.string/replace (token \"1\") do) (clojure.string/replace (token \"2\") dd) (clojure.string/replace (token \"3\") d) (clojure.string/replace (token \"4\") eeee) (clojure.string/replace (token \"5\") eee) (clojure.string/replace (token \"6\") ee) (clojure.string/replace (token \"7\") e) (clojure.string/replace (token \"8\") mmmm) (clojure.string/replace (token \"9\") mmm) (clojure.string/replace (token \"10\") mm) (clojure.string/replace (token \"11\") m) (clojure.string/replace (token \"12\") yyyy) (clojure.string/replace (token \"13\") yy) ) ) ) )) \t \t (map (fn [x] (update x :block/properties (fn [u] (-> u (assoc :marker (str (get x :block/marker)) ) (assoc :priority (str (get x :block/priority)) ) (assoc :deadline (parseDate (get x :block/deadline)) ) (assoc :repeated? (str (get x :block/repeated?)) ) ) )) ) result) ) } Plugins Click on Three Dots in top right corner of the screen and click on menu entry Plugins and then click on Marketplace Now install the following plugins: logseq-agenda logseq-automatic-linker logseq-datenlp-plugin logseq-diagrams-as-code logseq-doc logseq-emoji-picker-fork logseq-emoji-shortcodes logseq-luckysheet logseq-markdown-table logseq-paste-more logseq-plugin-automatic-url-title logseq-plugin-show-weekday-and-week-number logseq13-full-house logseq13-missing-commands Look and Feel Now, I quite like the Mia Quattro Theme that can either be installed as a theme from marketplace or just by including the following line in logseq/custom.css just under the comment /*Theme*/ right at the top of the file like so: 1 2 /*Theme*/ @import url('https://playerofgames.github.io/logseq-mia-theme/mia_quattro.css'); However this theme had some quirks which can be refined by adding following css overrides: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 /* Override tag and note color scheme for tags from mia_quattro theme */ a.tag{ font-size: 100%; color: var(--lx-accent-11,var(--ls-tag-text-color,hsl(var(--primary)))); } svg.note { color: var(--lx-accent-11, var(--rx-yellow-08)) } svg.tip { color: var(--lx-accent-11,var(--rx-blue-08)) } /* Selection lists (search, tag complete, etc.) */ #ui__ac .chosen, .chosen { \t--ls-primary-text-color: #ee0606; \t--ls-link-ref-text-color: #eaeaea; \t--ls-link-ref-text-hover-color: #fafafa; \t--ls-quaternary-background-color: var(--accent-dark-color); \t--ls-icon-color: var(--ls-primary-text-color); } #ui__ac .chosen { \tbackground-color: var(--accent-dark-color); } .menu-link.chosen { \tcolor: var(--ls-primary-text-color) !important; } /* to make todo checkbox visible */ .form-checkbox { \t--ls-page-checkbox-border-color: var(--accent-color); \tborder: 1px solid var(--ls-page-checkbox-border-color); \tborder-radius: 5px; \topacity: 1; } /* blockquote tweaks (reduce margin & padding and add custom colours) */ blockquote { padding: 8px 12px; border-left: 5px solid; border-left-color: var(--ls-page-blockquote-border-color, #7cfc00); margin: 0.3rem 0 !important; } blockquote.yellow { border-left-color: #ffe85580; } blockquote.blue { border-left-color: #84b5ff80; } blockquote.red { border-left-color: #ff558280; } .ls-block[data-refs-self^=\".blue\"] .blockquote{ border-left-color: #84b5ff80; } /* ==mark== tweaks */ mark { background: var(--ls-page-mark-bg-color); color: var(--ls-page-mark-color); padding: 1px 2px; margin: 0 2px; border-radius: 3px; } mark.yellow { background: var(--ls-page-mark-bg-color); color: var(--ls-page-mark-color); } mark.pink { background-color: #ff89be80; color: white; } mark.blue { background-color: #84b5ff80; color:white; } mark.green { background-color: #97ff9780; color: yellow; } mark.red { background-color: #ff558280; color: white; } mark.grey { background-color: #80808080; color: white; } mark.gray { background-color: #80808080; color: white; } mark.orange { background-color: #ffb86c80; color: white; } mark.purple { background-color: #c097ff80; color: white; } /* add traffic lights to prioritized tasks */ .priority[href=\"#/page/A\" i]::before { content: \"🔴\"; margin-right: 2px; } .priority[href=\"#/page/B\" i]::before { content: \"🟡\"; margin-right: 2px; } .priority[href=\"#/page/C\" i]::before { content: \"🟢\"; margin-right: 2px; } .opacity-50 { opacity: 1; } As our templates later will depend on v-kanban plugin but I did not want to include the whole css and also wanted to modify the icons it shows with Pros and Cons, I include the following css on logseq/custom.css: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 /* -- like dislike ----------------------------------------- */ .ls-block[data-refs-self*=\"pros\"] .block-children .bullet-container .bullet { display: none; } .ls-block[data-refs-self*=\"pros\"] .block-children .bullet-container:after { content: \"+\"; font-size: 20px; color: #1cd41c; } .ls-block[data-refs-self*=\"cons\"] .block-children .bullet-container:after { content: \"-\"; color: red; } a.tag[data-ref*=\"pros\"] { font-size: .8rem; background: #014935e0; color: rgb(202, 247, 118); padding: 0 6px 3px; border-radius: var(--ls-border-radius-low); border: 1px solid rgba(137, 207, 96, 0.925); } a.tag[data-ref*=\"cons\"] { font-size: 13px; background: #4b033bda; color: rgb(255, 116, 128); padding: 0 6px 3px; border-radius: var(--ls-border-radius-low); border: 1px solid rgba(182, 13, 41, 0.925); } a.tag[data-ref*=\"pros\"]:hover { filter: contrast(2) brightness(10); } a.tag[data-ref*=\"pros\"]:before { content: \"✅ \"; font-size: 13px } a.tag[data-ref*=\"cons\"]:before { content: \"❌ \"; font-size: 13px } a.tag[data-ref*=\"red\"]:before { content: \"🔴\"; margin-right: 2px; } a.tag[data-ref*=\"amber\"]:before { content: \"🟠\"; margin-right: 2px; } a.tag[data-ref*=\"green\"]:before { content: \"🟢\"; margin-right: 2px; } a.tag[data-ref*=\"on-hold\"]:before { content: \"🟣\"; margin-right: 2px; } a.tag[data-ref*=\"yellow\"]:before { content: \"🟡\"; margin-right: 2px; } a.tag[data-ref*=\"closed\"]:before { content: \"🔵\"; margin-right: 2px; } a.tag[data-ref*=\"no-go\"]:before { content: \"🚫\"; margin-right: 2px; } a.tag[data-ref*=\"merged\"]:before { content: \"⚪\"; margin-right: 2px; } /* -------------------------------- like dislike end ------ */ /*===========================================================*/ /* css columns view / kanban v20220510--------------------- */ /* use: inline tag #kanban, #kanban-small or #kanban-wXXX */ /* try: #kanban-w200,#kanban-w300, #kanban-w400 */ div[data-refs-self*=\"kanban\"]>.block-children-container.flex { width: 100%; } div[data-refs-self*=\"kanban\"]>.block-children-container.flex>.block-children.w-full { display: inline-flex; position: relative; overflow-x: auto !important; overflow-y: hidden; margin: 0 10px; } \t div[data-refs-self*=\"kanban\"]>.block-children-container.flex>.block-children.w-full>div.ls-block { display: inline-block; padding: 0; width: inherit; min-width: 200px; margin-right: 10px; } \t /* wide */ div[data-refs-self*=\"kanban-small\"]>.block-children-container.flex>.block-children, div[data-refs-self*=\"kanban-wide\"]>.block-children-container.flex>.block-children { min-width: 90vw; left: 50%; transform: translate(-50%); background-color: var(--ls-primary-background-color); overflow-x: scroll !important; overflow-y: hidden; margin: 10px 30px; } div[data-refs-self*=\"kanban-wide\"]>.block-children-container.flex>.block-children>div.ls-block { display: inline-block; min-width: 350px; padding: 8px 0px !important; font-size: 0.85rem; margin: 5px 0px; background-color: var(--ls-secondary-background-color); box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2); border-radius: var(--ls-border-radius-medium); } /* #kanbansmall : smaller font with hover zoom */ div[data-refs-self*=\"kanban-small\"]>.block-children-container.flex>.block-children>div.ls-block { display: inline-block; min-width: 350px; } div[data-refs-self*=\"kanban-small\"]>.block-children-container.flex>.block-children .block-content { font-size: 10px; font-weight: 300; } div[data-refs-self*=\"kanban-small\"]>.block-children-container.flex>.block-children .block-content:hover { font-size: 14px !important; min-width: 100px; } /* #kanban-w[100-300] : force width of the columns */ div[data-refs-self*=\"kanban-w100\"]>.block-children-container.flex>.block-children.w-full>div.ls-block { min-width: 100px; } div[data-refs-self*=\"kanban-w150\"]>.block-children-container.flex>.block-children.w-full>div.ls-block { min-width: 150px; } div[data-refs-self*=\"kanban-w200\"]>.block-children-container.flex>.block-children.w-full>div.ls-block { min-width: 200px; } div[data-refs-self*=\"kanban-w300\"]>.block-children-container.flex>.block-children.w-full>div.ls-block { min-width: 300px; } div[data-refs-self*=\"kanban-w400\"]>.block-children-container.flex>.block-children.w-full>div.ls-block { min-width: 400px; } div[data-refs-self*=\"kanban-fit\"]>.block-children-container.flex>.block-children.w-full>div.ls-block { min-width: 400px; width: max-content; } /* remove left border for kanbanized */ [data-refs-self*=\"kanban\"] .block-children-left-border { opacity: 0; } /* fix modal list not appearing*/ .block-children { overflow: visible !important; } .ls-block[data-refs-self*=\"kanban\"] .absolute-modal, .ls-block[data-refs-self*=\"kanban\"] #ui__ac { min-height: 80px; } /*--------------------------------------------- kanban end-- */ /*------------------expreimetal for better table view START-------------------*/ .table-wrapper { width: 100% !important; max-width: 100% !important; } table td { min-width:100px; word-wrap:break-word; } table th { word-break: keep-all; } /*------------------expreimetal for better table view END-------------------*/ In order to invoke some of the above tweaks, we will also create keyboard shortcuts and shortcodes to have a simpler way to change colour of the blockquote side border and highlights. So open logseq/config.edn and do the following: a) Search for: 1 2 3 4 5 6 7 8 9 ;; Macros replace texts and will make you more productive. ;; Example usage: ;; Change the :macros value below to: ;; {\"poem\" \"Rose is $1, violet's $2. Life's ordered: Org assists you.\"} ;; input \"{{poem red,blue}}\" ;; becomes ;; Rose is red, violet's blue. Life's ordered: Org assists you. :macros {} b) and replace above with: 1 2 3 4 5 6 7 8 9 10 11 12 ;; Macros replace texts and will make you more productive. ;; Example usage: ;; Change the :macros value below to: ;; {\"poem\" \"Rose is $1, violet's $2. Life's ordered: Org assists you.\"} ;; input \"{{poem red,blue}}\" ;; becomes ;; Rose is red, violet's blue. Life's ordered: Org assists you. :macros { \">\" \"<blockquote class='$1'>$2</blockquote>\" ;;usage {{ > orange,Text to be presented in the blockquote }} \"==\" \"<mark class='$1'>$2</mark>\" ;;usage {{ == red,Text to be highlighted without linebreak }} } c) search for: 1 2 3 4 5 6 7 8 9 ;; Add custom commands to the command palette ;; Example usage: ;; :commands ;; [ ;; [\"js\" \"Javascript\"] ;; [\"md\" \"Markdown\"] ;; ] :commands [] d) and replace above with: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ;; Add custom commands to the command palette ;; To quickly call these commands, just type / (backslash) followed by characters in square bracket :commands [ [\"bookmark [.b]\" [[:editor/input \"{{ renderer :template, Bookmark}}\" ]]], [\"date_today [dt]\" [[:editor/input \"{{renderer :template, Date Today}}\" ]]], [\"issue_table [.it]\" [[:editor/input \"{{renderer :template, Issue_table}}\" ]]], [\"issue [.is]\" [[:editor/input \"{{renderer :template, Issue}}\" ]]], [\"circle [.c]\" [[:editor/input \"{{renderer :template-view, circle-template, :color orange}}\" ]]], [\"Blue Highlighter [=b]\" [[:editor/input \"<mark class='blue'></mark>\" {:backward-pos 7}]]], [\"Green Highlighter [=g]\" [[:editor/input \"<mark class='green'></mark>\" {:backward-pos 7}]]], [\"Gray Highlighter [=gra]\" [[:edior/input \"<mark class='gray'></mark>\" {:backward-pos 7}]]], [\"Grey Highlighter [=gre]\" [[:editor/input \"<mark class='grey'></mark>\" {:backward-pos 7}]]], [\"Orange Highlighter [=o]\" [[:editor/input \"<mark class='orange'></mark>\" {:backward-pos 7}]]], [\"Pink Highlighter [=p]\" [[:editor/input \"<mark class='pink'></mark>\" {:backward-pos 7}]]], [\"Red Highlighter [=r]\" [[:editor/input \"<mark class='red'></mark>\" {:backward-pos 7}]]], [\"Yellow Highlighter [=y]\" [[:editor/input \"<mark class='yellow'></mark>\" {:backward-pos 7}]]], [\"Purple Highlighter [=pu]\" [[:editor/input \"<mark class='purple'></mark>\" {:backward-pos 7}]]], [\"Red Blockquote [>r]\" [[:editor/input \"<blockquote class='red'></blockquote>\" {:backward-pos 13}]]], [\"Yellow Blockquote [>y]\" [[:editor/input \"<blockquote class='yellow'></blockquote>\" {:backward-pos 13}]]], [\"Blue Blockquote [>b]\" [[:editor/input \"<blockquote class='blue'></blockquote>\" {:backward-pos 13}]]], ] Now, some of the short-codes above such as /.is, /.it, /dt and /.c will not work just yet because we have not created their associated template. We will get to that in next section. Create Templates Create a new page named templates. Press Ctrl+K and search for templates and open the page. Once on the page, click on Three dots in top right corner of the screen and from the drop down menu select Open in default app Here paste the following then save and close the default app: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 \t\ttitle:: templates \t\tvisibility:: false \t\ticon:: 🧾 \t\t \t\t- # Circle \t\t id:: 66b0d360-cde5-4e95-87b3-4e97a1e23bb5 \t\t template:: circle-template \t\t template-including-parent:: false \t\t arg-color:: red \t\t\t- <span class='circle' style='background: ``c.args.color``; display: inline-block; width: 15px; height: 15px; border-radius: 50%; vertical-align: middle;'></span> \t\t- # People Page \t\t template:: people page \t\t template-including-parent:: false \t\t\t- tags:: people \t\t\t icon:: 👨‍💼 \t\t\t- ## Tasks \t\t\t\t- query-table:: true \t\t\t\t query-properties:: [:priority :deadline :block] \t\t\t\t #+BEGIN_QUERY \t\t\t\t {:title [:h3 \"Owned\"] \t\t\t\t :query [:find (pull ?b [*]) \t\t\t\t :in $ ?tag \t\t\t\t :where \t\t\t\t [?b :block/marker ?marker] \t\t\t\t [(contains? #{\"TODO\" \"DOING\" \"NOW\" \"LATER\" \"WAITING\"} ?marker)] \t\t\t\t (page-ref ?b ?tag) \t\t\t\t [?ref :block/name \"project\"] \t\t\t\t (not [?b :block/refs ?ref])] \t\t\t\t :inputs [:query-page] \t\t\t\t :result-transform :add-task-attrs \t\t\t\t :breadcrumb-show? true \t\t\t\t :group-by-page? false \t\t\t\t :collapsed? false \t\t\t\t } \t\t\t\t #+END_QUERY \t\t\t\t- query-table:: true \t\t\t\t collapsed:: true \t\t\t\t query-properties:: [:marker :deadline :block] \t\t\t\t #+BEGIN_QUERY \t\t\t\t {:title [:h3 \"Closed or Cancelled\"] \t\t\t\t :query [:find (pull ?b [*]) \t\t\t\t :in $ ?tag \t\t\t\t :where \t\t\t\t [?b :block/marker ?marker] \t\t\t\t [(contains? #{\"DONE\" \"CANCELLED\" \"CANCELED\" } ?marker)] \t\t\t\t (page-ref ?b ?tag) \t\t\t\t [?ref :block/name \"project\"] \t\t\t\t (not [?b :block/refs ?ref])] \t\t\t\t :inputs [:query-page] \t\t\t\t :result-transform :add-task-attrs \t\t\t\t :breadcrumb-show? true \t\t\t\t :group-by-page? false \t\t\t\t :collapsed? true \t\t\t\t } \t\t\t\t #+END_QUERY \t\t- \t\t- # Issue Table \t\t template:: Issue_table \t\t template-including-parent:: false \t\t\t- #.tabular \t\t\t\t- ## 01 ``{|}``Issue Title``{|}`` \t\t\t\t \t\t\t\t owner:: \t\t\t\t status:: #Red \t\t\t\t #Issues #.v-kanban \t\t\t\t\t- ### **Issue Description** \t\t\t\t\t\t- \t\t\t\t\t- ### **Updates** \t\t\t\t\t\t- <% Today %> : \t\t\t\t- ## 02 Issue Title \t\t\t\t owner:: \t\t\t\t status:: #Amber \t\t\t\t #Issues #.v-kanban \t\t\t\t\t- ### **Issue Description** \t\t\t\t\t\t- \t\t\t\t\t- ### **Updates** \t\t\t\t\t\t- <% Today %> : \t\t\t\t- ## 03 Issue Title \t\t\t\t owner:: \t\t\t\t status:: #Yellow \t\t\t\t #Issues #.v-kanban \t\t\t\t\t- ### **Issue Description** \t\t\t\t\t\t- \t\t\t\t\t- ### **Updates** \t\t\t\t\t\t- <% Today %> : \t\t\t\t- ## 04 Issue Title \t\t\t\t owner:: \t\t\t\t status:: #Green \t\t\t\t #Issues #.v-kanban \t\t\t\t\t- ### **Issue Description** \t\t\t\t\t\t- \t\t\t\t\t- ### **Updates** \t\t\t\t\t\t- <% Today %> : \t\t\t\t- ## 05 Issue Title \t\t\t\t owner:: \t\t\t\t status:: #on-hold \t\t\t\t #Issues #.v-kanban \t\t\t\t\t- ### **Issue Description** \t\t\t\t\t\t- \t\t\t\t\t- ### **Updates** \t\t\t\t\t\t- <% Today %> : \t\t\t\t- ## 06 Issue Title \t\t\t\t owner:: \t\t\t\t status:: #no-go \t\t\t\t #Issues #.v-kanban \t\t\t\t\t- ### **Issue Description** \t\t\t\t\t\t- \t\t\t\t\t- ### **Updates** \t\t\t\t\t\t- <% Today %> : \t\t\t\t- ## 07 Issue Title \t\t\t\t owner:: \t\t\t\t status:: #closed \t\t\t\t #Issues #.v-kanban \t\t\t\t\t- ### **Issue Description** \t\t\t\t\t\t- \t\t\t\t\t- ### **Updates** \t\t\t\t\t\t- <% Today %> : \t\t- # Issue \t\t template:: Issue \t\t template-including-parent:: false \t\t\t- ## ``{|}``01 Issue Title``{|}`` \t\t\t \t\t\t owner:: \t\t\t status:: #Red \t\t\t #Issues #.v-kanban \t\t\t\t- ### **Issue Description** \t\t\t\t\t- \t\t\t\t- ### **Updates** \t\t\t\t\t- <% Today %> : \t\t- # Date Today \t\t template:: Date Today \t\t template-including-parent:: false \t\t\t- **``today``** ``{|}`` \t\t- # [[Bookmarks]] \t\t template:: Bookmark \t\t template-including-parent:: false \t\t\t- url:: ``{|}`` \t\t\t \t\t\t topic:: \t\t- # [[Project page]] \t\t template:: project page \t\t template-including-parent:: false \t\t\t- tags:: project page \t\t\t icon:: 📂 \t\t\t- ## Project Meta \t\t\t collapsed:: true \t\t\t\t- DOING [#B] #project <% current page %> \t\t\t- ## Actions Log \t\t\t\t- query-properties:: [:deadline :priority :block] \t\t\t\t #+BEGIN_QUERY \t\t\t\t {:title [:h4 \"On ToDo List\"] \t\t\t\t :query [:find (pull ?b [*]) \t\t\t\t :in $ ?tag \t\t\t\t :where \t\t\t\t [?b :block/marker ?marker] \t\t\t\t [(contains? #{\"TODO\"} ?marker)] \t\t\t\t (page-ref ?b ?tag) \t\t\t\t [?ref :block/name \"project\"] \t\t\t\t (not [?b :block/refs ?ref])] \t\t\t\t :inputs [:query-page] \t\t\t\t :result-transform :add-task-attrs \t\t\t\t :breadcrumb-show? true \t\t\t\t } \t\t\t\t #+END_QUERY \t\t\t\t- query-table:: false \t\t\t\t query-properties:: [:page :block] \t\t\t\t #+BEGIN_QUERY \t\t\t\t {:title [:h4 \"Ongoing Tasks\"] \t\t\t\t :query [:find (pull ?b [*]) \t\t\t\t :in $ ?tag \t\t\t\t :where \t\t\t\t [?b :block/marker ?marker] \t\t\t\t [(contains? #{\"DOING\" \"NOW\" \"LATER\" \"WAITING\"} ?marker)] \t\t\t\t (page-ref ?b ?tag) \t\t\t\t [?ref :block/name \"project\"] \t\t\t\t (not [?b :block/refs ?ref])] \t\t\t\t :inputs [:query-page] \t\t\t\t :result-transform :add-task-attrs \t\t\t\t :breadcrumb-show? true \t\t\t\t :group-by-page? false \t\t\t\t :collapsed? false \t\t\t\t } \t\t\t\t #+END_QUERY \t\t\t\t- query-properties:: [:deadline :priority :block] \t\t\t\t collapsed:: true \t\t\t\t #+BEGIN_QUERY \t\t\t\t {:title [:h4 \"Completed Tasks\"] \t\t\t\t :query [:find (pull ?b [*]) \t\t\t\t :in $ ?tag \t\t\t\t :where \t\t\t\t [?b :block/marker ?marker] \t\t\t\t [(contains? #{\"DONE\"} ?marker)] \t\t\t\t (page-ref ?b ?tag) \t\t\t\t [?ref :block/name \"project\"] \t\t\t\t (not [?b :block/refs ?ref])] \t\t\t\t :inputs [:query-page] \t\t\t\t :result-transform :add-task-attrs \t\t\t\t :breadcrumb-show? true \t\t\t\t :table-view? false \t\t\t\t :collapsed? true \t\t\t\t } \t\t\t\t #+END_QUERY \t\t\t- ## Issues Log \t\t\t\t- query-sort-by:: status \t\t\t\t query-table:: true \t\t\t\t query-sort-desc:: true \t\t\t\t query-properties:: [:block :owner :status] \t\t\t\t #+BEGIN_QUERY \t\t\t\t {:title [:h4 \"Open Issues\"] \t\t\t\t :query [:find (pull ?b [*]) \t\t\t\t :in $ ?query-page \t\t\t\t :where \t\t\t\t [?p :block/name ?query-page] \t\t\t\t \t [?tag2 :block/name \"issues\"] \t\t\t\t \t [?b :block/refs ?tag2] \t\t\t\t \t [?tag1 :block/name \"closed\"] \t\t\t\t \t (not [?b :block/refs ?tag1]) \t\t\t\t [?b :block/refs ?p] \t\t\t\t [?ref :block/name \"project\"] \t\t\t\t (not [?b :block/refs ?ref]) \t\t\t\t ] \t\t\t\t :inputs [:query-page] \t\t\t\t :breadcrumb-show? false \t\t\t\t :table-view? true \t\t\t\t :group-by-page? false \t\t\t\t :collapsed? false \t\t\t\t } \t\t\t\t #+END_QUERY \t\t\t\t- query-properties:: [:block :owner :status] \t\t\t\t collapsed:: true \t\t\t\t #+BEGIN_QUERY \t\t\t\t {:title [:h4 \"Closed Issues\"] \t\t\t\t :query [:find (pull ?b [*]) \t\t\t\t :in $ ?query-page \t\t\t\t :where \t\t\t\t [?p :block/name ?query-page] \t\t\t\t \t [?tag2 :block/name \"issues\"] \t\t\t\t \t [?b :block/refs ?tag2] \t\t\t\t \t [?tag1 :block/name \"closed\"] \t\t\t\t \t [?b :block/refs ?tag1] \t\t\t\t [?b :block/refs ?p] \t\t\t\t [?ref :block/name \"project\"] \t\t\t\t (not [?b :block/refs ?ref]) \t\t\t\t ] \t\t\t\t :inputs [:query-page] \t\t\t\t :breadcrumb-show? false \t\t\t\t :table-view? true \t\t\t\t :group-by-page? false \t\t\t\t :collapsed? true \t\t\t\t } \t\t\t\t #+END_QUERY \t\t\t- #+BEGIN_QUERY \t\t\t {:title [:h2 \"Project Notes\"] \t\t\t :query [:find (pull ?b [*]) \t\t\t :in $ ?query-page \t\t\t :where \t\t\t [?p :block/name ?query-page] \t\t\t [?b :block/refs ?p] \t\t\t \t [?tag2 :block/name \"issues\"] \t\t\t \t (not [?b :block/refs ?tag2]) \t\t\t [?ref :block/name \"project\"] \t\t\t (not [?b :block/refs ?ref]) \t\t\t (not [?b :block/marker _]) \t\t\t ] \t\t\t :inputs [:query-page] \t\t\t :result-transform (fn [result] \t\t\t (sort-by (fn [b] \t\t\t (get b :block/created-at \"A\")) result)) \t\t\t :breadcrumb-show? false \t\t\t :group-by-page? true \t\t\t :collapsed? false \t\t\t } \t\t\t #+END_QUERY \t\t- Contents page Press Ctrl+K and search for contents and open the page. Once on the page, click on Three dots in top right corner of the screen and from the drop down menu select Open in default app Here paste the following then save and close the default app: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - query-properties:: [:icon :page :updated-at] #+BEGIN_QUERY {:title [:h1 \"⏳ Ongoing Projects\"] :query [:find (pull ?page [*]) :where [?block :block/page ?page] [?page :block/name ?pagename] [?block :block/marker ?marker] [(contains? #{\"DOING\"} ?marker)] (page-ref ?block \"project\") (not [?page :block/name \"templates\"]) ] :breadcrumb-show? false :collapsed? false} #+END_QUERY - query-properties:: [:icon :page :updated-at] #+BEGIN_QUERY { :title [:h1 \"👨‍💼People\"] ;; ---- Get every block into variable ?block :query [:find (pull ?block [*]) ;; ---- filter command :where ;; ---- get page name (lowercase) from the special page block into variable ?pagename [?block :block/name ?pagename] ;; ---- Select if block is a special page block and has a single property (arg1) with value arg2 (page-property ?block :tags \"people\") ] } #+END_QUERY Sample Content Page Usage Sample Day to Day Capture Create Project To create a CARDIO Log for a project, first create a new page and give it name of the Project. Next, open the newly created project page. Once opened, press Ctrl+t and select project page template and press enter. This will create all relevant sections for the Project. Create User Page Create a new page with the user / resource name. Open the newly created page. Once opened, press Ctrl+t and select people page template and press enter. This will create all relevant sections to track actions for this resource. Create New Issues Now for everyday usage, each time there is a new issue or a number of issues they can simply be recorded by typing /.is for single issue and /.it for a table of issues. Once the issue or issues table is created, all usual fields can be filled but most importantly the issue title must include hashtag for the project that the issue belongs to. So an issue for project called North Star must have #[[North Star]] in the title. As an example say the issue is to do with lack of resources then the title should be #[[North Star]]: Lack of Resources The issue status or any topic to be highlighted with RAG status can be tagged with following tags: Tag Circle Appended #red 🔴 #amber 🟠 #green 🟢 #on-hold 🟣 #yellow 🟡 #Closed 🔵 #no-go 🚫 Circles with additional colours can be created using the template. To do so, press /.c and press enter. This should then put following text on the editor: ` {{renderer :template-view, circle-template, :color orange}} ` Now just replace orange in above with the colour desired. Maintain Issues Log Reviewing the logs is as simple as opening the Contents page, clicking the project name and scrolling down to the Issues Log section. Now if there are actions identified during the review of an issue, they can be simply added in the updates section under the date of review which can be quickly added by pressing Ctrl+Shift+D and then noting down the action as a sub bullet TODO [#A] Do Something /deadline I usually log decisions against a logged issue or if they resulted from a general discussion, just as #<projectname> #decision. Day to day notes All the project relevant notes will be automatically collected on the project’s page so long as they have been tagged with the project name so for the day to day notes all that is required is add entires in the Journal. Blockquotes As we added css for different coloured borders of blockquotes and also created shortcode and keyboard shortcut we can do this in multiple ways: Type />band press enter. This will place <blockquote class='blue'></blockquote> and the quote can be written between the tags. Type the following, replacing yellow with one of the predefined colours: yellow, pink, blue, green, red, grey, gray, orange or purple. {{> yellow,Some yellow quote}} Coloured Highlight Type /=y, select Yellow Highlighter from pop-up menu and press enter. This will place <mark class='yellow'></mark> and text to be highlighted can be written between the tags. It will be presented as Yellow Highlight Type the following and it will be presented as This text is highlighted. {{== pink,pink highlight}} ","categories": [], + "tags": [], + "url": "/logseq-customisation-for-project-management-teamplate/", + "teaser": null + }] diff --git a/assets/js/lunr/lunr.js b/assets/js/lunr/lunr.js new file mode 100644 index 0000000..6aa370f --- /dev/null +++ b/assets/js/lunr/lunr.js @@ -0,0 +1,3475 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + */ + +;(function(){ + +/** + * A convenience function for configuring and constructing + * a new lunr Index. + * + * A lunr.Builder instance is created and the pipeline setup + * with a trimmer, stop word filter and stemmer. + * + * This builder object is yielded to the configuration function + * that is passed as a parameter, allowing the list of fields + * and other builder parameters to be customised. + * + * All documents _must_ be added within the passed config function. + * + * @example + * var idx = lunr(function () { + * this.field('title') + * this.field('body') + * this.ref('id') + * + * documents.forEach(function (doc) { + * this.add(doc) + * }, this) + * }) + * + * @see {@link lunr.Builder} + * @see {@link lunr.Pipeline} + * @see {@link lunr.trimmer} + * @see {@link lunr.stopWordFilter} + * @see {@link lunr.stemmer} + * @namespace {function} lunr + */ +var lunr = function (config) { + var builder = new lunr.Builder + + builder.pipeline.add( + lunr.trimmer, + lunr.stopWordFilter, + lunr.stemmer + ) + + builder.searchPipeline.add( + lunr.stemmer + ) + + config.call(builder, builder) + return builder.build() +} + +lunr.version = "2.3.9" +/*! + * lunr.utils + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A namespace containing utils for the rest of the lunr library + * @namespace lunr.utils + */ +lunr.utils = {} + +/** + * Print a warning message to the console. + * + * @param {String} message The message to be printed. + * @memberOf lunr.utils + * @function + */ +lunr.utils.warn = (function (global) { + /* eslint-disable no-console */ + return function (message) { + if (global.console && console.warn) { + console.warn(message) + } + } + /* eslint-enable no-console */ +})(this) + +/** + * Convert an object to a string. + * + * In the case of `null` and `undefined` the function returns + * the empty string, in all other cases the result of calling + * `toString` on the passed object is returned. + * + * @param {Any} obj The object to convert to a string. + * @return {String} string representation of the passed object. + * @memberOf lunr.utils + */ +lunr.utils.asString = function (obj) { + if (obj === void 0 || obj === null) { + return "" + } else { + return obj.toString() + } +} + +/** + * Clones an object. + * + * Will create a copy of an existing object such that any mutations + * on the copy cannot affect the original. + * + * Only shallow objects are supported, passing a nested object to this + * function will cause a TypeError. + * + * Objects with primitives, and arrays of primitives are supported. + * + * @param {Object} obj The object to clone. + * @return {Object} a clone of the passed object. + * @throws {TypeError} when a nested object is passed. + * @memberOf Utils + */ +lunr.utils.clone = function (obj) { + if (obj === null || obj === undefined) { + return obj + } + + var clone = Object.create(null), + keys = Object.keys(obj) + + for (var i = 0; i < keys.length; i++) { + var key = keys[i], + val = obj[key] + + if (Array.isArray(val)) { + clone[key] = val.slice() + continue + } + + if (typeof val === 'string' || + typeof val === 'number' || + typeof val === 'boolean') { + clone[key] = val + continue + } + + throw new TypeError("clone is not deep and does not support nested objects") + } + + return clone +} +lunr.FieldRef = function (docRef, fieldName, stringValue) { + this.docRef = docRef + this.fieldName = fieldName + this._stringValue = stringValue +} + +lunr.FieldRef.joiner = "/" + +lunr.FieldRef.fromString = function (s) { + var n = s.indexOf(lunr.FieldRef.joiner) + + if (n === -1) { + throw "malformed field ref string" + } + + var fieldRef = s.slice(0, n), + docRef = s.slice(n + 1) + + return new lunr.FieldRef (docRef, fieldRef, s) +} + +lunr.FieldRef.prototype.toString = function () { + if (this._stringValue == undefined) { + this._stringValue = this.fieldName + lunr.FieldRef.joiner + this.docRef + } + + return this._stringValue +} +/*! + * lunr.Set + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A lunr set. + * + * @constructor + */ +lunr.Set = function (elements) { + this.elements = Object.create(null) + + if (elements) { + this.length = elements.length + + for (var i = 0; i < this.length; i++) { + this.elements[elements[i]] = true + } + } else { + this.length = 0 + } +} + +/** + * A complete set that contains all elements. + * + * @static + * @readonly + * @type {lunr.Set} + */ +lunr.Set.complete = { + intersect: function (other) { + return other + }, + + union: function () { + return this + }, + + contains: function () { + return true + } +} + +/** + * An empty set that contains no elements. + * + * @static + * @readonly + * @type {lunr.Set} + */ +lunr.Set.empty = { + intersect: function () { + return this + }, + + union: function (other) { + return other + }, + + contains: function () { + return false + } +} + +/** + * Returns true if this set contains the specified object. + * + * @param {object} object - Object whose presence in this set is to be tested. + * @returns {boolean} - True if this set contains the specified object. + */ +lunr.Set.prototype.contains = function (object) { + return !!this.elements[object] +} + +/** + * Returns a new set containing only the elements that are present in both + * this set and the specified set. + * + * @param {lunr.Set} other - set to intersect with this set. + * @returns {lunr.Set} a new set that is the intersection of this and the specified set. + */ + +lunr.Set.prototype.intersect = function (other) { + var a, b, elements, intersection = [] + + if (other === lunr.Set.complete) { + return this + } + + if (other === lunr.Set.empty) { + return other + } + + if (this.length < other.length) { + a = this + b = other + } else { + a = other + b = this + } + + elements = Object.keys(a.elements) + + for (var i = 0; i < elements.length; i++) { + var element = elements[i] + if (element in b.elements) { + intersection.push(element) + } + } + + return new lunr.Set (intersection) +} + +/** + * Returns a new set combining the elements of this and the specified set. + * + * @param {lunr.Set} other - set to union with this set. + * @return {lunr.Set} a new set that is the union of this and the specified set. + */ + +lunr.Set.prototype.union = function (other) { + if (other === lunr.Set.complete) { + return lunr.Set.complete + } + + if (other === lunr.Set.empty) { + return this + } + + return new lunr.Set(Object.keys(this.elements).concat(Object.keys(other.elements))) +} +/** + * A function to calculate the inverse document frequency for + * a posting. This is shared between the builder and the index + * + * @private + * @param {object} posting - The posting for a given term + * @param {number} documentCount - The total number of documents. + */ +lunr.idf = function (posting, documentCount) { + var documentsWithTerm = 0 + + for (var fieldName in posting) { + if (fieldName == '_index') continue // Ignore the term index, its not a field + documentsWithTerm += Object.keys(posting[fieldName]).length + } + + var x = (documentCount - documentsWithTerm + 0.5) / (documentsWithTerm + 0.5) + + return Math.log(1 + Math.abs(x)) +} + +/** + * A token wraps a string representation of a token + * as it is passed through the text processing pipeline. + * + * @constructor + * @param {string} [str=''] - The string token being wrapped. + * @param {object} [metadata={}] - Metadata associated with this token. + */ +lunr.Token = function (str, metadata) { + this.str = str || "" + this.metadata = metadata || {} +} + +/** + * Returns the token string that is being wrapped by this object. + * + * @returns {string} + */ +lunr.Token.prototype.toString = function () { + return this.str +} + +/** + * A token update function is used when updating or optionally + * when cloning a token. + * + * @callback lunr.Token~updateFunction + * @param {string} str - The string representation of the token. + * @param {Object} metadata - All metadata associated with this token. + */ + +/** + * Applies the given function to the wrapped string token. + * + * @example + * token.update(function (str, metadata) { + * return str.toUpperCase() + * }) + * + * @param {lunr.Token~updateFunction} fn - A function to apply to the token string. + * @returns {lunr.Token} + */ +lunr.Token.prototype.update = function (fn) { + this.str = fn(this.str, this.metadata) + return this +} + +/** + * Creates a clone of this token. Optionally a function can be + * applied to the cloned token. + * + * @param {lunr.Token~updateFunction} [fn] - An optional function to apply to the cloned token. + * @returns {lunr.Token} + */ +lunr.Token.prototype.clone = function (fn) { + fn = fn || function (s) { return s } + return new lunr.Token (fn(this.str, this.metadata), this.metadata) +} +/*! + * lunr.tokenizer + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A function for splitting a string into tokens ready to be inserted into + * the search index. Uses `lunr.tokenizer.separator` to split strings, change + * the value of this property to change how strings are split into tokens. + * + * This tokenizer will convert its parameter to a string by calling `toString` and + * then will split this string on the character in `lunr.tokenizer.separator`. + * Arrays will have their elements converted to strings and wrapped in a lunr.Token. + * + * Optional metadata can be passed to the tokenizer, this metadata will be cloned and + * added as metadata to every token that is created from the object to be tokenized. + * + * @static + * @param {?(string|object|object[])} obj - The object to convert into tokens + * @param {?object} metadata - Optional metadata to associate with every token + * @returns {lunr.Token[]} + * @see {@link lunr.Pipeline} + */ +lunr.tokenizer = function (obj, metadata) { + if (obj == null || obj == undefined) { + return [] + } + + if (Array.isArray(obj)) { + return obj.map(function (t) { + return new lunr.Token( + lunr.utils.asString(t).toLowerCase(), + lunr.utils.clone(metadata) + ) + }) + } + + var str = obj.toString().toLowerCase(), + len = str.length, + tokens = [] + + for (var sliceEnd = 0, sliceStart = 0; sliceEnd <= len; sliceEnd++) { + var char = str.charAt(sliceEnd), + sliceLength = sliceEnd - sliceStart + + if ((char.match(lunr.tokenizer.separator) || sliceEnd == len)) { + + if (sliceLength > 0) { + var tokenMetadata = lunr.utils.clone(metadata) || {} + tokenMetadata["position"] = [sliceStart, sliceLength] + tokenMetadata["index"] = tokens.length + + tokens.push( + new lunr.Token ( + str.slice(sliceStart, sliceEnd), + tokenMetadata + ) + ) + } + + sliceStart = sliceEnd + 1 + } + + } + + return tokens +} + +/** + * The separator used to split a string into tokens. Override this property to change the behaviour of + * `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens. + * + * @static + * @see lunr.tokenizer + */ +lunr.tokenizer.separator = /[\s\-]+/ +/*! + * lunr.Pipeline + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * lunr.Pipelines maintain an ordered list of functions to be applied to all + * tokens in documents entering the search index and queries being ran against + * the index. + * + * An instance of lunr.Index created with the lunr shortcut will contain a + * pipeline with a stop word filter and an English language stemmer. Extra + * functions can be added before or after either of these functions or these + * default functions can be removed. + * + * When run the pipeline will call each function in turn, passing a token, the + * index of that token in the original list of all tokens and finally a list of + * all the original tokens. + * + * The output of functions in the pipeline will be passed to the next function + * in the pipeline. To exclude a token from entering the index the function + * should return undefined, the rest of the pipeline will not be called with + * this token. + * + * For serialisation of pipelines to work, all functions used in an instance of + * a pipeline should be registered with lunr.Pipeline. Registered functions can + * then be loaded. If trying to load a serialised pipeline that uses functions + * that are not registered an error will be thrown. + * + * If not planning on serialising the pipeline then registering pipeline functions + * is not necessary. + * + * @constructor + */ +lunr.Pipeline = function () { + this._stack = [] +} + +lunr.Pipeline.registeredFunctions = Object.create(null) + +/** + * A pipeline function maps lunr.Token to lunr.Token. A lunr.Token contains the token + * string as well as all known metadata. A pipeline function can mutate the token string + * or mutate (or add) metadata for a given token. + * + * A pipeline function can indicate that the passed token should be discarded by returning + * null, undefined or an empty string. This token will not be passed to any downstream pipeline + * functions and will not be added to the index. + * + * Multiple tokens can be returned by returning an array of tokens. Each token will be passed + * to any downstream pipeline functions and all will returned tokens will be added to the index. + * + * Any number of pipeline functions may be chained together using a lunr.Pipeline. + * + * @interface lunr.PipelineFunction + * @param {lunr.Token} token - A token from the document being processed. + * @param {number} i - The index of this token in the complete list of tokens for this document/field. + * @param {lunr.Token[]} tokens - All tokens for this document/field. + * @returns {(?lunr.Token|lunr.Token[])} + */ + +/** + * Register a function with the pipeline. + * + * Functions that are used in the pipeline should be registered if the pipeline + * needs to be serialised, or a serialised pipeline needs to be loaded. + * + * Registering a function does not add it to a pipeline, functions must still be + * added to instances of the pipeline for them to be used when running a pipeline. + * + * @param {lunr.PipelineFunction} fn - The function to check for. + * @param {String} label - The label to register this function with + */ +lunr.Pipeline.registerFunction = function (fn, label) { + if (label in this.registeredFunctions) { + lunr.utils.warn('Overwriting existing registered function: ' + label) + } + + fn.label = label + lunr.Pipeline.registeredFunctions[fn.label] = fn +} + +/** + * Warns if the function is not registered as a Pipeline function. + * + * @param {lunr.PipelineFunction} fn - The function to check for. + * @private + */ +lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) { + var isRegistered = fn.label && (fn.label in this.registeredFunctions) + + if (!isRegistered) { + lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn) + } +} + +/** + * Loads a previously serialised pipeline. + * + * All functions to be loaded must already be registered with lunr.Pipeline. + * If any function from the serialised data has not been registered then an + * error will be thrown. + * + * @param {Object} serialised - The serialised pipeline to load. + * @returns {lunr.Pipeline} + */ +lunr.Pipeline.load = function (serialised) { + var pipeline = new lunr.Pipeline + + serialised.forEach(function (fnName) { + var fn = lunr.Pipeline.registeredFunctions[fnName] + + if (fn) { + pipeline.add(fn) + } else { + throw new Error('Cannot load unregistered function: ' + fnName) + } + }) + + return pipeline +} + +/** + * Adds new functions to the end of the pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction[]} functions - Any number of functions to add to the pipeline. + */ +lunr.Pipeline.prototype.add = function () { + var fns = Array.prototype.slice.call(arguments) + + fns.forEach(function (fn) { + lunr.Pipeline.warnIfFunctionNotRegistered(fn) + this._stack.push(fn) + }, this) +} + +/** + * Adds a single function after a function that already exists in the + * pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline. + * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline. + */ +lunr.Pipeline.prototype.after = function (existingFn, newFn) { + lunr.Pipeline.warnIfFunctionNotRegistered(newFn) + + var pos = this._stack.indexOf(existingFn) + if (pos == -1) { + throw new Error('Cannot find existingFn') + } + + pos = pos + 1 + this._stack.splice(pos, 0, newFn) +} + +/** + * Adds a single function before a function that already exists in the + * pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline. + * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline. + */ +lunr.Pipeline.prototype.before = function (existingFn, newFn) { + lunr.Pipeline.warnIfFunctionNotRegistered(newFn) + + var pos = this._stack.indexOf(existingFn) + if (pos == -1) { + throw new Error('Cannot find existingFn') + } + + this._stack.splice(pos, 0, newFn) +} + +/** + * Removes a function from the pipeline. + * + * @param {lunr.PipelineFunction} fn The function to remove from the pipeline. + */ +lunr.Pipeline.prototype.remove = function (fn) { + var pos = this._stack.indexOf(fn) + if (pos == -1) { + return + } + + this._stack.splice(pos, 1) +} + +/** + * Runs the current list of functions that make up the pipeline against the + * passed tokens. + * + * @param {Array} tokens The tokens to run through the pipeline. + * @returns {Array} + */ +lunr.Pipeline.prototype.run = function (tokens) { + var stackLength = this._stack.length + + for (var i = 0; i < stackLength; i++) { + var fn = this._stack[i] + var memo = [] + + for (var j = 0; j < tokens.length; j++) { + var result = fn(tokens[j], j, tokens) + + if (result === null || result === void 0 || result === '') continue + + if (Array.isArray(result)) { + for (var k = 0; k < result.length; k++) { + memo.push(result[k]) + } + } else { + memo.push(result) + } + } + + tokens = memo + } + + return tokens +} + +/** + * Convenience method for passing a string through a pipeline and getting + * strings out. This method takes care of wrapping the passed string in a + * token and mapping the resulting tokens back to strings. + * + * @param {string} str - The string to pass through the pipeline. + * @param {?object} metadata - Optional metadata to associate with the token + * passed to the pipeline. + * @returns {string[]} + */ +lunr.Pipeline.prototype.runString = function (str, metadata) { + var token = new lunr.Token (str, metadata) + + return this.run([token]).map(function (t) { + return t.toString() + }) +} + +/** + * Resets the pipeline by removing any existing processors. + * + */ +lunr.Pipeline.prototype.reset = function () { + this._stack = [] +} + +/** + * Returns a representation of the pipeline ready for serialisation. + * + * Logs a warning if the function has not been registered. + * + * @returns {Array} + */ +lunr.Pipeline.prototype.toJSON = function () { + return this._stack.map(function (fn) { + lunr.Pipeline.warnIfFunctionNotRegistered(fn) + + return fn.label + }) +} +/*! + * lunr.Vector + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A vector is used to construct the vector space of documents and queries. These + * vectors support operations to determine the similarity between two documents or + * a document and a query. + * + * Normally no parameters are required for initializing a vector, but in the case of + * loading a previously dumped vector the raw elements can be provided to the constructor. + * + * For performance reasons vectors are implemented with a flat array, where an elements + * index is immediately followed by its value. E.g. [index, value, index, value]. This + * allows the underlying array to be as sparse as possible and still offer decent + * performance when being used for vector calculations. + * + * @constructor + * @param {Number[]} [elements] - The flat list of element index and element value pairs. + */ +lunr.Vector = function (elements) { + this._magnitude = 0 + this.elements = elements || [] +} + + +/** + * Calculates the position within the vector to insert a given index. + * + * This is used internally by insert and upsert. If there are duplicate indexes then + * the position is returned as if the value for that index were to be updated, but it + * is the callers responsibility to check whether there is a duplicate at that index + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @returns {Number} + */ +lunr.Vector.prototype.positionForIndex = function (index) { + // For an empty vector the tuple can be inserted at the beginning + if (this.elements.length == 0) { + return 0 + } + + var start = 0, + end = this.elements.length / 2, + sliceLength = end - start, + pivotPoint = Math.floor(sliceLength / 2), + pivotIndex = this.elements[pivotPoint * 2] + + while (sliceLength > 1) { + if (pivotIndex < index) { + start = pivotPoint + } + + if (pivotIndex > index) { + end = pivotPoint + } + + if (pivotIndex == index) { + break + } + + sliceLength = end - start + pivotPoint = start + Math.floor(sliceLength / 2) + pivotIndex = this.elements[pivotPoint * 2] + } + + if (pivotIndex == index) { + return pivotPoint * 2 + } + + if (pivotIndex > index) { + return pivotPoint * 2 + } + + if (pivotIndex < index) { + return (pivotPoint + 1) * 2 + } +} + +/** + * Inserts an element at an index within the vector. + * + * Does not allow duplicates, will throw an error if there is already an entry + * for this index. + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @param {Number} val - The value to be inserted into the vector. + */ +lunr.Vector.prototype.insert = function (insertIdx, val) { + this.upsert(insertIdx, val, function () { + throw "duplicate index" + }) +} + +/** + * Inserts or updates an existing index within the vector. + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @param {Number} val - The value to be inserted into the vector. + * @param {function} fn - A function that is called for updates, the existing value and the + * requested value are passed as arguments + */ +lunr.Vector.prototype.upsert = function (insertIdx, val, fn) { + this._magnitude = 0 + var position = this.positionForIndex(insertIdx) + + if (this.elements[position] == insertIdx) { + this.elements[position + 1] = fn(this.elements[position + 1], val) + } else { + this.elements.splice(position, 0, insertIdx, val) + } +} + +/** + * Calculates the magnitude of this vector. + * + * @returns {Number} + */ +lunr.Vector.prototype.magnitude = function () { + if (this._magnitude) return this._magnitude + + var sumOfSquares = 0, + elementsLength = this.elements.length + + for (var i = 1; i < elementsLength; i += 2) { + var val = this.elements[i] + sumOfSquares += val * val + } + + return this._magnitude = Math.sqrt(sumOfSquares) +} + +/** + * Calculates the dot product of this vector and another vector. + * + * @param {lunr.Vector} otherVector - The vector to compute the dot product with. + * @returns {Number} + */ +lunr.Vector.prototype.dot = function (otherVector) { + var dotProduct = 0, + a = this.elements, b = otherVector.elements, + aLen = a.length, bLen = b.length, + aVal = 0, bVal = 0, + i = 0, j = 0 + + while (i < aLen && j < bLen) { + aVal = a[i], bVal = b[j] + if (aVal < bVal) { + i += 2 + } else if (aVal > bVal) { + j += 2 + } else if (aVal == bVal) { + dotProduct += a[i + 1] * b[j + 1] + i += 2 + j += 2 + } + } + + return dotProduct +} + +/** + * Calculates the similarity between this vector and another vector. + * + * @param {lunr.Vector} otherVector - The other vector to calculate the + * similarity with. + * @returns {Number} + */ +lunr.Vector.prototype.similarity = function (otherVector) { + return this.dot(otherVector) / this.magnitude() || 0 +} + +/** + * Converts the vector to an array of the elements within the vector. + * + * @returns {Number[]} + */ +lunr.Vector.prototype.toArray = function () { + var output = new Array (this.elements.length / 2) + + for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) { + output[j] = this.elements[i] + } + + return output +} + +/** + * A JSON serializable representation of the vector. + * + * @returns {Number[]} + */ +lunr.Vector.prototype.toJSON = function () { + return this.elements +} +/* eslint-disable */ +/*! + * lunr.stemmer + * Copyright (C) 2020 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + */ + +/** + * lunr.stemmer is an english language stemmer, this is a JavaScript + * implementation of the PorterStemmer taken from http://tartarus.org/~martin + * + * @static + * @implements {lunr.PipelineFunction} + * @param {lunr.Token} token - The string to stem + * @returns {lunr.Token} + * @see {@link lunr.Pipeline} + * @function + */ +lunr.stemmer = (function(){ + var step2list = { + "ational" : "ate", + "tional" : "tion", + "enci" : "ence", + "anci" : "ance", + "izer" : "ize", + "bli" : "ble", + "alli" : "al", + "entli" : "ent", + "eli" : "e", + "ousli" : "ous", + "ization" : "ize", + "ation" : "ate", + "ator" : "ate", + "alism" : "al", + "iveness" : "ive", + "fulness" : "ful", + "ousness" : "ous", + "aliti" : "al", + "iviti" : "ive", + "biliti" : "ble", + "logi" : "log" + }, + + step3list = { + "icate" : "ic", + "ative" : "", + "alize" : "al", + "iciti" : "ic", + "ical" : "ic", + "ful" : "", + "ness" : "" + }, + + c = "[^aeiou]", // consonant + v = "[aeiouy]", // vowel + C = c + "[^aeiouy]*", // consonant sequence + V = v + "[aeiou]*", // vowel sequence + + mgr0 = "^(" + C + ")?" + V + C, // [C]VC... is m>0 + meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$", // [C]VC[V] is m=1 + mgr1 = "^(" + C + ")?" + V + C + V + C, // [C]VCVC... is m>1 + s_v = "^(" + C + ")?" + v; // vowel in stem + + var re_mgr0 = new RegExp(mgr0); + var re_mgr1 = new RegExp(mgr1); + var re_meq1 = new RegExp(meq1); + var re_s_v = new RegExp(s_v); + + var re_1a = /^(.+?)(ss|i)es$/; + var re2_1a = /^(.+?)([^s])s$/; + var re_1b = /^(.+?)eed$/; + var re2_1b = /^(.+?)(ed|ing)$/; + var re_1b_2 = /.$/; + var re2_1b_2 = /(at|bl|iz)$/; + var re3_1b_2 = new RegExp("([^aeiouylsz])\\1$"); + var re4_1b_2 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + + var re_1c = /^(.+?[^aeiou])y$/; + var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + + var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + + var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + var re2_4 = /^(.+?)(s|t)(ion)$/; + + var re_5 = /^(.+?)e$/; + var re_5_1 = /ll$/; + var re3_5 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + + var porterStemmer = function porterStemmer(w) { + var stem, + suffix, + firstch, + re, + re2, + re3, + re4; + + if (w.length < 3) { return w; } + + firstch = w.substr(0,1); + if (firstch == "y") { + w = firstch.toUpperCase() + w.substr(1); + } + + // Step 1a + re = re_1a + re2 = re2_1a; + + if (re.test(w)) { w = w.replace(re,"$1$2"); } + else if (re2.test(w)) { w = w.replace(re2,"$1$2"); } + + // Step 1b + re = re_1b; + re2 = re2_1b; + if (re.test(w)) { + var fp = re.exec(w); + re = re_mgr0; + if (re.test(fp[1])) { + re = re_1b_2; + w = w.replace(re,""); + } + } else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = re_s_v; + if (re2.test(stem)) { + w = stem; + re2 = re2_1b_2; + re3 = re3_1b_2; + re4 = re4_1b_2; + if (re2.test(w)) { w = w + "e"; } + else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,""); } + else if (re4.test(w)) { w = w + "e"; } + } + } + + // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say) + re = re_1c; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem + "i"; + } + + // Step 2 + re = re_2; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = re_mgr0; + if (re.test(stem)) { + w = stem + step2list[suffix]; + } + } + + // Step 3 + re = re_3; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = re_mgr0; + if (re.test(stem)) { + w = stem + step3list[suffix]; + } + } + + // Step 4 + re = re_4; + re2 = re2_4; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = re_mgr1; + if (re.test(stem)) { + w = stem; + } + } else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = re_mgr1; + if (re2.test(stem)) { + w = stem; + } + } + + // Step 5 + re = re_5; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = re_mgr1; + re2 = re_meq1; + re3 = re3_5; + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) { + w = stem; + } + } + + re = re_5_1; + re2 = re_mgr1; + if (re.test(w) && re2.test(w)) { + re = re_1b_2; + w = w.replace(re,""); + } + + // and turn initial Y back to y + + if (firstch == "y") { + w = firstch.toLowerCase() + w.substr(1); + } + + return w; + }; + + return function (token) { + return token.update(porterStemmer); + } +})(); + +lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer') +/*! + * lunr.stopWordFilter + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * lunr.generateStopWordFilter builds a stopWordFilter function from the provided + * list of stop words. + * + * The built in lunr.stopWordFilter is built using this generator and can be used + * to generate custom stopWordFilters for applications or non English languages. + * + * @function + * @param {Array} token The token to pass through the filter + * @returns {lunr.PipelineFunction} + * @see lunr.Pipeline + * @see lunr.stopWordFilter + */ +lunr.generateStopWordFilter = function (stopWords) { + var words = stopWords.reduce(function (memo, stopWord) { + memo[stopWord] = stopWord + return memo + }, {}) + + return function (token) { + if (token && words[token.toString()] !== token.toString()) return token + } +} + +/** + * lunr.stopWordFilter is an English language stop word list filter, any words + * contained in the list will not be passed through the filter. + * + * This is intended to be used in the Pipeline. If the token does not pass the + * filter then undefined will be returned. + * + * @function + * @implements {lunr.PipelineFunction} + * @params {lunr.Token} token - A token to check for being a stop word. + * @returns {lunr.Token} + * @see {@link lunr.Pipeline} + */ +lunr.stopWordFilter = lunr.generateStopWordFilter([ + 'a', + 'able', + 'about', + 'across', + 'after', + 'all', + 'almost', + 'also', + 'am', + 'among', + 'an', + 'and', + 'any', + 'are', + 'as', + 'at', + 'be', + 'because', + 'been', + 'but', + 'by', + 'can', + 'cannot', + 'could', + 'dear', + 'did', + 'do', + 'does', + 'either', + 'else', + 'ever', + 'every', + 'for', + 'from', + 'get', + 'got', + 'had', + 'has', + 'have', + 'he', + 'her', + 'hers', + 'him', + 'his', + 'how', + 'however', + 'i', + 'if', + 'in', + 'into', + 'is', + 'it', + 'its', + 'just', + 'least', + 'let', + 'like', + 'likely', + 'may', + 'me', + 'might', + 'most', + 'must', + 'my', + 'neither', + 'no', + 'nor', + 'not', + 'of', + 'off', + 'often', + 'on', + 'only', + 'or', + 'other', + 'our', + 'own', + 'rather', + 'said', + 'say', + 'says', + 'she', + 'should', + 'since', + 'so', + 'some', + 'than', + 'that', + 'the', + 'their', + 'them', + 'then', + 'there', + 'these', + 'they', + 'this', + 'tis', + 'to', + 'too', + 'twas', + 'us', + 'wants', + 'was', + 'we', + 'were', + 'what', + 'when', + 'where', + 'which', + 'while', + 'who', + 'whom', + 'why', + 'will', + 'with', + 'would', + 'yet', + 'you', + 'your' +]) + +lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter') +/*! + * lunr.trimmer + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * lunr.trimmer is a pipeline function for trimming non word + * characters from the beginning and end of tokens before they + * enter the index. + * + * This implementation may not work correctly for non latin + * characters and should either be removed or adapted for use + * with languages with non-latin characters. + * + * @static + * @implements {lunr.PipelineFunction} + * @param {lunr.Token} token The token to pass through the filter + * @returns {lunr.Token} + * @see lunr.Pipeline + */ +lunr.trimmer = function (token) { + return token.update(function (s) { + return s.replace(/^\W+/, '').replace(/\W+$/, '') + }) +} + +lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer') +/*! + * lunr.TokenSet + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A token set is used to store the unique list of all tokens + * within an index. Token sets are also used to represent an + * incoming query to the index, this query token set and index + * token set are then intersected to find which tokens to look + * up in the inverted index. + * + * A token set can hold multiple tokens, as in the case of the + * index token set, or it can hold a single token as in the + * case of a simple query token set. + * + * Additionally token sets are used to perform wildcard matching. + * Leading, contained and trailing wildcards are supported, and + * from this edit distance matching can also be provided. + * + * Token sets are implemented as a minimal finite state automata, + * where both common prefixes and suffixes are shared between tokens. + * This helps to reduce the space used for storing the token set. + * + * @constructor + */ +lunr.TokenSet = function () { + this.final = false + this.edges = {} + this.id = lunr.TokenSet._nextId + lunr.TokenSet._nextId += 1 +} + +/** + * Keeps track of the next, auto increment, identifier to assign + * to a new tokenSet. + * + * TokenSets require a unique identifier to be correctly minimised. + * + * @private + */ +lunr.TokenSet._nextId = 1 + +/** + * Creates a TokenSet instance from the given sorted array of words. + * + * @param {String[]} arr - A sorted array of strings to create the set from. + * @returns {lunr.TokenSet} + * @throws Will throw an error if the input array is not sorted. + */ +lunr.TokenSet.fromArray = function (arr) { + var builder = new lunr.TokenSet.Builder + + for (var i = 0, len = arr.length; i < len; i++) { + builder.insert(arr[i]) + } + + builder.finish() + return builder.root +} + +/** + * Creates a token set from a query clause. + * + * @private + * @param {Object} clause - A single clause from lunr.Query. + * @param {string} clause.term - The query clause term. + * @param {number} [clause.editDistance] - The optional edit distance for the term. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.fromClause = function (clause) { + if ('editDistance' in clause) { + return lunr.TokenSet.fromFuzzyString(clause.term, clause.editDistance) + } else { + return lunr.TokenSet.fromString(clause.term) + } +} + +/** + * Creates a token set representing a single string with a specified + * edit distance. + * + * Insertions, deletions, substitutions and transpositions are each + * treated as an edit distance of 1. + * + * Increasing the allowed edit distance will have a dramatic impact + * on the performance of both creating and intersecting these TokenSets. + * It is advised to keep the edit distance less than 3. + * + * @param {string} str - The string to create the token set from. + * @param {number} editDistance - The allowed edit distance to match. + * @returns {lunr.Vector} + */ +lunr.TokenSet.fromFuzzyString = function (str, editDistance) { + var root = new lunr.TokenSet + + var stack = [{ + node: root, + editsRemaining: editDistance, + str: str + }] + + while (stack.length) { + var frame = stack.pop() + + // no edit + if (frame.str.length > 0) { + var char = frame.str.charAt(0), + noEditNode + + if (char in frame.node.edges) { + noEditNode = frame.node.edges[char] + } else { + noEditNode = new lunr.TokenSet + frame.node.edges[char] = noEditNode + } + + if (frame.str.length == 1) { + noEditNode.final = true + } + + stack.push({ + node: noEditNode, + editsRemaining: frame.editsRemaining, + str: frame.str.slice(1) + }) + } + + if (frame.editsRemaining == 0) { + continue + } + + // insertion + if ("*" in frame.node.edges) { + var insertionNode = frame.node.edges["*"] + } else { + var insertionNode = new lunr.TokenSet + frame.node.edges["*"] = insertionNode + } + + if (frame.str.length == 0) { + insertionNode.final = true + } + + stack.push({ + node: insertionNode, + editsRemaining: frame.editsRemaining - 1, + str: frame.str + }) + + // deletion + // can only do a deletion if we have enough edits remaining + // and if there are characters left to delete in the string + if (frame.str.length > 1) { + stack.push({ + node: frame.node, + editsRemaining: frame.editsRemaining - 1, + str: frame.str.slice(1) + }) + } + + // deletion + // just removing the last character from the str + if (frame.str.length == 1) { + frame.node.final = true + } + + // substitution + // can only do a substitution if we have enough edits remaining + // and if there are characters left to substitute + if (frame.str.length >= 1) { + if ("*" in frame.node.edges) { + var substitutionNode = frame.node.edges["*"] + } else { + var substitutionNode = new lunr.TokenSet + frame.node.edges["*"] = substitutionNode + } + + if (frame.str.length == 1) { + substitutionNode.final = true + } + + stack.push({ + node: substitutionNode, + editsRemaining: frame.editsRemaining - 1, + str: frame.str.slice(1) + }) + } + + // transposition + // can only do a transposition if there are edits remaining + // and there are enough characters to transpose + if (frame.str.length > 1) { + var charA = frame.str.charAt(0), + charB = frame.str.charAt(1), + transposeNode + + if (charB in frame.node.edges) { + transposeNode = frame.node.edges[charB] + } else { + transposeNode = new lunr.TokenSet + frame.node.edges[charB] = transposeNode + } + + if (frame.str.length == 1) { + transposeNode.final = true + } + + stack.push({ + node: transposeNode, + editsRemaining: frame.editsRemaining - 1, + str: charA + frame.str.slice(2) + }) + } + } + + return root +} + +/** + * Creates a TokenSet from a string. + * + * The string may contain one or more wildcard characters (*) + * that will allow wildcard matching when intersecting with + * another TokenSet. + * + * @param {string} str - The string to create a TokenSet from. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.fromString = function (str) { + var node = new lunr.TokenSet, + root = node + + /* + * Iterates through all characters within the passed string + * appending a node for each character. + * + * When a wildcard character is found then a self + * referencing edge is introduced to continually match + * any number of any characters. + */ + for (var i = 0, len = str.length; i < len; i++) { + var char = str[i], + final = (i == len - 1) + + if (char == "*") { + node.edges[char] = node + node.final = final + + } else { + var next = new lunr.TokenSet + next.final = final + + node.edges[char] = next + node = next + } + } + + return root +} + +/** + * Converts this TokenSet into an array of strings + * contained within the TokenSet. + * + * This is not intended to be used on a TokenSet that + * contains wildcards, in these cases the results are + * undefined and are likely to cause an infinite loop. + * + * @returns {string[]} + */ +lunr.TokenSet.prototype.toArray = function () { + var words = [] + + var stack = [{ + prefix: "", + node: this + }] + + while (stack.length) { + var frame = stack.pop(), + edges = Object.keys(frame.node.edges), + len = edges.length + + if (frame.node.final) { + /* In Safari, at this point the prefix is sometimes corrupted, see: + * https://github.com/olivernn/lunr.js/issues/279 Calling any + * String.prototype method forces Safari to "cast" this string to what + * it's supposed to be, fixing the bug. */ + frame.prefix.charAt(0) + words.push(frame.prefix) + } + + for (var i = 0; i < len; i++) { + var edge = edges[i] + + stack.push({ + prefix: frame.prefix.concat(edge), + node: frame.node.edges[edge] + }) + } + } + + return words +} + +/** + * Generates a string representation of a TokenSet. + * + * This is intended to allow TokenSets to be used as keys + * in objects, largely to aid the construction and minimisation + * of a TokenSet. As such it is not designed to be a human + * friendly representation of the TokenSet. + * + * @returns {string} + */ +lunr.TokenSet.prototype.toString = function () { + // NOTE: Using Object.keys here as this.edges is very likely + // to enter 'hash-mode' with many keys being added + // + // avoiding a for-in loop here as it leads to the function + // being de-optimised (at least in V8). From some simple + // benchmarks the performance is comparable, but allowing + // V8 to optimize may mean easy performance wins in the future. + + if (this._str) { + return this._str + } + + var str = this.final ? '1' : '0', + labels = Object.keys(this.edges).sort(), + len = labels.length + + for (var i = 0; i < len; i++) { + var label = labels[i], + node = this.edges[label] + + str = str + label + node.id + } + + return str +} + +/** + * Returns a new TokenSet that is the intersection of + * this TokenSet and the passed TokenSet. + * + * This intersection will take into account any wildcards + * contained within the TokenSet. + * + * @param {lunr.TokenSet} b - An other TokenSet to intersect with. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.prototype.intersect = function (b) { + var output = new lunr.TokenSet, + frame = undefined + + var stack = [{ + qNode: b, + output: output, + node: this + }] + + while (stack.length) { + frame = stack.pop() + + // NOTE: As with the #toString method, we are using + // Object.keys and a for loop instead of a for-in loop + // as both of these objects enter 'hash' mode, causing + // the function to be de-optimised in V8 + var qEdges = Object.keys(frame.qNode.edges), + qLen = qEdges.length, + nEdges = Object.keys(frame.node.edges), + nLen = nEdges.length + + for (var q = 0; q < qLen; q++) { + var qEdge = qEdges[q] + + for (var n = 0; n < nLen; n++) { + var nEdge = nEdges[n] + + if (nEdge == qEdge || qEdge == '*') { + var node = frame.node.edges[nEdge], + qNode = frame.qNode.edges[qEdge], + final = node.final && qNode.final, + next = undefined + + if (nEdge in frame.output.edges) { + // an edge already exists for this character + // no need to create a new node, just set the finality + // bit unless this node is already final + next = frame.output.edges[nEdge] + next.final = next.final || final + + } else { + // no edge exists yet, must create one + // set the finality bit and insert it + // into the output + next = new lunr.TokenSet + next.final = final + frame.output.edges[nEdge] = next + } + + stack.push({ + qNode: qNode, + output: next, + node: node + }) + } + } + } + } + + return output +} +lunr.TokenSet.Builder = function () { + this.previousWord = "" + this.root = new lunr.TokenSet + this.uncheckedNodes = [] + this.minimizedNodes = {} +} + +lunr.TokenSet.Builder.prototype.insert = function (word) { + var node, + commonPrefix = 0 + + if (word < this.previousWord) { + throw new Error ("Out of order word insertion") + } + + for (var i = 0; i < word.length && i < this.previousWord.length; i++) { + if (word[i] != this.previousWord[i]) break + commonPrefix++ + } + + this.minimize(commonPrefix) + + if (this.uncheckedNodes.length == 0) { + node = this.root + } else { + node = this.uncheckedNodes[this.uncheckedNodes.length - 1].child + } + + for (var i = commonPrefix; i < word.length; i++) { + var nextNode = new lunr.TokenSet, + char = word[i] + + node.edges[char] = nextNode + + this.uncheckedNodes.push({ + parent: node, + char: char, + child: nextNode + }) + + node = nextNode + } + + node.final = true + this.previousWord = word +} + +lunr.TokenSet.Builder.prototype.finish = function () { + this.minimize(0) +} + +lunr.TokenSet.Builder.prototype.minimize = function (downTo) { + for (var i = this.uncheckedNodes.length - 1; i >= downTo; i--) { + var node = this.uncheckedNodes[i], + childKey = node.child.toString() + + if (childKey in this.minimizedNodes) { + node.parent.edges[node.char] = this.minimizedNodes[childKey] + } else { + // Cache the key for this node since + // we know it can't change anymore + node.child._str = childKey + + this.minimizedNodes[childKey] = node.child + } + + this.uncheckedNodes.pop() + } +} +/*! + * lunr.Index + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * An index contains the built index of all documents and provides a query interface + * to the index. + * + * Usually instances of lunr.Index will not be created using this constructor, instead + * lunr.Builder should be used to construct new indexes, or lunr.Index.load should be + * used to load previously built and serialized indexes. + * + * @constructor + * @param {Object} attrs - The attributes of the built search index. + * @param {Object} attrs.invertedIndex - An index of term/field to document reference. + * @param {Object} attrs.fieldVectors - Field vectors + * @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens. + * @param {string[]} attrs.fields - The names of indexed document fields. + * @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms. + */ +lunr.Index = function (attrs) { + this.invertedIndex = attrs.invertedIndex + this.fieldVectors = attrs.fieldVectors + this.tokenSet = attrs.tokenSet + this.fields = attrs.fields + this.pipeline = attrs.pipeline +} + +/** + * A result contains details of a document matching a search query. + * @typedef {Object} lunr.Index~Result + * @property {string} ref - The reference of the document this result represents. + * @property {number} score - A number between 0 and 1 representing how similar this document is to the query. + * @property {lunr.MatchData} matchData - Contains metadata about this match including which term(s) caused the match. + */ + +/** + * Although lunr provides the ability to create queries using lunr.Query, it also provides a simple + * query language which itself is parsed into an instance of lunr.Query. + * + * For programmatically building queries it is advised to directly use lunr.Query, the query language + * is best used for human entered text rather than program generated text. + * + * At its simplest queries can just be a single term, e.g. `hello`, multiple terms are also supported + * and will be combined with OR, e.g `hello world` will match documents that contain either 'hello' + * or 'world', though those that contain both will rank higher in the results. + * + * Wildcards can be included in terms to match one or more unspecified characters, these wildcards can + * be inserted anywhere within the term, and more than one wildcard can exist in a single term. Adding + * wildcards will increase the number of documents that will be found but can also have a negative + * impact on query performance, especially with wildcards at the beginning of a term. + * + * Terms can be restricted to specific fields, e.g. `title:hello`, only documents with the term + * hello in the title field will match this query. Using a field not present in the index will lead + * to an error being thrown. + * + * Modifiers can also be added to terms, lunr supports edit distance and boost modifiers on terms. A term + * boost will make documents matching that term score higher, e.g. `foo^5`. Edit distance is also supported + * to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2. + * Avoid large values for edit distance to improve query performance. + * + * Each term also supports a presence modifier. By default a term's presence in document is optional, however + * this can be changed to either required or prohibited. For a term's presence to be required in a document the + * term should be prefixed with a '+', e.g. `+foo bar` is a search for documents that must contain 'foo' and + * optionally contain 'bar'. Conversely a leading '-' sets the terms presence to prohibited, i.e. it must not + * appear in a document, e.g. `-foo bar` is a search for documents that do not contain 'foo' but may contain 'bar'. + * + * To escape special characters the backslash character '\' can be used, this allows searches to include + * characters that would normally be considered modifiers, e.g. `foo\~2` will search for a term "foo~2" instead + * of attempting to apply a boost of 2 to the search term "foo". + * + * @typedef {string} lunr.Index~QueryString + * @example Simple single term query + * hello + * @example Multiple term query + * hello world + * @example term scoped to a field + * title:hello + * @example term with a boost of 10 + * hello^10 + * @example term with an edit distance of 2 + * hello~2 + * @example terms with presence modifiers + * -foo +bar baz + */ + +/** + * Performs a search against the index using lunr query syntax. + * + * Results will be returned sorted by their score, the most relevant results + * will be returned first. For details on how the score is calculated, please see + * the {@link https://lunrjs.com/guides/searching.html#scoring|guide}. + * + * For more programmatic querying use lunr.Index#query. + * + * @param {lunr.Index~QueryString} queryString - A string containing a lunr query. + * @throws {lunr.QueryParseError} If the passed query string cannot be parsed. + * @returns {lunr.Index~Result[]} + */ +lunr.Index.prototype.search = function (queryString) { + return this.query(function (query) { + var parser = new lunr.QueryParser(queryString, query) + parser.parse() + }) +} + +/** + * A query builder callback provides a query object to be used to express + * the query to perform on the index. + * + * @callback lunr.Index~queryBuilder + * @param {lunr.Query} query - The query object to build up. + * @this lunr.Query + */ + +/** + * Performs a query against the index using the yielded lunr.Query object. + * + * If performing programmatic queries against the index, this method is preferred + * over lunr.Index#search so as to avoid the additional query parsing overhead. + * + * A query object is yielded to the supplied function which should be used to + * express the query to be run against the index. + * + * Note that although this function takes a callback parameter it is _not_ an + * asynchronous operation, the callback is just yielded a query object to be + * customized. + * + * @param {lunr.Index~queryBuilder} fn - A function that is used to build the query. + * @returns {lunr.Index~Result[]} + */ +lunr.Index.prototype.query = function (fn) { + // for each query clause + // * process terms + // * expand terms from token set + // * find matching documents and metadata + // * get document vectors + // * score documents + + var query = new lunr.Query(this.fields), + matchingFields = Object.create(null), + queryVectors = Object.create(null), + termFieldCache = Object.create(null), + requiredMatches = Object.create(null), + prohibitedMatches = Object.create(null) + + /* + * To support field level boosts a query vector is created per + * field. An empty vector is eagerly created to support negated + * queries. + */ + for (var i = 0; i < this.fields.length; i++) { + queryVectors[this.fields[i]] = new lunr.Vector + } + + fn.call(query, query) + + for (var i = 0; i < query.clauses.length; i++) { + /* + * Unless the pipeline has been disabled for this term, which is + * the case for terms with wildcards, we need to pass the clause + * term through the search pipeline. A pipeline returns an array + * of processed terms. Pipeline functions may expand the passed + * term, which means we may end up performing multiple index lookups + * for a single query term. + */ + var clause = query.clauses[i], + terms = null, + clauseMatches = lunr.Set.empty + + if (clause.usePipeline) { + terms = this.pipeline.runString(clause.term, { + fields: clause.fields + }) + } else { + terms = [clause.term] + } + + for (var m = 0; m < terms.length; m++) { + var term = terms[m] + + /* + * Each term returned from the pipeline needs to use the same query + * clause object, e.g. the same boost and or edit distance. The + * simplest way to do this is to re-use the clause object but mutate + * its term property. + */ + clause.term = term + + /* + * From the term in the clause we create a token set which will then + * be used to intersect the indexes token set to get a list of terms + * to lookup in the inverted index + */ + var termTokenSet = lunr.TokenSet.fromClause(clause), + expandedTerms = this.tokenSet.intersect(termTokenSet).toArray() + + /* + * If a term marked as required does not exist in the tokenSet it is + * impossible for the search to return any matches. We set all the field + * scoped required matches set to empty and stop examining any further + * clauses. + */ + if (expandedTerms.length === 0 && clause.presence === lunr.Query.presence.REQUIRED) { + for (var k = 0; k < clause.fields.length; k++) { + var field = clause.fields[k] + requiredMatches[field] = lunr.Set.empty + } + + break + } + + for (var j = 0; j < expandedTerms.length; j++) { + /* + * For each term get the posting and termIndex, this is required for + * building the query vector. + */ + var expandedTerm = expandedTerms[j], + posting = this.invertedIndex[expandedTerm], + termIndex = posting._index + + for (var k = 0; k < clause.fields.length; k++) { + /* + * For each field that this query term is scoped by (by default + * all fields are in scope) we need to get all the document refs + * that have this term in that field. + * + * The posting is the entry in the invertedIndex for the matching + * term from above. + */ + var field = clause.fields[k], + fieldPosting = posting[field], + matchingDocumentRefs = Object.keys(fieldPosting), + termField = expandedTerm + "/" + field, + matchingDocumentsSet = new lunr.Set(matchingDocumentRefs) + + /* + * if the presence of this term is required ensure that the matching + * documents are added to the set of required matches for this clause. + * + */ + if (clause.presence == lunr.Query.presence.REQUIRED) { + clauseMatches = clauseMatches.union(matchingDocumentsSet) + + if (requiredMatches[field] === undefined) { + requiredMatches[field] = lunr.Set.complete + } + } + + /* + * if the presence of this term is prohibited ensure that the matching + * documents are added to the set of prohibited matches for this field, + * creating that set if it does not yet exist. + */ + if (clause.presence == lunr.Query.presence.PROHIBITED) { + if (prohibitedMatches[field] === undefined) { + prohibitedMatches[field] = lunr.Set.empty + } + + prohibitedMatches[field] = prohibitedMatches[field].union(matchingDocumentsSet) + + /* + * Prohibited matches should not be part of the query vector used for + * similarity scoring and no metadata should be extracted so we continue + * to the next field + */ + continue + } + + /* + * The query field vector is populated using the termIndex found for + * the term and a unit value with the appropriate boost applied. + * Using upsert because there could already be an entry in the vector + * for the term we are working with. In that case we just add the scores + * together. + */ + queryVectors[field].upsert(termIndex, clause.boost, function (a, b) { return a + b }) + + /** + * If we've already seen this term, field combo then we've already collected + * the matching documents and metadata, no need to go through all that again + */ + if (termFieldCache[termField]) { + continue + } + + for (var l = 0; l < matchingDocumentRefs.length; l++) { + /* + * All metadata for this term/field/document triple + * are then extracted and collected into an instance + * of lunr.MatchData ready to be returned in the query + * results + */ + var matchingDocumentRef = matchingDocumentRefs[l], + matchingFieldRef = new lunr.FieldRef (matchingDocumentRef, field), + metadata = fieldPosting[matchingDocumentRef], + fieldMatch + + if ((fieldMatch = matchingFields[matchingFieldRef]) === undefined) { + matchingFields[matchingFieldRef] = new lunr.MatchData (expandedTerm, field, metadata) + } else { + fieldMatch.add(expandedTerm, field, metadata) + } + + } + + termFieldCache[termField] = true + } + } + } + + /** + * If the presence was required we need to update the requiredMatches field sets. + * We do this after all fields for the term have collected their matches because + * the clause terms presence is required in _any_ of the fields not _all_ of the + * fields. + */ + if (clause.presence === lunr.Query.presence.REQUIRED) { + for (var k = 0; k < clause.fields.length; k++) { + var field = clause.fields[k] + requiredMatches[field] = requiredMatches[field].intersect(clauseMatches) + } + } + } + + /** + * Need to combine the field scoped required and prohibited + * matching documents into a global set of required and prohibited + * matches + */ + var allRequiredMatches = lunr.Set.complete, + allProhibitedMatches = lunr.Set.empty + + for (var i = 0; i < this.fields.length; i++) { + var field = this.fields[i] + + if (requiredMatches[field]) { + allRequiredMatches = allRequiredMatches.intersect(requiredMatches[field]) + } + + if (prohibitedMatches[field]) { + allProhibitedMatches = allProhibitedMatches.union(prohibitedMatches[field]) + } + } + + var matchingFieldRefs = Object.keys(matchingFields), + results = [], + matches = Object.create(null) + + /* + * If the query is negated (contains only prohibited terms) + * we need to get _all_ fieldRefs currently existing in the + * index. This is only done when we know that the query is + * entirely prohibited terms to avoid any cost of getting all + * fieldRefs unnecessarily. + * + * Additionally, blank MatchData must be created to correctly + * populate the results. + */ + if (query.isNegated()) { + matchingFieldRefs = Object.keys(this.fieldVectors) + + for (var i = 0; i < matchingFieldRefs.length; i++) { + var matchingFieldRef = matchingFieldRefs[i] + var fieldRef = lunr.FieldRef.fromString(matchingFieldRef) + matchingFields[matchingFieldRef] = new lunr.MatchData + } + } + + for (var i = 0; i < matchingFieldRefs.length; i++) { + /* + * Currently we have document fields that match the query, but we + * need to return documents. The matchData and scores are combined + * from multiple fields belonging to the same document. + * + * Scores are calculated by field, using the query vectors created + * above, and combined into a final document score using addition. + */ + var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]), + docRef = fieldRef.docRef + + if (!allRequiredMatches.contains(docRef)) { + continue + } + + if (allProhibitedMatches.contains(docRef)) { + continue + } + + var fieldVector = this.fieldVectors[fieldRef], + score = queryVectors[fieldRef.fieldName].similarity(fieldVector), + docMatch + + if ((docMatch = matches[docRef]) !== undefined) { + docMatch.score += score + docMatch.matchData.combine(matchingFields[fieldRef]) + } else { + var match = { + ref: docRef, + score: score, + matchData: matchingFields[fieldRef] + } + matches[docRef] = match + results.push(match) + } + } + + /* + * Sort the results objects by score, highest first. + */ + return results.sort(function (a, b) { + return b.score - a.score + }) +} + +/** + * Prepares the index for JSON serialization. + * + * The schema for this JSON blob will be described in a + * separate JSON schema file. + * + * @returns {Object} + */ +lunr.Index.prototype.toJSON = function () { + var invertedIndex = Object.keys(this.invertedIndex) + .sort() + .map(function (term) { + return [term, this.invertedIndex[term]] + }, this) + + var fieldVectors = Object.keys(this.fieldVectors) + .map(function (ref) { + return [ref, this.fieldVectors[ref].toJSON()] + }, this) + + return { + version: lunr.version, + fields: this.fields, + fieldVectors: fieldVectors, + invertedIndex: invertedIndex, + pipeline: this.pipeline.toJSON() + } +} + +/** + * Loads a previously serialized lunr.Index + * + * @param {Object} serializedIndex - A previously serialized lunr.Index + * @returns {lunr.Index} + */ +lunr.Index.load = function (serializedIndex) { + var attrs = {}, + fieldVectors = {}, + serializedVectors = serializedIndex.fieldVectors, + invertedIndex = Object.create(null), + serializedInvertedIndex = serializedIndex.invertedIndex, + tokenSetBuilder = new lunr.TokenSet.Builder, + pipeline = lunr.Pipeline.load(serializedIndex.pipeline) + + if (serializedIndex.version != lunr.version) { + lunr.utils.warn("Version mismatch when loading serialised index. Current version of lunr '" + lunr.version + "' does not match serialized index '" + serializedIndex.version + "'") + } + + for (var i = 0; i < serializedVectors.length; i++) { + var tuple = serializedVectors[i], + ref = tuple[0], + elements = tuple[1] + + fieldVectors[ref] = new lunr.Vector(elements) + } + + for (var i = 0; i < serializedInvertedIndex.length; i++) { + var tuple = serializedInvertedIndex[i], + term = tuple[0], + posting = tuple[1] + + tokenSetBuilder.insert(term) + invertedIndex[term] = posting + } + + tokenSetBuilder.finish() + + attrs.fields = serializedIndex.fields + + attrs.fieldVectors = fieldVectors + attrs.invertedIndex = invertedIndex + attrs.tokenSet = tokenSetBuilder.root + attrs.pipeline = pipeline + + return new lunr.Index(attrs) +} +/*! + * lunr.Builder + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * lunr.Builder performs indexing on a set of documents and + * returns instances of lunr.Index ready for querying. + * + * All configuration of the index is done via the builder, the + * fields to index, the document reference, the text processing + * pipeline and document scoring parameters are all set on the + * builder before indexing. + * + * @constructor + * @property {string} _ref - Internal reference to the document reference field. + * @property {string[]} _fields - Internal reference to the document fields to index. + * @property {object} invertedIndex - The inverted index maps terms to document fields. + * @property {object} documentTermFrequencies - Keeps track of document term frequencies. + * @property {object} documentLengths - Keeps track of the length of documents added to the index. + * @property {lunr.tokenizer} tokenizer - Function for splitting strings into tokens for indexing. + * @property {lunr.Pipeline} pipeline - The pipeline performs text processing on tokens before indexing. + * @property {lunr.Pipeline} searchPipeline - A pipeline for processing search terms before querying the index. + * @property {number} documentCount - Keeps track of the total number of documents indexed. + * @property {number} _b - A parameter to control field length normalization, setting this to 0 disabled normalization, 1 fully normalizes field lengths, the default value is 0.75. + * @property {number} _k1 - A parameter to control how quickly an increase in term frequency results in term frequency saturation, the default value is 1.2. + * @property {number} termIndex - A counter incremented for each unique term, used to identify a terms position in the vector space. + * @property {array} metadataWhitelist - A list of metadata keys that have been whitelisted for entry in the index. + */ +lunr.Builder = function () { + this._ref = "id" + this._fields = Object.create(null) + this._documents = Object.create(null) + this.invertedIndex = Object.create(null) + this.fieldTermFrequencies = {} + this.fieldLengths = {} + this.tokenizer = lunr.tokenizer + this.pipeline = new lunr.Pipeline + this.searchPipeline = new lunr.Pipeline + this.documentCount = 0 + this._b = 0.75 + this._k1 = 1.2 + this.termIndex = 0 + this.metadataWhitelist = [] +} + +/** + * Sets the document field used as the document reference. Every document must have this field. + * The type of this field in the document should be a string, if it is not a string it will be + * coerced into a string by calling toString. + * + * The default ref is 'id'. + * + * The ref should _not_ be changed during indexing, it should be set before any documents are + * added to the index. Changing it during indexing can lead to inconsistent results. + * + * @param {string} ref - The name of the reference field in the document. + */ +lunr.Builder.prototype.ref = function (ref) { + this._ref = ref +} + +/** + * A function that is used to extract a field from a document. + * + * Lunr expects a field to be at the top level of a document, if however the field + * is deeply nested within a document an extractor function can be used to extract + * the right field for indexing. + * + * @callback fieldExtractor + * @param {object} doc - The document being added to the index. + * @returns {?(string|object|object[])} obj - The object that will be indexed for this field. + * @example Extracting a nested field + * function (doc) { return doc.nested.field } + */ + +/** + * Adds a field to the list of document fields that will be indexed. Every document being + * indexed should have this field. Null values for this field in indexed documents will + * not cause errors but will limit the chance of that document being retrieved by searches. + * + * All fields should be added before adding documents to the index. Adding fields after + * a document has been indexed will have no effect on already indexed documents. + * + * Fields can be boosted at build time. This allows terms within that field to have more + * importance when ranking search results. Use a field boost to specify that matches within + * one field are more important than other fields. + * + * @param {string} fieldName - The name of a field to index in all documents. + * @param {object} attributes - Optional attributes associated with this field. + * @param {number} [attributes.boost=1] - Boost applied to all terms within this field. + * @param {fieldExtractor} [attributes.extractor] - Function to extract a field from a document. + * @throws {RangeError} fieldName cannot contain unsupported characters '/' + */ +lunr.Builder.prototype.field = function (fieldName, attributes) { + if (/\//.test(fieldName)) { + throw new RangeError ("Field '" + fieldName + "' contains illegal character '/'") + } + + this._fields[fieldName] = attributes || {} +} + +/** + * A parameter to tune the amount of field length normalisation that is applied when + * calculating relevance scores. A value of 0 will completely disable any normalisation + * and a value of 1 will fully normalise field lengths. The default is 0.75. Values of b + * will be clamped to the range 0 - 1. + * + * @param {number} number - The value to set for this tuning parameter. + */ +lunr.Builder.prototype.b = function (number) { + if (number < 0) { + this._b = 0 + } else if (number > 1) { + this._b = 1 + } else { + this._b = number + } +} + +/** + * A parameter that controls the speed at which a rise in term frequency results in term + * frequency saturation. The default value is 1.2. Setting this to a higher value will give + * slower saturation levels, a lower value will result in quicker saturation. + * + * @param {number} number - The value to set for this tuning parameter. + */ +lunr.Builder.prototype.k1 = function (number) { + this._k1 = number +} + +/** + * Adds a document to the index. + * + * Before adding fields to the index the index should have been fully setup, with the document + * ref and all fields to index already having been specified. + * + * The document must have a field name as specified by the ref (by default this is 'id') and + * it should have all fields defined for indexing, though null or undefined values will not + * cause errors. + * + * Entire documents can be boosted at build time. Applying a boost to a document indicates that + * this document should rank higher in search results than other documents. + * + * @param {object} doc - The document to add to the index. + * @param {object} attributes - Optional attributes associated with this document. + * @param {number} [attributes.boost=1] - Boost applied to all terms within this document. + */ +lunr.Builder.prototype.add = function (doc, attributes) { + var docRef = doc[this._ref], + fields = Object.keys(this._fields) + + this._documents[docRef] = attributes || {} + this.documentCount += 1 + + for (var i = 0; i < fields.length; i++) { + var fieldName = fields[i], + extractor = this._fields[fieldName].extractor, + field = extractor ? extractor(doc) : doc[fieldName], + tokens = this.tokenizer(field, { + fields: [fieldName] + }), + terms = this.pipeline.run(tokens), + fieldRef = new lunr.FieldRef (docRef, fieldName), + fieldTerms = Object.create(null) + + this.fieldTermFrequencies[fieldRef] = fieldTerms + this.fieldLengths[fieldRef] = 0 + + // store the length of this field for this document + this.fieldLengths[fieldRef] += terms.length + + // calculate term frequencies for this field + for (var j = 0; j < terms.length; j++) { + var term = terms[j] + + if (fieldTerms[term] == undefined) { + fieldTerms[term] = 0 + } + + fieldTerms[term] += 1 + + // add to inverted index + // create an initial posting if one doesn't exist + if (this.invertedIndex[term] == undefined) { + var posting = Object.create(null) + posting["_index"] = this.termIndex + this.termIndex += 1 + + for (var k = 0; k < fields.length; k++) { + posting[fields[k]] = Object.create(null) + } + + this.invertedIndex[term] = posting + } + + // add an entry for this term/fieldName/docRef to the invertedIndex + if (this.invertedIndex[term][fieldName][docRef] == undefined) { + this.invertedIndex[term][fieldName][docRef] = Object.create(null) + } + + // store all whitelisted metadata about this token in the + // inverted index + for (var l = 0; l < this.metadataWhitelist.length; l++) { + var metadataKey = this.metadataWhitelist[l], + metadata = term.metadata[metadataKey] + + if (this.invertedIndex[term][fieldName][docRef][metadataKey] == undefined) { + this.invertedIndex[term][fieldName][docRef][metadataKey] = [] + } + + this.invertedIndex[term][fieldName][docRef][metadataKey].push(metadata) + } + } + + } +} + +/** + * Calculates the average document length for this index + * + * @private + */ +lunr.Builder.prototype.calculateAverageFieldLengths = function () { + + var fieldRefs = Object.keys(this.fieldLengths), + numberOfFields = fieldRefs.length, + accumulator = {}, + documentsWithField = {} + + for (var i = 0; i < numberOfFields; i++) { + var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]), + field = fieldRef.fieldName + + documentsWithField[field] || (documentsWithField[field] = 0) + documentsWithField[field] += 1 + + accumulator[field] || (accumulator[field] = 0) + accumulator[field] += this.fieldLengths[fieldRef] + } + + var fields = Object.keys(this._fields) + + for (var i = 0; i < fields.length; i++) { + var fieldName = fields[i] + accumulator[fieldName] = accumulator[fieldName] / documentsWithField[fieldName] + } + + this.averageFieldLength = accumulator +} + +/** + * Builds a vector space model of every document using lunr.Vector + * + * @private + */ +lunr.Builder.prototype.createFieldVectors = function () { + var fieldVectors = {}, + fieldRefs = Object.keys(this.fieldTermFrequencies), + fieldRefsLength = fieldRefs.length, + termIdfCache = Object.create(null) + + for (var i = 0; i < fieldRefsLength; i++) { + var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]), + fieldName = fieldRef.fieldName, + fieldLength = this.fieldLengths[fieldRef], + fieldVector = new lunr.Vector, + termFrequencies = this.fieldTermFrequencies[fieldRef], + terms = Object.keys(termFrequencies), + termsLength = terms.length + + + var fieldBoost = this._fields[fieldName].boost || 1, + docBoost = this._documents[fieldRef.docRef].boost || 1 + + for (var j = 0; j < termsLength; j++) { + var term = terms[j], + tf = termFrequencies[term], + termIndex = this.invertedIndex[term]._index, + idf, score, scoreWithPrecision + + if (termIdfCache[term] === undefined) { + idf = lunr.idf(this.invertedIndex[term], this.documentCount) + termIdfCache[term] = idf + } else { + idf = termIdfCache[term] + } + + score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[fieldName])) + tf) + score *= fieldBoost + score *= docBoost + scoreWithPrecision = Math.round(score * 1000) / 1000 + // Converts 1.23456789 to 1.234. + // Reducing the precision so that the vectors take up less + // space when serialised. Doing it now so that they behave + // the same before and after serialisation. Also, this is + // the fastest approach to reducing a number's precision in + // JavaScript. + + fieldVector.insert(termIndex, scoreWithPrecision) + } + + fieldVectors[fieldRef] = fieldVector + } + + this.fieldVectors = fieldVectors +} + +/** + * Creates a token set of all tokens in the index using lunr.TokenSet + * + * @private + */ +lunr.Builder.prototype.createTokenSet = function () { + this.tokenSet = lunr.TokenSet.fromArray( + Object.keys(this.invertedIndex).sort() + ) +} + +/** + * Builds the index, creating an instance of lunr.Index. + * + * This completes the indexing process and should only be called + * once all documents have been added to the index. + * + * @returns {lunr.Index} + */ +lunr.Builder.prototype.build = function () { + this.calculateAverageFieldLengths() + this.createFieldVectors() + this.createTokenSet() + + return new lunr.Index({ + invertedIndex: this.invertedIndex, + fieldVectors: this.fieldVectors, + tokenSet: this.tokenSet, + fields: Object.keys(this._fields), + pipeline: this.searchPipeline + }) +} + +/** + * Applies a plugin to the index builder. + * + * A plugin is a function that is called with the index builder as its context. + * Plugins can be used to customise or extend the behaviour of the index + * in some way. A plugin is just a function, that encapsulated the custom + * behaviour that should be applied when building the index. + * + * The plugin function will be called with the index builder as its argument, additional + * arguments can also be passed when calling use. The function will be called + * with the index builder as its context. + * + * @param {Function} plugin The plugin to apply. + */ +lunr.Builder.prototype.use = function (fn) { + var args = Array.prototype.slice.call(arguments, 1) + args.unshift(this) + fn.apply(this, args) +} +/** + * Contains and collects metadata about a matching document. + * A single instance of lunr.MatchData is returned as part of every + * lunr.Index~Result. + * + * @constructor + * @param {string} term - The term this match data is associated with + * @param {string} field - The field in which the term was found + * @param {object} metadata - The metadata recorded about this term in this field + * @property {object} metadata - A cloned collection of metadata associated with this document. + * @see {@link lunr.Index~Result} + */ +lunr.MatchData = function (term, field, metadata) { + var clonedMetadata = Object.create(null), + metadataKeys = Object.keys(metadata || {}) + + // Cloning the metadata to prevent the original + // being mutated during match data combination. + // Metadata is kept in an array within the inverted + // index so cloning the data can be done with + // Array#slice + for (var i = 0; i < metadataKeys.length; i++) { + var key = metadataKeys[i] + clonedMetadata[key] = metadata[key].slice() + } + + this.metadata = Object.create(null) + + if (term !== undefined) { + this.metadata[term] = Object.create(null) + this.metadata[term][field] = clonedMetadata + } +} + +/** + * An instance of lunr.MatchData will be created for every term that matches a + * document. However only one instance is required in a lunr.Index~Result. This + * method combines metadata from another instance of lunr.MatchData with this + * objects metadata. + * + * @param {lunr.MatchData} otherMatchData - Another instance of match data to merge with this one. + * @see {@link lunr.Index~Result} + */ +lunr.MatchData.prototype.combine = function (otherMatchData) { + var terms = Object.keys(otherMatchData.metadata) + + for (var i = 0; i < terms.length; i++) { + var term = terms[i], + fields = Object.keys(otherMatchData.metadata[term]) + + if (this.metadata[term] == undefined) { + this.metadata[term] = Object.create(null) + } + + for (var j = 0; j < fields.length; j++) { + var field = fields[j], + keys = Object.keys(otherMatchData.metadata[term][field]) + + if (this.metadata[term][field] == undefined) { + this.metadata[term][field] = Object.create(null) + } + + for (var k = 0; k < keys.length; k++) { + var key = keys[k] + + if (this.metadata[term][field][key] == undefined) { + this.metadata[term][field][key] = otherMatchData.metadata[term][field][key] + } else { + this.metadata[term][field][key] = this.metadata[term][field][key].concat(otherMatchData.metadata[term][field][key]) + } + + } + } + } +} + +/** + * Add metadata for a term/field pair to this instance of match data. + * + * @param {string} term - The term this match data is associated with + * @param {string} field - The field in which the term was found + * @param {object} metadata - The metadata recorded about this term in this field + */ +lunr.MatchData.prototype.add = function (term, field, metadata) { + if (!(term in this.metadata)) { + this.metadata[term] = Object.create(null) + this.metadata[term][field] = metadata + return + } + + if (!(field in this.metadata[term])) { + this.metadata[term][field] = metadata + return + } + + var metadataKeys = Object.keys(metadata) + + for (var i = 0; i < metadataKeys.length; i++) { + var key = metadataKeys[i] + + if (key in this.metadata[term][field]) { + this.metadata[term][field][key] = this.metadata[term][field][key].concat(metadata[key]) + } else { + this.metadata[term][field][key] = metadata[key] + } + } +} +/** + * A lunr.Query provides a programmatic way of defining queries to be performed + * against a {@link lunr.Index}. + * + * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method + * so the query object is pre-initialized with the right index fields. + * + * @constructor + * @property {lunr.Query~Clause[]} clauses - An array of query clauses. + * @property {string[]} allFields - An array of all available fields in a lunr.Index. + */ +lunr.Query = function (allFields) { + this.clauses = [] + this.allFields = allFields +} + +/** + * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause. + * + * This allows wildcards to be added to the beginning and end of a term without having to manually do any string + * concatenation. + * + * The wildcard constants can be bitwise combined to select both leading and trailing wildcards. + * + * @constant + * @default + * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour + * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists + * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists + * @see lunr.Query~Clause + * @see lunr.Query#clause + * @see lunr.Query#term + * @example query term with trailing wildcard + * query.term('foo', { wildcard: lunr.Query.wildcard.TRAILING }) + * @example query term with leading and trailing wildcard + * query.term('foo', { + * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING + * }) + */ + +lunr.Query.wildcard = new String ("*") +lunr.Query.wildcard.NONE = 0 +lunr.Query.wildcard.LEADING = 1 +lunr.Query.wildcard.TRAILING = 2 + +/** + * Constants for indicating what kind of presence a term must have in matching documents. + * + * @constant + * @enum {number} + * @see lunr.Query~Clause + * @see lunr.Query#clause + * @see lunr.Query#term + * @example query term with required presence + * query.term('foo', { presence: lunr.Query.presence.REQUIRED }) + */ +lunr.Query.presence = { + /** + * Term's presence in a document is optional, this is the default value. + */ + OPTIONAL: 1, + + /** + * Term's presence in a document is required, documents that do not contain + * this term will not be returned. + */ + REQUIRED: 2, + + /** + * Term's presence in a document is prohibited, documents that do contain + * this term will not be returned. + */ + PROHIBITED: 3 +} + +/** + * A single clause in a {@link lunr.Query} contains a term and details on how to + * match that term against a {@link lunr.Index}. + * + * @typedef {Object} lunr.Query~Clause + * @property {string[]} fields - The fields in an index this clause should be matched against. + * @property {number} [boost=1] - Any boost that should be applied when matching this clause. + * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be. + * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline. + * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended. + * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents. + */ + +/** + * Adds a {@link lunr.Query~Clause} to this query. + * + * Unless the clause contains the fields to be matched all fields will be matched. In addition + * a default boost of 1 is applied to the clause. + * + * @param {lunr.Query~Clause} clause - The clause to add to this query. + * @see lunr.Query~Clause + * @returns {lunr.Query} + */ +lunr.Query.prototype.clause = function (clause) { + if (!('fields' in clause)) { + clause.fields = this.allFields + } + + if (!('boost' in clause)) { + clause.boost = 1 + } + + if (!('usePipeline' in clause)) { + clause.usePipeline = true + } + + if (!('wildcard' in clause)) { + clause.wildcard = lunr.Query.wildcard.NONE + } + + if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) { + clause.term = "*" + clause.term + } + + if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) { + clause.term = "" + clause.term + "*" + } + + if (!('presence' in clause)) { + clause.presence = lunr.Query.presence.OPTIONAL + } + + this.clauses.push(clause) + + return this +} + +/** + * A negated query is one in which every clause has a presence of + * prohibited. These queries require some special processing to return + * the expected results. + * + * @returns boolean + */ +lunr.Query.prototype.isNegated = function () { + for (var i = 0; i < this.clauses.length; i++) { + if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) { + return false + } + } + + return true +} + +/** + * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause} + * to the list of clauses that make up this query. + * + * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion + * to a token or token-like string should be done before calling this method. + * + * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an + * array, each term in the array will share the same options. + * + * @param {object|object[]} term - The term(s) to add to the query. + * @param {object} [options] - Any additional properties to add to the query clause. + * @returns {lunr.Query} + * @see lunr.Query#clause + * @see lunr.Query~Clause + * @example adding a single term to a query + * query.term("foo") + * @example adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard + * query.term("foo", { + * fields: ["title"], + * boost: 10, + * wildcard: lunr.Query.wildcard.TRAILING + * }) + * @example using lunr.tokenizer to convert a string to tokens before using them as terms + * query.term(lunr.tokenizer("foo bar")) + */ +lunr.Query.prototype.term = function (term, options) { + if (Array.isArray(term)) { + term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this) + return this + } + + var clause = options || {} + clause.term = term.toString() + + this.clause(clause) + + return this +} +lunr.QueryParseError = function (message, start, end) { + this.name = "QueryParseError" + this.message = message + this.start = start + this.end = end +} + +lunr.QueryParseError.prototype = new Error +lunr.QueryLexer = function (str) { + this.lexemes = [] + this.str = str + this.length = str.length + this.pos = 0 + this.start = 0 + this.escapeCharPositions = [] +} + +lunr.QueryLexer.prototype.run = function () { + var state = lunr.QueryLexer.lexText + + while (state) { + state = state(this) + } +} + +lunr.QueryLexer.prototype.sliceString = function () { + var subSlices = [], + sliceStart = this.start, + sliceEnd = this.pos + + for (var i = 0; i < this.escapeCharPositions.length; i++) { + sliceEnd = this.escapeCharPositions[i] + subSlices.push(this.str.slice(sliceStart, sliceEnd)) + sliceStart = sliceEnd + 1 + } + + subSlices.push(this.str.slice(sliceStart, this.pos)) + this.escapeCharPositions.length = 0 + + return subSlices.join('') +} + +lunr.QueryLexer.prototype.emit = function (type) { + this.lexemes.push({ + type: type, + str: this.sliceString(), + start: this.start, + end: this.pos + }) + + this.start = this.pos +} + +lunr.QueryLexer.prototype.escapeCharacter = function () { + this.escapeCharPositions.push(this.pos - 1) + this.pos += 1 +} + +lunr.QueryLexer.prototype.next = function () { + if (this.pos >= this.length) { + return lunr.QueryLexer.EOS + } + + var char = this.str.charAt(this.pos) + this.pos += 1 + return char +} + +lunr.QueryLexer.prototype.width = function () { + return this.pos - this.start +} + +lunr.QueryLexer.prototype.ignore = function () { + if (this.start == this.pos) { + this.pos += 1 + } + + this.start = this.pos +} + +lunr.QueryLexer.prototype.backup = function () { + this.pos -= 1 +} + +lunr.QueryLexer.prototype.acceptDigitRun = function () { + var char, charCode + + do { + char = this.next() + charCode = char.charCodeAt(0) + } while (charCode > 47 && charCode < 58) + + if (char != lunr.QueryLexer.EOS) { + this.backup() + } +} + +lunr.QueryLexer.prototype.more = function () { + return this.pos < this.length +} + +lunr.QueryLexer.EOS = 'EOS' +lunr.QueryLexer.FIELD = 'FIELD' +lunr.QueryLexer.TERM = 'TERM' +lunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE' +lunr.QueryLexer.BOOST = 'BOOST' +lunr.QueryLexer.PRESENCE = 'PRESENCE' + +lunr.QueryLexer.lexField = function (lexer) { + lexer.backup() + lexer.emit(lunr.QueryLexer.FIELD) + lexer.ignore() + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexTerm = function (lexer) { + if (lexer.width() > 1) { + lexer.backup() + lexer.emit(lunr.QueryLexer.TERM) + } + + lexer.ignore() + + if (lexer.more()) { + return lunr.QueryLexer.lexText + } +} + +lunr.QueryLexer.lexEditDistance = function (lexer) { + lexer.ignore() + lexer.acceptDigitRun() + lexer.emit(lunr.QueryLexer.EDIT_DISTANCE) + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexBoost = function (lexer) { + lexer.ignore() + lexer.acceptDigitRun() + lexer.emit(lunr.QueryLexer.BOOST) + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexEOS = function (lexer) { + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } +} + +// This matches the separator used when tokenising fields +// within a document. These should match otherwise it is +// not possible to search for some tokens within a document. +// +// It is possible for the user to change the separator on the +// tokenizer so it _might_ clash with any other of the special +// characters already used within the search string, e.g. :. +// +// This means that it is possible to change the separator in +// such a way that makes some words unsearchable using a search +// string. +lunr.QueryLexer.termSeparator = lunr.tokenizer.separator + +lunr.QueryLexer.lexText = function (lexer) { + while (true) { + var char = lexer.next() + + if (char == lunr.QueryLexer.EOS) { + return lunr.QueryLexer.lexEOS + } + + // Escape character is '\' + if (char.charCodeAt(0) == 92) { + lexer.escapeCharacter() + continue + } + + if (char == ":") { + return lunr.QueryLexer.lexField + } + + if (char == "~") { + lexer.backup() + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } + return lunr.QueryLexer.lexEditDistance + } + + if (char == "^") { + lexer.backup() + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } + return lunr.QueryLexer.lexBoost + } + + // "+" indicates term presence is required + // checking for length to ensure that only + // leading "+" are considered + if (char == "+" && lexer.width() === 1) { + lexer.emit(lunr.QueryLexer.PRESENCE) + return lunr.QueryLexer.lexText + } + + // "-" indicates term presence is prohibited + // checking for length to ensure that only + // leading "-" are considered + if (char == "-" && lexer.width() === 1) { + lexer.emit(lunr.QueryLexer.PRESENCE) + return lunr.QueryLexer.lexText + } + + if (char.match(lunr.QueryLexer.termSeparator)) { + return lunr.QueryLexer.lexTerm + } + } +} + +lunr.QueryParser = function (str, query) { + this.lexer = new lunr.QueryLexer (str) + this.query = query + this.currentClause = {} + this.lexemeIdx = 0 +} + +lunr.QueryParser.prototype.parse = function () { + this.lexer.run() + this.lexemes = this.lexer.lexemes + + var state = lunr.QueryParser.parseClause + + while (state) { + state = state(this) + } + + return this.query +} + +lunr.QueryParser.prototype.peekLexeme = function () { + return this.lexemes[this.lexemeIdx] +} + +lunr.QueryParser.prototype.consumeLexeme = function () { + var lexeme = this.peekLexeme() + this.lexemeIdx += 1 + return lexeme +} + +lunr.QueryParser.prototype.nextClause = function () { + var completedClause = this.currentClause + this.query.clause(completedClause) + this.currentClause = {} +} + +lunr.QueryParser.parseClause = function (parser) { + var lexeme = parser.peekLexeme() + + if (lexeme == undefined) { + return + } + + switch (lexeme.type) { + case lunr.QueryLexer.PRESENCE: + return lunr.QueryParser.parsePresence + case lunr.QueryLexer.FIELD: + return lunr.QueryParser.parseField + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expected either a field or a term, found " + lexeme.type + + if (lexeme.str.length >= 1) { + errorMessage += " with value '" + lexeme.str + "'" + } + + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } +} + +lunr.QueryParser.parsePresence = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + switch (lexeme.str) { + case "-": + parser.currentClause.presence = lunr.Query.presence.PROHIBITED + break + case "+": + parser.currentClause.presence = lunr.Query.presence.REQUIRED + break + default: + var errorMessage = "unrecognised presence operator'" + lexeme.str + "'" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + var errorMessage = "expecting term or field, found nothing" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.FIELD: + return lunr.QueryParser.parseField + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expecting term or field, found '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseField = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + if (parser.query.allFields.indexOf(lexeme.str) == -1) { + var possibleFields = parser.query.allFields.map(function (f) { return "'" + f + "'" }).join(', '), + errorMessage = "unrecognised field '" + lexeme.str + "', possible fields: " + possibleFields + + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.fields = [lexeme.str] + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + var errorMessage = "expecting term, found nothing" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expecting term, found '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseTerm = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + parser.currentClause.term = lexeme.str.toLowerCase() + + if (lexeme.str.indexOf("*") != -1) { + parser.currentClause.usePipeline = false + } + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseEditDistance = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + var editDistance = parseInt(lexeme.str, 10) + + if (isNaN(editDistance)) { + var errorMessage = "edit distance must be numeric" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.editDistance = editDistance + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseBoost = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + var boost = parseInt(lexeme.str, 10) + + if (isNaN(boost)) { + var errorMessage = "boost must be numeric" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.boost = boost + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + + /** + * export the module via AMD, CommonJS or as a browser global + * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js + */ + ;(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(factory) + } else if (typeof exports === 'object') { + /** + * Node. Does not work with strict CommonJS, but + * only CommonJS-like enviroments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + root.lunr = factory() + } + }(this, function () { + /** + * Just return a value to define the module export. + * This example returns an object, but the module + * can return a function as the exported value. + */ + return lunr + })) +})(); diff --git a/assets/js/lunr/lunr.min.js b/assets/js/lunr/lunr.min.js new file mode 100644 index 0000000..cdc94cd --- /dev/null +++ b/assets/js/lunr/lunr.min.js @@ -0,0 +1,6 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + */ +!function(){var e=function(t){var r=new e.Builder;return r.pipeline.add(e.trimmer,e.stopWordFilter,e.stemmer),r.searchPipeline.add(e.stemmer),t.call(r,r),r.build()};e.version="2.3.9",e.utils={},e.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),e.utils.asString=function(e){return void 0===e||null===e?"":e.toString()},e.utils.clone=function(e){if(null===e||void 0===e)return e;for(var t=Object.create(null),r=Object.keys(e),i=0;i0){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/assets/js/main.min.js b/assets/js/main.min.js new file mode 100644 index 0000000..7c176b6 --- /dev/null +++ b/assets/js/main.min.js @@ -0,0 +1,6 @@ +/*! + * Minimal Mistakes Jekyll Theme 4.23.0 by Michael Rose + * Copyright 2013-2021 Michael Rose - mademistakes.com | @mmistakes + * Licensed under MIT + */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";function m(e){return null!=e&&e===e.window}var t=[],n=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,o=t.indexOf,r={},i=r.toString,v=r.hasOwnProperty,a=v.toString,l=a.call(Object),y={},b=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},T=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function x(e,t,n){var r,o,i=(n=n||T).createElement("script");if(i.text=e,t)for(r in c)(o=t[r]||t.getAttribute&&t.getAttribute(r))&&i.setAttribute(r,o);n.head.appendChild(i).parentNode.removeChild(i)}function h(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?r[i.call(e)]||"object":typeof e}var f="3.5.1",E=function(e,t){return new E.fn.init(e,t)};function d(e){var t=!!e&&"length"in e&&e.length,n=h(e);return!b(e)&&!m(e)&&("array"===n||0===t||"number"==typeof t&&0>10|55296,1023&e|56320))}function r(){C()}var e,d,x,i,o,p,h,m,w,u,l,C,T,a,E,g,s,c,v,S="sizzle"+ +new Date,y=n.document,k=0,b=0,A=ue(),N=ue(),j=ue(),I=ue(),L=function(e,t){return e===t&&(l=!0),0},D={}.hasOwnProperty,t=[],O=t.pop,H=t.push,P=t.push,q=t.slice,M=function(e,t){for(var n=0,r=e.length;n+~]|"+$+")"+$+"*"),Q=new RegExp($+"|>"),Y=new RegExp(F),V=new RegExp("^"+R+"$"),G={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+B),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+$+"*(even|odd|(([+-]|)(\\d*)n|)"+$+"*(?:([+-]|)"+$+"*(\\d+)|))"+$+"*\\)|)","i"),bool:new RegExp("^(?:"+_+")$","i"),needsContext:new RegExp("^"+$+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+$+"*((?:-\\d)?\\d*)"+$+"*\\)|)(?=[^-]|$)","i")},K=/HTML$/i,Z=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,ee=/^[^{]+\{\s*\[native \w/,te=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ne=/[+~]/,re=new RegExp("\\\\[\\da-fA-F]{1,6}"+$+"?|\\\\([^\\r\\n\\f])","g"),oe=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},ae=ye(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{P.apply(t=q.call(y.childNodes),y.childNodes),t[y.childNodes.length].nodeType}catch(e){P={apply:t.length?function(e,t){H.apply(e,q.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function se(t,e,n,r){var o,i,a,s,u,l,c=e&&e.ownerDocument,f=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==f&&9!==f&&11!==f)return n;if(!r&&(C(e),e=e||T,E)){if(11!==f&&(s=te.exec(t)))if(l=s[1]){if(9===f){if(!(i=e.getElementById(l)))return n;if(i.id===l)return n.push(i),n}else if(c&&(i=c.getElementById(l))&&v(e,i)&&i.id===l)return n.push(i),n}else{if(s[2])return P.apply(n,e.getElementsByTagName(t)),n;if((l=s[3])&&d.getElementsByClassName&&e.getElementsByClassName)return P.apply(n,e.getElementsByClassName(l)),n}if(d.qsa&&!I[t+" "]&&(!g||!g.test(t))&&(1!==f||"object"!==e.nodeName.toLowerCase())){if(l=t,c=e,1===f&&(Q.test(t)||X.test(t))){for((c=ne.test(t)&&me(e.parentNode)||e)===e&&d.scope||((a=e.getAttribute("id"))?a=a.replace(oe,ie):e.setAttribute("id",a=S)),o=(u=p(t)).length;o--;)u[o]=(a?"#"+a:":scope")+" "+ve(u[o]);l=u.join(",")}try{return P.apply(n,c.querySelectorAll(l)),n}catch(e){I(t,!0)}finally{a===S&&e.removeAttribute("id")}}}return m(t.replace(W,"$1"),e,n,r)}function ue(){var n=[];function r(e,t){return n.push(e+" ")>x.cacheLength&&delete r[n.shift()],r[e+" "]=t}return r}function le(e){return e[S]=!0,e}function ce(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){for(var n=e.split("|"),r=n.length;r--;)x.attrHandle[n[r]]=t}function de(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function pe(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function he(a){return le(function(i){return i=+i,le(function(e,t){for(var n,r=a([],e.length,i),o=r.length;o--;)e[n=r[o]]&&(e[n]=!(t[n]=e[n]))})})}function me(e){return e&&void 0!==e.getElementsByTagName&&e}for(e in d=se.support={},o=se.isXML=function(e){var t=e.namespaceURI,e=(e.ownerDocument||e).documentElement;return!K.test(t||e&&e.nodeName||"HTML")},C=se.setDocument=function(e){var t,e=e?e.ownerDocument||e:y;return e!=T&&9===e.nodeType&&e.documentElement&&(a=(T=e).documentElement,E=!o(T),y!=T&&(t=T.defaultView)&&t.top!==t&&(t.addEventListener?t.addEventListener("unload",r,!1):t.attachEvent&&t.attachEvent("onunload",r)),d.scope=ce(function(e){return a.appendChild(e).appendChild(T.createElement("div")),void 0!==e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(T.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=ee.test(T.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!T.getElementsByName||!T.getElementsByName(S).length}),d.getById?(x.filter.ID=function(e){var t=e.replace(re,f);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if(void 0!==t.getElementById&&E){e=t.getElementById(e);return e?[e]:[]}}):(x.filter.ID=function(e){var t=e.replace(re,f);return function(e){e=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return e&&e.value===t}},x.find.ID=function(e,t){if(void 0!==t.getElementById&&E){var n,r,o,i=t.getElementById(e);if(i){if((n=i.getAttributeNode("id"))&&n.value===e)return[i];for(o=t.getElementsByName(e),r=0;i=o[r++];)if((n=i.getAttributeNode("id"))&&n.value===e)return[i]}return[]}}),x.find.TAG=d.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],o=0,i=t.getElementsByTagName(e);if("*"!==e)return i;for(;n=i[o++];)1===n.nodeType&&r.push(n);return r},x.find.CLASS=d.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],g=[],(d.qsa=ee.test(T.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&g.push("[*^$]="+$+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||g.push("\\["+$+"*(?:value|"+_+")"),e.querySelectorAll("[id~="+S+"-]").length||g.push("~="),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||g.push("\\["+$+"*name"+$+"*="+$+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||g.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||g.push(".#.+[+~]"),e.querySelectorAll("\\\f"),g.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=T.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&g.push("name"+$+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&g.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(d.matchesSelector=ee.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),g=g.length&&new RegExp(g.join("|")),s=s.length&&new RegExp(s.join("|")),t=ee.test(a.compareDocumentPosition),v=t||ee.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,t=t&&t.parentNode;return e===t||!(!t||1!==t.nodeType||!(n.contains?n.contains(t):e.compareDocumentPosition&&16&e.compareDocumentPosition(t)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},L=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==T||e.ownerDocument==y&&v(y,e)?-1:t==T||t.ownerDocument==y&&v(y,t)?1:u?M(u,e)-M(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,o=e.parentNode,i=t.parentNode,a=[e],s=[t];if(!o||!i)return e==T?-1:t==T?1:o?-1:i?1:u?M(u,e)-M(u,t):0;if(o===i)return de(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[r]===s[r];)r++;return r?de(a[r],s[r]):a[r]==y?-1:s[r]==y?1:0}),T},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(C(e),d.matchesSelector&&E&&!I[t+" "]&&(!s||!s.test(t))&&(!g||!g.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){I(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(re,f),e[3]=(e[3]||e[4]||e[5]||"").replace(re,f),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&Y.test(n)&&(t=p(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(re,f).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=A[e+" "];return t||(t=new RegExp("(^|"+$+")"+e+"("+$+"|$)"))&&A(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(t,n,r){return function(e){e=se.attr(e,t);return null==e?"!="===n:!n||(e+="","="===n?e===r:"!="===n?e!==r:"^="===n?r&&0===e.indexOf(r):"*="===n?r&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return b(n)?E.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?E.grep(e,function(e){return e===n!==r}):"string"!=typeof n?E.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(E.fn.init=function(e,t,n){if(!e)return this;if(n=n||L,"string"!=typeof e)return e.nodeType?(this[0]=e,this.length=1,this):b(e)?void 0!==n.ready?n.ready(e):e(E):E.makeArray(e,this);if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:I.exec(e))||!r[1]&&t)return(!t||t.jquery?t||n:this.constructor(t)).find(e);if(r[1]){if(t=t instanceof E?t[0]:t,E.merge(this,E.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:T,!0)),N.test(r[1])&&E.isPlainObject(t))for(var r in t)b(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(e=T.getElementById(r[2]))&&(this[0]=e,this.length=1),this}).prototype=E.fn;var L=E(T),D=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function H(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}E.fn.extend({has:function(e){var t=E(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,de=/^$|^module$|\/(?:java|ecma)script/i;f=T.createDocumentFragment().appendChild(T.createElement("div")),(p=T.createElement("input")).setAttribute("type","radio"),p.setAttribute("checked","checked"),p.setAttribute("name","t"),f.appendChild(p),y.checkClone=f.cloneNode(!0).cloneNode(!0).lastChild.checked,f.innerHTML="",y.noCloneChecked=!!f.cloneNode(!0).lastChild.defaultValue,f.innerHTML="",y.option=!!f.lastChild;var pe={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function he(e,t){var n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[];return void 0===t||t&&A(e,t)?E.merge([e],n):n}function me(e,t){for(var n=0,r=e.length;n",""]);var ge=/<|&#?\w+;/;function ve(e,t,n,r,o){for(var i,a,s,u,l,c=t.createDocumentFragment(),f=[],d=0,p=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&E(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Le(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function De(e,t){var n,r,o,i;if(1===t.nodeType){if(V.hasData(e)&&(i=V.get(e).events))for(o in V.remove(t,"handle events"),i)for(n=0,r=i[o].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",o=function(e){r.remove(),o=null,e&&t("error"===e.type?404:200,e.type)}),T.head.appendChild(r[0])},abort:function(){o&&o()}}});var Gt=[],Kt=/(=)\?(?=&|$)|\?\?/;E.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||E.expando+"_"+jt.guid++;return this[e]=!0,e}}),E.ajaxPrefilter("json jsonp",function(e,t,n){var r,o,i,a=!1!==e.jsonp&&(Kt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Kt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=b(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Kt,"$1"+r):!1!==e.jsonp&&(e.url+=(It.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return i||E.error(r+" was not called"),i[0]},e.dataTypes[0]="json",o=C[r],C[r]=function(){i=arguments},n.always(function(){void 0===o?E(C).removeProp(r):C[r]=o,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),i&&b(o)&&o(i[0]),i=o=void 0}),"script"}),y.createHTMLDocument=((f=T.implementation.createHTMLDocument("").body).innerHTML="
",2===f.childNodes.length),E.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=T.implementation.createHTMLDocument("")).createElement("base")).href=T.location.href,t.head.appendChild(r)):t=T),r=!n&&[],(n=N.exec(e))?[t.createElement(n[1])]:(n=ve([e],t,r),r&&r.length&&E(r).remove(),E.merge([],n.childNodes)));var r},E.fn.load=function(e,t,n){var r,o,i,a=this,s=e.indexOf(" ");return-1").append(E.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,i||[e.responseText,t,e])})}),this},E.expr.pseudos.animated=function(t){return E.grep(E.timers,function(e){return t===e.elem}).length},E.offset={setOffset:function(e,t,n){var r,o,i,a,s=E.css(e,"position"),u=E(e),l={};"static"===s&&(e.style.position="relative"),i=u.offset(),r=E.css(e,"top"),a=E.css(e,"left"),a=("absolute"===s||"fixed"===s)&&-1<(r+a).indexOf("auto")?(o=(s=u.position()).top,s.left):(o=parseFloat(r)||0,parseFloat(a)||0),null!=(t=b(t)?t.call(e,n,E.extend({},i)):t).top&&(l.top=t.top-i.top+o),null!=t.left&&(l.left=t.left-i.left+a),"using"in t?t.using.call(e,l):("number"==typeof l.top&&(l.top+="px"),"number"==typeof l.left&&(l.left+="px"),u.css(l))}},E.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){E.offset.setOffset(this,t,e)});var e,n=this[0];return n?n.getClientRects().length?(e=n.getBoundingClientRect(),n=n.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],o={top:0,left:0};if("fixed"===E.css(r,"position"))t=r.getBoundingClientRect();else{for(t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===E.css(e,"position");)e=e.parentNode;e&&e!==r&&1===e.nodeType&&((o=E(e).offset()).top+=E.css(e,"borderTopWidth",!0),o.left+=E.css(e,"borderLeftWidth",!0))}return{top:t.top-o.top-E.css(r,"marginTop",!0),left:t.left-o.left-E.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===E.css(e,"position");)e=e.offsetParent;return e||re})}}),E.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,o){var i="pageYOffset"===o;E.fn[t]=function(e){return F(this,function(e,t,n){var r;return m(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n?r?r[o]:e[t]:void(r?r.scrollTo(i?r.pageXOffset:n,i?n:r.pageYOffset):e[t]=n)},t,e,arguments.length)}}),E.each(["top","left"],function(e,n){E.cssHooks[n]=Ge(y.pixelPosition,function(e,t){if(t)return t=Ve(e,n),We.test(t)?E(e).position()[n]+"px":t})}),E.each({Height:"height",Width:"width"},function(a,s){E.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,i){E.fn[i]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),o=r||(!0===e||!0===t?"margin":"border");return F(this,function(e,t,n){var r;return m(e)?0===i.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?E.css(e,t,o):E.style(e,t,n,o)},s,n?e:void 0,n)}})}),E.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){E.fn[t]=function(e){return this.on(t,e)}}),E.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),E.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){E.fn[n]=function(e,t){return 0x

',t.appendChild(n.childNodes[1])),e&&i.extend(o,e),this.each(function(){var e=['iframe[src*="player.vimeo.com"]','iframe[src*="youtube.com"]','iframe[src*="youtube-nocookie.com"]','iframe[src*="kickstarter.com"][src*="video.html"]',"object","embed"];o.customSelector&&e.push(o.customSelector);var r=".fitvidsignore";o.ignore&&(r=r+", "+o.ignore);e=i(this).find(e.join(","));(e=(e=e.not("object object")).not(r)).each(function(e){var t,n=i(this);0').parent(".fluid-width-video-wrapper").css("padding-top",100*t+"%"),n.removeAttr("height").removeAttr("width"))})})}}(window.jQuery||window.Zepto),$(function(){var n,r,e,o,t=$("nav.greedy-nav .greedy-nav__toggle"),i=$("nav.greedy-nav .visible-links"),a=$("nav.greedy-nav .hidden-links"),s=$("nav.greedy-nav"),u=$("nav.greedy-nav .site-logo"),l=$("nav.greedy-nav .site-logo img"),c=$("nav.greedy-nav .site-title"),f=$("nav.greedy-nav button.search__toggle");function d(){function t(e,t){r+=t,n+=1,o.push(r)}r=n=0,e=1e3,o=[],i.children().outerWidth(t),a.children().each(function(){var e;(e=(e=$(this)).clone()).css("visibility","hidden"),i.append(e),t(0,e.outerWidth()),e.remove()})}d();var p,h,m,g,v=$(window).width(),y=v<768?0:v<1024?1:v<1280?2:3;function b(){var e=(v=$(window).width())<768?0:v<1024?1:v<1280?2:3;e!==y&&d(),y=e,h=i.children().length,p=s.innerWidth()-(0!==u.length?u.outerWidth(!0):0)-c.outerWidth(!0)-(0!==f.length?f.outerWidth(!0):0)-(h!==o.length?t.outerWidth(!0):0),m=o[h-1],po[h]&&(a.children().first().appendTo(i),h+=1,b()),t.attr("count",n-h),h===n?t.addClass("hidden"):t.removeClass("hidden")}$(window).resize(function(){b()}),t.on("click",function(){a.toggleClass("hidden"),$(this).toggleClass("close"),clearTimeout(g)}),a.on("mouseleave",function(){g=setTimeout(function(){a.addClass("hidden")},e)}).on("mouseenter",function(){clearTimeout(g)}),0===l.length||l[0].complete||0!==l[0].naturalWidth?b():l.one("load error",b)}),function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof exports?e(require("jquery")):e(window.jQuery||window.Zepto)}(function(l){function e(){}function c(e,t){h.ev.on("mfp"+e+x,t)}function f(e,t,n,r){var o=document.createElement("div");return o.className="mfp-"+e,n&&(o.innerHTML=n),r?t&&t.appendChild(o):(o=l(o),t&&o.appendTo(t)),o}function d(e,t){h.ev.triggerHandler("mfp"+e,t),h.st.callbacks&&(e=e.charAt(0).toLowerCase()+e.slice(1),h.st.callbacks[e]&&h.st.callbacks[e].apply(h,l.isArray(t)?t:[t]))}function p(e){return e===t&&h.currTemplate.closeBtn||(h.currTemplate.closeBtn=l(h.st.closeMarkup.replace("%title%",h.st.tClose)),t=e),h.currTemplate.closeBtn}function i(){l.magnificPopup.instance||((h=new e).init(),l.magnificPopup.instance=h)}var h,r,m,o,g,t,u="Close",v="BeforeClose",y="MarkupParse",b="Open",x=".mfp",w="mfp-ready",n="mfp-removing",a="mfp-prevent-close",s=!!window.jQuery,C=l(window);e.prototype={constructor:e,init:function(){var e=navigator.appVersion;h.isLowIE=h.isIE8=document.all&&!document.addEventListener,h.isAndroid=/android/gi.test(e),h.isIOS=/iphone|ipad|ipod/gi.test(e),h.supportsTransition=function(){var e=document.createElement("p").style,t=["ms","O","Moz","Webkit"];if(void 0!==e.transition)return!0;for(;t.length;)if(t.pop()+"Transition"in e)return!0;return!1}(),h.probablyMobile=h.isAndroid||h.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),m=l(document),h.popupsCache={}},open:function(e){if(!1===e.isObj){h.items=e.items.toArray(),h.index=0;for(var t,n=e.items,r=0;r(e||C.height())},_setFocus:function(){(h.st.focus?h.content.find(h.st.focus).eq(0):h.wrap).focus()},_onFocusIn:function(e){if(e.target!==h.wrap[0]&&!l.contains(h.wrap[0],e.target))return h._setFocus(),!1},_parseMarkup:function(o,e,t){var i;t.data&&(e=l.extend(t.data,e)),d(y,[o,e,t]),l.each(e,function(e,t){return void 0===t||!1===t||void(1<(i=e.split("_")).length?0<(n=o.find(x+"-"+i[0])).length&&("replaceWith"===(r=i[1])?n[0]!==t[0]&&n.replaceWith(t):"img"===r?n.is("img")?n.attr("src",t):n.replaceWith(l("").attr("src",t).attr("class",n.attr("class"))):n.attr(i[1],t)):o.find(x+"-"+e).html(t));var n,r})},_getScrollbarSize:function(){var e;return void 0===h.scrollbarSize&&((e=document.createElement("div")).style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",document.body.appendChild(e),h.scrollbarSize=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),h.scrollbarSize}},l.magnificPopup={instance:null,proto:e.prototype,modules:[],open:function(e,t){return i(),(e=e?l.extend(!0,{},e):{}).isObj=!0,e.index=t||0,this.instance.open(e)},close:function(){return l.magnificPopup.instance&&l.magnificPopup.instance.close()},registerModule:function(e,t){t.options&&(l.magnificPopup.defaults[e]=t.options),l.extend(this.proto,t.proto),this.modules.push(e)},defaults:{disableOn:0,key:null,midClick:!1,mainClass:"",preloader:!0,focus:"",closeOnContentClick:!1,closeOnBgClick:!0,closeBtnInside:!0,showCloseBtn:!0,enableEscapeKey:!0,modal:!1,alignTop:!1,removalDelay:0,prependTo:null,fixedContentPos:"auto",fixedBgPos:"auto",overflowY:"auto",closeMarkup:'',tClose:"Close (Esc)",tLoading:"Loading...",autoFocusLast:!0}},l.fn.magnificPopup=function(e){i();var t,n,r,o=l(this);return"string"==typeof e?"open"===e?(t=s?o.data("magnificPopup"):o[0].magnificPopup,n=parseInt(arguments[1],10)||0,r=t.items?t.items[n]:(r=o,(r=t.delegate?o.find(t.delegate):r).eq(n)),h._openClick({mfpEl:r},o,t)):h.isOpen&&h[e].apply(h,Array.prototype.slice.call(arguments,1)):(e=l.extend(!0,{},e),s?o.data("magnificPopup",e):o[0].magnificPopup=e,h.addGroup(o,e)),o};function T(){k&&(S.after(k.addClass(E)).detach(),k=null)}var E,S,k,A="inline";l.magnificPopup.registerModule(A,{options:{hiddenClass:"hide",markup:"",tNotFound:"Content not found"},proto:{initInline:function(){h.types.push(A),c(u+"."+A,function(){T()})},getInline:function(e,t){if(T(),e.src){var n,r=h.st.inline,o=l(e.src);return o.length?((n=o[0].parentNode)&&n.tagName&&(S||(E=r.hiddenClass,S=f(E),E="mfp-"+E),k=o.after(S).detach().removeClass(E)),h.updateStatus("ready")):(h.updateStatus("error",r.tNotFound),o=l("
")),e.inlineElement=o}return h.updateStatus("ready"),h._parseMarkup(t,{},e),t}}});function N(){I&&l(document.body).removeClass(I)}function j(){N(),h.req&&h.req.abort()}var I,L="ajax";l.magnificPopup.registerModule(L,{options:{settings:null,cursor:"mfp-ajax-cur",tError:'The content could not be loaded.'},proto:{initAjax:function(){h.types.push(L),I=h.st.ajax.cursor,c(u+"."+L,j),c("BeforeChange."+L,j)},getAjax:function(r){I&&l(document.body).addClass(I),h.updateStatus("loading");var e=l.extend({url:r.src,success:function(e,t,n){n={data:e,xhr:n};d("ParseAjax",n),h.appendContent(l(n.data),L),r.finished=!0,N(),h._setFocus(),setTimeout(function(){h.wrap.addClass(w)},16),h.updateStatus("ready"),d("AjaxContentAdded")},error:function(){N(),r.finished=r.loadError=!0,h.updateStatus("error",h.st.ajax.tError.replace("%url%",r.src))}},h.st.ajax.settings);return h.req=l.ajax(e),""}}});var D;l.magnificPopup.registerModule("image",{options:{markup:'
',cursor:"mfp-zoom-out-cur",titleSrc:"title",verticalFit:!0,tError:'The image could not be loaded.'},proto:{initImage:function(){var e=h.st.image,t=".image";h.types.push("image"),c(b+t,function(){"image"===h.currItem.type&&e.cursor&&l(document.body).addClass(e.cursor)}),c(u+t,function(){e.cursor&&l(document.body).removeClass(e.cursor),C.off("resize"+x)}),c("Resize"+t,h.resizeImage),h.isLowIE&&c("AfterChange",h.resizeImage)},resizeImage:function(){var e,t=h.currItem;t&&t.img&&h.st.image.verticalFit&&(e=0,h.isLowIE&&(e=parseInt(t.img.css("padding-top"),10)+parseInt(t.img.css("padding-bottom"),10)),t.img.css("max-height",h.wH-e))},_onImageHasSize:function(e){e.img&&(e.hasSize=!0,D&&clearInterval(D),e.isCheckingImgSize=!1,d("ImageHasSize",e),e.imgHidden&&(h.content&&h.content.removeClass("mfp-loading"),e.imgHidden=!1))},findImageSize:function(t){var n=0,r=t.img[0],o=function(e){D&&clearInterval(D),D=setInterval(function(){0
',srcAction:"iframe_src",patterns:{youtube:{index:"youtube.com",id:"v=",src:"//www.youtube.com/embed/%id%?autoplay=1"},vimeo:{index:"vimeo.com/",id:"/",src:"//player.vimeo.com/video/%id%?autoplay=1"},gmaps:{index:"//maps.google.",src:"%id%&output=embed"}}},proto:{initIframe:function(){h.types.push(P),c("BeforeChange",function(e,t,n){t!==n&&(t===P?H():n===P&&H(!0))}),c(u+"."+P,function(){H()})},getIframe:function(e,t){var n=e.src,r=h.st.iframe;l.each(r.patterns,function(){if(-1',preload:[0,2],navigateByImgClick:!0,arrows:!0,tPrev:"Previous (Left arrow key)",tNext:"Next (Right arrow key)",tCounter:"%curr% of %total%"},proto:{initGallery:function(){var i=h.st.gallery,e=".mfp-gallery";if(h.direction=!0,!i||!i.enabled)return!1;g+=" mfp-gallery",c(b+e,function(){i.navigateByImgClick&&h.wrap.on("click"+e,".mfp-img",function(){if(1=h.index,h.index=e,h.updateItemHTML()},preloadNearbyImages:function(){for(var e=h.st.gallery.preload,t=Math.min(e[0],h.items.length),n=Math.min(e[1],h.items.length),r=1;r<=(h.direction?n:t);r++)h._preloadItem(h.index+r);for(r=1;r<=(h.direction?t:n);r++)h._preloadItem(h.index-r)},_preloadItem:function(e){var t;e=q(e),h.items[e].preloaded||((t=h.items[e]).parsed||(t=h.parseEl(e)),d("LazyLoad",t),"image"===t.type&&(t.img=l('').on("load.mfploader",function(){t.hasSize=!0}).on("error.mfploader",function(){t.hasSize=!0,t.loadError=!0,d("LazyLoadError",t)}).attr("src",t.src)),t.preloaded=!0)}}});var _="retina";l.magnificPopup.registerModule(_,{options:{replaceSrc:function(e){return e.src.replace(/\.\w+$/,function(e){return"@2x"+e})},ratio:1},proto:{initRetina:function(){var n,r;1t.durationMax?t.durationMax:t.durationMin&&e=u)return b.cancelScroll(!0),e=t,n=g,0===(t=r)&&document.body.focus(),n||(t.focus(),document.activeElement!==t&&(t.setAttribute("tabindex","-1"),t.focus(),t.style.outline="none"),x.scrollTo(0,e)),E("scrollStop",m,r,o),!(y=f=null)},h=function(e){var t,n,r;l+=e-(f=f||e),d=i+s*(n=d=1<(d=0===c?0:l/c)?1:d,"easeInQuad"===(t=m).easing&&(r=n*n),"easeOutQuad"===t.easing&&(r=n*(2-n)),"easeInOutQuad"===t.easing&&(r=n<.5?2*n*n:(4-2*n)*n-1),"easeInCubic"===t.easing&&(r=n*n*n),"easeOutCubic"===t.easing&&(r=--n*n*n+1),"easeInOutCubic"===t.easing&&(r=n<.5?4*n*n*n:(n-1)*(2*n-2)*(2*n-2)+1),"easeInQuart"===t.easing&&(r=n*n*n*n),"easeOutQuart"===t.easing&&(r=1- --n*n*n*n),"easeInOutQuart"===t.easing&&(r=n<.5?8*n*n*n*n:1-8*--n*n*n*n),"easeInQuint"===t.easing&&(r=n*n*n*n*n),"easeOutQuint"===t.easing&&(r=1+--n*n*n*n*n),"easeInOutQuint"===t.easing&&(r=n<.5?16*n*n*n*n*n:1+16*--n*n*n*n*n),(r=t.customEasing?t.customEasing(n):r)||n),x.scrollTo(0,Math.floor(d)),p(d,a)||(y=x.requestAnimationFrame(h),f=e)},0===x.pageYOffset&&x.scrollTo(0,0),t=r,e=m,g||history.pushState&&e.updateURL&&history.pushState({smoothScroll:JSON.stringify(e),anchor:t.id},document.title,t===document.documentElement?"#top":"#"+t.id),"matchMedia"in x&&x.matchMedia("(prefers-reduced-motion)").matches?x.scrollTo(0,Math.floor(a)):(E("scrollStart",m,r,o),b.cancelScroll(!0),x.requestAnimationFrame(h)))};function t(e){if(!e.defaultPrevented&&!(0!==e.button||e.metaKey||e.ctrlKey||e.shiftKey)&&"closest"in e.target&&(o=e.target.closest(r))&&"a"===o.tagName.toLowerCase()&&!e.target.closest(v.ignore)&&o.hostname===x.location.hostname&&o.pathname===x.location.pathname&&/#/.test(o.href)){var t,n;try{n=a(decodeURIComponent(o.hash))}catch(e){n=a(o.hash)}if("#"===n){if(!v.topOnEmptyHash)return;t=document.documentElement}else t=document.querySelector(n);(t=t||"#top"!==n?t:document.documentElement)&&(e.preventDefault(),n=v,history.replaceState&&n.updateURL&&!history.state&&(e=(e=x.location.hash)||"",history.replaceState({smoothScroll:JSON.stringify(n),anchor:e||x.pageYOffset},document.title,e||x.location.href)),b.animateScroll(t,o))}}function i(e){var t;null!==history.state&&history.state.smoothScroll&&history.state.smoothScroll===JSON.stringify(v)&&("string"==typeof(t=history.state.anchor)&&t&&!(t=document.querySelector(a(history.state.anchor)))||b.animateScroll(t,null,{updateURL:!1}))}b.destroy=function(){v&&(document.removeEventListener("click",t,!1),x.removeEventListener("popstate",i,!1),b.cancelScroll(),y=n=o=v=null)};return function(){if(!("querySelector"in document&&"addEventListener"in x&&"requestAnimationFrame"in x&&"closest"in x.Element.prototype))throw"Smooth Scroll: This browser does not support the required JavaScript methods and browser APIs.";b.destroy(),v=w(S,e||{}),n=v.header?document.querySelector(v.header):null,document.addEventListener("click",t,!1),v.updateURL&&v.popstate&&x.addEventListener("popstate",i,!1)}(),b}}),function(e,t){"function"==typeof define&&define.amd?define([],function(){return t(e)}):"object"==typeof exports?module.exports=t(e):e.Gumshoe=t(e)}("undefined"!=typeof global?global:"undefined"!=typeof window?window:this,function(c){"use strict";function f(e,t,n){n.settings.events&&(n=new CustomEvent(e,{bubbles:!0,cancelable:!0,detail:n}),t.dispatchEvent(n))}function n(e){var t=0;if(e.offsetParent)for(;e;)t+=e.offsetTop,e=e.offsetParent;return 0<=t?t:0}function d(e){e&&e.sort(function(e,t){return n(e.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)}function p(e,t){var n,r,o=e[e.length-1];if(n=o,r=t,!(!s()||!a(n.content,r,!0)))return o;for(var i=e.length-1;0<=i;i--)if(a(e[i].content,t))return e[i]}function h(e,t){var n;!e||(n=e.nav.closest("li"))&&(n.classList.remove(t.navClass),e.content.classList.remove(t.contentClass),r(n,t),f("gumshoeDeactivate",n,{link:e.nav,content:e.content,settings:t}))}var m={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},r=function(e,t){!t.nested||(e=e.parentNode.closest("li"))&&(e.classList.remove(t.nestedClass),r(e,t))},g=function(e,t){!t.nested||(e=e.parentNode.closest("li"))&&(e.classList.add(t.nestedClass),g(e,t))};return function(e,t){var n,o,i,r,a,s={setup:function(){n=document.querySelectorAll(e),o=[],Array.prototype.forEach.call(n,function(e){var t=document.getElementById(decodeURIComponent(e.hash.substr(1)));t&&o.push({nav:e,content:t})}),d(o)}};s.detect=function(){var e,t,n,r=p(o,a);r?i&&r.content===i.content||(h(i,a),t=a,!(e=r)||(n=e.nav.closest("li"))&&(n.classList.add(t.navClass),e.content.classList.add(t.contentClass),g(n,t),f("gumshoeActivate",n,{link:e.nav,content:e.content,settings:t})),i=r):i&&(h(i,a),i=null)};function u(e){r&&c.cancelAnimationFrame(r),r=c.requestAnimationFrame(s.detect)}function l(e){r&&c.cancelAnimationFrame(r),r=c.requestAnimationFrame(function(){d(o),s.detect()})}s.destroy=function(){i&&h(i,a),c.removeEventListener("scroll",u,!1),a.reflow&&c.removeEventListener("resize",l,!1),a=r=i=n=o=null};return a=function(){var n={};return Array.prototype.forEach.call(arguments,function(e){for(var t in e){if(!e.hasOwnProperty(t))return;n[t]=e[t]}}),n}(m,t||{}),s.setup(),s.detect(),c.addEventListener("scroll",u,!1),a.reflow&&c.addEventListener("resize",l,!1),s}}),$(document).ready(function(){$("#main").fitVids();function e(){(0===$(".author__urls-wrapper button").length?1024<$(window).width():!$(".author__urls-wrapper button").is(":visible"))?$(".sidebar").addClass("sticky"):$(".sidebar").removeClass("sticky")}e(),$(window).resize(function(){e()}),$(".author__urls-wrapper button").on("click",function(){$(".author__urls").toggleClass("is--visible"),$(".author__urls-wrapper button").toggleClass("open")}),$(document).keyup(function(e){27===e.keyCode&&$(".initial-content").hasClass("is--hidden")&&($(".search-content").toggleClass("is--visible"),$(".initial-content").toggleClass("is--hidden"))}),$(".search__toggle").on("click",function(){$(".search-content").toggleClass("is--visible"),$(".initial-content").toggleClass("is--hidden"),setTimeout(function(){$(".search-content input").focus()},400)});new SmoothScroll('a[href*="#"]',{offset:20,speed:400,speedAsDuration:!0,durationMax:500});0<$("nav.toc").length&&new Gumshoe("nav.toc a",{navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:20,reflow:!0,events:!0}),$("a[href$='.jpg'],a[href$='.jpeg'],a[href$='.JPG'],a[href$='.png'],a[href$='.gif'],a[href$='.webp']").addClass("image-popup"),$(".image-popup").magnificPopup({type:"image",tLoading:"Loading image #%curr%...",gallery:{enabled:!0,navigateByImgClick:!0,preload:[0,1]},image:{tError:'Image #%curr% could not be loaded.'},removalDelay:500,mainClass:"mfp-zoom-in",callbacks:{beforeOpen:function(){this.st.image.markup=this.st.image.markup.replace("mfp-figure","mfp-figure mfp-with-anim")}},closeOnContentClick:!0,midClick:!0}),$(".page__content").find("h1, h2, h3, h4, h5, h6").each(function(){var e,t=$(this).attr("id");t&&((e=document.createElement("a")).className="header-link",e.href="#"+t,e.innerHTML='Permalink',e.title="Permalink",$(this).append(e))})}); \ No newline at end of file diff --git a/assets/js/scroll-to-top.js b/assets/js/scroll-to-top.js new file mode 100644 index 0000000..ec77c3f --- /dev/null +++ b/assets/js/scroll-to-top.js @@ -0,0 +1,50 @@ +function hasScrollBehavior() { + return 'scrollBehavior' in document.documentElement.style; +} + +function smoothScroll() { + var currentY = window.scrollY; + var int = setInterval(function () { + window.scrollTo(0, currentY); + + if (currentY > 500) { + currentY -= 70; + } else if (currentY > 100) { + currentY -= 50; + } else { + currentY -= 10; + } + + if (currentY <= 0) clearInterval(int); + }, 1000 / 60); // 60fps +} + +function scrollToTop() { + // document.getElementById('page-title').scrollIntoView({behavior: 'smooth'}); + if (hasScrollBehavior()) { + window.scrollTo({ top: 0, behavior: 'smooth' }); + } else { + smoothScroll(); + } +} + +function toggleScrollUpButton() { + var y = window.scrollY; + var e = document.getElementById('scroll-to-top'); + if (y >= 350) { + e.style.transform = 'translateY(-30%)' + e.style.opacity = 1; + } else { + e.style.opacity = 0; + e.style.transform = 'translateY(30%)' + } +} + +document.addEventListener("DOMContentLoaded", function () { + document.removeEventListener("DOMContentLoaded", arguments.callee, false); + + window.addEventListener("scroll", toggleScrollUpButton); + + var e = document.getElementById('scroll-to-top'); + e.addEventListener('click', scrollToTop, false); +}, false); \ No newline at end of file diff --git a/audio-problem-in-wine-under-kde-4-4-1-solved/index.html b/audio-problem-in-wine-under-kde-4-4-1-solved/index.html new file mode 100644 index 0000000..11cfba4 --- /dev/null +++ b/audio-problem-in-wine-under-kde-4-4-1-solved/index.html @@ -0,0 +1,675 @@ + + + + + + +Audio problem in Wine under KDE 4.4.1 - Solved - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Audio problem in Wine under KDE 4.4.1 - Solved +

+ + +

+ + + + + + + + + + + + less than 1 minute read + + + +

+ + +
+ + +
+ +

I was trying to install spotify on linux which used to work perfectly on Gnome and when I tried on KDE it was giving error box. I wrote winecfg on terminal and got following error:

+
fixme:jack:JACK_drvLoad error loading the jack library libjack.so.0, please install this library to use jack
+
+

After a bit of google I found the answer to be as simple as running this command on terminal:

+
sudo apt-get install libjack0
+
+

After this audio was working fine. Hope this helps few others.

+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/banner.js b/banner.js new file mode 100644 index 0000000..db3974c --- /dev/null +++ b/banner.js @@ -0,0 +1,19 @@ +const fs = require("fs"); +const pkg = require("./package.json"); +const filename = "assets/js/main.min.js"; +const script = fs.readFileSync(filename); +const padStart = str => ("0" + str).slice(-2); +const dateObj = new Date(); +const date = `${dateObj.getFullYear()}-${padStart( + dateObj.getMonth() + 1 +)}-${padStart(dateObj.getDate())}`; +const banner = `/*! + * Minimal Mistakes Jekyll Theme ${pkg.version} by ${pkg.author} + * Copyright 2013-${dateObj.getFullYear()} Michael Rose - mademistakes.com | @mmistakes + * Licensed under ${pkg.license} + */ +`; + +if (script.slice(0, 3) != "/**") { + fs.writeFileSync(filename, banner + script); +} diff --git a/call-us-and-several-other-countries-free-using-android/index.html b/call-us-and-several-other-countries-free-using-android/index.html new file mode 100644 index 0000000..fddc7cf --- /dev/null +++ b/call-us-and-several-other-countries-free-using-android/index.html @@ -0,0 +1,682 @@ + + + + + + +Call US and several other countries free using Android - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Call US and several other countries free using Android +

+ + +

+ + + + + + + + + + + + 1 minute read + + + +

+ + +
+ + +
+ +

That’s right…with smartphones all that was possible using computers is increasingly becoming feasible through phones. I just now configured my Nexus S to make free phone calls over internet to USA. How you ask? Well here we go: +The site offering free calls to USA landlines and mobiles is www.voipstunt.com so I registered an account with this site. +Now Android 2.3 has inbuilt SIP support so I directly configured instead of downloading any app from market but it can be as easily done using sipdroid or csipsimple but anyway since there is good dialer integration I opted for direct route.

+
    +
  1. Goto Settings -> Call Settings and under Internet Call Settings click on "Accounts".
  2. +
  3. Untick receive calls.
  4. +
  5. Click on Add Account.
  6. +
  7. Now in username enter the username with which you have registered on voipstunt website.
  8. +
  9. Enter the password used for voipstunt account in password field.
  10. +
  11. Enter sip.voipstunt.com in server.
  12. +
  13. Untick the "Set as primary account" field.
  14. +
  15. Press back button till you are back to call settings. Now under Internet Call Settings click on "Use Internet Calling" and select "Ask Each Time".
  16. +
+

This is it. All done. Try calling landline or mobile number of your US contact and before placing the call your phone will ask whether to place call over internet or cellular network. Choose internet and you are off to free calls.

+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..c2a863c --- /dev/null +++ b/categories/index.html @@ -0,0 +1,397 @@ + + + + + + +Posts by Category - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ +

Posts by Category

+ + + + + + + +
    + +
+ + + + +
+
+
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configure-blogtk-1-0-for-blogger/index.html b/configure-blogtk-1-0-for-blogger/index.html new file mode 100644 index 0000000..1594b7e --- /dev/null +++ b/configure-blogtk-1-0-for-blogger/index.html @@ -0,0 +1,676 @@ + + + + + + +Configure BlogTK 1.0 for blogger - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Configure BlogTK 1.0 for blogger +

+ + +

+ + + + + + + + + + + + 1 minute read + + + +

+ + +
+ + +
+ +

Right so I am happy with Blogilo, then why BlogTK? +It so happened that I was trying to edit my last post and for some unknown reason I was getting error in updating through Blogilo. I was not able to update through Blogilo and I think it is because of the snapshots included in the post. +Anyhoo…so I installed BlogTK and after entering the details I found it not connecting so I opened the gnome-blog and then when I was configuring its preference I saw the default entry in the field XML/RPC URL - http://www.blogger.com/api/RPC2 +I opened BlogTK, Edit -> Accounts and Settings and then in Server URL entered - http://www.blogger.com/api/RPC2 +Next entered username and password fields. Clicked Save and then OK. Now on main window File-> Connect and it showed the message connected to server at http://www.blogger.com/api/RPC2.
+I was then able to edit the post however the Edit post etc were not all that great and meanwhile I found that Blogilo is working fine so am back to Blogilo. +Not a huge tip and am sure many might already know but it took me a while to work out the working configuration. +However, if someone knows better way to connect, please do let me know as BlogTK sure wasn’t living up to the name and fame it has with this way of configuration so maybe there is a better way to do it.

+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conky-on-my-desktop-step-by-step/index.html b/conky-on-my-desktop-step-by-step/index.html new file mode 100644 index 0000000..991ac78 --- /dev/null +++ b/conky-on-my-desktop-step-by-step/index.html @@ -0,0 +1,716 @@ + + + + + + +Conky on my desktop - step by step - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Conky on my desktop - step by step +

+ + +

+ + + + + + + + + + + + 3 minute read + + + +

+ + +
+ + +
+ +

Fig-1 +My new friend Damjan recently mentioned that he liked the Conky on my desktop and asked for details as have few others so I figured a post on the topic will be useful.

+ +

Many who have been playing with conky seem to believe it’s real easy stuff but I feel there are too many options and very little explanation which means it can be lot of messing around with different options and can take a while to get to a point where you have what you want.

+ +


To make this conky appear exactly the way it is on my screen on your desktop follow steps 1 to 4 below and for changing time-zone settings it will be step 5:

+ +

Step 1. Install Conky from Synaptic.
+Step 2. Install the fonts used in this conky.
+Step 3. Change the conky config file.
+Step 4. Enable settings to run conky at start-up.
+Step 5: Configure time-zones on the conky config.

+ +

Step 1. Install Conky from Synaptic:

+

Fig-2
+Open the synaptic package manager and follow steps as shown in screenshot above. Actually I don’t think conky colors is required so if it does not appear in search result don’t bother. From memory, that was installed by me as part of experimenting.

+

Step 2. Install the fonts used in this conky:

+

Download and install the fonts "pf_tempesta_five_condensed.ttf" and "ZegoeUI-U.ttf" from web or I have uploaded these to mediafire and can be download from this link.

+

Step 3. Change the conky config file:

+
    +
  1. Download my conky configuration in the file named ".conkykrc" from this mediafire link.
  2. +
  3. Open "Home" directory and press "Ctrl+h" to show hidden files.
  4. +
  5. Locate the ".conkykrc" file and delete it.
  6. +
  7. Now paste the "conkykrc" file downloaded in step 1 to home folder.
  8. +
+

Step 4. Enable settings to run conky at start-up:

+

4.1. Open "Startup Application" from Menu > Preferences.
+Fig-3
+4.2. Click on "Add" button and fill the fields as shown above.
+Fig-4
+4.3. Restart the computer and it’s all done.

+

Step 5: Configure time-zones on the conky config:

+

OK, so you have conky running as it is for me but you might want different time-zones than the one I have in which case you will need to modify the .conkykrc file in "Home" directory.

+
    +
  1. Open "Home" directory and press "Ctrl+h" to show hidden files.
  2. +
  3. Locate the ".conkykrc" file and open it in text editor of your choice. I use gedit.
  4. +
  5. Now Open Terminal and Type "tzselect" and press enter.
  6. +
  7. A list of continents will be displayed, select the one of your interest by typing the number against it and press enter. (I selected "Americas" so I typed "2")
  8. +
  9. Then you will be presented a list of countries. Again select the one of your interest by typing the number against it and press enter. ( I selected "North America" so I typed "49")
  10. +
  11. Finally, it will display time-zones in that country. As above, select the one of your interest by typing the number against it and press enter. ( I selected "Mountain Time" so I typed "18")
  12. +
  13. It will confirm selection and then mention TZ being used. For my selection it displayed "Therefore TZ='America/Denver' will be used."
  14. +
  15. This text after "TZ=" is what is important for us. Copy this and go back to .conkykrc file.
  16. +
  17. Under "TIMEZONE" say you want to replace Nashville with Denver then you locate Nashville (Line 83, Col 64) and replace the text with the city of your choice, in this case "Denver".
  18. +
  19. Now to reflect the appropriate time-zone, replace the existing time-zone "America/Chicago" with the one from step 10. In this example with "America/Denver'".
  20. +
+ +

That’s it. All Done !!! +Hope you find it useful. Feel free to ask questions on settings here and if it’s something I was stuck with I might be able to help.

+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/converting-aax-audible-to-mp3/index.html b/converting-aax-audible-to-mp3/index.html new file mode 100644 index 0000000..8b217d7 --- /dev/null +++ b/converting-aax-audible-to-mp3/index.html @@ -0,0 +1,715 @@ + + + + + + +Converting AAX (Audible) to mp3 - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Converting AAX (Audible) to mp3 +

+ + +

+ + + + + + + + + + + + 2 minute read + + + +

+ + +
+ + +
+ +

Lately I have been a bit frustrated because while I subscribe to Audible services, Amazon and Google do not play nice with each other limiting me - the owner of books with rights to listen my purchase on any device - in my options on where I can listen. Now I am yet to find a good solution that can get my files playing on google home and not just Alexa, I guess first step was to free my audiobooks from Amazon jail. That as it turns out is rather simple to do.I must at this point mention OpenAudible which is one fine tool. However, their latest version (2.0.7 at the time of writing) added some limitations whereby you can get it to download the AAX files but not convert them to mp3 without purchasing their license. However,converting AAX to mp3 is accomplished using ffmpeg and I do not believe a nice GUI wrapper is what I need and hence I do not believe my money is well spent in getting a license just to achieve from a graphical user interface what I can achieve for free from the terminal with a single line.There are 3 steps in the conversion process:

+ +
    +
  1. +

    Get a copy of the AAX file you own from your audible account: This +can be done in one of two ways as listed below:

    + +

    a. Install OpenAudible and follow instructions to download the AAX +files.
    +b. Copy it from the filesystem of your Android phone.

    +
  2. +
  3. +

    Obtain the activation_bytes for your AAX file:

    + +

    a. If you installed OpenAudible, you can use the following steps:

    + +
    +
    cd /opt/OpenAudible/bin/linux/
    +ffprobe /home/OpenAudible/aax/name_of_the_book.AAX 
    +
    + +

    b. This should give you a lot of output and a hash code. Let’s say +the hashcode you get is a6ger35cf22u03c9x16743j06f6df50a00b811c3 +aax_hash_code +c. Use this hashcode with following command to get the activation_bytes:

    +
    +
       ./rcrack path /opt/OpenAudible/bin/tables/ -h a6ger35cf22u03c9x16743j06f6df50a00b811c3
    +
    +

    d. This should then output the activation_bytes in last line - something like hex:abc43opu as shown below: +activation_byte

    + +

    Above steps are required only once. Keep the activation_bytes code that you obtain above safe as it can be used for converting all your audiobooks.

    +
  4. +
  5. +

    Now use the following commands to convert AAX file to mp3:

    +
    +
    #change directory to where the AAX files are saved. If using OpenAudible it will be in /home/<username>/OpenAudible/aax
    +cd /home/<username>/OpenAudible/aax
    +ffmpeg -activation_bytes xxxxxxxx -i 'name of the book.AAX' -map_metadata 0 -codec:a libmp3lame -qscale:a 6 /home/<username>/OpenAudible/mp3/name\ of\ the\ book.mp3
    +
    +

    Remember to change xxxxxxxx with the activation_bytes code you obtained in step 2 and the name of book as well as destination path and name of the book.

    +
  6. +
+ +

Now ofcourse you can accomplish the first Step 2 above without OpenAudible. If you would want to do so, just follow the guidance on github.

+ +

Hope this will be helpful for a few. In the meantime, I am yet to work out the best way to make google home to play my audiobooks when I so desire. If anyone has any tips they will be gratefully received.Have fun !!!

+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crossover/index.html b/crossover/index.html new file mode 100644 index 0000000..628b14f --- /dev/null +++ b/crossover/index.html @@ -0,0 +1,738 @@ + + + + + + +Crossover - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Crossover +

+ + +

+ + + + + + + + + + + + 2 minute read + + + +

+ + +
+ + +
+ + + +

There are technologies that I can use in my personal life, that I am proud of and those that I play around with but reality is that as a Senior Project Manager, there aren’t very many times when I get the opportunity to influence the choice of software to be used.

+ +

As a principle, my personal choices are driven by opensource and professional choices are driven by client needs. However, listed below are opensource technologies that crossed over from my personal to professional realm.

+ +
+

Three open-source technologies that jumped from my hobbyist curiosity to my professional life

+
+ +

PlantUML

+

What is it?

+

PlantUML is a great tool to create flow and activity diagrams. It uses plain text to create nice looking flow diagrams which are really quick to prepare. Postioning and all the other stuff is just taken care of by the algorithm.

+ +

How do I use it?

+

As such there is no install required. For Chrome, there is an extension which can be installed or from any browser using [PlantText] (http://www.planttext.com).

+ +

I use it directly on the text editor at hand and create the flow I am after while taking notes on the process. It just works.

+ +

SpagoBI

+

What is it?

+

SpagoBI is a fully working Business Intelligence Suite which just works.

+ +

How do I use it?

+

As part of one of my projects. I had the freedom to pick any MI solution that was cost effective, robust and fit for purpose. SpagoBI ticks all the boxes so I took the opportunity to bring in this opensource solution.

+ +

It has very nice GUI and end user experience is great which meant it was an instant hit. It is live and am a user as well.

+ +

Talend

+

What is it?

+

Talend - does not really need an introduction. It is a well established name and is what techies call ETL - Enterprise Transport Layer.

+ +

To explain with an analogy, if different databases were people speaking different languages, then Talend is the translator who can not only explain what other party is speaking but can also perform some important jobs like getting the groceries, throwing garbage and arranging school pickups.

+ +

How do I use it?

+

Again as part of the aforementioned project, Talend is what was used to interact with an obscure offering from intuit called Quickbase as well as with MSSQL and mariaDB (another opensource tech we all love).

+ +

The biggest benefit that I derived apart from the regular data integration came from the fact that it is also able to place the dataset directly into a spreadsheet (Excel or LibreCalc).

+ +

Now, having all charts and formulae already in place as a template on spreadsheet kind of allows for report creation on a technology that these recipients are already working with and so they don’t have to learn any new technology.

+ +

Instead of learning and struggling with something new, they are happy to get the information in a format they all accept and are comfortable with and can get on with their day job and make informed decisions based on what the report says. Icing on the cake, the emailing for these spreadsheets, once they were agreed as final versions, is also scheduled so the reports can be delivered in the format end-user likes without any human intervention.

+ +

What more can a project manager ask. :D

+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dd-wrt-firmware-on-tp-link-tl-wr841n-v11/index.html b/dd-wrt-firmware-on-tp-link-tl-wr841n-v11/index.html new file mode 100644 index 0000000..7a39e26 --- /dev/null +++ b/dd-wrt-firmware-on-tp-link-tl-wr841n-v11/index.html @@ -0,0 +1,790 @@ + + + + + + +DD-WRT firmware on TP-LINK TL-WR841N v11 - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

DD-WRT firmware on TP-LINK TL-WR841N v11 +

+ + +

+ + + + + + + + + + + + 5 minute read + + + +

+ + +
+ + +
+ + + +

I have been with PlusNet for over two years now and am a happy camper as far as fiber optic broadband is concerned but as I am no longer on a broadband contract with PlusNet and had no intention of going on one, so the only way I could get a change to my ageing router was by purchasing a new one. +Hence I started reading about my options and soon enough realised that an old router can be given new lease of life using DD-WRT. Equally soon-ish I also realised that the router from PlusNet - TG582n - is quite rubbish and does not play nice with any of the open source firmwares. +So I figured that if I have to just play around a bit, I might as well start with something cheaper and cheaper is what I found in the TP-Link router TL-WR841n at just £16.00. You can’t get any cheaper than that in my opinion. OK, so now that we have established that I am cheap and my new router is cheap, let’s move on to interesting stuff.

+ +

I had read that TP-Link router and specifically TL-WR841n plays nicely with DD-WRT but it was only after I had my new toy did I realise that these things also come in hardware version and while interwebs is filled with instructions on installing DD-WRT for upto v9, when it comes to v11 in Europe, it can be a bit tricky to proceed. There are some instructions in forums[1] but nothing that walks one end to end hence this post.

+ +

Flash router with DD-WRT firmware

+

There are two ways to ensure that the DD-WRT firmware gets flashed on the router:

+ +
    +
  1. +Apply Unlocked stock firmware - This is the one I used and to use this I downloaded the modified firmware from NeDark that he has provided in a post on the OpenWRT forum here +
  2. +
+
    +
  • +Using TFTP server - This is considered to be a safe approach because you do not have to use any modded version of stock firmware to apply it. However, if like me you are anyway going to flash it with DD-WRT, I feel it's a bit of pain that can be avoided as it involves setting up TFTP server and making your router to connect to this server can take some time and effort. It is explained here.
  • +
+ +

Right, so assuming you want to go with the first and easier approach, first you need to download the modded firmware from NeDark. He has uploaded it on his dropbox link and I have also uploaded a copy of this firmware here.

+ +

I recommend that you download all the three files from my folder on Mediafire[2] using the link below but if you would much rather download directly from DD-WRT, then the links for rest of the two files that I used are also in references [3] and [4] below.

+ +

Assuming that now you have all the three files safely downloaded to you computer we just follow the simple steps below:

+ +
    +
  1. Connect the power supply to the TP-Link router and switch it on.
  2. +
  3. Connect the ethernet cable in one of the yellow LAN slots of the router.
  4. +
  5. Switch-off the wifi on your computer and connect the other end of the ethernet cable to the ethernet port of your computer.
  6. +
  7. Once the ethernet connection on your laptop is established, open a browser and type 192.168.0.1 and press enter.
  8. +
  9. You will be presented with TP-LINK admin interface.
  10. +
  11. Login using the credentials username: admin; password: admin.
  12. +
  13. On left hand navigation locate and click on System Tools and then on expanded menu click on Firmware Upgrade +
  14. +
  15. Now click on Browse button and select the file from NeDARK - wr841n(EU)_v11_150616.bin from your downloaded folder.
  16. +
  17. Click on Upgrade button. It will take roughly 30 to 40 seconds and router will reboot.
  18. +
  19. Refresh the browser screen and you will once again be presented with TP-LINK admin interface.
  20. +
  21. Once again login using the credentials username: admin; password: admin.
  22. +
  23. On left hand navigation locate and click on System Tools and then on expanded menu click on Firmware Upgrade +
  24. +
  25. Now click on Browse button and select the file factory-to-ddwrt.bin.
  26. +
  27. Click on Upgrade button. It will take roughly 30 to 40 seconds and router will reboot.
  28. +
  29. Disconnect the ethernet cable from Laptop and reboot the laptop - This isn't always required but just to be safe.
  30. +
  31. Reconnect the ethernet cable to the laptop. Make sure that wifi is switched off on the laptop.
  32. +
  33. Once the ethernet connection on your laptop is established, open a browser and type 192.168.1.1 and press enter. <-- Notice the different IP than what was used in step 4.
  34. +
  35. If all has gone well until now you will be greeted with DD-WRT login interface and will actually be asked to change the password.
  36. +
  37. After providing the password, navigate to tab Administration and then sub-tab named Firmware upgrade.
  38. +
  39. Click on Browse and this time select tl-wr841nd-webflash.bin and Click upgrade. This will take 40 seconds or so and your router has now been liberated.
  40. +
+ +

This is it for flashing TP-LINK TL-WR841N v11 router with DD-WRT. I will be writing more about how to configure DD-WRT to work with Plusnet fiber optic broadband and to play nice with NEST so stay tuned if this interests you. +I made fiber optic broadband work with no fuss but with NEST there were some issues basically down to auto setting changing to channel 13.

+ +

Making NEST play nice with DD-WRT

+

The issue was that NEST would suddenly drop internet connection and then not identify the SSID for my router. The SSID just won’t appear in the list of available wireless networks.

+ +
    +
  • NEST apparently does not like channel 13, possibly even Channel 11 and 12. Changing the channel to any of the single digit (1 - 9) works well.
  • +
  • In addition giving a static IP for NEST MAC seems to have resolved any network drops whatsoever.
  • +
  • Under Wireless Setting, I also changed the Beacon Interval to 211.
  • +
  • Under Administration tab -> Management -> IP Filter Settings, I changed the TCP timeout to 1800 and UDP timeout to 3600.
  • +
+ +

Configure router for PlusNet Fiber Optic Broadband

+

For configuring PlusNet fiber optic broadband, the settings I used are as below: +Setup -> Basic Setup
+Setup 1
+Setup 2
+Setup -> DDNS
+
+Wireless -> Basic Settings
+Wireless-Basic-Settings
+Wireless -> Wireless Security
+Wireless-Wireless-Security
+Administration -> Management
+Administration-Management
+Administration -> Commands

+ +

Add the following in the startup commands

+
# Fix lan port communication 841 v7, v9, v11 
+swconfig dev eth0 set enable_vlan 1 
+swconfig dev eth0 set apply
+
+ +

as shown below:
+Administration-Command

+ +

Above basic setting ensures that PlusNet fiber optic broadband works perfectly fine. +REFERENCES:

+ +
+ +
+
    +
  1. +https://www.quora.com/Is-the-TP-Link-TL-WR841n-v11-router-supported-by-DD-WRT ↩︎ +
  2. +
  3. +https://www.mediafire.com/folder/q56dcdecfh3v1/Router_Firmware ↩︎ +
  4. +
  5. +http://download1.dd-wrt.com/dd-wrtv2/downloads/betas/2016/12-15-2016-r30949/tplink_tl-wr841ndv11/factory-to-ddwrt.bin ↩︎ +
  6. +
  7. +http://download1.dd-wrt.com/dd-wrtv2/downloads/betas/2016/12-15-2016-r30949/tplink_tl-wr841ndv11/tl-wr841nd-webflash.bin ↩︎ +
  8. +
+ +
+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ddclient-on-fedora-2/index.html b/ddclient-on-fedora-2/index.html new file mode 100644 index 0000000..386fca1 --- /dev/null +++ b/ddclient-on-fedora-2/index.html @@ -0,0 +1,815 @@ + + + + + + +DDCLIENT set-up on Fedora for Namecheap - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

DDCLIENT set-up on Fedora for Namecheap +

+ + +

+ + + + + + + + + + + + 2 minute read + + + +

+ + +
+ + +
+ +

Configure Namecheap

+

Follow the Namecheap guide here

+ +

NOTE: For a subdomain “oxygen.copper.com”, just replace @ with “oxygen”

+ +

Set-up DDCLIENT

+ +
#Install DDCLIENT on Fedora
+sudo dnf install ddclient
+#Edit the configuration file to update IP on your Dynamic DNS host
+sudo nano /etc/ddclient/ddclient.conf
+
+ +

Press Ctrl+W and type the name of host for your Dynamic DNS. Mine is with Namecheap and I needed to configure for the subdomain hence following config reflects how to do it for Namecheap. For other hosts, you will need to refer their documentation.

+ +
## NameCheap (namecheap.com)
+use=web, web=dynamicdns.park-your-domain.com/getip
+protocol=namecheap, \
+server=dynamicdns.park-your-domain.com, \
+login=copper.com, \
+password=&lt;copy the password from namecheap advanced DN section&gt; \
+oxygen
+# myhost.namecheap.com
+
+ +
+ +

+ +

The password to be provided above is what you will find on namecheap dashboard (Ref. Screenshot above).

+ +
    +
  • Log in to the namecheap account.
  • +
  • Go to Advanced DNS
  • +
  • Scroll down to Dynamic DNS section
  • +
  • Copy the password
  • +
  • Paste in ddclient config file
  • +
+
+ +

Test DDCLIENT

+ +

Before we schedule ddclient to run at boot, we need to test if it has been configured properly and is able to communicate with Namecheap by sudo ddclient -daemon=0 -debug -verbose -noquiet. If it is configured properly, you will see a message similar to this as part of the final output.

+ +
SUCCESS: updating oxygen: good: IP address set to 92.117.273.56
+
+ +

If it is not what you see, and more importantly, if you do not see last line as “Success”, then there is something wrong with configuration and you must correct it before proceeding.

+ +

If this test worked, we are ready to update the DDCLIENT service.

+ +

Set up DDCLIENT to run at start-up

+ +

When we install ddclient using dnf, a ddclient.service file is automatically created in the location /etc/systemd/system/ddclient.service with following content.

+ +
[Unit]
+Description=A Perl Client Used To Update Dynamic DNS
+After=syslog.target network.target nss-lookup.target
+
+[Service]
+User=ddclient
+Group=ddclient
+Type=forking
+PIDFile=/var/run/ddclient/ddclient.pid
+EnvironmentFile=-/etc/sysconfig/ddclient
+ExecStartPre=/bin/touch /var/cache/ddclient/ddclient.cache
+ExecStart=/usr/sbin/ddclient $DDCLIENT_OPTIONS
+
+[Install]
+WantedBy=multi-user.target
+
+ +

We will enable and start this service by issuing following commands:

+ +
sudo systemctl enable ddclient.service
+sudo systemctl start ddclient.service
+
+ +

One would think that enabling and starting this service is all you need to do but that is not usually the case. I was getting following error:

+ +

/bin/touch: cannot touch `/var/cache/ddclient/ddclient.cache’: Permission denied

+ +

A quick google search establishes that there seems to be some bug in how the ddclient cache file is created and how the permissions are set. After lot of searching, scratching head later, I did the following which fixed the issue. So if sudo systemctl start ddclient results in error, you may need to do the following:

+ +

+
+
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
#Go Root
+su
+#Create a directory for ddclient
+mkdir /var/run/ddclient
+#Chown the various directories for ddclient as user
+chown ddclient:ddclient /etc/ddclient.conf
+chown ddclient:ddclient /var/run/ddclient/
+#change directory
+cd /var/run/ddclient
+#delete ddclient.cache if it exists
+rm ddclient.cache
+#change directory
+cd /etc/sysconfig
+#delete ddclients.cache
+rm ddclients.cache
+#create a blank ddclient.cache
+nano /var/run/ddclient/ddclient.cache
+#chown it for ddclient user
+chown ddclient:ddclient /var/run/ddclient/ddclient.cache
+#exit root
+exit
+#enable and start ddclient service
+sudo systemctl enable ddclient.service
+sudo systemctl start ddclient.service
+
+ +

Done !!!

+ +

Known Issue with DDCLIENT

+ +

There is a known issue and I can confirm that I have seen on my logfile as recently as today.

+ +

WARNING: cannot connect to dynamicdns.park-your-domain.com:80 socket: IO::Socket::INET: Bad hostname ‘dynamicdns.park-your-domain.com’

+ +

It isn’t major but it is there and restarting the service by issuing the command sudo systemctl restart ddclient.service fixes the problem.

+ + + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ethercalc/index.html b/ethercalc/index.html new file mode 100644 index 0000000..eec9560 --- /dev/null +++ b/ethercalc/index.html @@ -0,0 +1,707 @@ + + + + + + +Ethercalc - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Ethercalc +

+ + +

+ + + + + + + + + + + + 1 minute read + + + +

+ + +
+ + +
+ +

Ethercalc is good tool which can be selfhosted. It is fairly simple to do so. Though it will be available for anyone who has the URL because there is no inbuilt login mechanism.

+ +

I did not dig into making it accessible with a login interface as I lost interest after I made it work on my server and played around a bit with it but it was simply because I got interested in other things and not because the tool isn’t fascinating enough. I am fairly certain this will not be overly complicated but for a simple selfhosted spreadsheet solution this is definitely worth playing around with.

+ +

The steps I took are as below:

+ +
#Ethercalc plays well wth redis as per their documentation. So Install and start &quot;redis&quot;
+sudo dnf install redis
+sudo systemctl start redis.service
+
+#Test if &quot;redis&quot; is working
+redis-cli ping
+
+#Enable redis to automatically start at the time of system start-up
+sudo systemctl enable redis.service
+
+#check if it runs
+ethercalc
+#Press Ctrl+C to exit
+
+#To run it forever use pm2
+pm2 start ethercalc
+npm list -g --depth=0
+
+#Change port to whichever port you want Ethercalc to run on by opening app.js 
+#and changing port.
+nano /home/&lt;yourusername&gt;/.npm-global/lib/node_modules/ethercalc/app.js
+#change port and save
+
+#run with pm2 and alias as Ecalc
+pm2 start ethercalc  --name &quot;Ecalc&quot;
+
+#check logs using pm2
+pm2 logs Ecalc
+
+#Reverse proxy Ethercalc using nginx
+sudo nano /etc/nginx/conf.d/ecalc.conf
+sudo systemctl restart nginx.service
+
+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exchange-2007-on-thunderbird-using-davmail/index.html b/exchange-2007-on-thunderbird-using-davmail/index.html new file mode 100644 index 0000000..30330c2 --- /dev/null +++ b/exchange-2007-on-thunderbird-using-davmail/index.html @@ -0,0 +1,725 @@ + + + + + + +Exchange 2007 on Thunderbird using DAVMail - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + + + + +
+ +
+

Exchange 2007 on Thunderbird using DAVMail +

+ + +

+ + + + + + + + + + + + 3 minute read + + + +

+ + +
+ + +
+ +

The start of this week was like a nightmare for me. Whole family was down with flu and I had the fever that is probably the highest ever of my entire life at 40.5 C (~ 106 F). +Anyway, surviving that was easier compared to the aftermaths of this health problem that forced me to stay in bed and inadvertently deal with office mails at home on a non-IE browser with Lite version which is crap and makes you feel miserable enough to kill yourself. +So the option I had was to either boot windows on a virtual machine or find a solution within linux. Obviously, I prefer the latter and was glad to work out a solution that can help me avoid booting windows. +I have listed below the steps I followed to achieve this, though in all honesty the documentation is quite good on sourceforge site itself. It’s just that some of their screenshots are dated and in French (literally, no pun intended). +Now there are several options floating around but this set-up works flawlessly for me and so I will obviously recommend this over other methods. +It’s so easy to configure that there really is no reason not to give it a shot.

+

Step 1. Install DAVMail

+

You can get the latest version from sourcefourge by following the link below: +http://davmail.sourceforge.net/download.html
+Fig-1

+
Select the download based on your distro. For Linux Mint 12 it will be Green Box and for non-debian based distros the red.
+

Now double click on the downloaded .deb file and DAVMail will be installed taking care of any required dependencies.

+

Step 2. Configure DAVMail

+

Once installed, you can open DAVMail on Linux Mint 12 under Internet.
+Fig-2
+You can copy all the settings from this screenshot, leave them with what is there by default or change if you need to. Most important part is the first field OWA (Exchange) URL. You must paste your exchange 2007 OWA URL here.
+Fig-3 +Believe it or not, that’s all the configuration you need to do for DavMail.

+

Step 3. Configure Thunderbird Email Client

+

Now over to configuring the Thunderbird Email Client. Enter Details and Click on Continue. It will show next screen by itself.
+Fig-4
+Follow instructions as per next screenshot.
+Fig-5 +If you see following warning, tick the checkbox and Click on Create Account.
+Fig-6
+After this you may be presented with a dialogue box to enter the username and password once again. If so provide details. Remember to try just abc.xyz as username for abc.xyz@officemail.com and only if that does not work and you are presented with the dialogue box again should you try the whole email id as username. +It does take quite some time in first run as Thunderbird downloads all the mails. I was happy with the speed though YMMV. +I have applied an MS Office based theme that you can find in add-ons directory. Link below: +https://addons.mozilla.org/en-US/thunderbird/addon/ms-office-2003-jb-edition/ +Finally my mailbox looks as below:
+Fig-7

+

Step 4. Configure Calendar

+

On Linux Mint 12 Lightning the Thunderbird calendar client does not come pre-installed. So head over to Synaptic package manager and search for "xul-ext-light", select the shown items and install them.
+Fig-8
+Once installed, restart thunderbird and you will see calendar icon and Task Pane.
+Fig-9
+Either click on Calendar or just press +Ctrl+Shift+c +This will open Calendar view as shown below:
+Fig-10
+Select "New Calendar".
+Fig-11
+Select "On the Network" radio button and click on "Next"
+Fig-12
+Select CalDAV radio button, and fill the location field with http://localhost:1081/users/ankit@officemail.com/calendar.
+Make sure you adjust the port as per your settings of Step 2.
+Fig-13
+Give a name to this Calendar - say Office Cal for instance - and click Next.
+Fig-14
+After this you may be presented with a dialogue box to enter the username and password once again. If so provide details. Here provide abc.xyz@officemail.com and if that does not work and you are presented with the dialogue box again try with just the abc.xyz as username. +Once connected, your Calendar will be in sync as will be your tasks.

+

Step 5: Configure Global Address Book

+

Open Address Book and then select File > New > LDAP Directory. It will open the following box.
+Fig-15
+Fill as shown below and make sure Port number is as advised in red text. Then Click "OK"
+Fig-16
+That is it. Test and see if you are now able to update your calendar, tasks, emails etc. and if all has gone as explained above you should be good to go.

+ + +
+ +
+ + + + + + +

Updated:

+ + +
+ + + + + + + +
+ + +
+ + +

Leave a comment

+
+ +
+ + +
+ + + + + + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/feed.xml b/feed.xml new file mode 100644 index 0000000..99fcbbf --- /dev/null +++ b/feed.xml @@ -0,0 +1,3246 @@ +Jekyll2024-09-30T12:40:56+00:00https://mgw.dumatics.com/feed.xmlMake Gadgets WorkAn amazing website.Ankit MittalLogseq Customisations for Project Management Template2024-08-13T07:15:00+00:002024-08-13T08:15:00+00:00https://mgw.dumatics.com/logseq-customisation-for-project-management-teamplateBackground + +

While the overall planning of project timeline gets lot of attention in the world of software, the most important aspect of project management in my experience is maintaining and tracking Issues and Actions. This in normal project management practice is carried out through the use of an Actions Log and an Issues Log. In addition any medium complexity project invariably will have internal and external dependencies / constraints, risks which I tend to track on Constraints Log and Risks Log. Finally every project has decisions that I track on a Decisions Log.

+ +

Additionally, any opportunities that I identify during the course of project that are not in the scope of my project I capture those too in an Opportunities Log

+ +

Now all these logs have fairly standard fields so I created and started using an excel template and started calling it CARDIO Log short for log of Constraints, Actions, Risks, Decisions, Issues, Opportunities for a given project. This has worked well for me over the past 15 years or so but there are times where in order to maintain it meant cross linking an action to an issue or a risk and so on and more often than not it would become easier to just track actions in one of the other logs and that would make it a bit chaotic.

+ +

That problem, however, is what I thought, can be resolved using Logseq especially after starting with the template by the Logseq community user Luhman and starting with his template and explanation provided here.

+ +

Using logseq from browser when binary install is not possible

+ +

When binary install is not possible, one can still use logseq from browser by navigating to the web app demo page from Chrome browser and then selecting a local directory where notes will be saved. It works perfectly fine except there is no option to install plugins. So inorder for issues table etc to work from broswer following steps will suffice:

+ +

Option 1

+
    +
  • Select the directory where your notes (or graphs in logseq speak) will be saved.
  • +
  • Grant permission for local file access when asked by the browser.
  • +
  • Now if you will press Ctrl+k it will open search box but the cusrsor goes to the omnibox (where you type the url for the page) and then you will have to either manually click into the logseq search or press esc key three times to be able to get the cursor back in the search box. It may be easier to just click on Search icon in left hand navigation at top of the screen (next to the hamburger menu). +
      +
    • Alternatively configure the keyboard shortcut to Alt+k as that does not conflict with any other keyboard shortcut.
    • +
    • To add custom shortcuts, you can navigate to the shortcuts page with g s. Press the blue button corresponding to a given shortcut and a modal should pop up. Press the keybinding you want and then press Save.
    • +
    +
  • +
  • Now search for logseq/custom.css and add the whole css from this gist.
  • +
  • Then search for logseq/config.edn and add the whole edn from this gist
  • +
+ +

If you were installing the binary, you can still copy the entire css and edn from above. If you do that, you can directly skip to the section - Create Templates.

+ +

Option 2

+
    +
  • Download the archive from my github repository here
  • +
  • Unzip the downlaoded repository and delete the README.md file.
  • +
  • Navigate to journals directory where there will be three files. Delete all these files.
  • +
  • Navigate back and open the pages directory which will have 7 files. Except for content.md and templates.md, delete all other files.
  • +
  • Now copy the logseq and pages directories into the directory where you want your notes saved.
  • +
  • Open logseq from chrome and add the directory from step above where you copied the two folders.
  • +
+ +

If you were installing the binary, you can follow option 2 and then there will be no need to read rest of this article :).

+ +

Customisations

+ +

Sample Project Log

+

Screenshot_13-8-2024_15949_

+ +

Preparation

+ +
    +
  • +

    First we will ensure our actions log which will be generated using the Logseq’s inbuilt feature for To Do Lists displays fields that we want it to display.

    + +
      +
    • Press Ctrl+K and search for logseq/config.edn
    • +
    • In the code block search for following lines:
    • +
    +
  • +
+ +
1
+2
+3
+4
+5
+6
+7
+
    ;; Advanced queries `:result-transform` function.
+    ;; Transform the query result before displaying it.
+    :query/result-transforms
+    {:sort-by-priority
+     (fn [result] (sort-by (fn [h] (get h :block/priority "Z")) result))
+    }
+    
+
+ +
    +
  • Now replace these with following which is slightly reduced compared to the Source as I only needed “Deadline” for my purposes and I don’t use the “Scheduled” part of the Logseq task management feature:
  • +
+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+
    ;; Advanced queries `:result-transform` function.
+    ;; Transform the query result before displaying it.
+    :query/result-transforms
+    {:sort-by-priority
+     (fn [result] (sort-by (fn [h] (get h :block/priority "Z")) result))
+     :add-task-attrs (fn [result]
+       (def months {1 "January" 2 "February"  3 "March" 4 "April" 5 "May" 6 "June" 7 "July" 8 "August" 9 "September" 10 "October" 11 "November" 12 "December"})
+       (def monthName (fn [dd]
+             (get months (int dd))
+            ))
+    	  
+       ;; source: https://discuss.logseq.com/t/add-query-input-or-function-day-of-week/18361/12
+       (def days {0 "Saturday" 1 "Sunday" 2 "Monday" 3 "Tuesday" 4 "Wednesday" 5 "Thursday" 6 "Friday"})
+       (def weekDay (fn [date]
+             (def month (quot (mod date 10000) 100))
+             (def month6 (quot (- month 8) 6))
+             (def year6 (+ (quot date 10000) month6))
+             (def yearnum (mod year6 100))
+             (def century (quot year6 100))
+             (def d (mod (+ (mod date 100) (quot (* 13 (inc (- month (* month6 12)))) 5) yearnum (quot   yearnum 4) 
+             (quot century 4) (* 5 century)) 7))
+             (get days d)
+            ))
+    	  
+       (def suffixes {0 "th" 1 "st" 2 "nd" 3 "rd" 4 "th" 5 "th" 6 "th" 7 "th" 8 "th" 9 "th"})
+       (def positionalSuffix (fn [dd]
+             (if (or (= dd "11") (= dd "12") (= dd "13"))
+               (get suffixes 0)
+               (get suffixes (int (subs dd (count dd) 1))) )
+            ))
+    	  
+       (def token (fn [s] (str "⟨" s "⟩")))
+       (def format
+         (-> (get (js->clj (call-api "get_user_configs")) "preferredDateFormat")
+            (clojure.string/replace "do" (token "1"))
+            (clojure.string/replace "dd" (token "2"))
+            (clojure.string/replace "d" (token "3"))
+            (clojure.string/replace "EEEE" (token "4"))
+            (clojure.string/replace "EEE" (token "5"))
+            (clojure.string/replace "EE" (token "6"))
+            (clojure.string/replace "E" (token "7"))
+            (clojure.string/replace "MMMM" (token "8"))
+            (clojure.string/replace "MMM" (token "9"))
+            (clojure.string/replace "MM" (token "10"))
+            (clojure.string/replace "M" (token "11"))
+            (clojure.string/replace "yyyy" (token "12"))
+            (clojure.string/replace "yy" (token "13"))
+         ))
+    	  
+       (def parseDate (fn [date]
+             (if-not date nil
+               (let [
+                   regex (re-pattern "(\\d{4})(\\d{2})(\\d{2})")
+                   [_ yyyy mm dd] (re-matches regex (str date))
+                   yy (subs yyyy 2 4)
+                   d (str (int dd))
+                   do (str d (positionalSuffix dd))
+                   mmmm (monthName mm)
+                   mmm (subs mmmm 0 3)
+                   m (str (int mm))
+                   eeee (weekDay date)
+                   eee (subs eeee 0 3)
+                   ee (subs eeee 0 2)
+                   e eee
+                 ]
+                 (-> format
+                   (clojure.string/replace (token "1") do)
+                   (clojure.string/replace (token "2") dd)
+                   (clojure.string/replace (token "3") d)
+                   (clojure.string/replace (token "4") eeee)
+                   (clojure.string/replace (token "5") eee)
+                   (clojure.string/replace (token "6") ee)
+                   (clojure.string/replace (token "7") e)
+                   (clojure.string/replace (token "8") mmmm)
+                   (clojure.string/replace (token "9") mmm)
+                   (clojure.string/replace (token "10") mm)
+                   (clojure.string/replace (token "11") m)
+                   (clojure.string/replace (token "12") yyyy)
+                   (clojure.string/replace (token "13") yy)
+                 )
+               )
+             )
+            ))
+    	  
+    	  
+       (map (fn [x]
+         (update x :block/properties (fn [u]
+           (-> u
+               (assoc :marker (str (get x :block/marker)) )
+               (assoc :priority (str (get x :block/priority)) )
+               (assoc :deadline (parseDate (get x :block/deadline)) )
+               (assoc :repeated? (str (get x :block/repeated?)) )
+           )
+         ))
+       )
+       result)
+     )
+     }
+    
+
+ +

Plugins

+ +
    +
  • Click on Three Dots in top right corner of the screen and click on menu entry Plugins and then click on Marketplace
  • +
  • Now install the following plugins: +
      +
    • logseq-agenda
    • +
    • logseq-automatic-linker
    • +
    • logseq-datenlp-plugin
    • +
    • logseq-diagrams-as-code
    • +
    • logseq-doc
    • +
    • logseq-emoji-picker-fork
    • +
    • logseq-emoji-shortcodes
    • +
    • logseq-luckysheet
    • +
    • logseq-markdown-table
    • +
    • logseq-paste-more
    • +
    • logseq-plugin-automatic-url-title
    • +
    • logseq-plugin-show-weekday-and-week-number
    • +
    • logseq13-full-house
    • +
    • logseq13-missing-commands
    • +
    +
  • +
+ +

Look and Feel

+
    +
  • Now, I quite like the Mia Quattro Theme that can either be installed as a theme from marketplace or just by including the following line in logseq/custom.css just under the comment /*Theme*/ right at the top of the file like so:
  • +
+ +
1
+2
+
/*Theme*/
+@import url('https://playerofgames.github.io/logseq-mia-theme/mia_quattro.css');
+
+ +
    +
  • However this theme had some quirks which can be refined by adding following css overrides:
  • +
+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+
/* Override tag and note color scheme for tags from mia_quattro theme  */
+a.tag{
+ font-size: 100%;
+ color: var(--lx-accent-11,var(--ls-tag-text-color,hsl(var(--primary))));
+}
+
+svg.note {
+   color: var(--lx-accent-11, var(--rx-yellow-08))
+}
+
+svg.tip {
+   color: var(--lx-accent-11,var(--rx-blue-08))
+}
+
+/* Selection lists (search, tag complete, etc.) */
+
+#ui__ac .chosen,
+.chosen {
+	--ls-primary-text-color: #ee0606;
+	--ls-link-ref-text-color: #eaeaea;
+	--ls-link-ref-text-hover-color: #fafafa;
+	--ls-quaternary-background-color: var(--accent-dark-color);
+	--ls-icon-color: var(--ls-primary-text-color);
+}
+
+#ui__ac .chosen {
+	background-color: var(--accent-dark-color);
+}
+
+.menu-link.chosen  {
+	color: var(--ls-primary-text-color) !important;
+}
+
+/* to make todo checkbox visible */
+.form-checkbox {
+	--ls-page-checkbox-border-color: var(--accent-color);
+
+	border: 1px solid var(--ls-page-checkbox-border-color);
+	border-radius: 5px;
+	opacity: 1;
+}
+/* blockquote tweaks (reduce margin & padding and add custom colours) */
+
+blockquote {
+ padding: 8px 12px;
+ border-left: 5px solid;
+ border-left-color: var(--ls-page-blockquote-border-color, #7cfc00);
+ margin: 0.3rem 0 !important;
+}
+
+blockquote.yellow {
+ border-left-color: #ffe85580;
+}
+
+blockquote.blue {
+ border-left-color: #84b5ff80;
+}
+
+blockquote.red {
+ border-left-color: #ff558280;
+}
+
+.ls-block[data-refs-self^=".blue"] .blockquote{
+ border-left-color: #84b5ff80;
+}
+
+/* ==mark==  tweaks */
+
+mark {
+  background: var(--ls-page-mark-bg-color);
+  color: var(--ls-page-mark-color);
+  padding: 1px 2px;
+  margin: 0 2px;
+  border-radius: 3px;
+}
+
+mark.yellow {
+  background: var(--ls-page-mark-bg-color);
+  color: var(--ls-page-mark-color);
+}
+
+mark.pink {
+  background-color: #ff89be80;
+  color: white;
+}
+
+mark.blue {
+  background-color: #84b5ff80;
+  color:white;
+}
+
+mark.green {
+  background-color: #97ff9780;
+  color: yellow;
+}
+
+mark.red {
+  background-color: #ff558280;
+  color: white;
+}
+
+mark.grey {
+  background-color: #80808080;
+  color: white;
+}
+
+mark.gray {
+  background-color: #80808080;
+  color: white;
+}
+
+mark.orange {
+  background-color: #ffb86c80;
+  color: white;
+}
+
+mark.purple {
+  background-color: #c097ff80;
+  color: white;
+}
+
+/* add traffic lights to prioritized tasks */
+
+.priority[href="#/page/A" i]::before {
+ content: "🔴";
+ margin-right: 2px;
+}
+
+.priority[href="#/page/B" i]::before {
+ content: "🟡";
+ margin-right: 2px;
+}
+
+.priority[href="#/page/C" i]::before {
+ content: "🟢";
+ margin-right: 2px;
+}
+
+.opacity-50 {
+ opacity: 1;
+}
+
+ +
    +
  • As our templates later will depend on v-kanban plugin but I did not want to include the whole css and also wanted to modify the icons it shows with Pros and Cons, I include the following css on logseq/custom.css:
  • +
+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+
/* -- like dislike ----------------------------------------- */
+
+.ls-block[data-refs-self*="pros"] .block-children .bullet-container .bullet {
+ display: none;
+}
+
+.ls-block[data-refs-self*="pros"] .block-children .bullet-container:after {
+ content: "+";
+ font-size: 20px;
+ color: #1cd41c;
+}
+
+.ls-block[data-refs-self*="cons"] .block-children .bullet-container:after {
+ content: "-";
+ color: red;
+}
+
+a.tag[data-ref*="pros"] {
+ font-size: .8rem;
+ background: #014935e0;
+ color: rgb(202, 247, 118);
+ padding: 0 6px 3px;
+ border-radius: var(--ls-border-radius-low);
+ border: 1px solid rgba(137, 207, 96, 0.925);
+}
+
+a.tag[data-ref*="cons"] {
+ font-size: 13px;
+ background: #4b033bda;
+ color: rgb(255, 116, 128);
+ padding: 0 6px 3px;
+ border-radius: var(--ls-border-radius-low);
+ border: 1px solid rgba(182, 13, 41, 0.925);
+}
+
+a.tag[data-ref*="pros"]:hover {
+ filter: contrast(2) brightness(10);
+}
+
+a.tag[data-ref*="pros"]:before {
+ content: "✅ ";
+ font-size: 13px
+}
+
+a.tag[data-ref*="cons"]:before {
+ content: "❌ ";
+ font-size: 13px
+}
+
+
+a.tag[data-ref*="red"]:before {
+ content: "🔴";
+ margin-right: 2px;
+}
+
+a.tag[data-ref*="amber"]:before {
+ content: "🟠";
+ margin-right: 2px;
+}
+
+a.tag[data-ref*="green"]:before {
+ content: "🟢";
+ margin-right: 2px;
+}
+
+a.tag[data-ref*="on-hold"]:before {
+ content: "🟣";
+ margin-right: 2px;
+}
+
+a.tag[data-ref*="yellow"]:before {
+ content: "🟡";
+ margin-right: 2px;
+}
+
+a.tag[data-ref*="closed"]:before {
+ content: "🔵";
+ margin-right: 2px;
+}
+
+a.tag[data-ref*="no-go"]:before {
+ content: "🚫";
+ margin-right: 2px;
+}
+
+a.tag[data-ref*="merged"]:before {
+ content: "⚪";
+ margin-right: 2px;
+}
+
+/* -------------------------------- like dislike end  ------ */
+
+/*===========================================================*/
+/* css columns view / kanban  v20220510--------------------- */
+/* use: inline tag #kanban, #kanban-small or #kanban-wXXX    */
+/* try:  #kanban-w200,#kanban-w300, #kanban-w400             */
+
+div[data-refs-self*="kanban"]>.block-children-container.flex {
+    width: 100%;
+}
+
+div[data-refs-self*="kanban"]>.block-children-container.flex>.block-children.w-full {
+    display: inline-flex;
+    position: relative;
+    overflow-x: auto !important;
+    overflow-y: hidden;
+    margin: 0 10px;
+}
+	  
+div[data-refs-self*="kanban"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
+    display: inline-block;
+    padding: 0;
+    width: inherit;
+    min-width: 200px;
+    margin-right: 10px;
+}
+	  
+/* wide */
+
+div[data-refs-self*="kanban-small"]>.block-children-container.flex>.block-children,
+div[data-refs-self*="kanban-wide"]>.block-children-container.flex>.block-children {
+   min-width: 90vw;
+   left: 50%;
+   transform: translate(-50%);
+   background-color: var(--ls-primary-background-color);
+   overflow-x: scroll !important;
+   overflow-y: hidden;
+   margin: 10px 30px;
+}
+
+div[data-refs-self*="kanban-wide"]>.block-children-container.flex>.block-children>div.ls-block {
+   display: inline-block;
+   min-width: 350px;
+   padding: 8px 0px !important;
+   font-size: 0.85rem;
+   margin: 5px 0px;
+   background-color: var(--ls-secondary-background-color);
+   box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
+   border-radius: var(--ls-border-radius-medium);
+}
+
+
+/* #kanbansmall : smaller font with hover zoom */
+
+div[data-refs-self*="kanban-small"]>.block-children-container.flex>.block-children>div.ls-block {
+   display: inline-block;
+   min-width: 350px;
+}
+
+div[data-refs-self*="kanban-small"]>.block-children-container.flex>.block-children .block-content {
+   font-size: 10px;
+   font-weight: 300;
+}
+
+div[data-refs-self*="kanban-small"]>.block-children-container.flex>.block-children .block-content:hover {
+   font-size: 14px !important;
+   min-width: 100px;
+}
+
+
+/* #kanban-w[100-300] : force width of the columns */
+
+div[data-refs-self*="kanban-w100"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
+   min-width: 100px;
+}
+
+div[data-refs-self*="kanban-w150"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
+   min-width: 150px;
+}
+
+div[data-refs-self*="kanban-w200"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
+   min-width: 200px;
+}
+div[data-refs-self*="kanban-w300"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
+   min-width: 300px;
+}
+div[data-refs-self*="kanban-w400"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
+   min-width: 400px;
+}
+div[data-refs-self*="kanban-fit"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
+   min-width: 400px;
+       width: max-content; 
+}
+
+/* remove left border for kanbanized */
+[data-refs-self*="kanban"] .block-children-left-border {
+   opacity: 0;
+}
+
+/* fix modal list not appearing*/
+.block-children {
+   overflow: visible !important;
+} 
+
+.ls-block[data-refs-self*="kanban"] .absolute-modal,
+.ls-block[data-refs-self*="kanban"] #ui__ac {
+   min-height: 80px;
+}
+           
+/*--------------------------------------------- kanban end-- */
+
+/*------------------expreimetal for better table view START-------------------*/
+.table-wrapper {
+    width: 100% !important;
+    max-width: 100% !important;
+}
+
+table td {
+    min-width:100px;
+    word-wrap:break-word;
+}
+
+
+table th {
+    word-break: keep-all;
+}
+/*------------------expreimetal for better table view END-------------------*/
+
+ +
    +
  • In order to invoke some of the above tweaks, we will also create keyboard shortcuts and shortcodes to have a simpler way to change colour of the blockquote side border and highlights. So open logseq/config.edn and do the following: +a) Search for:
  • +
+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+
      ;; Macros replace texts and will make you more productive.
+      ;; Example usage:
+      ;; Change the :macros value below to:
+      ;; {"poem" "Rose is $1, violet's $2. Life's ordered: Org assists you."}
+      ;; input "{{poem red,blue}}"
+      ;; becomes
+      ;; Rose is red, violet's blue. Life's ordered: Org assists you.
+      :macros {}
+      
+
+ +

b) and replace above with:

+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
      ;; Macros replace texts and will make you more productive.
+      ;; Example usage:
+      ;; Change the :macros value below to:
+      ;; {"poem" "Rose is $1, violet's $2. Life's ordered: Org assists you."}
+      ;; input "{{poem red,blue}}"
+      ;; becomes
+      ;; Rose is red, violet's blue. Life's ordered: Org assists you.
+      :macros {
+       ">" "<blockquote class='$1'>$2</blockquote>" ;;usage {{ > orange,Text to be presented in the blockquote }}
+       "==" "<mark class='$1'>$2</mark>" ;;usage {{ == red,Text to be highlighted without linebreak }}
+      }
+      
+
+ +

c) search for:

+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+
      ;; Add custom commands to the command palette
+      ;; Example usage:
+      ;; :commands
+      ;; [
+      ;;  ["js" "Javascript"]
+      ;;  ["md" "Markdown"]
+      ;;  ]
+      :commands []
+      
+
+ +

d) and replace above with:

+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
      ;; Add custom commands to the command palette
+      ;; To quickly call these commands, just type / (backslash) followed by characters in square bracket
+      :commands [
+                  ["bookmark                                        [.b]" [[:editor/input "{{ renderer :template, Bookmark}}" ]]],
+                  ["date_today                                      [dt]" [[:editor/input "{{renderer :template, Date Today}}" ]]],
+                  ["issue_table                                     [.it]" [[:editor/input "{{renderer :template, Issue_table}}" ]]],
+                  ["issue                                           [.is]" [[:editor/input "{{renderer :template, Issue}}" ]]],
+                  ["circle                                          [.c]" [[:editor/input "{{renderer :template-view, circle-template, :color orange}}" ]]],          
+                  ["Blue Highlighter                                [=b]" [[:editor/input "<mark class='blue'></mark>" {:backward-pos 7}]]],
+                  ["Green Highlighter                               [=g]" [[:editor/input "<mark class='green'></mark>" {:backward-pos 7}]]],
+                  ["Gray Highlighter                                [=gra]" [[:edior/input "<mark class='gray'></mark>" {:backward-pos 7}]]],
+                  ["Grey Highlighter                                [=gre]" [[:editor/input "<mark class='grey'></mark>" {:backward-pos 7}]]],
+                  ["Orange Highlighter                              [=o]" [[:editor/input "<mark class='orange'></mark>" {:backward-pos 7}]]],
+                  ["Pink Highlighter                                [=p]" [[:editor/input "<mark class='pink'></mark>" {:backward-pos 7}]]],
+                  ["Red Highlighter                                 [=r]" [[:editor/input "<mark class='red'></mark>" {:backward-pos 7}]]],
+                  ["Yellow Highlighter                              [=y]" [[:editor/input "<mark class='yellow'></mark>" {:backward-pos 7}]]],
+                  ["Purple Highlighter                              [=pu]" [[:editor/input "<mark class='purple'></mark>" {:backward-pos 7}]]],
+                  ["Red Blockquote                                  [>r]" [[:editor/input "<blockquote class='red'></blockquote>" {:backward-pos 13}]]],
+                  ["Yellow Blockquote                               [>y]" [[:editor/input "<blockquote class='yellow'></blockquote>" {:backward-pos 13}]]],
+                  ["Blue Blockquote                                 [>b]" [[:editor/input "<blockquote class='blue'></blockquote>" {:backward-pos 13}]]],     
+                ]
+      
+
+ +
+

Now, some of the short-codes above such as /.is, /.it, /dt and /.c will not work just yet because we have not created their associated template. We will get to that in next section.

+
+ +

Create Templates

+ +
    +
  • Create a new page named templates.
  • +
  • Press Ctrl+K and search for templates and open the page.
  • +
  • Once on the page, click on Three dots in top right corner of the screen and from the drop down menu select Open in default app
  • +
  • Here paste the following then save and close the default app:
  • +
+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+
		title:: templates
+		visibility:: false
+		icon:: 🧾
+		
+		- # Circle
+		  id:: 66b0d360-cde5-4e95-87b3-4e97a1e23bb5
+		  template:: circle-template
+		  template-including-parent:: false
+		  arg-color:: red
+			- <span class='circle' style='background: ``c.args.color``; display: inline-block; width: 15px; height: 15px; border-radius: 50%; vertical-align: middle;'></span>
+		- # People Page
+		  template:: people page
+		  template-including-parent:: false
+			- tags:: people
+			  icon:: 👨‍💼
+			- ## Tasks
+				- query-table:: true
+				     query-properties:: [:priority :deadline :block]
+				  #+BEGIN_QUERY
+				  {:title [:h3 "Owned"]
+				  :query [:find (pull ?b [*])
+				       :in $ ?tag
+				       :where
+				       [?b :block/marker ?marker]
+				       [(contains? #{"TODO" "DOING" "NOW" "LATER" "WAITING"} ?marker)]
+				       (page-ref ?b ?tag)
+				       [?ref :block/name "project"]
+				       (not [?b :block/refs ?ref])]
+				  :inputs [:query-page]
+				  :result-transform :add-task-attrs
+				  :breadcrumb-show? true
+				  :group-by-page? false
+				  :collapsed? false
+				  }
+				  #+END_QUERY
+				- query-table:: true
+				  collapsed:: true
+				     query-properties:: [:marker :deadline :block]
+				  #+BEGIN_QUERY
+				  {:title [:h3 "Closed or Cancelled"]
+				  :query [:find (pull ?b [*])
+				       :in $ ?tag
+				       :where
+				       [?b :block/marker ?marker]
+				       [(contains? #{"DONE" "CANCELLED" "CANCELED" } ?marker)]
+				       (page-ref ?b ?tag)
+				       [?ref :block/name "project"]
+				       (not [?b :block/refs ?ref])]
+				  :inputs [:query-page]
+				  :result-transform :add-task-attrs
+				  :breadcrumb-show? true
+				  :group-by-page? false
+				  :collapsed? true
+				  }
+				  #+END_QUERY
+		-
+		- # Issue Table
+		  template:: Issue_table
+		  template-including-parent:: false
+			- #.tabular
+				- ## 01 ``{|}``Issue Title``{|}``
+				  
+				  owner::
+				  status:: #Red 
+				  #Issues #.v-kanban
+					- ### **Issue Description**
+						-
+					- ### **Updates**
+						- <% Today %> :
+				- ## 02 Issue Title
+				  owner::
+				  status:: #Amber
+				  #Issues #.v-kanban
+					- ### **Issue Description**
+						-
+					- ### **Updates**
+						- <% Today %> :
+				- ## 03 Issue Title
+				  owner:: 
+				  status:: #Yellow
+				  #Issues #.v-kanban
+					- ### **Issue Description**
+						-
+					- ### **Updates**
+						- <% Today %> :
+				- ## 04 Issue Title
+				  owner:: 
+				  status:: #Green
+				  #Issues #.v-kanban
+					- ### **Issue Description**
+						-
+					- ### **Updates**
+						- <% Today %> :
+				- ## 05 Issue Title
+				  owner:: 
+				  status:: #on-hold 
+				  #Issues #.v-kanban
+					- ### **Issue Description**
+						-
+					- ### **Updates**
+						- <% Today %> :
+				- ## 06 Issue Title
+				  owner:: 
+				  status:: #no-go
+				  #Issues #.v-kanban
+					- ### **Issue Description**
+						-
+					- ### **Updates**
+						- <% Today %> :
+				- ## 07 Issue Title
+				  owner:: 
+				  status:: #closed
+				  #Issues #.v-kanban
+					- ### **Issue Description**
+						-
+					- ### **Updates**
+						- <% Today %> :
+		- # Issue
+		  template:: Issue
+		  template-including-parent:: false
+			- ## ``{|}``01 Issue Title``{|}``
+			  
+			  owner::
+			  status:: #Red
+			  #Issues #.v-kanban
+				- ### **Issue Description**
+					-
+				- ### **Updates**
+					- <% Today %> :
+		- # Date Today
+		  template:: Date Today
+		  template-including-parent:: false
+			- **``today``** ``{|}``
+		- # [[Bookmarks]]
+		  template:: Bookmark
+		  template-including-parent:: false
+			- url:: ``{|}``
+			  
+			  topic::
+		- # [[Project page]]
+		  template:: project page
+		  template-including-parent:: false
+			- tags:: project page
+			  icon:: 📂
+			- ## Project Meta
+			  collapsed:: true
+				- DOING [#B] #project <% current page %>
+			- ## Actions Log
+				- query-properties:: [:deadline :priority :block]
+				  #+BEGIN_QUERY
+				  {:title [:h4 "On ToDo List"]
+				  :query [:find (pull ?b [*])
+				       :in $ ?tag
+				       :where
+				       [?b :block/marker ?marker]
+				       [(contains? #{"TODO"} ?marker)]
+				       (page-ref ?b ?tag)
+				       [?ref :block/name "project"]
+				       (not [?b :block/refs ?ref])]
+				  :inputs [:query-page]
+				  :result-transform :add-task-attrs
+				  :breadcrumb-show? true
+				  }
+				  #+END_QUERY
+				- query-table:: false
+				  query-properties:: [:page :block]
+				  #+BEGIN_QUERY
+				  {:title [:h4 "Ongoing Tasks"]
+				  :query [:find (pull ?b [*])
+				       :in $ ?tag
+				       :where
+				       [?b :block/marker ?marker]
+				       [(contains? #{"DOING" "NOW" "LATER" "WAITING"} ?marker)]
+				       (page-ref ?b ?tag)
+				       [?ref :block/name "project"]
+				       (not [?b :block/refs ?ref])]
+				  :inputs [:query-page]
+				  :result-transform :add-task-attrs
+				  :breadcrumb-show? true
+				  :group-by-page? false
+				  :collapsed? false
+				  }
+				  #+END_QUERY
+				- query-properties:: [:deadline :priority :block]
+				  collapsed:: true
+				  #+BEGIN_QUERY
+				  {:title [:h4 "Completed Tasks"]
+				  :query [:find (pull ?b [*])
+				       :in $ ?tag
+				       :where
+				       [?b :block/marker ?marker]
+				       [(contains? #{"DONE"} ?marker)]
+				       (page-ref ?b ?tag)
+				       [?ref :block/name "project"]
+				       (not [?b :block/refs ?ref])]
+				  :inputs [:query-page]
+				  :result-transform :add-task-attrs
+				  :breadcrumb-show? true
+				  :table-view? false
+				  :collapsed? true
+				  }
+				  #+END_QUERY
+			- ## Issues Log
+				- query-sort-by:: status
+				     query-table:: true
+				     query-sort-desc:: true
+				     query-properties:: [:block :owner :status]
+				  #+BEGIN_QUERY
+				  {:title [:h4 "Open Issues"]
+				  :query [:find (pull ?b [*])
+				       :in $ ?query-page
+				       :where
+				       [?p :block/name ?query-page]
+				  	 [?tag2 :block/name "issues"]
+				  	 [?b :block/refs ?tag2]
+				  	 [?tag1 :block/name "closed"]
+				  	 (not [?b :block/refs ?tag1])
+				       [?b :block/refs ?p]
+				       [?ref :block/name "project"]
+				       (not [?b :block/refs ?ref])         
+				       ]
+				  :inputs [:query-page]
+				  :breadcrumb-show? false
+				  :table-view? true
+				  :group-by-page? false
+				  :collapsed? false
+				  }
+				  #+END_QUERY
+				- query-properties:: [:block :owner :status]
+				  collapsed:: true
+				  #+BEGIN_QUERY
+				  {:title [:h4 "Closed Issues"]
+				  :query [:find (pull ?b [*])
+				       :in $ ?query-page
+				       :where
+				       [?p :block/name ?query-page]
+				  	 [?tag2 :block/name "issues"]
+				  	 [?b :block/refs ?tag2]
+				  	 [?tag1 :block/name "closed"]
+				  	 [?b :block/refs ?tag1]
+				       [?b :block/refs ?p]
+				       [?ref :block/name "project"]
+				       (not [?b :block/refs ?ref])         
+				       ]
+				  :inputs [:query-page]
+				  :breadcrumb-show? false
+				  :table-view? true
+				  :group-by-page? false
+				  :collapsed? true
+				  }
+				  #+END_QUERY
+			- #+BEGIN_QUERY
+			  {:title [:h2 "Project Notes"]
+			  :query [:find (pull ?b [*])
+			       :in $ ?query-page
+			       :where
+			       [?p :block/name ?query-page]
+			       [?b :block/refs ?p]
+			  	 [?tag2 :block/name "issues"]
+			  	 (not [?b :block/refs ?tag2])
+			       [?ref :block/name "project"]
+			       (not [?b :block/refs ?ref])        
+			       (not [?b :block/marker _])
+			       ]
+			  :inputs [:query-page]
+			  :result-transform (fn [result]
+			                   (sort-by (fn [b]
+			                              (get b :block/created-at "A")) result))
+			  :breadcrumb-show? false
+			  :group-by-page? true
+			  :collapsed? false
+			  }
+			  #+END_QUERY
+		-
+
+ +

Contents page

+ +
    +
  • Press Ctrl+K and search for contents and open the page.
  • +
  • Once on the page, click on Three dots in top right corner of the screen and from the drop down menu select Open in default app
  • +
  • Here paste the following then save and close the default app:
  • +
+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+
  - query-properties:: [:icon :page :updated-at]
+  #+BEGIN_QUERY
+  {:title [:h1 "⏳ Ongoing Projects"]
+    :query [:find (pull ?page [*])
+        :where
+          [?block :block/page ?page]
+          [?page :block/name ?pagename]
+          [?block :block/marker ?marker]
+          [(contains? #{"DOING"} ?marker)]
+          (page-ref ?block "project")
+          (not [?page :block/name "templates"])
+      ]
+      :breadcrumb-show? false
+      :collapsed? false}
+  #+END_QUERY
+  
+  - query-properties:: [:icon :page :updated-at]
+    #+BEGIN_QUERY
+    {
+    :title [:h1 "👨‍💼People"]
+    ;; ---- Get every block into variable ?block
+    :query [:find (pull ?block [*])
+        ;; ---- filter command
+        :where
+        ;; ---- get page name (lowercase) from the special page block into variable ?pagename
+        [?block :block/name ?pagename]
+        ;; ---- Select if block is a special page block and has a single property (arg1) with value arg2
+        (page-property ?block :tags "people")
+    ]
+    }
+#+END_QUERY
+
+ +

Sample Content Page

+

screenshot_20240813-152544

+ +

Usage

+ +

Sample Day to Day Capture

+

Screenshot_13-8-2024_151147_

+ +

Create Project

+
    +
  • To create a CARDIO Log for a project, first create a new page and give it name of the Project.
  • +
  • Next, open the newly created project page.
  • +
  • Once opened, press Ctrl+t and select project page template and press enter.
  • +
  • This will create all relevant sections for the Project.
  • +
+ +

Create User Page

+
    +
  • Create a new page with the user / resource name.
  • +
  • Open the newly created page.
  • +
  • Once opened, press Ctrl+t and select people page template and press enter.
  • +
  • This will create all relevant sections to track actions for this resource.
  • +
+ +

Create New Issues

+
    +
  • Now for everyday usage, each time there is a new issue or a number of issues they can simply be recorded by typing /.is for single issue and /.it for a table of issues.
  • +
  • Once the issue or issues table is created, all usual fields can be filled but most importantly the issue title must include hashtag for the project that the issue belongs to. +
      +
    • So an issue for project called North Star must have #[[North Star]] in the title.
    • +
    • As an example say the issue is to do with lack of resources then the title should be #[[North Star]]: Lack of Resources
    • +
    • +

      The issue status or any topic to be highlighted with RAG status can be tagged with following tags:

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      TagCircle Appended
      #red🔴
      #amber🟠
      #green🟢
      #on-hold🟣
      #yellow🟡
      #Closed🔵
      #no-go🚫
      +
    • +
    +
  • +
  • +

    Circles with additional colours can be created using the template. To do so, press /.c and press enter. This should then put following text on the editor: ` {{renderer :template-view, circle-template, :color orange}} `

    +
  • +
  • Now just replace orange in above with the colour desired.
  • +
+ +

Maintain Issues Log

+
    +
  • Reviewing the logs is as simple as opening the Contents page, clicking the project name and scrolling down to the Issues Log section.
  • +
  • Now if there are actions identified during the review of an issue, they can be simply added in the updates section under the date of review which can be quickly added by pressing Ctrl+Shift+D and then noting down the action as a sub bullet TODO [#A] Do Something /deadline
  • +
  • I usually log decisions against a logged issue or if they resulted from a general discussion, just as #<projectname> #decision.
  • +
+ +

Day to day notes

+
    +
  • All the project relevant notes will be automatically collected on the project’s page so long as they have been tagged with the project name so for the day to day notes all that is required is add entires in the Journal.
  • +
+ +

Blockquotes

+
    +
  • As we added css for different coloured borders of blockquotes and also created shortcode and keyboard shortcut we can do this in multiple ways: +
      +
    • Type />band press enter. This will place <blockquote class='blue'></blockquote> and the quote can be written between the tags.
    • +
    • +

      Type the following, replacing yellow with one of the predefined colours: yellow, pink, blue, green, red, grey, gray, orange or purple. + {{> yellow,Some yellow quote}}

      + +

      image

      +
    • +
    +
  • +
+ +

Coloured Highlight

+
    +
  • Type /=y, select Yellow Highlighter from pop-up menu and press enter. This will place <mark class='yellow'></mark> and text to be highlighted can be written between the tags. It will be presented as Yellow Highlight
  • +
  • +

    Type the following and it will be presented as This text is highlighted. + {{== pink,pink highlight}}

    + +

    image

    +
  • +
]]>
Ankit Mittal
Python Function read excel / csv files from a given directory and its subdirectories2023-03-29T17:16:00+00:002023-04-18T20:26:00+00:00https://mgw.dumatics.com/read-excel-csv-recursiveCode + +

The code for the function is as shown below:

+ +
import os
+import pandas as pd
+import numpy as np
+from datetime import datetime 
+
+def extract_data(directory_path, columns_to_extract, date_columns=[], rows_to_skip=0, output_filename='output.csv'):
+    """
+    Extracts data from all Excel and CSV files in the specified directory and its subdirectories that contain all the
+    specified columns.
+
+    :param directory_path: The path to the directory to search for Excel and CSV files.
+    :param columns_to_extract: A list of column names to extract from each file.
+    :param date_columns: A list of column names to parse as dates using pd.to_datetime().
+    :param output_filename: The path and filename to save the extracted data in a csv
+    :return: A DataFrame containing the extracted data from all Excel and CSV files that contain the specified columns,
+    or None if no files contain the specified columns.
+    """
+    start_time = datetime.now()
+    extracted_data = pd.DataFrame()
+    files_with_no_columns = []
+    sheet_read = ''
+    extracted_columns = columns_to_extract.copy()
+    extracted_columns.extend(['File Name','SubDir','CreatedDate','LastModifiedDate'])
+
+    for root, dirs, files in os.walk(directory_path):
+        for file in files:
+            print('Started reading ' + file + ' at: {}'.format(datetime.now()) + ' ...')
+            if file.endswith('.xlsx') or file.endswith('.xls') or file.endswith('.csv'):
+                file_path = os.path.join(root, file)
+                columns_found_outer = False
+                if file.endswith('.csv'):
+                    df = pd.read_csv(file_path,skiprows=rows_to_skip)
+                    for col in columns_to_extract:
+                        if col in df.columns:
+                            columns_found_outer = True
+                            #print(col + 'in csv true loop')
+                        else:
+                            columns_found_outer = False
+                            #print(col + 'in false loop')
+                            break
+                else:
+                    dfs = pd.read_excel(file_path, sheet_name=None,skiprows=rows_to_skip)
+                    for key in dfs.keys():
+                        columns_found = False
+                        for col in columns_to_extract:
+                            if col in dfs[key].columns:
+                                columns_found = True
+                                #print(col + 'in true loop')
+                            else:
+                                columns_found = False
+                                #print(col + 'in false loop')
+                                break
+                        if columns_found:
+                            columns_found_outer = True
+                            df = dfs[key]
+                            sheet_read = key
+                            break
+                if columns_found_outer:
+                    if len(date_columns) > 0:
+                        for col in date_columns:
+                            if col in df.columns:
+                                df[col] = pd.to_datetime(df[col])
+                    df['File Name'] = file
+                    df['SubDir'] = os.path.basename(root)
+
+                    createx = modx = os.path.getctime(file_path)
+                    xcreate = datetime.fromtimestamp(modx)
+                    df['CreatedDate'] = xcreate
+
+                    modx = os.path.getmtime(file_path)
+                    xmod = datetime.fromtimestamp(modx)
+                    df['LastModifiedDate'] = xmod
+                    extracted_data = pd.concat([extracted_data, df[extracted_columns]], ignore_index=True)
+                    #print(xmod)
+                    if file.endswith('.csv'):
+                        print(file + ' has been read')
+                    else:
+                        print(file + ' has been read and it was last modified on ' + xmod.strftime('%Y-%m-%d') + '. The name of the sheet that was read is: ' + sheet_read)
+                else:
+                    files_with_no_columns.append(file_path)
+    if len(files_with_no_columns) > 0:
+        print("The following files do not contain the specified columns:")
+        for file_path in files_with_no_columns:
+            print(file_path)
+    if not extracted_data.empty:
+        extracted_data = extracted_data.applymap(lambda s: s.upper() if type(s) == str else s).fillna('')
+        extracted_data.to_csv(output_filename)
+        print('Started at: {}'.format(start_time) + '. \nEnded at: {}'.format(datetime.now()) + '. \nTime elapsed (hh:mm:ss.ms) {}'.format(datetime.now() - start_time))
+        return extracted_data
+    else:
+        print("Specified columns do not exist in any file in the provided directory.")
+        print('Started at: {}'.format(start_time) + '. \nEnded at: {}'.format(datetime.now()) + '. \nTime elapsed (hh:mm:ss.ms) {}'.format(datetime.now() - start_time))
+        return None
+
+ +

Explanation

+ +
    +
  1. +

    The function extract_data takes in the following parameters:

    + +
      +
    • directory_path: the path to the directory to search for Excel and CSV files
    • +
    • columns_to_extract: a list of column names to extract from each file
    • +
    • date_columns: a list of column names to parse as dates using pd.to_datetime()
    • +
    • rows_to_skip: the number of rows to skip when reading each file
    • +
    • output_filename: the path and filename to save the extracted data in a CSV file
    • +
    +
  2. +
  3. +

    The function starts by creating an empty DataFrame called extracted_data.

    +
  4. +
  5. +

    It also initializes variables files_with_no_columns and sheet_read to track files that do not contain the specified columns and to keep track of the current sheet being read when extracting data from Excel files.

    +
  6. +
  7. +

    The list extracted_columns is created by copying the input columns_to_extract list and adding additional columns for the filename, subdirectory name, file creation date, and last modified date.

    +
  8. +
  9. +

    The function then loops through all the files in the specified directory and its subdirectories using os.walk().

    +
  10. +
  11. +

    For each file, it checks if it has a “.xlsx”, “.xls”, or “.csv” extension.

    +
  12. +
  13. +

    If the file is a CSV file, the function reads it into a DataFrame using pd.read_csv(), skipping the number of rows specified by rows_to_skip.

    +
  14. +
  15. +

    It then checks if all the columns in columns_to_extract are present in the DataFrame. If so, it sets columns_found_outer to True and proceeds to the next step. If not, it sets columns_found_outer to False and moves on to the next file.

    +
  16. +
  17. +

    If the file is an Excel file, the function reads all sheets in the file into a dictionary of DataFrames using pd.read_excel() and sheet_name=None. It then loops through all the sheets and all the columns in columns_to_extract, checking if each column is present in each sheet’s DataFrame. If all the columns are present in a sheet, it sets columns_found to True and proceeds to the next step. If not, it sets columns_found to False and moves on to the next sheet in the same Excel file. If at least one sheet contains all the specified columns, the function combines the DataFrames of all sheets into one using pd.concat().

    +
  18. +
  19. +

    If columns_found_outer is True, it extracts the filename, subdirectory name, file creation date, and last modified date using os.path.basename(), os.path.getctime(), and os.path.getmtime(), and adds them as new columns to the DataFrame. It then appends the DataFrame to the extracted_data DataFrame.

    +
  20. +
  21. +

    If columns_found_outer is False, it increments the files_with_no_columns counter and prints a warning message.

    +
  22. +
  23. +

    Finally, the function checks if any files contained the specified columns. If not, it returns None. Otherwise, it sorts the extracted_data DataFrame by filename and saves it to a CSV file using to_csv(). It then prints the number of files processed, the number of files that did not contain the specified columns, and the path to the output file.

    +
  24. +
+ +

Sample Usage

+ +

The function can be called in python as shown below:

+ +
# set directory path
+directory_path = './Work' # Path for the directory where all the files containing data for  extraction are to be searched
+
+# set the columns you want to extract
+columns_to_extract = ['Region','Country', 'Product Number','Quantity','Date of Sale']
+date_col = ['Date of Sale']
+# set the rows to skip
+skiprows = 0 #this basically will be number of rows in begining of the files which must be skipped to reach the header row of the data
+output_filename = 'Regional Sales Data.csv'
+#Call function
+df = extract_data(directory_path,columns_to_extract,date_col,skiprows,output_filename)
+
+ +

Now, assuming there were 12 separate files for past 12 months inside the folder then so long as all those files, irrespective of whether they are csv or excel, have the columns Region,Country, Product Number,Quantity,Date of Sale; the function will read the files and extract the data and return it to the dataframe df.

+ +

GUI Implementation

+ +

A very basic GUI implementation of above function using PySimpleGUI with all codeis available here

+ +

Usage

+

The script can directly be copied to a Jupyter cell or can be run from terminal. Following command should ensure all dependencies are installed:

+ +
py -m pip install pandas, numpy, pysimplegui
+
+ +

Some things the GUI takes care of are:

+ +
    +
  • Allows selection of columns to be extracted from a sample .csv file
  • +
  • Allows user to specify which of the selected columns should be parsed as date
  • +
  • Gives a date based filename to output
  • +
  • Shows colour coded log for which files were read in green and which were ignored in red background.
  • +
+ +

Screenshots

+ +

Some screenshots of the resulting app are as shown below:

+ +

Empty Form

+

image

+ +

Filled Form

+

image

+ +

Displays extracted output

+

image

+ +

Displays filename of the output and location where it is saved

+

image

+ +

Colour coded log

+

image

]]>
Ankit Mittal
Git Jargon2023-02-28T17:50:00+00:002023-02-28T17:50:00+00:00https://mgw.dumatics.com/git-jargonGit Jargon + +

Before we delve into the topic of how collaboration happens using git, lets get some terminology out of the way.

+ +

repo

+ +

When talking to techy chaps, one will encounter the word repo very often. However, repo is just a shortform for the complete word repository.

+ +

repository

+ +

Now repository in git speak is basically specific to a project and is nothing but a fancy alias for the working directory where all the digital assets being tracked for the project are placed.

+ +

root

+ +

The term root when used with reference to a folder, directory, repo or repository is basically the directory in which all the digital assets of your project are stored. This directory may or may not contain other sub directories.

+ +

remote

+ +

remote is a way to refer to server. So if a techy were to say “I have updated File X on remote” - it will basically translate in plain English to something like this - “I updated the File X on my laptop and have then also taken required actions to get those updates reflected on the copy saved on the server.”

+ +

push

+ +

This refers to sending updates carried out (commited) on a local machine to the server (remote). Fairly frequently one may come across statements when checking for status updates on the lines of “I have pushed my changes to remote” which in normal English means “I made required changes on my local machine, committed them and then sent (pushed) them to the server (remote)”

+ +

pull

+ +

This refers to getting updates from the server (remote) on to the local machine. This is an activity typically carried out before one is planning to make any changes or updates to last known version. So if say a task is assigned to an individual for updating file , a reply can be “Alright, I will pull the latest from remote and make changes and will let you know once I have pushed my changes back.” which will translate to “Alright, I will get the latest copy from server, make the changes you have asked for and then commit those changes and send it back as updated version on the server”

+ +

main

+ +

This refers to the final / published version of project’s repository.

+ +

branch

+ +

This term is more of a concept really. You see branch is a mechanism to carry out changes without impacting the main version and therefore a branch is basically just a new or separate version of the main repository where one can make changes to their heart’s content without worrying about messing up with the last known final version.

+ +

merge

+ +

Once the experimental changes in a branch are completed and proven not be breaking any other digital assets, it is desirable indeed to get these changes reflected on the main version and this action of bringing digital assets in main to reflect same changes as that on the branch is called merge.

+ +

modified

+ +

In git speak, any changes (updates, additions, deletions) to a digital asset (including deletion of entire digital asset) then that digital asset will be identified as modified

+ +

staged

+ +

The modified digital assets are not automatically assumed to contribute a version change and that is only done when those changes are added explicity by issuing the command git add <filenaem / foldername> and at this point the changes are said to be staged.

+ +

Recap

+ +

With this in mind, the flow of GIT can now be explained as below:

+ +
    +
  1. To work with Git, you first initialise it on a directory which will make it a Repository
  2. +
  3. Once initialised, Git creates a hidden firectory called .git which is used to keep track of changes in the initialised directory.
  4. +
  5. At this point, we need to make Git aware of the files in this repository which we want Git to track and in doing so we stage those digital assets.
  6. +
  7. From this point on, any changes made to digital assets in project folder can be tracked.
  8. +
  9. If a digital asset which is being tracked is changed, it gets the status of modified.
  10. +
  11. Any digital asset with status must be staged before it can be passed on for version control.
  12. +
  13. Once staged files must be committed so that the changes can be treated as the latest version snapshot.
  14. +
  15. As each commit is tracked by Git, it is possible to revert to a previous version by first identifying the commit id one wants to revert to using git log and then providing that commit id through git revert <commit_id>
  16. +
]]>
Ankit Mittal
Git Basics2023-02-26T17:50:00+00:002023-02-26T17:50:00+00:00https://mgw.dumatics.com/git-basicsBasics of Git + +

Recently, I was asked to help people understand what is this whole business with GIT and I ran a few sessions on the topic specifically keeping in mind that my audience consisted of tech enthusiasts with minimal tech background. The content below is how I explained and as it went down well, I figured it may be helpful to someone else too; hence this post. :)

+ +

Version Control System

+ +

To understand the tool git lets first start by understanding what problem does it actually solve. At the most basic level git is a version control system. What this means is that it allows automated control of maintaining different versions of a digital asset such as a text file, CV, or indeed code files for a software. Now for simpler things like CV or text file one may not need something as sophisticated and complex as git but when it comes to maintaining assets for a software it is indeed a very useful tool.

+ +

To understand how significant this is, lets put it in perspective of a typical business operations scenario completely unrelated to software development:

+ +
    +
  • Say there was an RFP response on which multiple people from Sales, Marketing, Pursuit and Technical team were helping prepare a response.
  • +
  • Each team will complete one section of the response document relevant to their area.
  • +
  • This will then all need to be integrated into the final response and reviewed by the Sales Lead.
  • +
  • Any changes that Sales Lead identifies will need further changes which will then again need to be amended on the document.
  • +
+ +

Even with change tracking enabled in MS Word, we all know how painful this entire exercise is. So much so that at times we try doing it together so all changes can be done once or we opt for the approach of making changes sequentially where one section is completed by one team before another can be started by another team. This when we have one document!!!

+ +

Now imagine if we had 1000s of such documents - that is a real scenario for software developers as each software has multiple files that may need input from multiple people. So having an automated version control helps save time because it, at the most basic level, saves time on integrating changes carried out by different people or teams that were carried out in parallel.

+ +

Now when it comes to version control there are two main ways in which it can be achieved:

+ +
    +
  • +

    Centralised Version Control System: In our RFP example, the approach where everyone gathers together to carry out change on the main version will be a good simulation of centralised version control. What this means is that the changes to digital asset are done at one central location which is tightly controlled. This might as well be right approach for a smaller undertaking such as our RFP example but when it comes to software development it is not considered to be the best.

    +
  • +
  • +

    Distributed Version Control System: This is loosely what would have been the approach in our RFP example if people were to carry out changes on their copy of document which will then be merged by Sales Lead to prepare the final response. Basically a distributed version control system works on the principle that each contributor to the change of digital asset will make change on their local copy and then submit the change for version maintenance to the version controller. Git as a system shines in this approach of distributed version control.

    +
  • +
+ +

Working Directory

+ +

Directory is technically correct name for what are called folders in Windows Operating System and Working Directory is the directory in which the user is currently working. For example, let’s say I create a folder called “Project_1” in windows on path “C:/Documents” and save a python script in that folder, then the working directory will be “Project_1” and working directory path will be “C:/Documents/Project_1”.

+ +

In order to version control this file, one can use git but this will include understanding the steps of how to go about it as explained in following sections.

+ +

Initiatlise

+ +

Now when let’s say the script file script.py is created by developer. It is not being tracked by git at this time. Now if we want it tracked by git, we need to first initiate git on the working directory. This is done using the command: git init but before typing this on command line, one needs to first get into the working directory on command line or terminal.

+ +

In our example this will be done like so:

+ +
cd C:/Documents/Project_1
+git init
+
+ +

This will create a .git directory (folder) inside the working directory.

+ +

This is equivalent of git software saying it is now ready to start tracking but it will still need to know what exactly we want it to track. This is what git add does.

+ +

Add

+ +

To tell git what we want it to track, we have to use the command git add <filenames / foldernames>. However, if there are 100 script files in directory it will be very cumbersome to type every single filename and as a shortcut one can use dot notation for current working directory which is dot(.) and the command can be issued as:

+ +
# To add all content of the working directory
+git add .
+
+# To add specific file say script.py
+git add script.py
+
+ +

This tells git that we want all files and directories (folders) in the current working directory to be tracked.

+ +

At this point, git is basically aware that it has to monitor changes on these files but it will not assume that every change we have made needs to be applied to maintained version so when we have added these files, we have basically also just said prepare these files for finalising their version - In other words we have asked git to stage.

+ +

In order to finalise the versioning, we have to now tell this specifically to git which is done using commit. This will also be reflected in the tracking status which can be checked using status

+ +

Status

+ +

One can check current tracking status of various files and directories on the git initialised working directory by using the command:

+ +
git status
+
+ +

Commit

+ +

So we have got git to stage the files and now we want it to label the latest changes as a new version and this is achieved using the command:

+ +
git commit -m <Comment for the commit>
+
+

This command ensures that git now makes the latest changes baseline and still keep record of previous changes. This is especially useful if in case we want to revert to a previous version for some reason - say we found that in latest version there were quite a few mistakes which will need to be rewworked and until then previous version is what we want to keep as baseline for anyone who wants to refer and make changes to. This reverting can be achieved using revert

+ +

Revert

+ +

Now reverting to a previous version requires that we know the commit id that git applied to previous commits and then identify the exact commit id to which we want git to revert to. This can be achieved by checking git logs, so the sequence to git revert will be like so:

+ +
# Find the commit id to revert to
+git log
+
+# Copy the commit id of the version to revert to and issue following command
+git revert <commit id to revert to>
+
+ +

Now at this point, all changes are being tracked on the local machine of the user, so how can this help with collboration. This is achieved by synchronising the latest version on local machine to a central server. This could be a server hosted by an organisation or it can be something public like Github, Gitlab and such. As GitHub is most frequently used in enterprise environment, lets see how that works.

+ +

Github basically is a web based utility which acts as a remote location where we can push our finalised version to be saved in what is called a github repository for other collborators to then download (clone) as their local copy and work on. Once each poarty has made changes, the can each push their changes to the github with help of git and the version control will be applied automagically or in some instance with minimal manual intervention from the maintaining or one pushing the changes.

+ +

+ +

There is lots going on in this last bit so lets break it down to digestable chunks of information in next session.

]]>
Ankit Mittal
JupyterLite on Github Enterprise with Panel enabled2023-01-25T17:50:00+00:002023-01-25T17:50:00+00:00https://mgw.dumatics.com/jupyterlite-%20on-github-enterprise-with-panel-enabledJupyterLite on Github Enterprise with Panel enabled + +

Recently I found myself in a scenario where I had access to Github Enterprise but was limited to a windows machine. Now I wanted to host JupyterLite on Github Enterprise so it can be used by some other people on my team. Challenge was the “Actions” on this instance of Github enterprise were disabled and so I had to build Jupyterlite locally and push it on Github. I figured while at it, I might as well enable the “Panel” and few other dependencies. The steps that worked for me are documented here. This is ofcourse assuming that Python is already installed on the device.

+ +

The final hosted JupyterLite can be seen in action here.

+ +

The steps below created a Jupyterlite instance that allows use of Panel using the %pip install panel magic command, and if used on Chrome or its derivatives it also allows exploring local file system.

+ +

Create and activate Jupyterlite Environment

+ +
mkdir panel_lite
+cd .\panel_lite\
+py -m venv panel_v
+.\panel_v\Scripts\activate
+pip install --upgrade pip
+
+ +

Install jupyterlite and other extensions

+ +
pip install --pre jupyterlite
+pip install jupyter-bokeh ipython ipywidgets jupyterlab-drawio jupyterlab-markup jupyterlab-myst jupyterlab-pygments jupyterlite-p5-kernel jupyterlite-xeus-sqlite libarchive-c matplotlib matplotlib-inline matplotlib-venn myst-nb myst-parser nbconvert numpy openpyxl pandas pandocfilters pkginfo pyopenssl python-dateutil python-dotenv pyviz-comms pyxlsb scipy sql SQLAlchemy sqlparse tornado widgetsnbextension xlrd XlsxWriter zipp jupyterlab-filesystem-access
+
+ +

Prepare build directory

+ +
mkdir jupyterlite_panel
+cd .\jupyterlite_panel\
+mkdir pypi
+cd .\pypi\
+wget "https://cdn.holoviz.org/panel/0.14.2/dist/wheels/bokeh-2.4.3-py3-none-any.whl" -outfile "bokeh-2.4.3-py3-none-any.whl"
+wget "https://cdn.holoviz.org/panel/0.14.2/dist/wheels/panel-0.14.2-py3-none-any.whl" -outfile "panel-0.14.2-py3-none-any.whl"
+
+ +

Make a record of all that you have installed.

+ +
pip freeze > requirements.txt
+
+ +

Including any existing notebooks

+ +

If you have some existing notebooks or files you would want to include on the hosted version of Jupyterlite, you must create a directory and name it files

+ +

Build Jupyterlite and serve locally to test

+ +
jupyter lite build --output-dir ./dist
+cd .\dist\
+py -m http.server 8000
+deactivate
+
+ +

The folder structure after build should look somewhat like below:

+ +
panel_lite:.
+├───.cache
+├───dist
+│   ├───api
+│   ├───build
+│   ├───doc
+│   ├───extensions
+│   ├───files
+│   ├───kernelspecs
+│   ├───lab
+│   ├───pypi
+│   ├───repl
+│   ├───retro
+│   └───tree
+├───files
+│   ├───data
+│   └───sample.ipynb
+├───pypi
+└───panel_v
+
+ +

Directory Structure Overall +Directory Structure of built dist folder

+ +

Push it to Github Enterprise

+ +

Github Desktop is the easiest way to do it.

+ +

Open Github desktop and create new reporsitory:

+ +

Create New Repository

+ +

On Github desktop point the Local Path field to \panel_lite\jupyterlite_panel\dist and do the initial commit and push to origin.

+ +

Provide Local Path

+ +

Host Jupyterlite as Github pages

+ +
    +
  • Click on button View on Github on Github Desktop.
  • +
+ +

Open Github Enterprise

+ +
    +
  • Github Enterprise will open in browser to the created repository
  • +
+ +

Enable Pages on Github Enterprise

+ +
    +
  • Click on Settings > Pages
  • +
  • select Branch: master (or main) and root as source.
  • +
  • Click Save and the message should be displayed in green box with the URL to access Jupyterlite.
  • +
+ +

Finally open the URL to access JupyterLite

+ +

JupyterLite hosted on Github Enterprise

]]>
Ankit Mittal
Tech for Diabetics2021-03-05T17:50:00+00:002021-03-12T13:05:15+00:00https://mgw.dumatics.com/tech-for-diabeticsBackground + +

As a diabetic there are a number of things we are not able to control +but one thing we can do is keep tabs on our data and while in past it +would have meant meticulously noting down BG readings in a diary, today +it is much much easier with all the apps and connected services. In this +article, I aim to go through the list of technologies that I am aware of +and use to keep on top of my diabetic data right from my phone.

+ +

Blood Glucose Meter

+ +

While there are a number of options available, I settled for CareSens +Dual. The +biggest advantage with this model is that it plays very nicely with +xdrip+ app although lately I am relying more on Diabox so the point is +kind of moot. Apart from that, one factor we must bear in mind while +chosing the device is not just the cost of the device itself but also +that of testing strips as it will be a recurring expense and this device +has one of the most reasonably priced testing strips.

+ +

Freestyle Libre 2 (FSL2)

+ +

FSL2 became available in the UK only from Jan’21 and this or it’s +previous version allow for a more regular check of the BG trends. FSL1 +in my experience was a bit less accurate than FSL2. However these +sensors along with the official app from manufacturer require manual +scan, don’t give all datapoints and can not be calibrated which is again +where xdrip+ / Diabox apps come into the picture.

+ +
+

Freestyle Libre 1 (FSL1) with Transmitter (Miaomiao / Bubble)

+ +

Before Jan’21, in the UK we only had the option of FSL1 and as this version does not have bluetooth communication; the possibility to +calibrate and get automated data points required usage of a transmitter device such as Miaomiao or Bubble. I ordered a Miaomiao +transmitter back then and I still can use it with FSL2 albiet with slightly modified approach. I do it because I have the device but with +FSL2 a transmitter is not needed as explained later.

+
+ +

Freestyle Libre App

+ +

Now the fact is I dont really use this app for anything other than +activating the sensor but it is for this reason alone an app you can not +avoid or do without. In order to use the sensors to feed data directly +and automatically to Diabox the sensor at the time of activation should +not be paired through bluetooth to this app. Now there are several ways +to achieve this. One is to install the app on a completely separate +phone and turn off Bluetooth on that phone. Another is just do not give +this app the “Location” permission which would then not allow app access +to Bluetooth. Either scenario should work in theory but I use the safest +approach of activating sensor through this app on a compltely different +phone (that of my daughter actually :))

+ +

Diabox (Especially with FSL2)

+ +

I have been using this app only for 10 days at the time of writing but +it works really well with FSL2 and as it gets the data directly from the +sensor it removes the additional cost of the transmitter and that makes +this a very useful app indeed. The app is available for both iOS as well +as Android but from what I gathered at some point Abbot Laboratories +(Manufacturers of FSL sensor) complained about the app to Google and so +google was obligated to takedown this app from playstore. App therefore +is available as an apk file on github and the latest release for it can +be downloaded from this GitHub +Link The developer +has a Diabox Dev +website +with lot more information on user interface and setup etc. but I will +explain my setup below.

+ +
+ +
    +
  1. Download the latest version of the app from GitHub +Link
  2. +
  3. Install the app - As it is not from playstore, the permission to +install from unknown sources1 should be +activated in Android Settings.
  4. +
  5. Once installed, open the app. It will ask to scan the sensor. Once +you scan, the phone will automatically pair the sensor with the +Bluetooth of the Libre.
  6. +
  7. Now to enable calibration tap on the sensor image as shown in +screenshot below: +adfd44c4c0cf4e91aeeb8792970dbf67
  8. +
  9. Now tap 5 times on the text “Factory” in the last entry “Calibration +Mode” as shown below: +00aec12e25c74fed8e124e788850090e
  10. +
  11. This will present a dialogbox “Sensor Magic +Code”. Type “GODMODE” in the +dialogbox field and press “OK”. This should activate the calibration +mode and screen should look as shown below: +4aa44c106c724c16b27129ccc3acfb3a
  12. +
  13. Go back to homepage and you will now see a red coloured circular +button with “+” symbol on it as you can see on the first screenshot +above.
  14. +
  15. Tapping on the add button will reveal an icon with a glucometer as +shown below: +d417b24684524a05985e737b656b55a9
  16. +
  17. Tapping on the glucometer icon will open a sliding scale to provide +the calibration value. +8f671fb4efa542d08bdf60c1ead2756d
  18. +
  19. Add your calibration and click on Save button.
  20. +
+ +
+ +

Now, the app also allows uploading the data it collects to LibreView and +Nighscout. I am not so keen on LibreView web interface so I never tried +uploading on that service but have it syncing with Nightscout. For +Nightscout sync, you will need to have a Nightscout instance up and +running2 but assuming it is in place, the steps +will be simple to follow:

+ +
    +
  1. Click on “Settings”
  2. +
  3. Click on “Integration”
  4. +
  5. Enable “Nightscout Share Server Upload”
  6. +
  7. Enter the URL for Nightscout instance, something like +https://your_nightscout.herokuapp.com/api/v1/
  8. +
  9. Enter the password for your instance.
  10. +
  11. Click on Connect Test
  12. +
  13. If the test is a success, click on Save button.
  14. +
+ +

Nightscout

+ +

Nightscout is a fairly detailed open source solution with loads and +loads of functionalities but as a type 2 user I merely use it as a +backup of my data.

+ +
+

One key thing to note is that once you setup this server your BG data +is available to anyone with access to your URL as there is no password +block or similar which in my eyes is no big deal. What can anyone do +with just my blood glucose data and how or why will they get access to +an obscure URL anyway but if this makes you worried perhaps you can +skip this part. If this is not of concern then I must say that while +the setup guide2 looks very long and complex, +it really isn’t. They have taken care to make it so very simple that +its infact easier than even completing the registration form for some +of the online services we have got accustomed to and it will just +work.

+
+ +

To set it up you can follow the official set-up and installation +guide.

+ +

Diabetes:M

+ +

Diabetes-M is a very good app and creates some very useful and pretty +reports that can be helpful during discussion with Diabetic Nurse / GP. +One big advantage of using this app is that it is able to import data +from Nightscout directly and keep it in sync every 15 minutes and then +that data is also backed-up in Diabetes-M servers. The app also allows +for automatic data sync with a number of popular Glucometers but that is +a paid feature and I do not use it so I cannot comment on its +reliability. For my usecase I anyway calibrate Diabox each time manually +when I take the reading from finger prick and that is synched to +Nightscout which is synched to Diabetes-M so the data is anyway there +when I need it. Steps to sync with Nightscout are as below:

+ +
    +
  1. Open your nightscout instance in a browser on a laptop / PC .
  2. +
  3. Click on the site in sequence as shown below:
    +2707fa8797d94ed08f1d6c44fc04ef85
  4. +
  5. On resulting screen - Administration panel, check if the role +“readable” exists.
    +cc9cdbfe94474be4b87515de72d9f98c
  6. +
  7. If not create it by clicking on the button Add new Role and in +“Permissions” type *.*.read as shown below.
    +053fbb025cc74ebc880f729d3dc3b6b2
  8. +
  9. If it exists, go to the section “Subjects - People, Devices, etc” +and click on “Add new Subject” and fill the resulting dialogbox like +shown below and click Save:
    +64ed999845d644239615c3b0e2aab454
  10. +
  11. This will generate the Access Token for the app and it will be shown +in the table in the section:
    +8c623ef74cdc41d8a6ba5fd05e30b32d
  12. +
  13. Open the Diabetes:M app and click on the hamburger menu on top left +corner of the screen. Then on the panel locate and tap on the Data +Sync icon.
    +5229542f7a364b07bbed0356ec8f3565
  14. +
  15. On resulting screen under section “Link external sources” locate +Nightscout and click on setting icon.
    +4c718c71ade84cb7a2c9eeb3596e32c1
  16. +
  17. Now fill the URL for your nightscout instance, something like +https://your_nightscout.herokuapp.com
  18. +
  19. Leave “Secret” empty and in in the field “Access token” type the +access token shown generated in step 6 above from the browser and +click Save.
  20. +
+ +

Direct links to appstore:

+ +
    +
  1. Google +Playstore
  2. +
  3. Apple Appstore
  4. +
+ +

XDRIP+

+ +

xdrip+ is an opensource Android app (an iOS variant exists but I havent +read much about it so have no knowledge to share) and as it is not +available from playstore it can be obtained directly from developer +site: https://jamorham.github.io/#xdrip-plus To use xdrip, you will +also need to order a transmitter called miaomiao - It usually comes +within 2 to 3 days - for me atleast it came in 2 days back in October +when I started using FSL1. It is a bit of investment at about £162 but +it pays for itself in long run. I ordered from their +site3. Once you get the transmitter, the process +will be like so:

+ +
    +
  1. It will come with a USB charging cable so place it on charger. It +should start blinking blue but if it does not there is a very small +hole and you will need a safety pin to press and reset it. Insert +the safety pin and keep it pressed for 10 seconds. Then place it on +charge.
  2. +
  3. Once the transmitter is charged it will display green light.
  4. +
  5. Now there is also a strap they provide with the transmitter which +you can use to place it over the libre sensor on your arm.
  6. +
  7. Open xdrip app and follow the xdrip+ installation +guide4.
  8. +
+ +

Once connected, it will ask you to calibrate the app with readings from +two finger prick tests. You can do one and provide twice or you can do +twice. I usually do it twice for each new sensor.

+ +

Tidepool

+ +

This is a not-for-profit site which xdrip+ can directly communicate with +and push the readings to every 15 minutes thus creating a backup for +your data in the cloud. As I still use the miaomiao transmitter paired +with xdrip+ I have created a sync from xdrip+ to tidepool. Now the good +thing with tidepool is that it shows glucometer readings in a separate +table and while for Diabox I have to manually enter the finger prick +test data, Caresens Dual is paired perfectly with xdrip and every single +time I take a finger prick test, xdrip+ automatically fetches the +reading from meter via Bluetooth and then that reading is fed into +Tidepool which allows me to see my BG data at one place like so: +tidepool

+ +

Eufy Smartscales

+ +

Eufy smartscale is very reasonably priced and I ordered it from Amazon. +The scale is reliable, uses standard 3x “AAA” battery which last for +years not months. The app interface is good too. One drawback perhaps is +that data is only accessible through app on the phone and not through +any website. It does sync weight with google fit so that can be accessed +and shared across other services if one is so inclined but rest of it +will need to be manually captured for any statistical fun. However, if I +knew before what I know now I would have perhaps opted for a device from +this +list +as then I could have had my data collected through the open source app +openScale +and would have had better freedom on how to move it around. Hope this +list is useful. If there are any questions, please do not hesitate to +ask. :)

+ +
+ +]]>
Ankit Mittal
Upgrading PHP version on Linux for Apache2021-02-10T00:04:01+00:002021-02-10T00:14:40+00:00https://mgw.dumatics.com/upgrading-php-version-on-linux-for-apache +
  • +

    Install the Apache module for specific php version

    + +
    sudo apt install libapache2-mod-php7.3
    +
    +
  • +
  • +

    Copy the php.ini from previous version to newer version after making +a backup of the original php.ini for new version.

    + +
    sudo cp /etc/php/7.3/apache2/php.ini php.ini.original
    +sudo cp /etc/php/7.2/apache2/php.ini /etc/php/7.3/apache2/php.ini
    +
    +
  • +
  • +

    Install specific php modules for Apache and enable the php modules +on new version of php.

    + +
    sudo apt install php7.3-curl php7.3-gd php7.3-gmp php7.3-intl php7.3-mbstring php7.3-simplexml php7.3-soap php7.3-wddx php7.3-xmlreader php7.3-xmlrpc php7.3-xmlwriter php7.3-xsl php7.3-zip php7.3-xml php7.3-mysql
    +sudo phpenmod -v 7.3 pdo_mysql soap wddx xmlreader xmlrpc xsl zip intl gd dom curl mysqlnd gmp simplexml mysqli mbstring
    +
    +
  • +
  • +

    Disable old php version, enable new version and restart Apache +server.

    + +
    sudo a2dismod php7.2
    +sudo a2enmod php7.3
    +sudo systemctl restart apache2
    +
    +
  • +]]>
    Ankit Mittal
    Converting AAX (Audible) to mp32020-05-19T19:36:30+00:002020-05-21T11:48:26+00:00https://mgw.dumatics.com/converting-aax-audible-to-mp3Lately I have been a bit frustrated because while I subscribe to Audible services, Amazon and Google do not play nice with each other limiting me - the owner of books with rights to listen my purchase on any device - in my options on where I can listen. Now I am yet to find a good solution that can get my files playing on google home and not just Alexa, I guess first step was to free my audiobooks from Amazon jail. That as it turns out is rather simple to do.I must at this point mention OpenAudible which is one fine tool. However, their latest version (2.0.7 at the time of writing) added some limitations whereby you can get it to download the AAX files but not convert them to mp3 without purchasing their license. However,converting AAX to mp3 is accomplished using ffmpeg and I do not believe a nice GUI wrapper is what I need and hence I do not believe my money is well spent in getting a license just to achieve from a graphical user interface what I can achieve for free from the terminal with a single line.There are 3 steps in the conversion process:

    + +
      +
    1. +

      Get a copy of the AAX file you own from your audible account: This +can be done in one of two ways as listed below:

      + +

      a. Install OpenAudible and follow instructions to download the AAX +files.
      +b. Copy it from the filesystem of your Android phone.

      +
    2. +
    3. +

      Obtain the activation_bytes for your AAX file:

      + +

      a. If you installed OpenAudible, you can use the following steps:

      + +
      cd /opt/OpenAudible/bin/linux/
      +ffprobe /home/OpenAudible/aax/name_of_the_book.AAX 
      +
      + +

      b. This should give you a lot of output and a hash code. Let’s say +the hashcode you get is a6ger35cf22u03c9x16743j06f6df50a00b811c3 +aax_hash_code +c. Use this hashcode with following command to get the activation_bytes:

      +
         ./rcrack path /opt/OpenAudible/bin/tables/ -h a6ger35cf22u03c9x16743j06f6df50a00b811c3
      +
      +

      d. This should then output the activation_bytes in last line - something like hex:abc43opu as shown below: +activation_byte

      + +

      Above steps are required only once. Keep the activation_bytes code that you obtain above safe as it can be used for converting all your audiobooks.

      +
    4. +
    5. +

      Now use the following commands to convert AAX file to mp3:

      +
      #change directory to where the AAX files are saved. If using OpenAudible it will be in /home/<username>/OpenAudible/aax
      +cd /home/<username>/OpenAudible/aax
      +ffmpeg -activation_bytes xxxxxxxx -i 'name of the book.AAX' -map_metadata 0 -codec:a libmp3lame -qscale:a 6 /home/<username>/OpenAudible/mp3/name\ of\ the\ book.mp3
      +
      +

      Remember to change xxxxxxxx with the activation_bytes code you obtained in step 2 and the name of book as well as destination path and name of the book.

      +
    6. +
    + +

    Now ofcourse you can accomplish the first Step 2 above without OpenAudible. If you would want to do so, just follow the guidance on github.

    + +

    Hope this will be helpful for a few. In the meantime, I am yet to work out the best way to make google home to play my audiobooks when I so desire. If anyone has any tips they will be gratefully received.Have fun !!!

    ]]>
    Ankit Mittal
    Metabase - A BI solution that just works2018-12-05T14:35:02+00:002019-04-08T17:36:09+00:00https://mgw.dumatics.com/metabase-a-bi-solution-that-just-worksI like exploring new solutions and anything to do with data always piques my interest. I came across this nice tool through the list of free self hosted software.

    + +
    +

    UPDATE: +I am still getting introduced to the world of docker and while most of it just works few things confuse the hell out of me.

    + +

    Anyway what happened is there was an update for Metabase and I wanted to apply it on my container and in doing so I realised that some of this post can be changed to make it a better experience from get go because after update all my config and dashboards were gone and I had to reconfigure everything.

    + +

    It could be that my lack of knowledge meant I did something wrong and faced the issue but irrespective doing it as per updated post below will ensure you dont face the issue at all because I have now tested it twice to ensure it works well.

    +
    + +

    I have also added the steps to update the metabase to latest docker image as it worked for me.

    + +

    The overall impression when I opened their site can be summed up with WOW!! Now they offer simple docker install supported officially by them and the guide to install docker image is simple enough but the few +simple steps I followed are as below:

    + +
      +
    1. Create an A-record for a subdomain for metabase
    2. +
    3. Download the official metabse docker image from dockerhub
    4. +
    5. Create an apache server conf file for metabase
    6. +
    7. Run the metabase docker image
    8. +
    9. Access metabase docker image using subdomain URL
    10. +
    11. Configure your database on metabase
    12. +
    13. Run Certbot to make the URL https
    14. +
    15. Done!!
    16. +
    + +

    Create A-record for subdomain

    + +

    Using the process for your Domain Name Regsitrar create an A-Record for +a subdomain you would want to use for metabase. For this guide we will +assume that the subdomain being created is called “metabase” so if +your domain name is say yourdomain.com then the URL to access metabase +will be metabase .yourdomain .com.

    + +

    It is important that if you use a different subdomain name, please replace metabase with the subdomain name you have chosen in all Apache conf file samples in sections below.

    + +

    More detailed steps are available on previous post HERE

    + +

    Download and Create Apache Conf

    + +
    docker pull metabase/metabase
    +cd /etc/apache2/sites-available/
    +sudo nano metabase.conf
    +
    +

    Create a conf file similar to what is shown below:

    + +
    <VirtualHost *:80>
    +    ServerName metabase.yourdomain.com
    +    ServerAlias metabase.yourdomain.com
    +
    +    ErrorLog ${APACHE_LOG_DIR}/error_metabase.log
    +    CustomLog ${APACHE_LOG_DIR}/access_metabase.log combined
    +
    +    ProxyPreserveHost On
    +    ProxyPass / http://localhost:3000/
    +    ProxyPassReverse / http:/localhost:3000/
    +    ProxyPass "/ws2/"  "ws://localhost:3000/"
    +    ProxyPass "/wss2/" "wss://localhost:3000/"        
    + </VirtualHost>
    +
    +

    Save this by pressing Ctrl+X and issue following commands to enable site and reload the apache server.

    + +
     sudo a2ensite metabase.conf
    + sudo systemctl reload apache2
    +
    + +

    Run Metabase docker image

    +
    docker run -d -p 3000:3000 --name metabase metabase/metabase
    +
    +

    If all goes well, you should have metabase already running. If you get an error to the effect saying port 3000 is already in use, you will perhaps have to run it on another port (say 12345) but as metabase container would have been created you will need to issue following commands.

    + +
    docker rm metabase
    +docker run -d -p 12345:3000 --name metabase metabase/metabase
    +
    +

    In addition to avoid running the database through the container we must anyway move it to filesystem. Using commands below:

    +
    mkdir /var/www/metabase
    +
    +

    This command will create metabase directory on the path /var/www/metabase but you can as easily chose another path as you wish. If you do, just remember to update it in next commands.

    +
    docker ps
    +
    +

    This will show you the active docker containers. Copy the container ID in first column for the row where under Names you see “metabase” which is the container name we have given using previosu commands.Now copy the +database from container to local filesystem.

    +
    cd /var/www/metabase
    +docker cp <container ID>:/metabase.db ./
    +
    +

    Now Stop and rename the container

    +
    docker stop metabase
    +docker rename metabase metabase_old
    +
    +

    Finally run Metabase Docker Image while pointing it to your filesystem for database file.

    +
    docker run -d -p 12345:3000 -v ~/metabase-data:/var/www/metabase -e "MB_DB_FILE=/var/www/metabase/metabase.db" --name metabase metabase/metabase
    +
    +

    The above docker command can be explained as below:

    + +

    docker run -d - This is telling system to start a container in detached mode. By design, containers started in detached mode exit when the root process used to run the container exits.

    + +

    ` -p 12345:3000` - This is telling docker to make the container map port 12345 to port 3000 which is the default port on which metabase starts its server.

    + +

    -v ~/metabase-data:/var/www/metabase- This part is telling docker to map the “metabase-data” directory in the container to “/var/www/metabase” on the filesystem. In other words, mounting the local volume at path “/var/www/metabase” onto the container in “metabase-data” directory.

    + +

    --name metabase metabase/metabase- Finally this part of command is telling docker to name the container “metabase” and to use the docker image “metabase/metabase” from dockerhub

    + +

    Do bear in mind if you change the port as explained above, you will also need to change the port in apache conf file in previous step. So your apache conf file in this case will look as shown below:

    +
        <VirtualHost *:80>
    +        ServerName metabase.yourdomain.com
    +        ServerAlias metabase.yourdomain.com
    +    
    +        ErrorLog ${APACHE_LOG_DIR}/error_metabase.log
    +        CustomLog ${APACHE_LOG_DIR}/access_metabase.log combined
    +    
    +        ProxyPreserveHost On
    +        ProxyPass / http://localhost:12345/
    +        ProxyPassReverse / http:/localhost:12345/
    +        ProxyPass "/ws2/"  "ws://localhost:12345/"
    +        ProxyPass "/wss2/" "wss://localhost:12345/"
    +        
    +    </VirtualHost>
    +
    +

    Access and configure on browser

    + +

    Access your metabase using the subdomain url - http:// metabase.yourdomain .com and it should walk you through the initial configuration. If you do not want to add a database and just play with the tool, they have kindly provided a sample database.

    + +

    It might be useful for you to create a “user” in your database that has “read only” access and then use that “user” when you configure metabase. Metabase will only ever need read access as it cannot be used to modify data in your database.

    + +

    Enable SSL

    + +

    Once you are happy with your configuration from step above, just run the certbot to enable https using letsencrypt certificate. On Debian this could be as simple as issuing the command

    +
    sudo certbot
    +
    +

    Once above step is created you will have an additional conf file named metabase-le-ssl.conf created in the location /etc/apache2/sites-available. Check this and once it is there you are all set to access your new functional metabase instance on https. More detailed steps are available on previous post HERE

    + +

    Update version

    + +

    Updating on docker image is pretty straight forward and provided you have followed steps above should not result in you loosing all the effort once update has completed.

    + +
    #docker rm metabase_old
    +
    +docker stop metabase
    +
    +#docker rename metabase metabase_old
    +
    +docker pull metabase/metabase
    +
    +docker start metabase
    +
    +

    From my recent messing around, I have come to realise that so long as the first start of the docker image was done using environment variables pointing to correct database directory, there is no need to run the commented lines above and a simple stop, upgrade and start should work just fine. Enjoy!!!

    ]]>
    Ankit Mittal
    Prosody behind Apache on Debian Stretch with Conversations2018-06-15T12:36:19+00:002020-05-21T11:56:42+00:00https://mgw.dumatics.com/prosody-behind-apache-on-debian-stretchGenreal Guidance + +

    This how-to assumes that:

    + +
      +
    • User has access to a registered domain - domain.name. Any reference to domain.name throughout the tutorial therefore, must be replaced with your own domain name.
    • +
    • If stuck anywhere, please do get in touch with prosody support chat - They are a very helpful lot and are very kind too.
    • +
    + +

    Network Setup:

    + +

    It is not mandatory to follow the flow of this how-to but if you do, please do it till the end before you try and access the IM server from any client to save yourself some pain.

    + +

    Obtain the IP address

    + +

    .… of the machine where your XMPP server will be installed using following command on the terminal ip -a address On the result shown, IP address against inet family is the one we are interested in as can be seen in screenshot below: +PUTTY_2018-06-15_12-49-42

    + +

    Copy this ip address, we will need it in next step.

    + +

    Port Forwarding on router if hosting from home.

    + +

    You have to ensure that following ports are opened and directed to above IP address from your router. 5222, 5269, 5280, 5281 On DD-WRT this can be done as shown in screenshot below. This will, ofcourse, vary based on router you are using. +chrome_2018-06-15_11-34-57 +Use the IP address from previous step to fill the IP address field. Basically this set-up is informing your router that if a request comes to you asking for one of the above mentioned ports direct that request to this IP address on the same port.

    + +

    Create a sub-domain record

    + +
    +
    Please note that the official Prosody Documentation recommends using Server Record and even the experts on their chat support strongly recommend that approach.
    +
    +I did not have that option unfortunately - read I was being lazy / adventurous - anyway the C-Name set-up has worked fine for me and hence the guide follows this route.
    +
    +You have been warned !!!
    +
    +
    + +

    This step will vary based on the domain name registrar you use. I use Namecheap for which you could do it as shown in screenshot below: +chrome_2018-06-15_11-43-31

    + +

    Ensure you have some sort of Dynamic DNS update set-up in place

    + +

    You can create an account here -> DNSOMATIC If you are going to use DNSOMATIC which allows you to update several places like so: +chrome_2018-06-15_11-53-35 +For namecheap in order to get the value for password, login to Namecheap dashboard, go to Advanced DNS and scroll down to "Dynamic DNS" section and copy the value in front of the field named "Dynamic DNS Password” as shown below: +chrome_2018-06-15_17-02-32.png +The setting to update DNSOMATIC on DD-WRT are as shown below: +DYDNS Server:updates.dnsomatic.com +Hostname: all.dnsomatic.com +URL: https://updates.dnsomatic.com/nic/update?hostname= +chrome_2018-06-15_11-48-32 +For all other Dynamic DNS set-ups ensure that your subdomain will always get updated with latest IP address to ensure continuity of service.

    + +

    Server Setup:

    + +

    Normally the XMPP clients on phone directly access port 5222 or 5269 for MUC (Multi User Chats aka Group Chats) but to make sure image uploads work and for being able to access IM from web we need to enable reverse proxy using Apache server. As this is fairly straight forward, lets get that out of the way.

    + +

    Apache with reverse Proxy enabled

    + +

    Make directory for subdomain:

    + +

    sudo mkdir /var/www/html/im

    + +

    Then create a server conf file and open for editing in nano using following command:\

    + +

    sudo nano /etc/apache2/sites-available/prosody.conf

    + +

    Paste the following in conf file:

    +
    <VirtualHost *:80>
    +    ServerName im.domain.name
    +    ServerAlias im.domain.name
    +    DocumentRoot /var/www/html/im
    +
    +    ErrorLog ${APACHE_LOG_DIR}/error_im.log
    +    CustomLog ${APACHE_LOG_DIR}/access_im.log combined
    +
    +    ProxyPass /http-bind/ http://im.domain.name:5280/http-bind/
    +    ProxyPassReverse /http-bind/ http://im.domain.name:5280/http-bind/
    +
    +
    +RewriteEngine on
    +RewriteCond %{SERVER_NAME} =im.domain.name
    +RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
    +</VirtualHost>
    +
    +

    Save the file with Ctrl+x, y and Enter. Finally enable site and reload server.

    +
        sudo a2ensite prosody.conf
    +    sudo systemctl reload apache2
    +
    +

    Enable SSL set-up using Let’s Encrypt {#enablesslsetupusingletsencrypt} +————————————–

    + +

    It is assumed at this point that certbot is already installed but if not, follow the tutorial here On terminal give the command sudo certbot +PUTTY_2018-06-15_12-08-21 +Then follow the wizard and once completed check that an additional conf file named prosody-le-ssl.conf is created. If not something did not go well so check again and repeat but if yes move to next step.

    + +

    Ensure certbot is renewing automatically

    + +

    Debian stretch gets the service certbot.timer already on it which when enabled will try to autorenew twice a day ensuring a continuous certificate availability. To locate and edit the certbot.timerfollowing commands are helpful:
    +PUTTY_2018-06-15_12-19-30

    +
    locate certbot.timer
    +nano /etc/systemd/system/timers.target.wants/certbot.timer
    +
    +

    The certbot.timer entry on my system is as below: +PUTTY_2018-06-15_17-23-18

    + +

    Once edited, make sure it is enabled using following commands:

    +
    sudo systemctl enable certbot.timer
    +sudo systemctl start certbot.timer
    +
    + +

    Prosody XMPP server

    +

    Assumption - Apache 2.4 web server is already installed and functioning.

    + +

    Install

    + +

    Option 1 (Not Recommended):

    + +

    sudo nano /etc/apt/sources.list +Then add this -> deb http://packages.prosody.im/debian stretch mainon the last line.

    + +

    Option 2 (Recommended):

    + +
    echo deb http://packages.prosody.im/debian \$\(lsb\_release -sc) main | sudo tee -a /etc/apt/sources.list
    +
    + +

    Above command is recommended because it takes the release of your installed version and adds it directly rather than the hard coded “stretch” in Option 1 which must be changes specific to the version of debian. Now add the key, update repository, upgrade for good measure and then install using the following commands:

    + +
        wget https://prosody.im/files/prosody-debian-packages.key -O- | sudo apt-key add
    +    sudo apt-get update
    +    sudo apt-get upgrade
    +    sudo apt-get install prosody
    +
    +

    Install plugins / modules

    + +

    Latest updated documentation is ofcourse on project website but at the time of writing these are the instructions that were followed:

    +
    cd /usr/lib/prosody/
    +sudo hg clone https://hg.prosody.im/prosody-modules/ prosody-modules
    +
    +

    To update:

    +
    cd /usr/lib/prosody/prosody-modules/
    +sudo hg pull --update
    +
    + +

    Import SSL using certbot

    + +

    Issue following commands to get the SSL activated for Prosody:

    + +
    sudo certbot renew --deploy-hook "prosodyctl --root cert import /etc/letsencrypt/live"
    +sudo prosodyctl --root cert import /etc/letsencrypt/live
    +sudo prosodyctl cert generate localhost 
    +
    + +

    Configure

    + +

    The configuration file for prosody is very well documented and only needs minor tweaks. However, it is lot of commenting and to keep this readable, I have put my configuration here that can either be directly pasted or can be compared to update the default conf file. Either way, first you would need to open the config file:

    + +

    sudo nano /etc/prosody/prosody.cfg.lua

    + +

    Now the working config file for me is as shown below and can be compared to amend or can be directly copy pasted.

    + +

    Bear in mind that all references to domain.name must be replaced with your registered domain name.

    + +
    admins = {"admin1@im.domain.name"}
    +
    +plugin_paths = {"/usr/lib/prosody/prosody-modules" }
    +consider_bosh_secure = true
    +cross_domain_bosh = true
    +modules_enabled = {
    +
    +        -- Generally required
    +                "roster"; -- Allow users to have a roster. Recommended ;)
    +                "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
    +                "tls"; -- Add support for secure TLS on c2s/s2s connections
    +                "dialback"; -- s2s dialback support
    +                "disco"; -- Service discovery
    +
    +        -- Not essential, but recommended
    +                "carbons"; -- Keep multiple clients in sync
    +                "pep"; -- Enables users to publish the1ir mood, activity, playing music and more
    +                "omemo_all_access"; -- xep-0060 for enabling omemo access to non subscribers
    +                "private"; -- Private XML storage (for room bookmarks, etc.)
    +                "blocklist"; -- Allow users to block communications with other users
    +                "vcard"; -- Allow users to set vCards
    +
    +        -- Nice to have
    +                "version"; -- Replies to server version requests
    +                "uptime"; -- Report how long server has been running
    +                "time"; -- Let others know the time here on this server
    +                "ping"; -- Replies to XMPP pings with pongs
    +                "register"; -- Allow users to register on this server using a client and change passwords
    +                "mam"; -- Store messages in an archive and allow users to access it
    +                --"mam_muc"; -- store group chat messages
    +
    +        -- Admin interfaces
    +                "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
    +                --"admin_telnet"; -- Opens telnet console interface on localhost port 5582
    +
    +        -- HTTP modules
    +                "bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
    +                "websocket"; -- XMPP over WebSockets
    +                "http_files"; -- Serve static files from a directory over HTTP
    +                "http_upload"; -- module to enable file upload in group chat
    +
    +        -- Other specific functionality
    +                "groups"; -- Shared roster support
    +                "proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
    +                "smacks";
    +                "csi";
    +                "cloud_notify";
    +                "conversejs";
    +}
    +
    +modules_disabled = {
    +        -- "offline"; -- Store offline messages
    +        -- "c2s"; -- Handle client connections
    +        -- "s2s"; -- Handle server-to-server connections
    +        -- "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
    +}
    +
    +allow_registration = false
    +
    +c2s_require_encryption = false
    +
    +s2s_require_encryption = true
    +
    +s2s_secure_auth = true
    +
    +s2s_secure_domains = { "domain.name" }
    +
    +pidfile = "/var/run/prosody/prosody.pid"
    +
    +authentication = "internal_hashed"
    +
    +archive_expires_after = "1w" -- Remove archived messages after 1 week
    +
    +log = {
    +        info = "/var/log/prosody/prosody.log"; -- Change 'info' to 'debug' for verbose logging
    +        error = "/var/log/prosody/prosody.err";
    +        -- "*syslog"; -- Uncomment this for logging to syslog
    +        -- "*console"; -- Log to the console, useful for debugging with daemonize=false
    +}
    +
    +certificates = "certs"
    +
    +----------- Virtual hosts -----------
    +-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
    +-- Settings under each VirtualHost entry apply *only* to that host.
    +
    +VirtualHost "localhost"
    +
    +VirtualHost "im.domain.name"
    +
    +------ Components ------
    +---Set up a MUC (multi-user chat) room server on conference.example.com:
    +   Component "conference.im.domain.name" "muc"
    +     restrict_room_creation = "local"
    +
    + +

    Once completed, save it and proceed to next steps.

    + +
    sudo service prosody start
    +sudo service prosody stop
    +sudo service prosody start -v
    +sudo service prosody stop
    +
    +## Enable prosody as a service so it restarts each time after system reboot
    +
    +sudo systemctl is-enabled prosody.service
    +sudo service prosody restart
    +
    +

    Add User

    + +

    Use prosodyctl command to add user like so:

    + +

    sudo prosodyctl adduser admin1@im.domain.name

    + +

    and then provide password for the user. Ofcourse you must add as many users as you need and bear in mind the conf file above has registration switched-off so the users must be manually added using this step. Check the manpage using man prosodyctl to see how to update password for an existing user and to delete a user.

    + +

    Clients Setup:

    + +

    Conversations on Android

    + +

    Conversations is the best client for XMPP on Android and is available on playstore for a small amount or it can be installed from f-droid for free. The free version has some limitations with regards to push but I have been using it with no issues what-so-ever.

    + +

    OpenPGP using OpenKeyChain

    + +

    Install the OpenKeyChain app, create the key pair and trigger Conversations to start from OpenKeyChain. The other person must also have OpenPGP Key pair and you should have the public key of other person added on openkeychain instaled on your phone. Do bear in mind Conversations developer says it is an experimental feature. Although it did work perfectly fine for me while I tested with about 4 or 5 users.

    + +

    Conversejs.org on web

    + +

    Go to the web root we defined initially and create an index.html:

    + +

    sudo nano /var/www/html/im/index.html

    + +

    Then add following html code in there:

    + +

    Bear in mind that all references to domain.name must be replaced with your registered domain name.

    +
        <!doctype html>
    +    <html class="no-js" lang="en">
    +    <head>
    +        <meta charset="utf-8"/>
    +        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    +        <title>Converse</title>
    +        <link rel="shortcut icon" type="image/ico" href="css/images/favicon.ico"/>
    +        <link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/3.3.4/css/converse.min.css" />
    +        <link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/3.3.4/css/mobile.min.css" />
    +        <script src="https://cdn.conversejs.org/3.3.4/dist/converse.min.js"></script>
    +    </head>
    +    <body class="reset">
    +        <div class="content">
    +            <div class="inner-content">
    +                <h1 class="brand-heading"><i class="icon-conversejs"></i> Converse</h1>
    +            </div>
    +        </div>
    +    <script>
    +        converse.initialize({
    +            authentication: 'login',
    +            auto_away: 300,
    +            auto_reconnect: true,
    +            bosh_service_url: 'https://im.domain.name/http-bind/',
    +            show_controlbox_by_default: true,
    +            message_archiving: 'always',
    +            allow_logout: true,
    +            allow_dragresize: true
    +        });
    +    </script>
    +    </body>
    +    </html>
    +
    +

    Save the file with Ctrl+x, y and Enter. Once done restart the webserver as well as prosody for good measure:

    +
    sudo systemctl restart apache2.service
    +sudo prosodyctl restart
    +
    + +

    ChatSecure on iOS

    + +

    Now I do not have an iphone but a few of my friends do. They were able to use ChatSecure which is also an opensource app. Unfortunately ChatSecure is unable to get the images on group chat from Conversations but I am hoping that developer of ChatSecure will fix this issue sooner rather than later.

    + +

    Desktop Client

    + +

    For desktop I found Gajim to be most polished and it can be downloaded for your OS from here All works great on it except for the http_upload which seems to fail for transferring images with an error “Unsecured”. I am not sure what the issue is a search results did not help much - I left it because I dont really need this feature from desktop. However if someone has any ideas on how to fix this, please do leave it in the comments. The plugins on Gajim that I have applied and activated can be seen below: +Gajim_2018-06-15_17-10-11.png

    + +

    Maintenance

    + +

    The service does generate a lot of logs and it will serve you well to delete generated logs daily using Cronjob like so

    +
    sudo crontab -e
    +
    +

    Then add the following on the cronjob:

    +
    ############## DELETE PROSODY LOG FILE ARCHIVES ###########################
    +MAILTO="your@email.com"
    +0 0 * * 0-6 rm  /var/log/prosody/*.gz
    +
    ]]>
    Ankit Mittal
    \ No newline at end of file diff --git a/fix-for-php-issues-after-upgrade-to-ubuntu-16-04-1-xenial/index.html b/fix-for-php-issues-after-upgrade-to-ubuntu-16-04-1-xenial/index.html new file mode 100644 index 0000000..171e19e --- /dev/null +++ b/fix-for-php-issues-after-upgrade-to-ubuntu-16-04-1-xenial/index.html @@ -0,0 +1,709 @@ + + + + + + +Fix for PHP Issues after upgrade to Ubuntu 16.04.1 (Xenial) - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Fix for PHP Issues after upgrade to Ubuntu 16.04.1 (Xenial) +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +
    + + +
    + +

    After updating from Ubuntu 14.04, the php and Apache stopped being friends and one of the WordPress site I maintain went all white and admin page was just showing php code. +This is apparently because of a known issue in 16.04 with upgrade to php7 as shown on the ubuntu forum here.

    + +

    Using the guidance from this link and with some more of duckduckgo search later, I managed to resolve the problem thus:

    + +
    #1. Install aptitude if it is not already installed using 
    +sudo apt-get install aptitude
    +
    +#2. Removed php7 and unwanted php using
    +sudo aptitude purge `dpkg -l | grep php| awk '{print $2}' |tr &quot;\n&quot; &quot; &quot;`
    +
    +#3. Added old repo using
    +sudo add-apt-repository ppa:ondrej/php
    +
    +#4. Updated repo
    +sudo apt-get update
    +
    +#5. Installed php5.6
    +sudo apt-get install php5.6
    +sudo apt-get install php5.6-mbstring php5.6-mcrypt php5.6-mysql php5.6-xml php5.6-curl php5.6-gd php5.6-zip
    +
    +#6. Checked php version
    +sudo php -v
    +
    +#7. Enabled mod_php
    +sudo a2enmod php5
    +Ignored error message
    +
    +#8. Opened php5.6 conf
    +sudo nano /etc/apache2/mods-enabled/php5.6.conf
    +
    +#9. Commented following lines
    +<IfModule mod_userdir.c>
    +   <Directory /home/*/public_html>
    +      php_admin_flag engine Off
    +    </Directory>
    +</IfModule>
    +
    +#10. Restarted the server
    +sudo service apache2 restart
    +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flash-samsung-galaxy-s-gt-i9000-with-cyanogenmod-9-on-linux/index.html b/flash-samsung-galaxy-s-gt-i9000-with-cyanogenmod-9-on-linux/index.html new file mode 100644 index 0000000..d019fd2 --- /dev/null +++ b/flash-samsung-galaxy-s-gt-i9000-with-cyanogenmod-9-on-linux/index.html @@ -0,0 +1,709 @@ + + + + + + +Flash Samsung Galaxy S (GT -I9000) with Cyanogenmod 9 on Linux - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Flash Samsung Galaxy S (GT -I9000) with Cyanogenmod 9 on Linux +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +
    + + +
    + +

    I had an old Samsung Galaxy S which was still on stock ROM hence it only ever got to gingerbread and then Samsung just decided not to upgrade and I upgraded the phone so this little gadget was till yesterday destined to live with the old gingerbread. +Then yesterday, I just decided to play around with it and started reading so I know what are my options. Now wiki guide on Cyanogenmod site is quite nicely written but there were one or two steps here and there which had me confused for a little while so here is my usual step-by-step guide on how to go about it. +UPDATE: Uploaded all the files to mediafire as requested in one of the comments below. These can be downloaded from following link: +http://www.mediafire.com/?ims1bxp6b8yp8 +Step 1: +a) Download Heimdall Suite 1.3.2 Command-line Binary for your OS from here ; for Linux Mint you can use the Ubuntu link and install the downloaded "heimdall_1.3.2_i386.deb" file. +b) Download hardcore’s Kernel with the ClockworkMod Recovery 2.5 here. This will download a file named "hardcore-speedmod.tar". I am assuming that it will be saved in "Downloads" directory but if you have a different location, please replace "Downloads" with appropriate directory. +Step 2: Just to avoid any confusion, make a new directory in Downloads and name it "Galaxy_S". +Step 3: Copy the downloaded file (from 1-b) "hardcore-speedmod.tar" into the new directory "Galaxy_S" +Step 4: Now right-click on "hardcore-speedmod.tar" and select extract here as shown. This will extract the file zImage into the directory Galaxy_S. +Fig-1 +Step 5: Connect microUSB cable to your computer but not the phone. +Step 6: Power off the Samsung Galaxy S. +Step 7: Connect the microUSB cable to Samsung Galaxy S. +Step 8: Boot the phone in download mode by holding "HOME+Volume Down+POWER" buttons. +Step 9: Open the terminal and type following commands:

    +
    cd Downloads/Galaxy_S
    +sudo heimdall flash --kernel zImage
    +

    A blue transfer bar will appear on the phone showing the kernel being transferred. Once completed, the device will reboot automatically. +Step 10: Disconnect the phone from microUSB cable, switch it on and connect to your computer using the microUSB cable as mass storage.

    +
    You may need to go to phone settings and change USB connection settings to be able to connect the phone as mass storage.
    +

    Step 11: Download the latest Cyanogenmod ROM from here. +Fig-2 +Step 12: Follow this link to land at above page and then download the latest version of Google Apps. +Fig-3 +Step 13: You will now have following two zip files in your "Downloads" directory:

    +
              a) cm-9.1.0-galaxysmtd.zip from Step 11.
    +          b) gapps-ics-20120317-signed.zip from Step 12
    +
    +

    Copy these two files into the root directory of the Samsung Galaxy S. +Step 14: Now, disconnect the phone from microUSB and switch it off. +Step 15: Boot the phone in Recovery mode by holding "HOME+Volume Up+POWER" buttons. You will be presented with various recovery options such as reboot phone etc. +Step 17: Now use the Volume Up and Volume Down buttons to navigate options. Use Volume Down button to reach the option to Wipe data/factory reset and press POWER button to select this option. +Step 18: Once done, Use Volume Down button to reach the option to wipe cache partition and press POWER button to select this option. +Step 19: Next select "Install zip from sdcard" which will present another set of options where you should select "Choose zip from sdcard" +Step 20: Now you will see the list of files on your SD Cards root directory. Select the file "cm-9.1.0-galaxysmtd.zip" and on following screen of options select "Yes". +Step 21: Once installed, select +++++Go Back+++++ and again select "Install zip from sdcard" which will present another set of options where you should select "Choose zip from sdcard" +Step 22: This time select the file "gapps-ics-20120317-signed.zip" and on following screen of options select "Yes". +Step 23: Once installed, select +++++Go Back+++++ to return to main menu. +Step 24: On main menu select "Reboot System now" option. +Step 25: If all has gone as above, your phone will restart and after a while you will see cyanogenmod flash screen. This screen will be there for a good 1 to 2 minutes. Don’t panic and dont mess. Let system do it’s work and in some time you would have given a fresh lease of life to your old dieing Samsung Galaxy S.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/get-gmail-as-push-email-on-sony-p990i/index.html b/get-gmail-as-push-email-on-sony-p990i/index.html new file mode 100644 index 0000000..6744bdd --- /dev/null +++ b/get-gmail-as-push-email-on-sony-p990i/index.html @@ -0,0 +1,765 @@ + + + + + + +Get Gmail as Push Email on Sony P990i - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Get Gmail as Push Email on Sony P990i +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +
    + + +
    + +

    While I have moved on from P990i which is now in safe hands of my dear wife, I remember doing some good amount of web searching and still had no idea how to set up push-mail on P990i. +I then thought I will configure a mail anyway and to my surprise the device is capable of enabling push-mail on it’s own as I found while fiddling along. Anyway I have configured push-mail for wifey dear and she was mighty impressed…:-) +The steps I followed are as below:

    +
      +
    • +Main Menu -> Tools -> Control Panel -> Messaging -> Email Accounts -> New +
    • +
    • +Fill Account Name as Gmail +
    • +
    • +Your Name as ..well... your name..:) +
    • +
    • +Email Address - Your gmail address +
    • +
    • +In Connection Type select IMAP from the drop down list. +
    • +
    • +Tick on Push email +
    • +
    • +Now goto "Inbox" Tab. +
    • +
    • +Type imap.gmail.com under "Incoming Server Address" +
    • +
    • +Type your email address "xyz@gmail.com" under "Username" +
    • +
    • +Enter your "password" under "Password" +
    • +
    • +Depending on you data plan you might want to put something else but I have unlimited internet plan so I have selected "No Restriction" under "Download Restrictions" +
    • +
    • +Limit number of Emails - I have selected 100, you can chose as per your needs. +
    • +
    • +Receive using Group - Select the group of internet account that has your phone provider configured. (I am assuming that you have already configured internet settings on your phone. If not that is a separate topic but you can easily find information on user manual so at the moment let's consider it as out of scope for this entry.) +
    • +
    • +Now goto "Outbox" Tab. +
    • +
    • +Type "smtp.gmail.com" under "Outgoing Server Address" +
    • +
    • +Tick the checkbox "Use SMTP Authentication" +
    • +
    • +Tick the checkbox "Use Inbox Login details" +
    • +
    • +Select the same internet group as above under "Send using Group" +
    • +
    • +Now click on the arrow next to "Email account" on top of the screen and click on "Advanced" from the dropdown list +
    • +
    • +Under "Incoming" tab Select "SSL" under the field "Secure connection" +
    • +
    • +Type "993" under field "Incoming mail port" +
    • +
    • +Next in "Outgoing" tab under "Secure Connection" filed select "TLS" +
    • +
    • +Type "587" under field "Outgoing mail port" +
    • +
    • +Tick the checkbox "Use MIME encoding" +
    • +
    • +Click on "Save" +
    • +
    • +Click on "Save" again +
    • +
    • +Now the screen will show Gmail listed as your email account. +
    • +
    • +On this screen again click on the arrow next to Email account on top of the screen and select "Always on Pushmail". +
    • +
    • +Tick the checkbox next to "Always On" +
    • +
    • +Select the timings and select the phone providers datasetting name under "Internet Account" and click "Save". +
    • +
    +

    This is it. You are now all set to receive your mails on the go… +Have fun !!!

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ghost-on-fedora-24/index.html b/ghost-on-fedora-24/index.html new file mode 100644 index 0000000..a0359f9 --- /dev/null +++ b/ghost-on-fedora-24/index.html @@ -0,0 +1,826 @@ + + + + + + +Ghost on Fedora 24 - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Ghost on Fedora 24 +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +
    + + +
    + +

    To install Ghost as my blogging platform, I had to go through a number of hoops and one of them was to get the nodejs working and what not. I figured this might as well be worth documenting in case I have to do this all over again. It might also be helpful for some other inquisitive minds. :) +The most useful reference I found was the post on rosehosting website specific to CentOS 7[1] +It would have all gone well too; had it not been for the nodejs related issues which resulted in me finding the other helpful pointers from various forums. +Anyway, the steps I took to get this all working are:

    + +

    These are detailed in my notes below - keeping it, where I can, true to the post I have referred above:

    +

    Step 1: Install nodejs and npm

    +

    On Fedora 24 node.js package already includes npm and if you try installing npm separately it will throw an error so just install node.js and npm will be installed along with it.

    +
    sudo dnf distro-sync
    +sudo dnf install nodejs
    +

    Step 2: Install dependencies

    +
    sudo dnf install php php-fpm php-cli php-mysql php-curl php-gd
    +
    +#Create a directory for the website:
    +mkdir /var/www/html/[sitefolder. eg: blog, myblog, banana] 
    +
    +#Change to the newly created directory:
    +cd /var/www/html/[sitefolder. eg: blog, myblog, banana] 
    +
    +#Set access permissions for this directory
    +chown -R /var/www/html/[sitefolder. eg: blog, myblog, banana]
    +
    +#Download latest version of Ghost:
    +curl -L https://ghost.org/zip/ghost-latest.zip -o ghost.zip
    +
    +#Unzip the downloaded file
    +unzip ghost.zip
    +
    +#Finally check the directory structure
    +tree -L 2
    +
    +#OUTPUT of above command should look like as shown below:
    +.
    +├── config.example.js
    +├── config.js
    +├── content
    +│   ├── apps
    +│   ├── data
    +│   ├── images
    +│   └── themes
    +├── core
    +│   ├── built
    +│   ├── index.js
    +│   ├── server
    +│   └── shared
    +├── ghost.zip
    +├── Gruntfile.js
    +├── index.js
    +├── LICENSE
    +├── npm-shrinkwrap.json
    +├── package.json
    +├── PRIVACY.md
    +└── README.md
    +

    Step 3: Install npm modules

    +

    While installing/initiating npm modules, there were several errors that system was throwing. They were in two categories:

    +
      +
    • Access Related
    • +
    • Dependencies Related
    • +
    +

    Access Related - I was getting EACCES error and solution given on on npmjs.com under Option 2 is what sorted the access issues [1:1].

    +
    mkdir ~/.npm-global
    +npm config set prefix '~/.npm-global'
    +nano ~/.profile
    +

    Add the line export PATH=~/.npm-global/bin:$PATH in the opened file. +<pre class=”line-numbers language-bash”style=”counter-reset: linenumber 3;”>source ~/.profile</pre> +Dependencies Related - Some forum hopping later I just followed the advice on Ghost support[1:2] and installed the dependencies. Steps below:

    +
    #install required dependencies:
    +npm install -g node-gyp
    +sudo dnf install gcc gcc-c++
    +

    Once above dependencies are installed following code should just work.

    +
    NOTE: +Make sure you are in the directory you created in step2.
    +
    #Install PM2 a process manager to control Node.js applications 
    +#It will help in keeping specified Node.js applications alive forever:
    +npm install pm2 -g
    +#Install 
    +npm install --production
    +#Start Ghost with pm2 and create a name for the pm2
    +NODE_ENV=production pm2 start index.js --name "Ghost"
    +
    +

    Step 4: Tell Ghost your blog URL

    +

    A very simple change is required to config.js file as shown below:

    +
    #Copy the sample config file
    +cp config.example.js config.js
    +nano config.js
    +

    File that opens will have following javascript:

    +
    // # Ghost Configuration
    +// Setup your Ghost install for various [environments](http://support.ghost.org$
    +
    +// Ghost runs in `development` mode by default. Full documentation can be found$
    +
    +var path = require('path'),
    +    config;
    +
    +config = {
    +    // ### Production
    +    // When running Ghost in the wild, use the production environment.
    +    // Configure your URL and mail settings here
    +    production: {
    +        url: 'http://your.blog.com',
    +
    +

    It’s the line number 14 in above code block where you need to replace http://your.blog.com with actual url of your blog.

    +

    Step 4: Configure Nginx

    +

    NGINX install and configuration is something I covered in my post for installing Seafile on Fedora 24[1:3]. So I already had a running nginx. I just needed to create a reverse proxy for Ghost on the existing nginx server. +Open the hostfile using following command:

    +
    sudo nano /etc/hosts
    +

    Now in the hosts file add the localhost alias for blog - in this example it is your.localhost.com.

    +
    127.0.0.1  localhost.localdomain localhost your.seafile.com your.blog.com
    +

    Open the file using following command.

    +
    NOTE: +Replace `yourblog.conf` with your actual blog's conf file name.
    +
    nano /etc/nginx/conf.d/yourblog.conf
    +

    On the file that opens copy and paste the following code.

    +
    NOTE: +Replace `your.blog.com` on line number 6 below with alias for localhost for this blog you added to the host file above.
    +
    upstream ghost {
    +server 127.0.0.1:2368;
    +}
    +server {
    +     listen      80;
    +     server_name your.blog.com;
    +     access_log  /var/log/nginx/ghost.access.log;
    +     error_log   /var/log/nginx/ghost.error.log;
    +     proxy_buffers 16 64k;
    +     proxy_buffer_size 128k;
    +
    +        location / {
    +         proxy_pass  http://ghost;
    +         proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
    +         proxy_redirect off;
    +         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;
    +     }
    +  }
    +
    +

    Step 5: Start Ghost and nginx

    +

    After all the above steps are completed issue following commands to restart ghost and nginx.

    +
    sudo systemctl restart nginx.service
    +pm2 restart Ghost
    +

    All Done !!!

    +

    References:

    +
    + +
    +
      +
    1. +GHOST_URL/seafile-server-behind-nginx-on-fedora-24-security-lab-spin/ ↩︎ ↩︎ ↩︎ ↩︎ +
    2. +
    +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ghost-upgrade-errors-and-fixes-1-19-x/index.html b/ghost-upgrade-errors-and-fixes-1-19-x/index.html new file mode 100644 index 0000000..2bfe169 --- /dev/null +++ b/ghost-upgrade-errors-and-fixes-1-19-x/index.html @@ -0,0 +1,729 @@ + + + + + + +Ghost Upgrade errors and fixes (1.19.x) - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Ghost Upgrade errors and fixes (1.19.x) +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    I have found the recent ghost upgrades quite painless but there have been few hiccups for last two times so I kept a record of what helped and it is as listed below: +General Update should be as simple as:

    +
    cd /var/www/html/ghost/
    +ghost update
    +
    +

    If after this step, there are any errors or an indication to update ghost-cli, following command should be used.

    +
    sudo npm install -g ghost-cli
    +
    +

    After this if there are issues accessing the blog over internet, we may need to do a bit of checks. Logical sequence is to first check that access for all folders is right and proper. If it needs to be updated, command to be used is:

    +
    cd ghost
    +sudo chown -R &lt;your username&gt;:www-data .
    +
    +cd content/
    +sudo chown -R ghost:ghost images/
    +
    +

    If there are any errors when starting ghost, following command is indicated by the ghost-cli and it does help.

    +
    ghost setup linux-user systemd
    +
    +

    If still accessing the blog is an issue following command will list the ghost log file:
    +ghost log +If there are database migration errors on log file, following commands may help:

    +
    sudo npm install -g knex-migrator
    +ghost setup migrate
    +
    +

    ghost setup migrate does work very well and must be remembered.

    + +

    If you have digitalocean setup such as mine, memory can be an issue you may want to restart the virtual machine and reload the swap files.

    +
    sudo reboot now
    +sudo swapon /swapfile1
    +sudo swapon /swapfile2
    +
    + +

    If update if failing with the message not enough memory try running the following:

    +
    ghost update --no-check-mem
    +
    + +

    If there are still issues accessing the blog with error 503, check the apache logs:

    +
    sudo nano /var/log/apache2/error.log
    +
    + +

    If issue is with accessing the upstream ghost server. Try changing the port on ghost config and updating the apache conf files.

    +
    ##Change Directory to ghost install
    +cd /var/www/html/ghost
    +##Stop the ghost server
    +ghost stop
    +##Change port to another number
    +nano config.production.json
    +##Change directory apache2 server
    +cd /etc/apache2/sites-available/
    +##Open the ghost.conf file and change the localhost port to same number that was changed in config.production.json
    +sudo nano ghost.conf
    +##Open the ghost-le-ssl.conf file and change the localhost port to same number that was changed in config.production.json
    +sudo nano ghost-le-ssl.conf
    +##Disable and enable the conf files on apache.
    +sudo a2dissite ghost.conf ghost-le-ssl.conf
    +sudo a2ensite ghost-le-ssl.conf ghost.conf 
    +sudo service apache2 reload 
    +##Finally change directory to your ghost install and start ghost server.
    +cd /var/www/html/ghost
    +ghost start
    +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ghost-v1-0-upgrade-related-quirks-and-fixes/index.html b/ghost-v1-0-upgrade-related-quirks-and-fixes/index.html new file mode 100644 index 0000000..6f6e54e --- /dev/null +++ b/ghost-v1-0-upgrade-related-quirks-and-fixes/index.html @@ -0,0 +1,824 @@ + + + + + + +Ghost V1.0 Upgrade on Apache stack, related quirks and fixes - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Ghost V1.0 Upgrade on Apache stack, related quirks and fixes +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +
    + + +
    + +

    Right then, the Ghost V1.0 was out a while back and they made Ghost 0.11.x an LTS so I was not in any rush to upgrade too. I have not had much time to sort this out for a while and two days back when I finally came around to check how to upgrade, my first moment of concern was that officially supported stack is for NGINX.

    + +

    I have moved my blog to the Apache Stack on DigitalOcean and while on my sandbox environment I still have NGINX, that is not a place I want to host my blog from. Anyhoo, I realised soon enough that while not officially supported it s easy to bypass the restrictions so I went ahead. +The upgrade itself couldn’t have been simpler considering the major version bump. The answer to the question "Was it worth it?" is something we will have to wait and see although I am liking what I see except for the initial hiccups.

    + +

    EDITED AFTER THE POST: Boy oh boy - just after I finished this post I saw the latest version of Ghost V1.12 is out and it was such a painless process compared to past. Just a simple command ‘ghost update’ and job done. That itself makes this whole pain kind of worth it.

    + +

    OK so the steps I took are as below:

    +
      +
    1. Backup
    2. +
    3. Download and Install
    4. +
    5. Setup Wizard
    6. +
    7. Configure Apache
    8. +
    9. Restore
    10. +
    11. Tweak
    12. +
    + +

    Backup

    +

    We will take the back-up from front end for all the posts and we will also backup on the server the entire directory where old instance of the blog is residing. +To take backup of all the content and download it in a json file, open your ghost site on a browser, navidate to "Settings" and then click on "Export". +Next for the backup of folder on the server itself. To do this issue the following commands on the terminal.

    + +
    #Updateand upgrade the OS repo
    +sudo apt-get update
    +sudo apt-get upgrade
    +#Stop the ghost server
    +pm2 stop Ghost &quot;assuming Ghost is the pm2 id for the site&quot;
    +# Change directory to web-server root
    +cd /var/www/html/&quot;path to your ghost directory say 'ghost' &quot;
    +# Create a new directory for backup
    +sudo mkdir old_ghost_bkp
    +sudo mv ghost old_ghost_bkp
    +# Recreate the ghost directory
    +sudo mkdir ghost
    +cd ghost
    +# Give right privileges to the new directory
    +sudo chown -R &lt;your username&gt;:www-data .
    +
    + +

    Download and Install

    +

    As we are already in the right directory lets get on with installing the latest version of Ghost using npm.

    + +
    sudo npm install -g ghost-cli
    +#Make sure you are in the directory where new ghost is to be installed.
    +#If you have followed all commands so far, you will already be in 
    +#required the directory
    +ghost install
    +
    + +

    It is at this point that you will have to deviate from official guide if you have Apache instead of NGINX. You will be prompted by the installer that it could not find NGINX and do you still want to continue. Default is "No" so make sure you enter "Y" and then press enter.

    + +

    nginx-not-found
    +For me rest of the install went smoothly.
    +finish-install-process

    + +

    Setup Wizard

    +

    Immediately after the install is complete, you will be presented with following questions:

    + +

    Please note that if you have configured SSL using LetsEncrypt as explained in previous posts on this blog then even if you are using https, the answer to blog url must be the with http and not https.

    + +

    For example: I gave http://mgw.dumatics.com and not https://mgw.dumatics.com +</code></pre>

    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +
    Enter your blog url: http://your.blog.url
    +Enter your MySQL Hostname: localhost
    +Enter your MySQL Username: root
    +Enter your MySQL Password: &quot;your mysql root password&quot;
    +Enter your Ghost database Name: "a relevant name" 
    +# for security reasons you may want to keep it different from your blog name";
    +Do you wish to set up Nginx: no
    +Do you wish to set uo Ghost MySQL User: yes
    +Do you wish to set up Systemd: yes
    +Do you want to start Ghost: yes
    +
    + +

    Please do note that the response on line 6 above to "Setup Nginx" must be "no"

    + +

    After the questions are complete you will get a notification You can access you blog at http://your.blog.url. +At this point, it is best to see which port is configured by ghost CLI for this installation. you can do so by checking the configuration file like so:

    + +
    nano config.production.json
    +
    +

    You can change the port if you like but if it is different than the port you originally had for old version of ghost you can either change it here or you need to change Apache conf file in next step. +If you do decide to change the port here, then there should be no need to carry out the next step - Configure Apache.

    + +

    Configure Apache

    +

    Assuming that the port in Ghost config file was 1234, there will be some changes that you will need to make in Apache conf files like so:

    + +
    cd /etc/apache2/sites-available/
    +sudo nano ghost.conf
    +
    + +

    Now change the port on ProxyPass and ProxyPassReverse to be same as what is in the config.production.json file and save it by pressing Ctrl+x and y.- so for this example it will be changed to 1234 and change will look as below:

    + +
    ProxyRequests off
    +ProxyPass / http://localhost:1234/
    +ProxyPassReverse / http:/localhost:1234/
    +
    + +

    Now open the ssl config file for the site using commands below and make the same changes as above.

    +
    sudo nano ghost-le-ssl.conf
    +
    + +

    TIP: If done using LetsEncrypt, it will be named something like ghost-le-ssl.conf.

    + +

    Once the changes are saved, disable and enable the configurations using following commands:

    +
    sudo a2dissite ghost.conf
    +sudo a2dissite ghost-le-ssl.conf
    +sudo a2ensite ghost.conf
    +sudo a2ensite ghost-le-ssl
    +#Restart the server
    +sudo service apache2 restart
    +
    + +

    Now if you enter you blog url in a browser, you should be presented with vanilla Ghost site. If not, something in server set-up has not worked and you will need to troubleshoot it and fix - luckily for me all worked like a charm.

    +

    Restore

    +

    Right, so you are now on the browser looking at the Vanilla Ghost install. First thing you need to do now is create the user with same credentials you had on your old version of ghost. To do this you will first need to enter the admin url for ghost and follow the steps to create your user. +Once you are into the admin interface, navigate to "Settings" -> "Labs" and click on "Choose File" button, select the json backup that was exported from your old version of the blog and then click on "Import" button. +Now to restore the images from your old blog on the server issue following commands:

    +
    cd /var/www/html/ghost/content
    +sudo rm images
    +sudo cp /var/www/html/old_ghost_bkp/ghost/content/images /var/www/html/ghost/content/
    +### Make the new image directory is writable or image uploads will fail
    +sudo chown -R ghost:ghost images/
    +### Restart Ghost
    +cd ..
    +ghost restart
    +
    + +
      +
    1. The ghost CLI commands like stop, start and restart will require you to be in the directory where ghost is installed.
    2. +
    3. While start and stop commands of ghost specifically ask for sudo credentials, restart command just keeps rotating and hence it is better to issue a command with sudo before you issue ghost restart.
    4. +
    + +

    This is it. Your old blog is now fully restored.

    + +

    Tweak

    +

    This section is a bit of a pain because there are quite a few things that break with this version. So if you have heavily used html, you will painstakingly need to go through posts and add a new line between markdown and html content for it to be parsed properly or else it will display quite wiered outputs on your blog. +If you have used code blocks with syntax highlighting, another change is with older version you would have given three backticks followed by language-sql but now you just need to give three backticks followed by sql. +If you have used line numbering using prism.js, it just wont work and you will need to apply changes to your theme the way I did. Without going too much in detail on that, you can get the copy of prism.js, prism-custom-line-number.js, prism.css, prism-line-number.css using the github links for my theme and place them in assets directory of your theme. Then make sure you include them in relevant files where your theme calls the javascripts. +Once done, issue the command ghost restart and things should look pretty again. +Happy Migrating !!!

    + + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/git-basics/index.html b/git-basics/index.html new file mode 100644 index 0000000..5625947 --- /dev/null +++ b/git-basics/index.html @@ -0,0 +1,789 @@ + + + + + + +Git Basics - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Git Basics +

    + + +

    + + + + + + + + + + + + 7 minute read + + + +

    + + +
    + + +
    + + + +

    Basics of Git

    + +

    Recently, I was asked to help people understand what is this whole business with GIT and I ran a few sessions on the topic specifically keeping in mind that my audience consisted of tech enthusiasts with minimal tech background. The content below is how I explained and as it went down well, I figured it may be helpful to someone else too; hence this post. :)

    + +

    Version Control System

    + +

    To understand the tool git lets first start by understanding what problem does it actually solve. At the most basic level git is a version control system. What this means is that it allows automated control of maintaining different versions of a digital asset such as a text file, CV, or indeed code files for a software. Now for simpler things like CV or text file one may not need something as sophisticated and complex as git but when it comes to maintaining assets for a software it is indeed a very useful tool.

    + +

    To understand how significant this is, lets put it in perspective of a typical business operations scenario completely unrelated to software development:

    + +
      +
    • Say there was an RFP response on which multiple people from Sales, Marketing, Pursuit and Technical team were helping prepare a response.
    • +
    • Each team will complete one section of the response document relevant to their area.
    • +
    • This will then all need to be integrated into the final response and reviewed by the Sales Lead.
    • +
    • Any changes that Sales Lead identifies will need further changes which will then again need to be amended on the document.
    • +
    + +

    Even with change tracking enabled in MS Word, we all know how painful this entire exercise is. So much so that at times we try doing it together so all changes can be done once or we opt for the approach of making changes sequentially where one section is completed by one team before another can be started by another team. This when we have one document!!!

    + +

    Now imagine if we had 1000s of such documents - that is a real scenario for software developers as each software has multiple files that may need input from multiple people. So having an automated version control helps save time because it, at the most basic level, saves time on integrating changes carried out by different people or teams that were carried out in parallel.

    + +

    Now when it comes to version control there are two main ways in which it can be achieved:

    + +
      +
    • +

      Centralised Version Control System: In our RFP example, the approach where everyone gathers together to carry out change on the main version will be a good simulation of centralised version control. What this means is that the changes to digital asset are done at one central location which is tightly controlled. This might as well be right approach for a smaller undertaking such as our RFP example but when it comes to software development it is not considered to be the best.

      +
    • +
    • +

      Distributed Version Control System: This is loosely what would have been the approach in our RFP example if people were to carry out changes on their copy of document which will then be merged by Sales Lead to prepare the final response. Basically a distributed version control system works on the principle that each contributor to the change of digital asset will make change on their local copy and then submit the change for version maintenance to the version controller. Git as a system shines in this approach of distributed version control.

      +
    • +
    + +

    Working Directory

    + +

    Directory is technically correct name for what are called folders in Windows Operating System and Working Directory is the directory in which the user is currently working. For example, let’s say I create a folder called “Project_1” in windows on path “C:/Documents” and save a python script in that folder, then the working directory will be “Project_1” and working directory path will be “C:/Documents/Project_1”.

    + +

    In order to version control this file, one can use git but this will include understanding the steps of how to go about it as explained in following sections.

    + +

    Initiatlise

    + +

    Now when let’s say the script file script.py is created by developer. It is not being tracked by git at this time. Now if we want it tracked by git, we need to first initiate git on the working directory. This is done using the command: git init but before typing this on command line, one needs to first get into the working directory on command line or terminal.

    + +

    In our example this will be done like so:

    + +
    cd C:/Documents/Project_1
    +git init
    +
    + +

    This will create a .git directory (folder) inside the working directory.

    + +

    This is equivalent of git software saying it is now ready to start tracking but it will still need to know what exactly we want it to track. This is what git add does.

    + +

    Add

    + +

    To tell git what we want it to track, we have to use the command git add <filenames / foldernames>. However, if there are 100 script files in directory it will be very cumbersome to type every single filename and as a shortcut one can use dot notation for current working directory which is dot(.) and the command can be issued as:

    + +
    # To add all content of the working directory
    +git add .
    +
    +# To add specific file say script.py
    +git add script.py
    +
    + +

    This tells git that we want all files and directories (folders) in the current working directory to be tracked.

    + +

    At this point, git is basically aware that it has to monitor changes on these files but it will not assume that every change we have made needs to be applied to maintained version so when we have added these files, we have basically also just said prepare these files for finalising their version - In other words we have asked git to stage.

    + +

    In order to finalise the versioning, we have to now tell this specifically to git which is done using commit. This will also be reflected in the tracking status which can be checked using status

    + +

    Status

    + +

    One can check current tracking status of various files and directories on the git initialised working directory by using the command:

    + +
    git status
    +
    + +

    Commit

    + +

    So we have got git to stage the files and now we want it to label the latest changes as a new version and this is achieved using the command:

    + +
    git commit -m <Comment for the commit>
    +
    +

    This command ensures that git now makes the latest changes baseline and still keep record of previous changes. This is especially useful if in case we want to revert to a previous version for some reason - say we found that in latest version there were quite a few mistakes which will need to be rewworked and until then previous version is what we want to keep as baseline for anyone who wants to refer and make changes to. This reverting can be achieved using revert

    + +

    Revert

    + +

    Now reverting to a previous version requires that we know the commit id that git applied to previous commits and then identify the exact commit id to which we want git to revert to. This can be achieved by checking git logs, so the sequence to git revert will be like so:

    + +
    # Find the commit id to revert to
    +git log
    +
    +# Copy the commit id of the version to revert to and issue following command
    +git revert <commit id to revert to>
    +
    + +

    Now at this point, all changes are being tracked on the local machine of the user, so how can this help with collboration. This is achieved by synchronising the latest version on local machine to a central server. This could be a server hosted by an organisation or it can be something public like Github, Gitlab and such. As GitHub is most frequently used in enterprise environment, lets see how that works.

    + +

    Github basically is a web based utility which acts as a remote location where we can push our finalised version to be saved in what is called a github repository for other collborators to then download (clone) as their local copy and work on. Once each poarty has made changes, the can each push their changes to the github with help of git and the version control will be applied automagically or in some instance with minimal manual intervention from the maintaining or one pushing the changes.

    + +

    + +

    There is lots going on in this last bit so lets break it down to digestable chunks of information in next session.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/git-jargon/index.html b/git-jargon/index.html new file mode 100644 index 0000000..dbc9c76 --- /dev/null +++ b/git-jargon/index.html @@ -0,0 +1,759 @@ + + + + + + +Git Jargon - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Git Jargon +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +
    + + +
    + + + +

    Git Jargon

    + +

    Before we delve into the topic of how collaboration happens using git, lets get some terminology out of the way.

    + +

    repo

    + +

    When talking to techy chaps, one will encounter the word repo very often. However, repo is just a shortform for the complete word repository.

    + +

    repository

    + +

    Now repository in git speak is basically specific to a project and is nothing but a fancy alias for the working directory where all the digital assets being tracked for the project are placed.

    + +

    root

    + +

    The term root when used with reference to a folder, directory, repo or repository is basically the directory in which all the digital assets of your project are stored. This directory may or may not contain other sub directories.

    + +

    remote

    + +

    remote is a way to refer to server. So if a techy were to say “I have updated File X on remote” - it will basically translate in plain English to something like this - “I updated the File X on my laptop and have then also taken required actions to get those updates reflected on the copy saved on the server.”

    + +

    push

    + +

    This refers to sending updates carried out (commited) on a local machine to the server (remote). Fairly frequently one may come across statements when checking for status updates on the lines of “I have pushed my changes to remote” which in normal English means “I made required changes on my local machine, committed them and then sent (pushed) them to the server (remote)”

    + +

    pull

    + +

    This refers to getting updates from the server (remote) on to the local machine. This is an activity typically carried out before one is planning to make any changes or updates to last known version. So if say a task is assigned to an individual for updating file , a reply can be “Alright, I will pull the latest from remote and make changes and will let you know once I have pushed my changes back.” which will translate to “Alright, I will get the latest copy from server, make the changes you have asked for and then commit those changes and send it back as updated version on the server”

    + +

    main

    + +

    This refers to the final / published version of project’s repository.

    + +

    branch

    + +

    This term is more of a concept really. You see branch is a mechanism to carry out changes without impacting the main version and therefore a branch is basically just a new or separate version of the main repository where one can make changes to their heart’s content without worrying about messing up with the last known final version.

    + +

    merge

    + +

    Once the experimental changes in a branch are completed and proven not be breaking any other digital assets, it is desirable indeed to get these changes reflected on the main version and this action of bringing digital assets in main to reflect same changes as that on the branch is called merge.

    + +

    modified

    + +

    In git speak, any changes (updates, additions, deletions) to a digital asset (including deletion of entire digital asset) then that digital asset will be identified as modified

    + +

    staged

    + +

    The modified digital assets are not automatically assumed to contribute a version change and that is only done when those changes are added explicity by issuing the command git add <filenaem / foldername> and at this point the changes are said to be staged.

    + +

    Recap

    + +

    With this in mind, the flow of GIT can now be explained as below:

    + +
      +
    1. To work with Git, you first initialise it on a directory which will make it a Repository +
    2. +
    3. Once initialised, Git creates a hidden firectory called .git which is used to keep track of changes in the initialised directory.
    4. +
    5. At this point, we need to make Git aware of the files in this repository which we want Git to track and in doing so we stage those digital assets.
    6. +
    7. From this point on, any changes made to digital assets in project folder can be tracked.
    8. +
    9. If a digital asset which is being tracked is changed, it gets the status of modified.
    10. +
    11. Any digital asset with status must be staged before it can be passed on for version control.
    12. +
    13. Once staged files must be committed so that the changes can be treated as the latest version snapshot.
    14. +
    15. As each commit is tracked by Git, it is possible to revert to a previous version by first identifying the commit id one wants to revert to using git log and then providing that commit id through git revert <commit_id> +
    16. +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/glympse/index.html b/glympse/index.html new file mode 100644 index 0000000..ee6f43f --- /dev/null +++ b/glympse/index.html @@ -0,0 +1,680 @@ + + + + + + +Glympse - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Glympse +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    Every now and then we come across something that has a strong potential, something that is a great phenomenon in making and Glympse to me is that idea.

    + +

    The simple to use and highly effective application available on android market for free that allows you to share your position during the travel in real time.

    + +

    While it’s moderately useful for long journeys as was evident on my holiday travel this weekend when my host was able to monitor my progress to their house 250+ miles away, I think this application can find great and effective use in postal and transportation sector.

    + +

    It not only allows you to share your journey but also shows you when was your progress last viewed and who is viewing it right now.I provides you the control on who can have access to this real time view and for how long. You provide a time-frame for which you would prefer this monitoring to be available and then allows you to add more time in 15 minutes intervals. It integrates with phone contacts and has a very slick GUI.

    + +

    All in all very impressed with this application and the fact that it’s available for free is the icing on cake.

    + +

    If you have an android phone, do lot of travelling and need to keep calling for your whereabouts, guess this application can save you on unnecessary calls while letting you concentrate on your journey. +Highly recommended application.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/google-voice-sip2sip-ikall-free-international-calls-to-known-contacts/index.html b/google-voice-sip2sip-ikall-free-international-calls-to-known-contacts/index.html new file mode 100644 index 0000000..67c4d9e --- /dev/null +++ b/google-voice-sip2sip-ikall-free-international-calls-to-known-contacts/index.html @@ -0,0 +1,783 @@ + + + + + + +Google Voice + SIP2SIP + Ikall = Free international Calls to known contacts - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Google Voice + SIP2SIP + Ikall = Free international Calls to known contacts +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +
    + + +
    + +

    Second post in succession on the topic but believe me when I get a new gadget I try getting all information and then once I have had the stuff working I have to make a post right away or I will forget and hence this post. This post also overrides the previous posts. +Right then, let’s get down to business. +What do we aim to do?
    +We aim to make free international calls and there is nothing illegal in this set-up, not to my knowledge. +How?
    +Google has introduced Google Voice and allows free calls to all US numbers. This can be done using their google talk plugin in the gmail browser. +Pre-Requisites:

    +
      +
    • Gmail account Preferably a new one and not the one you use for your day to day usage.
    • +
    • Ability to install X-Lite 4.0 which will require a machine running Windows PC directly or on virtualbox.
    • +
    • Ability to install Hotspot Shield if residing outside of USA.
    • +
    • An Android device at both source and destination of the call.
    • +
    +

    Steps:

    +
      +
    1. Get a SIP number.
    2. +
    +

    If outside of USA, install hotspot shield on windows machine by going to this site - http://www.hotspotshield.com .I am not sure how safe this is from spyware and all security perspective. I am not really worried about it as I carry out these things on virtualmachine using virtualbox and there is no real threat to my actual machine which runs on Linux. So if Windows is your primary machine you do it on your own risk. +Once installed, open the site www.sip2sip.info.Register on sip2sip using your new gmail account. Ensure that you select US Central as your region. +You will get an email from sip2sip on the new gmail account giving your login details which will be something like:

    +
    SIP address: 2233xxxxxx@sip2sip.info
    +Password: abcdabcdabcd
    +
    +

    Open the email and click on the link provided for lgging in. Enter the login details provided and goto settings tab (3rd from left). +In the first field under SIP Account, enter a new easy to remember password and click SAVE. +This completes Step 1 and you have successfully created SIP number for yourself.

    +
      +
    1. +Get a US phone number using ipkall.
      +Go to the website http://www.ipkall.com and click on sign-up.On the sign-up page complete the following details using the email from sip2sip:
    2. +
    +
    SIP username:  2233xxxxxx
    +Hostname or IP address: sip2sip@info  
    +Email Address: Preferably the email address you used to register at sip2sip
    +Password: Prefereably the same password as what you changed on sip2sip in Step 1-5.
    +Enter the human verification codes and click "Submit".
    +
    +

    You will receive a mail with in your email account with a new US number with text of something to following effect:

    +
    Thank you for signing up. Your IPKall phone number is: 253-XXX-XXXX.
    +SIP Phone Number: 2233xxxxxx
    +SIP Proxy: sip2sip.info
    +Email: abcdefg@gmail.com
    +Password: qwerty
    +
    +

    This completes Step 2. You have now received a US phone number that is linked to your sip2sip account.

    +
      +
    1. Set-up Xlite / softphone to receive calls made to US phone number.
    2. +
    +

    In order to activate Google Voice account, it is important to be able to receive call on the new phone number that we have created in Step 2 so install Xlite v4.0 from here - http://download.cnet.com/X-Lite/3000-2349_4-10547103.html +Once X-Lite is installed, open it and click on Softphone -> Account Settings. Now fill the following fields:

    +
    Account Name: Fill your gmail username.
    +User ID: 2233xxxxxx
    +Domain: sip2sip.info
    +Password: sip2sip password (qwerty for this example)
    +
    +

    Click OK at the bottom of the window. This completes Step 3.

    +
      +
    1. Activate Google Voice for this Gmail account.
    2. +
    +
      +
    • Log into the google voice account - https://www.google.com/voice.
    • +
    • Provide a user pin to retrieve voicemails
    • +
    • Now provide the US phone number obtained in Step 2-4 (253-XXX-XXXX)
    • +
    • A window will be shown with two numbers and a button call now.
    • +
    • Click on Call Now and you should receive call on Xlite phone.
    • +
    • Accept the call on Xlite and enter the two numbers shown in google's window.
    • +
    • You will get confirmation that the numbers are correct and will be asked to set-up voice mail greeting or hang-up.
    • +
    • Hang-up now as you can set this up later.
    • +
    +

    Google Voice account is now set-up.

    +
      +
    1. Configure Android to receive calls using Google Voice.
    2. +
    +

    If you are using nexus S, good news, it has inbuilt capability to get the SIP calls though this can be also be done using SIPDROID on other android devices. For Nexus S you can follow the steps below:

    +
      +
    • Goto Settings -> Call Settings and under Internet Call Settings click on "Accounts".
    • +
    • Untick receive calls.
    • +
    • Click on Add Account.
    • +
    • Now in username enter the 2233xxxxxx provided by sip2sip
    • +
    • Enter the password used for sip2sip account in password field. (qwerty in this example)
    • +
    • Enter sip2sip.info in server.
    • +
    • Untick the "Set as primary account" field.
    • +
    • Click on Optional Settings and in Outbound proxy address enter proxy.sipthor.net
    • +
    • Press back button till you are back to call settings. Now under Internet Call Settings click on "Use Internet Calling" and select "Only for Internet Calls".
    • +
    +

    For SIPDROID once you have downloaded and installed it from android market, follow the steps below:

    +
      +
    • Open SIPDROID, and goto Settings ->SIP Account.
    • +
    • In Authorization Username enter: 2233xxxxxx@sip2sip.info +
    • +
    • Enter the password used for sip2sip account in password field. (qwerty in this example)
    • +
    • In Server or Proxy enter proxy.sipthor.net
    • +
    • In Domain enter sip2sip.info
    • +
    +

    Now save and exit. SIPDROID will register the VOIP and turn green. +Now from gtalk plugin in the browser from some other gmail account try to call US phone number obtained in Step 2-4 (253-XXX-XXXX). Your phone should ring and so should the Xlite. +If step 4 did not happen as expected, you need to review the configuration and once it does happen as expected, your set-up to recieve calls is completed.

    +
      +
    1. Configure Android to make calls using Google Voice.
    2. +
    +
      +
    • Download and install Google Voice Callback on android device - https://market.android.com/details?id=com.xinlu.gvdial&feature=search_result +
    • +
    • Provide the gmail credentials for the application. This will perhaps explain my recommendation for a new gmail account. You will be giving login credentials to a third party application but since it's a new account with no confidential info, it should really be safe.
    • +
    • In the settings for when to use callback select "Ask Everytime" if you are outside of USA.
    • +
    +

    This is it. Try calling one of your USA contact and this application should make a call back and you should be able to talk for free to your US friends. +This is happy ending for those who don’t have sight of our aim - make free international calls. For free international calls though you have reached a point where you will have dependency on person you are calling. Following 3 options will be possible: +If the person you are calling also has android device and they follow this tutorial they will have a US number which you can then store in your contacts against that person’s name and from thereon you both can call each other absolutely free. +If your friend has a SIP enabled device, they can follow this tutorial and replace the set-up of Android device to setting up their own SIP device. +If above two are not viable options, you can ask your friend to call you on your international number through googletalk plugin in their web browser. Unfortunately this takes away the flexibility of you being able to call them but given the constraints this may still be a good option to talk for free.

    +
    +Final words - I know it's a long post and looks complex but believe me if you do it right it takes roughly 20 minutes. +
    +

    Hope you find the post helpful.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/grav-cms-with-a-difference/index.html b/grav-cms-with-a-difference/index.html new file mode 100644 index 0000000..72e0470 --- /dev/null +++ b/grav-cms-with-a-difference/index.html @@ -0,0 +1,738 @@ + + + + + + +Grav - CMS with a difference - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Grav - CMS with a difference +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +
    + + +
    + +

    While I love Ghost as a blogging platform, it is not best placed for things other than blogs - after all that is the basic idea behind creation of this wonderful tool.

    + +

    As I wanted to host a static website using tools that don’t require a database or rely on php, I went searching on interwebs. +I came across a lot of options and the most popular one appears to be Jekyll and it’s variants (Nikola and such) but they require a lot of terminal activity which won’t go well for regular end user responsible for maintaining content of the static website in question.So I continued looking and came across this wonderful project called Grav.

    + +

    Grav is super fast, very pretty and extremely easy to deploy and maintain. Additionally, it has very good documentation.

    + +

    The key features that I absolutely loved are as below:

    + +
      +
    1. Easy installation - * Easier than Ghost / Wordpress IMHO *
    2. +
    3. Good help and documentation
    4. +
    5. Responsive themes and skeletons
    6. +
    7. Built-in Markdown Support and then some
    8. +
    9. Lot of useful plugins
    10. +
    11. Browser based admin page
    12. +
    13. Active development
    14. +
    15. Easy upgrade and back-up
    16. +
    + +

    My steps for installation:

    + +
      +
    • Download a skeleton you fancy
    • +
    • +

      Unzip the downloaded file into your server root and move it into a folder named grav:

      + +
      +
      sudo unzip grav-skeleton-appi-1.0.0.zip -d /var/www/html/
      +cd /var/www/html/
      +sudo mv grav-skeleton-appi-1.0.0/ grav/
      +
      +
    • +
    • +

      Fix permissions (replace username with your user on the server. This is important to ensure files can be modified both from browser as well as from terminal. So create a bash file by issuing command nano fixpermissions.sh and paste the following code in there.

      + +
      +
      ##!/bin/sh
      +chown &lt;username&gt;:www-data .
      +chown -R &lt;username&gt;:www-data *
      +find . -type f | xargs chmod 664
      +find ./bin -type f | xargs chmod 775
      +find . -type d | xargs chmod 775
      +find . -type d | xargs chmod +s
      +umask 0002
      +
      +
    • +
    • +

      Now make this bash file executable using chmod a+x fixpermissions.sh

      +
    • +
    • +

      Finally run the bash file from within the grav directory:

      + +
      +
      cd /var/www/html/grav/
      +sudo bash /<path_to_fixpermissions.sh>/fixpermissions.sh
      +
      +
    • +
    • Open <server-ip-address>/grav from the browser and check your install is working
    • +
    • +

      Once it is confirmed to be working, install admin plugin from terminal like so:

      + +
      +
      /bin/gpm install admin
      +
      +
    • +
    • Open <server-ip-address>/grav/admin from the browser and create your admin login details
    • +
    + +

    Working on Grav is an absolutely pleasing experience and the swift turnaround for a static website is phenomenal. Suffice to say, I hope that this project goes from strength to strength.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/home-networking/index.html b/home-networking/index.html new file mode 100644 index 0000000..eacd3be --- /dev/null +++ b/home-networking/index.html @@ -0,0 +1,757 @@ + + + + + + +Home Networking - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Home Networking +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +
    + + +
    + + + +

    Network routing

    +
      +
    1. +Router - Router is the device at home that connects all devices in your house to the internet. It does so by assigning IP address to each of your device but this IP address is only relevant for devices connected to this router. What this means is that if you use an IP address allocated by this router from a coffee shop and you are not connected to your home router then you will not be able to reach the device that IP address belonged to. For this reason these IP addresses are called internal IP addresses.

      +Now what happens is when you try to access a website, your device sends the request to your router, router in turn sends it to the router of your ISP which in turn eventually connects to the server that is hosting the website. The information flows back through same route and your router finally gets the information and then passes it back to your device and it is able to do so because it identifies your device based on the Internal IP address it assigned to your device.

      +By default pretty much all routers are configured to allocate these Internal IP Addresses using DHCP which is just fancy term for allocating a random number to the device. They do also provide the facility to reserve an IP address for a particular device if we tell it to do so - This is called "assigning a static IP on the router" in technical speak. This is important when we are trying to host service from home based server and is explained next

      +Many ISP provided routers are locked in and it will serve you well to get a router and flash it with an open source firmware like DD-WRT. +
    2. +
    3. +Internal LAN set-up - Ensure that on your internal LAN network you assign static IP address to device that will be running the server. If your router allows for LAN Domain to be configured then do so and allocate appropriate LAN Hostname as it makes it easier to access the server from within LAN than just by trying to remember the IP address. +
    4. +
    5. +Port Forwarding - When the request comes from Internet, it will be with http, ftp, smtp those kind of headers. These headers operate on standard ports. However for security as well as application related issues, the ports on actual server within your home network can be significantly different. All this needs to be translated for communication to be completed and application to provide requested information.

      +Port Forwarding at router level is simply us telling router which IP address to pass on the received request based on the port that request is trying to access. For example http requests are on port 80 so if we tell router that any requests coming on port 80 must be forwarded to a laptop in your house with internal ip address of 192.168.1.44 then port forwarding should be the way to do so. +
    6. +
    +

    Internet to Home

    +

    Use of Dynamic DNS Services

    +
      +
    1. Ensure that the router is informing your Dynamic DNS provider with latest IP address assigned by your ISP. +
        +
      • Use of duckdns.org or dtdns.com etc
      • +
      • Use of DNS-O-Matic to dynamically update the External IP address for your home router as soon as it is changed by the ISP.
      • +
      +
    2. +
    3. Allocate the Dynamic DNS to DNS name purchased from Domain Name registrar like Namecheap etc.
    4. +
    +

    Hosting a service from Home Server:

    +

    Question we are first going to answer is - If we want to host on server from home. what do we need to do?

    +

    1. Ensure you are able to do changes to your router setting that allows you to:

    +

    a. Create port forwarding rules
    +b. Update the DynamicDNS each time your ISP changes External IP address for your router.

    +The concept for these two topics are covered in previous sections but we will revisit in greater detail later. For now let’s start at something even more basic.

    +

    2. We need to decide the operating system and web server we want to use?

    +

    The request from Internet when it reaches the router it is just dealing in numbers and not strings so it does not really know what information to send back unless it gets it back from the device where the information resides. In order for the device that has the information to provide it such that router can send it out, we need to have these web servers that do the job of translating the information into a format that the router and eventually the device can understand. So as an example if the request was on http protocol, it would mean it is for a web page and router knows that it is for port 80, and will forward it to the IP address you assigned - Router is assuming ofcourse that you know what you are doing. In other words it is your (human) responsibility to ensure that when this request lands on said IP address someone is there to receive it - that someone is the "Web Server" we are now talking about.

    +

    Web Servers

    +

    The two commonly used web-servers are:

    +
      +
    1. Apache
    2. +
    3. Nginx
    4. +
    +

    There are other servers too for various other scenarios but the most popular options are the two above.

    +
    +QUESTION:
    +Can we have both web servers running on same machine? +
    +ANSWER:
    +Yes, the routing of information will be similar to the flow below:
    +Internet -> Home Router -> Primary Server -> Other servers in home network accessed through reverse proxy and connected to world only through Primary Server. +
    +

    Operating System

    +

    Assuming that we decide to go with Apache Server. You will need to install it on your laptop. This brings us to the first controversial topic of operating system that this laptop is using. It can be anything from Windows, Mac to a range of Linux Distros. As we are talking open source, lets assume it is will be a linux distro but there are so many to chose from so which one? +A safe choice is to opt for Debian based distro as it is easy to start with. In that too Debian 9 (code named: Stretch) at the time of writing is latest stable version and is most recommended. I will assume for the purpose of this article that the reader will use Debian 9 (Stretch). I will also assume that reader is not aware of working on a Linux environment. Advanced users may find some of the information here obvious or boring as I explain few things at length.

    +

    Prepare the laptop:

    +

    Step 1 - Find out whether the laptop is 32 bit or 64 bit.
    +Step 2 - Download a copy of the Debian 9 (Stretch) applicable to the specific version of your laptop (32 bit / 64 bit)
    +Step 3 - Create a Live DVD / USB and test drive the OS
    +Step 4 - Install it
    +You will be asked to provide root password, username and user password. Make note of each of these as you will need them later.
    +Step 5 - Update, Upgrade and Dist Upgrade
    +Open the terminal of the laptop and type following commands:

    + +
    sudo apt update
    +sudo apt upgrade
    +sudo apt dist-upgrade
    +
    +

    ....More to come...Work in progress.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/how-to-boot-from-usb-when-bios-does-not-have-the-option/index.html b/how-to-boot-from-usb-when-bios-does-not-have-the-option/index.html new file mode 100644 index 0000000..940f32d --- /dev/null +++ b/how-to-boot-from-usb-when-bios-does-not-have-the-option/index.html @@ -0,0 +1,810 @@ + + + + + + +How to boot from USB when BIOS does not have the option. - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    How to boot from USB when BIOS does not have the option. +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +
    + + +
    + +

    I have an old Sony VAIO which is not in it’s best of health and has long been really a companion for my telly, faithfully streaming media from bbc iplayer, youtube, dailymotion and likes. Internet enabled TV arrived in my home long long back :).

    + +

    Now the thing with this laptop is that it’s kinda gimpy - inbuilt keyboard won’t work, battery is dead and it hangs on life with constant supply of energy from the AC source on the wall and the one thing that helps me load new OS on this machine - the optical reader - is temperamental and may or may not work and is moody in selecting which CD / DVD it will read and which it won’t. It does in particular like CD’s authored by Linux Format guys though. Writing is a skill it has forgotten long back and if it reads something, anything I am found celebrating.

    + +

    Anyway, I had ubuntu installed on this laptop for quite some time but as this laptop has one more flaw - the nvidia graphic card - and the latest update from ubuntu broke the nvidia drivers which aren’t all that well supported anyway, I was being forced to reformat the machine. So I decided to try a new distro named Bodhi Linux which is very cool and uses Enlightenment as desktop which is very very good and way better than Unity and some might argue even Gnome 3.2. However, in order to do so I had to cross the hurdle of burning a CD that my laptop’s Optical Drive will find intresting enough to read.

    + +

    I have checked and rechecked the BIOS of this laptop and there is no way to make BIOS understand that it can boot from USB. The only options it provides are Hard Drive, Floppy Disk, Optical Reader and Network Boot. None of these were particularly useful for reasons explained above and unfortunately I was hitting the wall. So I started looking for alternative ways to get Bodhi Linux installed. Alternative is what I found in Plop Boot Manager.

    + +

    Now open source enthusiasts at this point be aware, this nifty piece of software is not open source but it is so useful that this one minor flaw must be completely ignored. It gives you options to boot from USB in several ways - you can burn a CD and fool BIOS to boot from CD onto plop boot manager which in turn allows you to boot from device of your choice aka USB, it can also be put on floppy or the option that I have used - install on hard drive and configured through GRUB.

    + +

    So presented below without further ado is the guide to how you can install this on your hard drive but before I do so a quick thanks to several boards and posts I referred in the process of making this work:

    + +

    Steps:

    + +

    Please note that these instructions will work for machines that have one of these OS installed: Bodhi Linux and Ubuntu variants. For other linux versions it should be on similar lines

    + +
      +
    1. +Download the Plop Boot Manager (plpbt-5.0.14.zip file) on the machine where you want to achieve the result from http://www.plop.at/en/bootmanager/download.html
      +Fig-1 +
    2. +
    3. +Extract the zip file in folder where it was downloaded. In my case I have my browser setting set to download everything to "Download" folder.
      +Fig-2 +
    4. +
    5. +Now open the terminal and type following command relevant to your distro: +
    6. +
    +

    For UBUNTU: +gksu nautilus /boot +For Bodhi: +sudo pcmanfm /boot +You will be presented with a dialogue box to enter password. Once you enter the password you will be presented the contents of boot folder as shown below. + Fig-3

    +
      +
    1. Now
      +(a) Go to the extracted plpbt-5.0.14 from step 2, click on Linux folder
    2. + Fig-4
      +(b) Then copy the files - "plpbt.bin" and "plpcfgbt" and paste them in the boot folder opened through step 3.
      +Fig-5
      +
    +
      +
    1. Once the files are copied in boot folder, double click on folder named grub and there open the file named "grub.cfg" in texteditor.
      +Fig-6 +
    2. +
    +

    Once the file is opened, press "Ctrl+f" and search for string "END /etc/grub.d/10_linux". Now copy the text as highlighted in screen below and paste it in a new text-editor window. + Fig-7

    +
      +
    1. In the new text editor after pasting the four lines from above
    2. +
    +

    a) Edit the fourth line so it reads as below: +linux16 /boot/plpbt.bin +b) Complete the block in new editor to read as below, keeping the first three lines intact from what was copied in step 5 from grub.cfg file. In our example it will read as below:

    +
        menuentry "Plop Bootmanager" {
    +    insmod ext2
    +    set root='(hd0,6)'
    +    search --no-floppy --fs-uuid --set cb7a6eb7-b355-4d0f-865e-f7312880f887
    +    linux16 /boot/plpbt.bin
    +    }
    +
    +

    Once again, this is important so remember the final structure will be achieved by following steps below. +Copy Paste this in Line 1: +menuentry "Plop Bootmanager" { +Next three lines remain same as copied from step 5:

    +
        insmod ext2
    +    set root='(hd0,6)'
    +    search --no-floppy --fs-uuid --set cb7a6eb7-b355-4d0f-865e-f7312880f887
    +
    +

    Then the fourth line will be edited to look as shown below. You can copy paste this in fourth line. +linux16 /boot/plpbt.bin +Fifth line will be closing bracket. +}

    +
      +
    1. Now open another terminal window and type following command relevant to your distro:
    2. + +For UBUNTU: gksu nautilus /etc/grub.d +
      +For Bodhi: sudo pcmanfm /etc/grub.d +
      +You will be presented with a dialogue box to enter password. Once you enter the password you will be presented the contents of boot folder as shown below.
      + +Fig-8
      +
    +
      +
    1. +Open the file 40_custom (highlighted in screenshot above) in texteditor - gedit on ubuntu or leafpad on Bodhi and paste the block from step 6(b) in this file and save it. +
       #!/bin/sh
      + exec tail -n +3 $0
      + # This file provides an easy way to add custom menu entries.  Simply type the
      + # menu entries you want to add after this comment.  Be careful not to change
      + # the 'exec tail' line above.
      + menuentry "Plop Bootmanager" {
      + insmod ext2
      + set root='(hd0,6)'
      + search --no-floppy --fs-uuid --set cb7a6eb7-b355-4d0f-865e-f7312880f887
      + linux16 /boot/plpbt.bin
      + }
      +
      +
    2. +
    +

    Very Important - Press "Enter" at-least twice after pasting to ensure there are atleast two new lines below closing bracket.

    +
      +
    1. Close all windows and open terminal once again and type following command:
    2. +
    +

    sudo update-grub

    +
      +
    1. +Now open /boot/grub/grub.conf and you should find the following entry on it: +
       ### BEGIN /etc/grub.d/40_custom ###
      + # This file provides an easy way to add custom menu entries.  Simply type the
      + # menu entries you want to add after this comment.  Be careful not to change
      + # the 'exec tail' line above.
      + menuentry "Plop Bootmanager" {
      + insmod ext2
      +set root='(hd0,6)'
      +search --no-floppy --fs-uuid --set cb7a6eb7-b355-4d0f-865e-f7312880f887
      +linux16 /boot/plpbt.bin
      + }
      + ### END /etc/grub.d/40_custom ###
      +
      +
    2. +
    3. +Now reboot your machine with your Live USB plugged in. If you already dual boot your grub will show on restart and will have an additional option in the end - "Plop Bootmanager". +
    4. +
    +

    If however, you just have single OS ubuntu, press shift once the bios logo shows up and keep holding for getting the system to show Grub. +Once Grub is shown it will have the additional "Plop Bootmanager" menu entry on grub. +Select the "Plop Bootmanager" and press enter.

    +
      +
    1. Plop Bootmanager will show option to boot from USB. It was third option for me. Select and press enter. If your Live USB is working, you will be able to load OS from it.
    2. +
    +

    This is it. You can check the video below:

    + + + +
    + +
    + +

    Please do let me know in comments if you found this useful. If there are any other ways I will be keen to hear those too.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/how-to-edit-pdf-in-linux-the-easy-way/index.html b/how-to-edit-pdf-in-linux-the-easy-way/index.html new file mode 100644 index 0000000..f5c1ec3 --- /dev/null +++ b/how-to-edit-pdf-in-linux-the-easy-way/index.html @@ -0,0 +1,731 @@ + + + + + + +How to edit PDF in Linux - The easy way. - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    How to edit PDF in Linux - The easy way. +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +
    + + +
    + +

    Recently someone asked this question to me and after some search on Google I came across mentions of PDFedit, Scribus, flpsed, Gimp, PDFMod and even the openoffice plugin for importing PDF and I tried all of these. PDFescape is probably a good solution but is only online and limited to 30 pages in it’s free avatar. If your aim is similar to the one stated below, these tools are way too complicated (PDFedit), limiting (PDFescape) or ineffective (rest of them). +So, Let’s start with the usual what was my aim question. +Aim: +To be able to highlight and put his own annotations, highlights and comments in the lecture notes that he gets from his college lecturers in pdf and be able to print the amended notes from any platform. (Linux / Mac / Windows.) +What Worked? +What I gave my friend as his final solution in the end kills two birds with one stone - Use of "Calibre" to convert the pdf to rtf and then use the open office to edit the notes. Where is the second bird, you ask…well Calibre is a great tool, infact in my opinion the best tool for ebook management. Using Calibre will not only help in editing the notes from the pdf but enable us to have a copy of final notes for ready reference right on our smartphone. There you have the second bird. :) +Now the steps involved are listed below: +Steps:

    +
    1. Install and set-up Calibre
    +2. Import pdf into Calibre
    +3. Convert pdf to rtf using Calibre
    +4. Open rtf file and start editing
    +5. Convert final version of rtf to epub and transfer to your phone.
    +
    +

    Step 1 - Install and set-up Calibre:

    +

    The current version of Calibre at the time of writing is 0.8 while the one in ubuntu repository is 0.6 so I recommend you should update after installing from repo as that will ensure all dependencies are satisfied and then you have the latest version as well. +So first open your package manager (on Linux Mint: Menu - Package Manager). You will need to provide root password and then search for Calibre and install it. +Now to update to latest version, open the terminal (on Linux Mint: Menu - Terminal) and type the following command:

    +
    sudo python -c "import urllib2; exec urllib2.urlopen('http://status.calibre-ebook.com/linux_installer').read(); main()"
    +

    You will be asked for your root password so provide that and terminal will show a question:

    +
    Enter the installation directory for calibre [/opt]:
    +
    +

    Type /opt and press enter. +Now type Calibre and it will fire up the welcome wizard. +Follow these screen-shots: +Select your language and Directory where you will want your ebooks to be stored. I keep it default.
    +Alt Text +Select your ebook Device. I have selected Android as that is the smartphone I use for reading ebooks.
    +Alt Text +That’s Calibre configured. Easy-Peasy !!! :)
    +Alt Text

    +

    Step 2: Import pdf into Calibre

    +

    Open Calibre (On Linux Mint: Menu - All Applications - Office - Calibre or just by typing calibre on terminal).
    +Alt Text +Now click on "Add Books" and select "Add books from single directory.
    +Alt Text +It will open the dialogue box for you to select file. Go to the location where you have saved your pdf. In my case it was in /Downloads/Tutorials as you can see in next screenshot.
    +Alt Text
    +Select the file and click open. Calibre will then import the pdf. +While importing it shows the "Adding" screen as can be seen in screenshot below.
    +Alt Text +Depending on size of the pdf it can take a while or be very quick. In this case as it’s a small lecture note it was hardly few seconds. Once completed, imported file will appear in your library as shown.
    +Alt Text +Once completed, it’s time to move to next step.

    +

    Step 3: Convert pdf to rtf using Calibre

    +

    Select and right click on the pdf imported into Calibre and select Convert - Convert Individually.
    +Alt Text +It will open the window to show options for conversion as shown below. In this window on right hand side corner select "rtf" from the drop-down against "Output".
    +Alt Text
    +Now click OK. +Once Converted it will show the rtf format in right pane as shown in red circle in following figure.
    +Alt Text

    +

    Step 4: Open rtf file and start editing

    +

    Click on rtf and it will open the file in open-office and you are ready to edit, highlight and do pretty much what you like. +You may notice that some pictures are not coming on the page but all that is required is to do some formatting for openoffice which is quite straight forward. +Double click on the picture and in most cases it will be fixed if you click on "keep Ratio" and changing the anchor to Page.
    +Alt Text +Some changes to page layout from Portrait to landscape may also help. To do so go to Format and select Page. It will open the dialogue box as shown below where you can switch between Portrait and landscape.
    +Alt Text

    +

    Step 5: Convert final version of rtf to epub and transfer to your phone (Optional)

    +

    Once done save your work with a different filename and follow steps 2 and 3 to import the saved rtf file into calibre and convert to epub this time (Keep output to epub in step 3) and then transfer it to your smartphone through mail or by usb transfer. +That’s it all done. It’s a long tutorial keeping in mind people who are new to Linux but carrying out the steps to convert doesn’t take more than 2 minutes so trying this will be well worth it. +I must add that this may not be required if all you will ever need is to add notes and comments and highlight in pdf and then print from your own laptop / computer. In which case OKULAR is a far superior way of doing it and you must try it out. However, having said that I don’t know many college going students who have easy access to Printers at their hostel and in all likelihood their print-outs will be from college library in which case solution above should come handy.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..939cbba --- /dev/null +++ b/index.html @@ -0,0 +1,654 @@ + + + + + + +Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Git Jargon + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    Familiarisation with some Git jargon +

    +
    +
    + + + + + + +
    +
    + +

    + + Git Basics + + +

    + + +

    + + + + + + + + + + + + 7 minute read + + + +

    + + +

    Basics of Git + +

    +
    +
    + + + + + + +
    + +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/install-maitreya-vedic-astrology-software-on-ubuntu-kbuntu/index.html b/install-maitreya-vedic-astrology-software-on-ubuntu-kbuntu/index.html new file mode 100644 index 0000000..75f8a26 --- /dev/null +++ b/install-maitreya-vedic-astrology-software-on-ubuntu-kbuntu/index.html @@ -0,0 +1,773 @@ + + + + + + +Install Maitreya - Vedic Astrology Software on Ubuntu / Kbuntu - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Install Maitreya - Vedic Astrology Software on Ubuntu / Kbuntu +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +
    + + +
    + +

    First I must thank the Maitreya developers for coming up with such a wonderful Vedic Astrology software that works on Linux. I am not aware of any other vedic astrology software that works on Linux. +Next I have to say that the install instructions on the site (and in the tarball) are not written with a newbie in mind and I had to struggle a bit before I could get it to work. The compiling of source code is really similar for I guess all source codes in general on linux and in particular in Ubuntu / Kubuntu but thing is when you are new to the platform and probably have almost all required software in repositories you rarely come across a situation where you need to compile from a source code to install your favourite software. Anyway so without wasting time in chit-chat, below is the step by step installation of Maitreya 6 on Ubuntu / Kubuntu:

    +
    1. Download Maitreya 6 tarball.
    +2. Prepare the system for installing
    +3. Install wxWidgets 2.8 Dependencies
    +4. Configure and Build
    +5. Install
    +6. Create a desktop icon
    +
    +
      +
    1. +Download Maitreya 6 tarball - From your browser goto http://maitreya.svn.sourceforge.net/viewvc/maitreya/ and click on the link "Download GNU tarball". This will download the maitreya.tar.gz file on your system. +
    2. +
    3. +Prepare the system for installing - I used the instructions from ubuntu help documentation that can be found here. However, I have documented the step that I used in sequence to achieve the objective. +
        +
      • Open the terminal.
      • +
      • Type - sudo apt-get install build-essential checkinstall
      • +
      • You will be asked for the password. Enter it.
      • +
      • Once system completes the above instruction and is ready to take next instruction then Type: sudo apt-get install cvs subversion git-core mercurial
      • +
      • Next type: sudo chown yourusername /usr/local/src +
      • +
      +
      Please note that here you need to put the your user name so don't just copy paste. So if say user name is "ankit" , you will type sudo chown ankit /usr/local/src
      +
        +
      • Next Type: sudo chmod u+rwx /usr/local/src +
      • +
      +Now the system is prepared for installing. +
    4. +
    5. +Install wxWidgets 2.8 Dependencies - As mentioned on Maitreya site, the software depends on wxWidgets 2.8 and corresponding packages must be installed. On the site itself is given the code that needs to copied onto the terminal to achieve this. +
        +
      • Either from the site or from below select the text.
      • +
      • sudo apt-get install libwxbase2.8-0 libwxbase2.8-dev libwxgtk2.8-0 libwxgtk2.8-dev wx2.8-headers wx2.8-i18n
      • +
      • Press Ctrl+C
      • +
      • Goto Terminal window and press Ctrl+Shift+V or click on Edit from top menu and select Paste.
      • +
      • Press enter.
      • +
      • You may have to provide password.
      • +
      • This should now install wxWidgets 2.8
      • +
      +Over to next step. +
    6. +
    7. +Configure and Build - There are few things that should be done before we start to configure the downloaded source code. +
        +
      • First goto the folder where maitreya.tar.gz file was downloaded in Step 1.
      • +
      • Now right click and select "Extract Archive To".
      • +
      • Select the location /usr/local/src
      • +
      • This will extract a folder named maiterya into the location /usr/local/src
      • +
      +The content downloaded may change in future but at the time of writing this article it has two folders maitreya5 and maitreya6. All next steps will assume that folders name maitreya6 is present in your download but if not you will just have to replace with whatever folder is inside the extracted maitreya folder. +
        +
      • Goto your termincal window and type: cd /usr/local/src/maitreya/maitreya6/trunk
      • +
      • +
        Now type `./configure`.
        +
        +
      • +
      • +
        If it gives some bash error like permission denied or file does not exist etc try `sh ./confgure`. This is what worked from me though everywhere instructions were to just use without sh.
        +
        +
      • +
      • +
        It will take a bit of time but it should complete without any error and end with some instructions about using "make".
        +
        +
      • +
      • +
        Now type `make` on terminal.
        +
        +
      • +
      • +
        It should be done with no errors if all steps were followed correctly so far.
        +
        +
      • +
      +Over to next step. +
    8. +
    9. +Install - Again there were options around using sudo make install but the ubuntu help I have referred to (link above), suggested that checkinstall is better and it also creates a .deb file and I like the idea so that next time if I have to reinstall I don't need to do all this circus so I used the following and would recommend the same. +
        +
      • So in terminal window type sudo checkinstall.
      • +
      • +
        When you run it it will ask some questions like doc-pak is not there, do you want to create?(Y) just type Y.
        +
        +
      • +
      • +
        Then it shows some options and asks if you want to edit. I typed 2 to edit name and entered Maitreya.
        +
        +
      • +
      • +
        It threw a warning that maitreya 6-1_i386 is not complaint with debian standard or something to that effect and I just pressed enter.
        +
        +
      • +
      • +
        After this it was all fine and finally a message was shown telling a debian package has been created and to remove use dpkg ...
        +
        +
      • +
      +The software should now be installed and you can test it by clicking the file named Maitreya6 at Location - /usr/local/src/maitreya/maitreya6/trunk/src/gui. However if you want to access it easily from desktop there is just one more step. +
    10. +
    11. +Create a desktop icon - To create a desktop icon goto the folder /usr/local/src/maitreya/maitreya6/trunk and copy the file maitreya6.desktop and paste in on desktop. +
    12. +
    +

    This is it you have now installed the Maitreya software !!!

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/install-open-workbench-and-jre-on-wine-in-linux-mint/index.html b/install-open-workbench-and-jre-on-wine-in-linux-mint/index.html new file mode 100644 index 0000000..a6e9a59 --- /dev/null +++ b/install-open-workbench-and-jre-on-wine-in-linux-mint/index.html @@ -0,0 +1,767 @@ + + + + + + +Install Open Workbench and JRE on Wine in Linux Mint - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Install Open Workbench and JRE on Wine in Linux Mint +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +
    + + +
    + +

    What is Open Workbench?

    +

    Open Workbench is a Project Planning Software comparable to Microsoft Project. There are mixed views on whether it is truly open source or not but as it is considered a very good alternative for Microsoft Project and it is free to download, it is something I as a Project Manager would want to know about. After all some of my clients are going to be using this. :) +Now then, as of writing this article Open Workbench is not available from the well publicised site www.openworkbench.org which seems to be down. Instead it is available for download fromhttp://www.itdesign.de/en/products-solutions/open-workbench.html +As you will find that the installer is an exe and the product is made for Windows platform and requires Java Runtime Environment, we will need to either use Windows in virtualbox or Wine. I am going to cover below the steps for installing the software on Linux using Wine. +Steps to install Open Workbench and JRE on Wine V1.2.2 in Linux Mint:

    +
      +
    1. Install JRE in wine
    2. +
    3. Install Open Workbench in wine
    4. +
    5. Configure wine to play well with Open Workbench
    6. +
    7. Configure icon to launch Open Workbench
    8. +
    +

    What do we need?

    +
      +
    1. We will need to download the installer exe file from http://www.itdesign.de/en/products-solutions/open-workbench.html +
    2. +
    3. As the product requires Java Runtime Environment, we will need to download the JRE for windows.
    4. +
    5. We will need access to system32 folder of a WindowsXP machine or in Virtualbox.
    6. +
    +

    Step 1: Install JRE in wine

    +

    You can download the JRE file from http://www.oracle.com/technetwork/java/javase/downloads/index.html . Remember, we only need JRE not JDK so click the download button in second column. +This will open another page for you to select which JRE package you want to download.
    +Alt Text +Click on the radio-button to accept license agreement and then select Windows x86 Offline for download.

    +
    I am assuming that your hardware is 32 bit, if not please select Windows x64 but it will not have the offline version and I have no way of checking if it will work or not; so please leave a comment either way as it will help others.
    +

    Alt Text +a) Copy the downloaded (jre-6u25-windows-i586.exe) file into .wine/drive_C/Program Files. You can do this using GUI on Linux Mint using steps below:

    +
      +
    • Goto MENU-ALL APPLICATIONS-WINE and click on BROWSE C: DRIVE
    • +
    • Click on Program Files folder.
    • +
    • Paste the copied exe file here.
    • +
    +

    Alt Text +b) Right click on the exe file and select "open with wine windows program loader".
    +c) Follow the installation wizard.
    +Alt Text
    +Once completed move on to next step.

    +

    Step 2: Install Open Workbench in wine

    +

    a) Download the “Open Workbench” installer file(Open_Workbench_Setup_1.1.6.exe) and copy it into .wine/drive_C/Program Files.

    +
    TIP: You can use Step 1 bullet "a" to reach drive_c and then click on windows folder and then on system32 folder.
    +

    b) Right click on the exe file and select "open with wine windows program loader".

    +
    TIP: You can use Step 1 bullet "b" to reach drive_c and then click on windows folder and then on system32 folder.
    +

    c) Follow the installation wizard. +Step 3: Configure wine to play well with Open Workbench +Due to some bug identified in Wine V1.2 the odbc32.dll file does not work and gives the error “err:module:attach_process_dlls “odbc32.dll” failed to initialize, aborting” if you try to run file from Terminal. +The following workaround is one suggested on Launchpad (https://bugs.launchpad.net/ubuntu/+source/wine1.2/+bug/572393) and does solve the problem. +I have just tried to make it easier to follow the instructions by providing screenshots and explaining each step.

    +
      +
    1. +On the WindowsXP machine / virtualbox goto following location – C:\WINDOWS\system32
      +Alt Text +
    2. +
    3. +Now copy odbc32.dll and odbcint.dll files onto the Linux machine at this location - .wine/drive_c/windows/system32 . +
    4. +
    +
    TIP: You can use Step 1 bullet "a" to reach drive_c and then click on windows folder and then on system32 folder.
    +
      +
    1. +Now goto wine configuration (On Linux Mint: MENU-ALL APPLICATIONS-WINE-CONFIGURE WINE) +
    2. +
    3. +Click on Libraries tab. +
    4. +
    5. +From the dropdown select odbc32 and click add. +
    6. +
    7. +Click OK +
    8. +
    +

    Step 4: Configure Open Workbench icon to launch the application

    +
      +
    1. +Open terminal (MENU - TERMINAL) +
    2. +
    3. +Type: wine ~/.wine/drive_c/Program\ Files/Open\ Workbench/bin/npWBench.exe +
    4. +
    5. +Once Open Workbench is opened we are sure the command works. Select this command and right click and select copy. +
    6. +
    +
    TIP: You can also press Ctrl+Shift+C to copy the selected text from terminal
    +
      +
    1. +Close "Open Workbench" and goto icon in MENU-ALL APPLICATIONS-WINE. +
    2. +
    3. +Right click the "Open Workbench" icon and click on "Edit Properties". +
    4. +
    5. +In Command click on browse and navigate to following location: .wine/drive_c/Program Files/Open Workbench/bin/ and select the file npWBench.exe. +
    6. +
    +

    Alt Text

    +
      +
    1. Now click "OK"
    2. +
    +

    This is it. You are ready to use Open workbench on Linux using wine.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jagannath-hora-on-linux-play-on-linux-magic/index.html b/jagannath-hora-on-linux-play-on-linux-magic/index.html new file mode 100644 index 0000000..b83dd5b --- /dev/null +++ b/jagannath-hora-on-linux-play-on-linux-magic/index.html @@ -0,0 +1,703 @@ + + + + + + +Jagannath Hora on Linux - Play on Linux Magic - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Jagannath Hora on Linux - Play on Linux Magic +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +
    + + +
    + +

    While I prefer Maitreya as it can run with Linux as native, quite a few of my friends have asked me if Jagannatha Hora will work on Linux and hence this post. +Right, when I tried last time around mid Oct’09, JHora was not working using wine. However, recently things have changed. +If you are not aware or have not used Play On Linux then you should try it out. It works. Anyway back to original topic, the steps to install Jagannatha Hora on Kubuntu 10.04 are as below. (I am mighty sure this will work on other linux machines but I have tried it only on Kubuntu 10.04 which is increasingly becoming my OS of choice.)

    +
    Step 1: Install Play On Linux (POL)
    +Step 2: Download Jagannatha Hora ( JH )
    +Step 3: Install JH using POL
    +
    +
      +
    1. +Install Play On Linux (POL) - You can install using Package Manager - Synaptic or KPackagekit but you will most likely get a little older version. I installed using Synaptic but then updated it by downloading directly from the site or follow this link to download the version 3.7.6. This is latest at the time of writing but it may be best to just get it from the website here. Once downloaded and installed, we move to a very simple and straightforward Step 2. +
    2. +
    3. +Download Jagannatha Hora ( JH ) +
      I am sure if you have come here searching on net, you don't need introduction to this software but in any case I admire the author of this software Mr. P. V. R Narsimha Rao for various reasons. One I consider him my Guru and as I am just a beginner to the world of astrology I find his to book the best reference one can get. I purchased it in 2006 when it was not easily available in UK but now the book itself is included in the software. Though the topic of this post does not require mention of book, I will take a moment to write few words on it. As the author is an engineer from IIT, his approach to subject was structured much like an engineering book. Very logical flow that takes you through the concepts in a way I have not found in any other book till date. It does not mean I don't like other authors. It just means that I recommend this to any beginner as it makes the concepts easy to grasp.
      + OK, now back to topic, if you don't already know, you can get this software by following this link - http://www.vedicastrologer.org/jh/ +Once you download the zip file, extract it in the same folder. Just remember where you extracted. The file you have extracted will be named - jh_full_install.exe. +
    4. +
    5. +Install JH using POL - Open the Play on Linux software. In Kubuntu you will find it by clicking KLauncher -> Applications -> Games. Once it starts, click on "Install".This will open a new window. +In this window, click on the line in lower left hand side corner - “Install a .pol or an unsupported application”. This will close the above window and open another window. +
      Please note that if this window does not close, it appears that Play On Linux has hanged. In such case click on cross to close that window and it will show an error message suggesting that “PlayOnLinux” is not responding or something to that effect but you select to force close it and you will still land up on the next window. I found that this problem went away when I installed the latest version 3.7.6.
      +a) In this window select “Manual installation” and click on “Next”. +b) On the next window that appears, click “Forward”. +c) You will be presented with another window with choices. Default choice being – “Install a program in a new prefix”. Keep it selected or if it is not selected, select it and click on “Forward”, +d) Next will be presented a window asking name for wine prefix. Type “Jhora” and click “Forward” +e) On the next window, there will be two check-boxes. Leave them un-ticked, do nothing and click Forward. +f) Once prefix is created, you will be presented with this window. Browse to the the file extracted in step 2 and then press Forward. +g) Now it should start installing and once complete, it will ask if you want it to be shown on desktop. I chose Yes but it will be up to personal preference. +
    6. +
    +

    Play On Linux can also be used to install Spotify and 7zip, but more importantly you can see that an entry for Jhora has now been created. +Finally on desktop you should be able to see the link for JH. +You should now be able to use JH without any problems. +Hope you found this post useful. +-Ankit.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jupyterlite-on-github-enterprise-with-panel-enabled/index.html b/jupyterlite-on-github-enterprise-with-panel-enabled/index.html new file mode 100644 index 0000000..0295172 --- /dev/null +++ b/jupyterlite-on-github-enterprise-with-panel-enabled/index.html @@ -0,0 +1,800 @@ + + + + + + +JupyterLite on Github Enterprise with Panel enabled - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    JupyterLite on Github Enterprise with Panel enabled +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +
    + + +
    + + + +

    JupyterLite on Github Enterprise with Panel enabled

    + +

    Recently I found myself in a scenario where I had access to Github Enterprise but was limited to a windows machine. Now I wanted to host JupyterLite on Github Enterprise so it can be used by some other people on my team. Challenge was the “Actions” on this instance of Github enterprise were disabled and so I had to build Jupyterlite locally and push it on Github. I figured while at it, I might as well enable the “Panel” and few other dependencies. The steps that worked for me are documented here. This is ofcourse assuming that Python is already installed on the device.

    + +

    The final hosted JupyterLite can be seen in action here.

    + +

    The steps below created a Jupyterlite instance that allows use of Panel using the %pip install panel magic command, and if used on Chrome or its derivatives it also allows exploring local file system.

    + +

    Create and activate Jupyterlite Environment

    + +
    mkdir panel_lite
    +cd .\panel_lite\
    +py -m venv panel_v
    +.\panel_v\Scripts\activate
    +pip install --upgrade pip
    +
    + +

    Install jupyterlite and other extensions

    + +
    pip install --pre jupyterlite
    +pip install jupyter-bokeh ipython ipywidgets jupyterlab-drawio jupyterlab-markup jupyterlab-myst jupyterlab-pygments jupyterlite-p5-kernel jupyterlite-xeus-sqlite libarchive-c matplotlib matplotlib-inline matplotlib-venn myst-nb myst-parser nbconvert numpy openpyxl pandas pandocfilters pkginfo pyopenssl python-dateutil python-dotenv pyviz-comms pyxlsb scipy sql SQLAlchemy sqlparse tornado widgetsnbextension xlrd XlsxWriter zipp jupyterlab-filesystem-access
    +
    + +

    Prepare build directory

    + +
    mkdir jupyterlite_panel
    +cd .\jupyterlite_panel\
    +mkdir pypi
    +cd .\pypi\
    +wget "https://cdn.holoviz.org/panel/0.14.2/dist/wheels/bokeh-2.4.3-py3-none-any.whl" -outfile "bokeh-2.4.3-py3-none-any.whl"
    +wget "https://cdn.holoviz.org/panel/0.14.2/dist/wheels/panel-0.14.2-py3-none-any.whl" -outfile "panel-0.14.2-py3-none-any.whl"
    +
    + +

    Make a record of all that you have installed.

    + +
    pip freeze > requirements.txt
    +
    + +

    Including any existing notebooks

    + +

    If you have some existing notebooks or files you would want to include on the hosted version of Jupyterlite, you must create a directory and name it files

    + +

    Build Jupyterlite and serve locally to test

    + +
    jupyter lite build --output-dir ./dist
    +cd .\dist\
    +py -m http.server 8000
    +deactivate
    +
    + +

    The folder structure after build should look somewhat like below:

    + +
    panel_lite:.
    +├───.cache
    +├───dist
    +│   ├───api
    +│   ├───build
    +│   ├───doc
    +│   ├───extensions
    +│   ├───files
    +│   ├───kernelspecs
    +│   ├───lab
    +│   ├───pypi
    +│   ├───repl
    +│   ├───retro
    +│   └───tree
    +├───files
    +│   ├───data
    +│   └───sample.ipynb
    +├───pypi
    +└───panel_v
    +
    + +

    Directory Structure Overall +Directory Structure of built dist folder

    + +

    Push it to Github Enterprise

    + +

    Github Desktop is the easiest way to do it.

    + +

    Open Github desktop and create new reporsitory:

    + +

    Create New Repository

    + +

    On Github desktop point the Local Path field to \panel_lite\jupyterlite_panel\dist and do the initial commit and push to origin.

    + +

    Provide Local Path

    + +

    Host Jupyterlite as Github pages

    + +
      +
    • Click on button View on Github on Github Desktop.
    • +
    + +

    Open Github Enterprise

    + +
      +
    • Github Enterprise will open in browser to the created repository
    • +
    + +

    Enable Pages on Github Enterprise

    + +
      +
    • Click on Settings > Pages
    • +
    • select Branch: master (or main) and root as source.
    • +
    • Click Save and the message should be displayed in green box with the URL to access Jupyterlite.
    • +
    + +

    Finally open the URL to access JupyterLite

    + +

    JupyterLite hosted on Github Enterprise

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kubuntu-blog-entry/index.html b/kubuntu-blog-entry/index.html new file mode 100644 index 0000000..d64d698 --- /dev/null +++ b/kubuntu-blog-entry/index.html @@ -0,0 +1,772 @@ + + + + + + +KUBUNTU Blog Entry - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    KUBUNTU Blog Entry +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +
    + + +
    + +

    This is how my love for Kubuntu has started….and growing by the minute… +I was experimenting with different Linux distros and then I went ahead with Linux Mint – which is quite popular and is know as “ubuntu done right”. I liked it and started dwelling more and played around with themes and such before I figured it is becoming kind of prescriptive and thought will actually play with the a distro with KDE not too far from Ubuntu – Kubuntu and boy was I surprised with the difference … all to their own opinion but mine is KDE is one helluva beautiful thing….the stuff that is done here is simply amazing …. alright once I was done with dropping my jaw at every step I figured there is a bit of learning curve right from the word go and so before I start forgetting what all I have learnt I felt it will be safe jotting it all down and thus this post: +OK, so lets start with all the steps involved:

    +
      +
    • How to upgrade Ubuntu 9.10 to Kubuntu 9.10
    • +
    • Restoring the default panel at bottom of the screen?
    • +
    • Problems with system audio, you-tube audio and how it all finally started working for me.
    • +
    • Change the theme.
    • +
    • Change the splash screen
    • +
    • How I managed to change the Log-in Screen (KDM Screen)
    • +
    • Finally, change boot splash screen
    • +
    +

    Right so if any of this catches your fancy, please be my guest…:)

    +
      +
    1. +How to upgrade Ubuntu 9.10 to Kubuntu 9.10:
      +Plenty of information on the website and better presented at the one site than other. I liked the way it was explained here: +http://www.psychocats.net/ubuntu/kde +This site is also a good source of information in general so well worth a bookmark. +
    2. +
    3. +Restoring the default panel at bottom of the screen? +Once I finished installing Kubuntu as per the instruction from above link, I started playing around and ended up deleting the panel thus learning about how to get that back. In the process I learnt important lesson on making a backup of setting as soon as you have configured system to your first level of satisfaction as if you do not take this precaution you will have to start all over again. +The steps I took were as below: +
        +
      • +Goto Applications tab, click on Settings → Terminal and open the terminal, Type following commands: +sudo cp -R ~/.kde .kde_backup sudo rm -rfv .kde kquitapp plasma-desktop sudo restart +
      • +
      +This is it. +Now once the system restarts, your panel will be in place. However you will have to put all your configuration again. So to avoid that in future, repeat first two steps and next time you end up in similar situation all you need to do is give this command sudo cp -R ~/.kde_backup .kde and you should be good to go. +
    4. +
    5. +Problems with system audio, you-tube audio and how it all finally started working for me. +When I opened a video on youtube, I was not getting any sound. After some googling with no results I tried this and again today after restoring panel the sound was gone and trying this made it work so I assume at-least for all Dell Inspiron 1525 this should work. +Open the voice control by clicking on volume icon in system tray and clicking on Mixer as and then unmute the “Headphone”. +After this I restarted the computer and logged into GNOME session, and played a youtube video. Sound was coming alright. I logged out from GNOME session and logged in again in KDE session and tried a video on youtube and it was working fine. +
    6. +
    7. +Change the theme and wallpaper. +To change the theme, it is quite simple. Just right click anywhere on desktop and select desktop settings, and under theme either select the available option or download from available options and install. For wallpaper also do the same thing. +
    8. +
    9. +Change the splash screen +Download the splash screen. I downloaded “Kcarbon” splash screen from
      +http://kde-look.org/index.php?xcontentmode=35x45 as it was going nicely with my desktop theme and wallpaper. +Once it was downloaded, I opened from Application Menu → Computer → System Settings → Appearance (under look and feel section) → Splash Screens to get to this screen and then click install, select location where you downloaded the tar file. +
    10. +
    11. +How I managed to change the Log-in Screen (KDM Screen) +
      +UPDATE +
        +
      1. Open the "System Settings" KDE application.
      2. +
      3. Choose the "Advanced" tab.
      4. +
      5. Go to "Login Manager".
      6. +
      7. Choose "Theme" tab.
      8. +
      9. Click on the "Install new theme" button.
      10. +
      11. Choose the compressed file and click ok.
      12. +
      +
      +This one was rather flaky solution. I could not find anything that could change the initial background screen where we enter user credentials and password. This is called KDM Theme as I learnt while finding out how to do it. Several searches on google talked about something called KDM Theme Manager but they are all old mails and for new KDE 4+ it comes pre-installed and I did not know what to do. +So I downloaded one of the KDM themes from http://kde-look.org and unzipped it into the following location as root. +
      `kdesudo dolphin /usr/share/kde4/apps/kdm/themes/`
      +
      +Next, I renamed the folder “oxygen-air” as “oxygen-air_old” and the downloaded and unzipped folder as “oxygen-air”. +This is it. When I restarted I had the brand new KDM Theme to my satisfaction.:) +
    12. +
    13. +Finally, This is how I changed my boot splash screen:
      +I tried many suggestions but only after going through the whole GRUB2 entries as explained on this link I got the result I was looking for. However, I did have to do a little bit of trial and error so I will put the steps that finally worked for me. These may also come handy if you do not want to know in depth about all GRUB 2 stuff. +http://ubuntuforums.org/showthread.php?t=1195275&highlight=grub2 +
        +
      1. In GIMP, open the image you want to show as the background when GRUB shows choices to select which OS you want to load at start-up
      2. +
      3. Now goto Image -> Scale and then enter height as 640, press tab.
      4. +
      5. Now save the image as a .png file.
      6. +
      +
        +
      • +in the terminal type kdesudo dolphin /usr/share/images/desktop-base +
      • +
      • +Copy the image saved in step 3 and paste it in the desktop-base folder +
      • +
      • +Now in the terminal type kdesudo kate /etc/grub.d/05_debian_theme +
      • +
      • +This will open 05_debian_theme file with kate text editor. +
      • +
      • +Go to line 16 and find the following line and edit the highlighted area, replacing it with the name of image copied into desktop-base folder in Step 5 for i in {/boot/grub,/usr/share/images/desktop-base}/moreblue-orbit-grub.{png,tga} ; do +
      • +
      • +Save the file and in the terminal type: +
          +
        • sudo update-grub2
        • +
        • sudo reboot
        • +
        +
      • +
      +
    14. +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linux-mint-on-android-through-vnc-and-jump/index.html b/linux-mint-on-android-through-vnc-and-jump/index.html new file mode 100644 index 0000000..4a5a3c2 --- /dev/null +++ b/linux-mint-on-android-through-vnc-and-jump/index.html @@ -0,0 +1,787 @@ + + + + + + +Linux Mint on Android through VNC and Jump - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Linux Mint on Android through VNC and Jump +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +
    + + +
    + +

    Today “Jump” was available for free on Amazon as the app of the day and since it’s nearly 7 quids on google play store, I downloaded. For windows and Mac users they have a pretty straight forward set-up but as usual for Linux it means some work but in the end it leaves you with a set-up you can trust and feel secure about.

    + +

    There are three things that need to be set-up for this to work:

    + + + +

    1. Linux Mint machine should be set-up for x11vnc and ssh servers

    + +

    1.1: Install X11VNC by typing following command in terminal: +sudo apt-get install x11vnc

    + +

    1.2: Create a password for VNC using following command in terminal and providing a password and answering yes for the prompt to store password in a file: +x11vnc -storepasswd

    + +

    1.3: Now to ensure that X11VNC starts at boot go to menu and type start, click on startup application as shown in the screenshot below:
    +Fig-1
    +Then in the window that this will open click on “Add” and enter a “Name” and in “Command” field enter x11vnc -forever -xkb -usepw -display :0 as shown below.
    +Fig-2
    +VNC set-up on machine is complete.

    + +

    1.4 Now install openssh-server using following command on terminal: +sudo apt-get install openssh-server

    + +

    1.5 We will need to change some parameters in ssh configuration for making it secure as by default it allows root login but I dont want that for remote access and would advice most regular users to do so as well. So we will first make a backup of existing configuration file using the command below: +sudo cp /etc/ssh/sshd_config ~

    + +

    1.6 Now, we will edit the actual config file using following command: +gksudo gedit /etc/ssh/sshd_config

    + +

    1.7 Once the file is open change the parameter "PermitRootLogin" to "no". It’s on line 27 for me.

    + +

    1.8 Now the default port for ssh is 22 but I recommend changing it to something else such as 5432. To do so change the parameter “Port” from 22 to whatever port you want to put. In this example it will be 5432. For me “Port” parameter is on line 5.

    + +

    1.9 Save the changes and close gedit.

    + +

    1.10 Now we will restart the ssh server using following command in terminal +sudo restart ssh +For Arch, you can use the command: +sudo systemctl start sshd +followed by +sudo systemctl enable sshd.service +to ensure ssh daemon is enabled at startup.

    + +

    1.11 Restart the machine and machine set-up is done.

    + +

    2. Router firewall should be configured to allow inbound traffic on specific ports.

    + +

    This may involve different step from those given below depending on the router in use. Following steps are meant for configuring the sky router. However principle is same. We will be creating specific service definition and port on router and then create a firewall rule that allows inbound traffic and directs it to Linux machine we configured above.

    + +

    2.1 Type following command on terminal: +ifconfig

    + +

    2.2 this will list lot of numbers, what we are interested in is the number just after “inet addr:” under wlan0. It will be something like 192.168.0.10.

    + +

    2.3 Open sky router config through browser using 192.168.0.1 and click on “Security”. You will need to enter router username and password.
    +Fig-3

    + +

    2.4 Then click on “Services” and then click on “Add Custom Services”.

    + +

    2.5 Enter as shown in Figure 4 and Start Port as 5900, Finish Port as 5900 and click on “Apply”.
    +Fig-4

    + +

    5900 is default port for display 0 in VNC. If you have changed it like me you will need to enter that port. To change port you will need to use “x11vnc -forever -xkb -usepw -autoport nnnn -display :0” option in step 1.3. This is not required for security but in case you have two different machines then this approach will come handy.

    + +

    2.6 Now click on “Add Custom Services” again and this time enter as shown in next screenshot. Start Port and End Port should be same as entered in step 1.8, so for this example it will be 5432. Then click on “Apply”.
    +Fig-5

    + +

    2.7 Now we need to set the firewall for these services. To do so, click on “Firewall Rules” then click on “Add” under inbound services. +2.8 Configure fields as shown in next screen-shot below and click on “Apply”:
    +Fig-6

    + +

    2.9 Now we will do same for SSH, so again click on “Add” under inbound services and configure fields as shown in screen-shot below and click on “Apply”:
    +Fig-7

    + +

    2.10 Click on “Apply” under “Inbound Services”.

    + +

    2.11 In browser on the router management page, click on “Advanced” > “Remote Management” and on this screen make note of the IP address (number after http:// in red box in next screen-grab) shown under “Remote Management Address”.
    +Fig-8

    + +

    2.12 Go to https://www.dlinkddns.com/signin and create an account. Refer this page for the how-to (http://www.dlinkddns.com/howto) and you will need to use the IP from step 2.11 above as the host. At the end of it you will have a hostname like “yourname.dlinkddns.com”, username and password for logging in to dlinkddns site.

    + +

    2.13 Once this is done, go to the browser with sky router management and click on “Advanced”>“Dynamic DNS” and fill as shown in screen-shot below:
    +Fig-9

    + +
    Host Name: Hostname from Step 2.12 (yourname.dlinkddns.com in this example)
    +User Name: D-Link site username
    +Password: D-Link site password
    +
    +


    +Fig-10 +

    + +

    2.14 Once above information is filled, click on “Apply” and then click on “Show Status”. It should open a separate window and showing the message “request successful”.

    + +

    Sky Router is now configured.

    + +

    3. Jump or an equivalent VNC viewer should be configured on the android device.

    + +

    3.1 On the android device open Jump and click on the “+” sign in right hand corner. +3.2 In the “Address” Field enter the hostname from 2.12 (yourname.dlinkddns.com in this example) and select connection type as “VNC” and click save. +3.4 Change the “Authentication Method” to “VNC Password” +3.5 Tap on “SSH Tunnel”, click on “Enabled” checkbox. +3.6 In Username enter the username used to log into the machine configured above in Step 1. +3.7 In Host Name, use the the hostname from 2.12 (yourname.dlinkddns.com in this example) +3.8 Change the port to one used in 1.8. So in this case 5432. +3.9 Password can be left empty and when asked during connection provide the one used to log on to the machine with this username. +3.10 Press back button and click on entry. You will be shown a SSH key notification, say ok. Then you will be asked for a password, provide the password you use to log onto your machine with the username provided in 3.6. +3.11 Then you will be asked for the VNC password, provide the password from step 1.2.

    + +

    You will now be able to view your desktop on your android machine.

    + +

    Fig-11 +Fig-12

    + +

    All Done !!!

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logitech-ex100-wireless-keyboard-and-mouse-not-working-read-on/index.html b/logitech-ex100-wireless-keyboard-and-mouse-not-working-read-on/index.html new file mode 100644 index 0000000..c579bbd --- /dev/null +++ b/logitech-ex100-wireless-keyboard-and-mouse-not-working-read-on/index.html @@ -0,0 +1,706 @@ + + + + + + +Logitech Ex100 - Wireless Keyboard and Mouse not working? Read on.. - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Logitech Ex100 - Wireless Keyboard and Mouse not working? Read on.. +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    Today for some unknown reason suddenly my Logitech Wireless Keyboard and Mouse - +Model Number EX100 stopped working and very stubbornly denied to work despite my +repeated attempts on all obvious and logical steps - resets, battery change and +such.

    + +

    Logitech Cordless Desktop EX100 (920-000879) +After trawling through the internet for some time I figured that I am not alone +in facing the issue but surely have been hit quite late by it. It appears +Logitech was more than happy to replace it at one point.

    + +

    Anyway, further forum hopping lead me to a post that felt like a very unlikely +solution but I gave it a shot and should I say more? It worked indeed. So this +post is not only for world but for my future reference.

    + +

    With thanks to “Andromedia” on logitech forum, I present the solution that +resurrected my dead keyboard and mouse.

    + +

    For Keyboard:

    + +
      +
    1. Remove the batteries from the Keyboard.
    2. +
    3. Type on the keyboard for about 10 to 30 seconds.
    4. +
    5. Insert the batteries back into Keyboard.
    6. +
    7. Press Left Alt+Left Ctrl+F12 all at the same time and hold for about a +second.
    8. +
    9. Now PRESS the Connect Button on Receiver. (DO NOT HOLD)
    10. +
    11. Now PRESS the Connect Button on Keyboard. (DO NOT HOLD)
    12. +
    13. Finally, Press “Esc” on Keyboard.
    14. +
    + +

    For Mouse:

    + +
      +
    1. PRESS the Connect Button on Receiver. (DO NOT HOLD)
    2. +
    3. Now PRESS the Connect Button on Mouse. (DO NOT HOLD)
    4. +
    + +

    That should do the trick. At least for me it did. :)

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logseq-customisation-for-project-management-teamplate/index.html b/logseq-customisation-for-project-management-teamplate/index.html new file mode 100644 index 0000000..fc17d87 --- /dev/null +++ b/logseq-customisation-for-project-management-teamplate/index.html @@ -0,0 +1,2672 @@ + + + + + + +Logseq Customisations for Project Management Template - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Logseq Customisations for Project Management Template +

    + + +

    + + + + + + + + + + + + 23 minute read + + + +

    + + +
    + + +
    + + + +

    Background

    + +

    While the overall planning of project timeline gets lot of attention in the world of software, the most important aspect of project management in my experience is maintaining and tracking Issues and Actions. This in normal project management practice is carried out through the use of an Actions Log and an Issues Log. In addition any medium complexity project invariably will have internal and external dependencies / constraints, risks which I tend to track on Constraints Log and Risks Log. Finally every project has decisions that I track on a Decisions Log.

    + +

    Additionally, any opportunities that I identify during the course of project that are not in the scope of my project I capture those too in an Opportunities Log

    + +

    Now all these logs have fairly standard fields so I created and started using an excel template and started calling it CARDIO Log short for log of Constraints, Actions, Risks, Decisions, Issues, Opportunities for a given project. This has worked well for me over the past 15 years or so but there are times where in order to maintain it meant cross linking an action to an issue or a risk and so on and more often than not it would become easier to just track actions in one of the other logs and that would make it a bit chaotic.

    + +

    That problem, however, is what I thought, can be resolved using Logseq especially after starting with the template by the Logseq community user Luhman and starting with his template and explanation provided here.

    + +

    Using logseq from browser when binary install is not possible

    + +

    When binary install is not possible, one can still use logseq from browser by navigating to the web app demo page from Chrome browser and then selecting a local directory where notes will be saved. It works perfectly fine except there is no option to install plugins. So inorder for issues table etc to work from broswer following steps will suffice:

    + +

    Option 1

    +
      +
    • Select the directory where your notes (or graphs in logseq speak) will be saved.
    • +
    • Grant permission for local file access when asked by the browser.
    • +
    • Now if you will press Ctrl+k it will open search box but the cusrsor goes to the omnibox (where you type the url for the page) and then you will have to either manually click into the logseq search or press esc key three times to be able to get the cursor back in the search box. It may be easier to just click on Search icon in left hand navigation at top of the screen (next to the hamburger menu). +
        +
      • Alternatively configure the keyboard shortcut to Alt+k as that does not conflict with any other keyboard shortcut.
      • +
      • To add custom shortcuts, you can navigate to the shortcuts page with g s. Press the blue button corresponding to a given shortcut and a modal should pop up. Press the keybinding you want and then press Save.
      • +
      +
    • +
    • Now search for logseq/custom.css and add the whole css from this gist.
    • +
    • Then search for logseq/config.edn and add the whole edn from this gist +
    • +
    + +

    If you were installing the binary, you can still copy the entire css and edn from above. If you do that, you can directly skip to the section - Create Templates.

    + +

    Option 2

    +
      +
    • Download the archive from my github repository here +
    • +
    • Unzip the downlaoded repository and delete the README.md file.
    • +
    • Navigate to journals directory where there will be three files. Delete all these files.
    • +
    • Navigate back and open the pages directory which will have 7 files. Except for content.md and templates.md, delete all other files.
    • +
    • Now copy the logseq and pages directories into the directory where you want your notes saved.
    • +
    • Open logseq from chrome and add the directory from step above where you copied the two folders.
    • +
    + +

    If you were installing the binary, you can follow option 2 and then there will be no need to read rest of this article :).

    + +

    Customisations

    + +

    Sample Project Log

    +

    Screenshot_13-8-2024_15949_

    + +

    Preparation

    + +
      +
    • +

      First we will ensure our actions log which will be generated using the Logseq’s inbuilt feature for To Do Lists displays fields that we want it to display.

      + +
        +
      • Press Ctrl+K and search for logseq/config.edn +
      • +
      • In the code block search for following lines:
      • +
      +
    • +
    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +
        ;; Advanced queries `:result-transform` function.
    +    ;; Transform the query result before displaying it.
    +    :query/result-transforms
    +    {:sort-by-priority
    +     (fn [result] (sort-by (fn [h] (get h :block/priority "Z")) result))
    +    }
    +    
    +
    + +
      +
    • Now replace these with following which is slightly reduced compared to the Source as I only needed “Deadline” for my purposes and I don’t use the “Scheduled” part of the Logseq task management feature:
    • +
    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    +49
    +50
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +73
    +74
    +75
    +76
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +97
    +98
    +99
    +
        ;; Advanced queries `:result-transform` function.
    +    ;; Transform the query result before displaying it.
    +    :query/result-transforms
    +    {:sort-by-priority
    +     (fn [result] (sort-by (fn [h] (get h :block/priority "Z")) result))
    +     :add-task-attrs (fn [result]
    +       (def months {1 "January" 2 "February"  3 "March" 4 "April" 5 "May" 6 "June" 7 "July" 8 "August" 9 "September" 10 "October" 11 "November" 12 "December"})
    +       (def monthName (fn [dd]
    +             (get months (int dd))
    +            ))
    +    	  
    +       ;; source: https://discuss.logseq.com/t/add-query-input-or-function-day-of-week/18361/12
    +       (def days {0 "Saturday" 1 "Sunday" 2 "Monday" 3 "Tuesday" 4 "Wednesday" 5 "Thursday" 6 "Friday"})
    +       (def weekDay (fn [date]
    +             (def month (quot (mod date 10000) 100))
    +             (def month6 (quot (- month 8) 6))
    +             (def year6 (+ (quot date 10000) month6))
    +             (def yearnum (mod year6 100))
    +             (def century (quot year6 100))
    +             (def d (mod (+ (mod date 100) (quot (* 13 (inc (- month (* month6 12)))) 5) yearnum (quot   yearnum 4) 
    +             (quot century 4) (* 5 century)) 7))
    +             (get days d)
    +            ))
    +    	  
    +       (def suffixes {0 "th" 1 "st" 2 "nd" 3 "rd" 4 "th" 5 "th" 6 "th" 7 "th" 8 "th" 9 "th"})
    +       (def positionalSuffix (fn [dd]
    +             (if (or (= dd "11") (= dd "12") (= dd "13"))
    +               (get suffixes 0)
    +               (get suffixes (int (subs dd (count dd) 1))) )
    +            ))
    +    	  
    +       (def token (fn [s] (str "⟨" s "⟩")))
    +       (def format
    +         (-> (get (js->clj (call-api "get_user_configs")) "preferredDateFormat")
    +            (clojure.string/replace "do" (token "1"))
    +            (clojure.string/replace "dd" (token "2"))
    +            (clojure.string/replace "d" (token "3"))
    +            (clojure.string/replace "EEEE" (token "4"))
    +            (clojure.string/replace "EEE" (token "5"))
    +            (clojure.string/replace "EE" (token "6"))
    +            (clojure.string/replace "E" (token "7"))
    +            (clojure.string/replace "MMMM" (token "8"))
    +            (clojure.string/replace "MMM" (token "9"))
    +            (clojure.string/replace "MM" (token "10"))
    +            (clojure.string/replace "M" (token "11"))
    +            (clojure.string/replace "yyyy" (token "12"))
    +            (clojure.string/replace "yy" (token "13"))
    +         ))
    +    	  
    +       (def parseDate (fn [date]
    +             (if-not date nil
    +               (let [
    +                   regex (re-pattern "(\\d{4})(\\d{2})(\\d{2})")
    +                   [_ yyyy mm dd] (re-matches regex (str date))
    +                   yy (subs yyyy 2 4)
    +                   d (str (int dd))
    +                   do (str d (positionalSuffix dd))
    +                   mmmm (monthName mm)
    +                   mmm (subs mmmm 0 3)
    +                   m (str (int mm))
    +                   eeee (weekDay date)
    +                   eee (subs eeee 0 3)
    +                   ee (subs eeee 0 2)
    +                   e eee
    +                 ]
    +                 (-> format
    +                   (clojure.string/replace (token "1") do)
    +                   (clojure.string/replace (token "2") dd)
    +                   (clojure.string/replace (token "3") d)
    +                   (clojure.string/replace (token "4") eeee)
    +                   (clojure.string/replace (token "5") eee)
    +                   (clojure.string/replace (token "6") ee)
    +                   (clojure.string/replace (token "7") e)
    +                   (clojure.string/replace (token "8") mmmm)
    +                   (clojure.string/replace (token "9") mmm)
    +                   (clojure.string/replace (token "10") mm)
    +                   (clojure.string/replace (token "11") m)
    +                   (clojure.string/replace (token "12") yyyy)
    +                   (clojure.string/replace (token "13") yy)
    +                 )
    +               )
    +             )
    +            ))
    +    	  
    +    	  
    +       (map (fn [x]
    +         (update x :block/properties (fn [u]
    +           (-> u
    +               (assoc :marker (str (get x :block/marker)) )
    +               (assoc :priority (str (get x :block/priority)) )
    +               (assoc :deadline (parseDate (get x :block/deadline)) )
    +               (assoc :repeated? (str (get x :block/repeated?)) )
    +           )
    +         ))
    +       )
    +       result)
    +     )
    +     }
    +    
    +
    + +

    Plugins

    + +
      +
    • Click on Three Dots in top right corner of the screen and click on menu entry Plugins and then click on Marketplace +
    • +
    • Now install the following plugins: +
        +
      • logseq-agenda
      • +
      • logseq-automatic-linker
      • +
      • logseq-datenlp-plugin
      • +
      • logseq-diagrams-as-code
      • +
      • logseq-doc
      • +
      • logseq-emoji-picker-fork
      • +
      • logseq-emoji-shortcodes
      • +
      • logseq-luckysheet
      • +
      • logseq-markdown-table
      • +
      • logseq-paste-more
      • +
      • logseq-plugin-automatic-url-title
      • +
      • logseq-plugin-show-weekday-and-week-number
      • +
      • logseq13-full-house
      • +
      • logseq13-missing-commands
      • +
      +
    • +
    + +

    Look and Feel

    +
      +
    • Now, I quite like the Mia Quattro Theme that can either be installed as a theme from marketplace or just by including the following line in logseq/custom.css just under the comment /*Theme*/ right at the top of the file like so:
    • +
    + +
    
    +
    +
    +
    1
    +2
    +
    /*Theme*/
    +@import url('https://playerofgames.github.io/logseq-mia-theme/mia_quattro.css');
    +
    + +
      +
    • However this theme had some quirks which can be refined by adding following css overrides:
    • +
    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    +49
    +50
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +73
    +74
    +75
    +76
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +97
    +98
    +99
    +100
    +101
    +102
    +103
    +104
    +105
    +106
    +107
    +108
    +109
    +110
    +111
    +112
    +113
    +114
    +115
    +116
    +117
    +118
    +119
    +120
    +121
    +122
    +123
    +124
    +125
    +126
    +127
    +128
    +129
    +130
    +131
    +132
    +133
    +134
    +135
    +136
    +137
    +138
    +139
    +140
    +141
    +
    /* Override tag and note color scheme for tags from mia_quattro theme  */
    +a.tag{
    + font-size: 100%;
    + color: var(--lx-accent-11,var(--ls-tag-text-color,hsl(var(--primary))));
    +}
    +
    +svg.note {
    +   color: var(--lx-accent-11, var(--rx-yellow-08))
    +}
    +
    +svg.tip {
    +   color: var(--lx-accent-11,var(--rx-blue-08))
    +}
    +
    +/* Selection lists (search, tag complete, etc.) */
    +
    +#ui__ac .chosen,
    +.chosen {
    +	--ls-primary-text-color: #ee0606;
    +	--ls-link-ref-text-color: #eaeaea;
    +	--ls-link-ref-text-hover-color: #fafafa;
    +	--ls-quaternary-background-color: var(--accent-dark-color);
    +	--ls-icon-color: var(--ls-primary-text-color);
    +}
    +
    +#ui__ac .chosen {
    +	background-color: var(--accent-dark-color);
    +}
    +
    +.menu-link.chosen  {
    +	color: var(--ls-primary-text-color) !important;
    +}
    +
    +/* to make todo checkbox visible */
    +.form-checkbox {
    +	--ls-page-checkbox-border-color: var(--accent-color);
    +
    +	border: 1px solid var(--ls-page-checkbox-border-color);
    +	border-radius: 5px;
    +	opacity: 1;
    +}
    +/* blockquote tweaks (reduce margin & padding and add custom colours) */
    +
    +blockquote {
    + padding: 8px 12px;
    + border-left: 5px solid;
    + border-left-color: var(--ls-page-blockquote-border-color, #7cfc00);
    + margin: 0.3rem 0 !important;
    +}
    +
    +blockquote.yellow {
    + border-left-color: #ffe85580;
    +}
    +
    +blockquote.blue {
    + border-left-color: #84b5ff80;
    +}
    +
    +blockquote.red {
    + border-left-color: #ff558280;
    +}
    +
    +.ls-block[data-refs-self^=".blue"] .blockquote{
    + border-left-color: #84b5ff80;
    +}
    +
    +/* ==mark==  tweaks */
    +
    +mark {
    +  background: var(--ls-page-mark-bg-color);
    +  color: var(--ls-page-mark-color);
    +  padding: 1px 2px;
    +  margin: 0 2px;
    +  border-radius: 3px;
    +}
    +
    +mark.yellow {
    +  background: var(--ls-page-mark-bg-color);
    +  color: var(--ls-page-mark-color);
    +}
    +
    +mark.pink {
    +  background-color: #ff89be80;
    +  color: white;
    +}
    +
    +mark.blue {
    +  background-color: #84b5ff80;
    +  color:white;
    +}
    +
    +mark.green {
    +  background-color: #97ff9780;
    +  color: yellow;
    +}
    +
    +mark.red {
    +  background-color: #ff558280;
    +  color: white;
    +}
    +
    +mark.grey {
    +  background-color: #80808080;
    +  color: white;
    +}
    +
    +mark.gray {
    +  background-color: #80808080;
    +  color: white;
    +}
    +
    +mark.orange {
    +  background-color: #ffb86c80;
    +  color: white;
    +}
    +
    +mark.purple {
    +  background-color: #c097ff80;
    +  color: white;
    +}
    +
    +/* add traffic lights to prioritized tasks */
    +
    +.priority[href="#/page/A" i]::before {
    + content: "🔴";
    + margin-right: 2px;
    +}
    +
    +.priority[href="#/page/B" i]::before {
    + content: "🟡";
    + margin-right: 2px;
    +}
    +
    +.priority[href="#/page/C" i]::before {
    + content: "🟢";
    + margin-right: 2px;
    +}
    +
    +.opacity-50 {
    + opacity: 1;
    +}
    +
    + +
      +
    • As our templates later will depend on v-kanban plugin but I did not want to include the whole css and also wanted to modify the icons it shows with Pros and Cons, I include the following css on logseq/custom.css:
    • +
    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    +49
    +50
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +73
    +74
    +75
    +76
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +97
    +98
    +99
    +100
    +101
    +102
    +103
    +104
    +105
    +106
    +107
    +108
    +109
    +110
    +111
    +112
    +113
    +114
    +115
    +116
    +117
    +118
    +119
    +120
    +121
    +122
    +123
    +124
    +125
    +126
    +127
    +128
    +129
    +130
    +131
    +132
    +133
    +134
    +135
    +136
    +137
    +138
    +139
    +140
    +141
    +142
    +143
    +144
    +145
    +146
    +147
    +148
    +149
    +150
    +151
    +152
    +153
    +154
    +155
    +156
    +157
    +158
    +159
    +160
    +161
    +162
    +163
    +164
    +165
    +166
    +167
    +168
    +169
    +170
    +171
    +172
    +173
    +174
    +175
    +176
    +177
    +178
    +179
    +180
    +181
    +182
    +183
    +184
    +185
    +186
    +187
    +188
    +189
    +190
    +191
    +192
    +193
    +194
    +195
    +196
    +197
    +198
    +199
    +200
    +201
    +202
    +203
    +204
    +205
    +206
    +207
    +208
    +209
    +210
    +211
    +212
    +213
    +214
    +215
    +216
    +217
    +
    /* -- like dislike ----------------------------------------- */
    +
    +.ls-block[data-refs-self*="pros"] .block-children .bullet-container .bullet {
    + display: none;
    +}
    +
    +.ls-block[data-refs-self*="pros"] .block-children .bullet-container:after {
    + content: "+";
    + font-size: 20px;
    + color: #1cd41c;
    +}
    +
    +.ls-block[data-refs-self*="cons"] .block-children .bullet-container:after {
    + content: "-";
    + color: red;
    +}
    +
    +a.tag[data-ref*="pros"] {
    + font-size: .8rem;
    + background: #014935e0;
    + color: rgb(202, 247, 118);
    + padding: 0 6px 3px;
    + border-radius: var(--ls-border-radius-low);
    + border: 1px solid rgba(137, 207, 96, 0.925);
    +}
    +
    +a.tag[data-ref*="cons"] {
    + font-size: 13px;
    + background: #4b033bda;
    + color: rgb(255, 116, 128);
    + padding: 0 6px 3px;
    + border-radius: var(--ls-border-radius-low);
    + border: 1px solid rgba(182, 13, 41, 0.925);
    +}
    +
    +a.tag[data-ref*="pros"]:hover {
    + filter: contrast(2) brightness(10);
    +}
    +
    +a.tag[data-ref*="pros"]:before {
    + content: "✅ ";
    + font-size: 13px
    +}
    +
    +a.tag[data-ref*="cons"]:before {
    + content: "❌ ";
    + font-size: 13px
    +}
    +
    +
    +a.tag[data-ref*="red"]:before {
    + content: "🔴";
    + margin-right: 2px;
    +}
    +
    +a.tag[data-ref*="amber"]:before {
    + content: "🟠";
    + margin-right: 2px;
    +}
    +
    +a.tag[data-ref*="green"]:before {
    + content: "🟢";
    + margin-right: 2px;
    +}
    +
    +a.tag[data-ref*="on-hold"]:before {
    + content: "🟣";
    + margin-right: 2px;
    +}
    +
    +a.tag[data-ref*="yellow"]:before {
    + content: "🟡";
    + margin-right: 2px;
    +}
    +
    +a.tag[data-ref*="closed"]:before {
    + content: "🔵";
    + margin-right: 2px;
    +}
    +
    +a.tag[data-ref*="no-go"]:before {
    + content: "🚫";
    + margin-right: 2px;
    +}
    +
    +a.tag[data-ref*="merged"]:before {
    + content: "⚪";
    + margin-right: 2px;
    +}
    +
    +/* -------------------------------- like dislike end  ------ */
    +
    +/*===========================================================*/
    +/* css columns view / kanban  v20220510--------------------- */
    +/* use: inline tag #kanban, #kanban-small or #kanban-wXXX    */
    +/* try:  #kanban-w200,#kanban-w300, #kanban-w400             */
    +
    +div[data-refs-self*="kanban"]>.block-children-container.flex {
    +    width: 100%;
    +}
    +
    +div[data-refs-self*="kanban"]>.block-children-container.flex>.block-children.w-full {
    +    display: inline-flex;
    +    position: relative;
    +    overflow-x: auto !important;
    +    overflow-y: hidden;
    +    margin: 0 10px;
    +}
    +	  
    +div[data-refs-self*="kanban"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
    +    display: inline-block;
    +    padding: 0;
    +    width: inherit;
    +    min-width: 200px;
    +    margin-right: 10px;
    +}
    +	  
    +/* wide */
    +
    +div[data-refs-self*="kanban-small"]>.block-children-container.flex>.block-children,
    +div[data-refs-self*="kanban-wide"]>.block-children-container.flex>.block-children {
    +   min-width: 90vw;
    +   left: 50%;
    +   transform: translate(-50%);
    +   background-color: var(--ls-primary-background-color);
    +   overflow-x: scroll !important;
    +   overflow-y: hidden;
    +   margin: 10px 30px;
    +}
    +
    +div[data-refs-self*="kanban-wide"]>.block-children-container.flex>.block-children>div.ls-block {
    +   display: inline-block;
    +   min-width: 350px;
    +   padding: 8px 0px !important;
    +   font-size: 0.85rem;
    +   margin: 5px 0px;
    +   background-color: var(--ls-secondary-background-color);
    +   box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
    +   border-radius: var(--ls-border-radius-medium);
    +}
    +
    +
    +/* #kanbansmall : smaller font with hover zoom */
    +
    +div[data-refs-self*="kanban-small"]>.block-children-container.flex>.block-children>div.ls-block {
    +   display: inline-block;
    +   min-width: 350px;
    +}
    +
    +div[data-refs-self*="kanban-small"]>.block-children-container.flex>.block-children .block-content {
    +   font-size: 10px;
    +   font-weight: 300;
    +}
    +
    +div[data-refs-self*="kanban-small"]>.block-children-container.flex>.block-children .block-content:hover {
    +   font-size: 14px !important;
    +   min-width: 100px;
    +}
    +
    +
    +/* #kanban-w[100-300] : force width of the columns */
    +
    +div[data-refs-self*="kanban-w100"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
    +   min-width: 100px;
    +}
    +
    +div[data-refs-self*="kanban-w150"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
    +   min-width: 150px;
    +}
    +
    +div[data-refs-self*="kanban-w200"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
    +   min-width: 200px;
    +}
    +div[data-refs-self*="kanban-w300"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
    +   min-width: 300px;
    +}
    +div[data-refs-self*="kanban-w400"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
    +   min-width: 400px;
    +}
    +div[data-refs-self*="kanban-fit"]>.block-children-container.flex>.block-children.w-full>div.ls-block {
    +   min-width: 400px;
    +       width: max-content; 
    +}
    +
    +/* remove left border for kanbanized */
    +[data-refs-self*="kanban"] .block-children-left-border {
    +   opacity: 0;
    +}
    +
    +/* fix modal list not appearing*/
    +.block-children {
    +   overflow: visible !important;
    +} 
    +
    +.ls-block[data-refs-self*="kanban"] .absolute-modal,
    +.ls-block[data-refs-self*="kanban"] #ui__ac {
    +   min-height: 80px;
    +}
    +           
    +/*--------------------------------------------- kanban end-- */
    +
    +/*------------------expreimetal for better table view START-------------------*/
    +.table-wrapper {
    +    width: 100% !important;
    +    max-width: 100% !important;
    +}
    +
    +table td {
    +    min-width:100px;
    +    word-wrap:break-word;
    +}
    +
    +
    +table th {
    +    word-break: keep-all;
    +}
    +/*------------------expreimetal for better table view END-------------------*/
    +
    + +
      +
    • In order to invoke some of the above tweaks, we will also create keyboard shortcuts and shortcodes to have a simpler way to change colour of the blockquote side border and highlights. So open logseq/config.edn and do the following: +a) Search for:
    • +
    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +
          ;; Macros replace texts and will make you more productive.
    +      ;; Example usage:
    +      ;; Change the :macros value below to:
    +      ;; {"poem" "Rose is $1, violet's $2. Life's ordered: Org assists you."}
    +      ;; input "{{poem red,blue}}"
    +      ;; becomes
    +      ;; Rose is red, violet's blue. Life's ordered: Org assists you.
    +      :macros {}
    +      
    +
    + +

    b) and replace above with:

    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +
          ;; Macros replace texts and will make you more productive.
    +      ;; Example usage:
    +      ;; Change the :macros value below to:
    +      ;; {"poem" "Rose is $1, violet's $2. Life's ordered: Org assists you."}
    +      ;; input "{{poem red,blue}}"
    +      ;; becomes
    +      ;; Rose is red, violet's blue. Life's ordered: Org assists you.
    +      :macros {
    +       ">" "<blockquote class='$1'>$2</blockquote>" ;;usage {{ > orange,Text to be presented in the blockquote }}
    +       "==" "<mark class='$1'>$2</mark>" ;;usage {{ == red,Text to be highlighted without linebreak }}
    +      }
    +      
    +
    + +

    c) search for:

    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +
          ;; Add custom commands to the command palette
    +      ;; Example usage:
    +      ;; :commands
    +      ;; [
    +      ;;  ["js" "Javascript"]
    +      ;;  ["md" "Markdown"]
    +      ;;  ]
    +      :commands []
    +      
    +
    + +

    d) and replace above with:

    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +
          ;; Add custom commands to the command palette
    +      ;; To quickly call these commands, just type / (backslash) followed by characters in square bracket
    +      :commands [
    +                  ["bookmark                                        [.b]" [[:editor/input "{{ renderer :template, Bookmark}}" ]]],
    +                  ["date_today                                      [dt]" [[:editor/input "{{renderer :template, Date Today}}" ]]],
    +                  ["issue_table                                     [.it]" [[:editor/input "{{renderer :template, Issue_table}}" ]]],
    +                  ["issue                                           [.is]" [[:editor/input "{{renderer :template, Issue}}" ]]],
    +                  ["circle                                          [.c]" [[:editor/input "{{renderer :template-view, circle-template, :color orange}}" ]]],          
    +                  ["Blue Highlighter                                [=b]" [[:editor/input "<mark class='blue'></mark>" {:backward-pos 7}]]],
    +                  ["Green Highlighter                               [=g]" [[:editor/input "<mark class='green'></mark>" {:backward-pos 7}]]],
    +                  ["Gray Highlighter                                [=gra]" [[:edior/input "<mark class='gray'></mark>" {:backward-pos 7}]]],
    +                  ["Grey Highlighter                                [=gre]" [[:editor/input "<mark class='grey'></mark>" {:backward-pos 7}]]],
    +                  ["Orange Highlighter                              [=o]" [[:editor/input "<mark class='orange'></mark>" {:backward-pos 7}]]],
    +                  ["Pink Highlighter                                [=p]" [[:editor/input "<mark class='pink'></mark>" {:backward-pos 7}]]],
    +                  ["Red Highlighter                                 [=r]" [[:editor/input "<mark class='red'></mark>" {:backward-pos 7}]]],
    +                  ["Yellow Highlighter                              [=y]" [[:editor/input "<mark class='yellow'></mark>" {:backward-pos 7}]]],
    +                  ["Purple Highlighter                              [=pu]" [[:editor/input "<mark class='purple'></mark>" {:backward-pos 7}]]],
    +                  ["Red Blockquote                                  [>r]" [[:editor/input "<blockquote class='red'></blockquote>" {:backward-pos 13}]]],
    +                  ["Yellow Blockquote                               [>y]" [[:editor/input "<blockquote class='yellow'></blockquote>" {:backward-pos 13}]]],
    +                  ["Blue Blockquote                                 [>b]" [[:editor/input "<blockquote class='blue'></blockquote>" {:backward-pos 13}]]],     
    +                ]
    +      
    +
    + +
    +

    Now, some of the short-codes above such as /.is, /.it, /dt and /.c will not work just yet because we have not created their associated template. We will get to that in next section.

    +
    + +

    Create Templates

    + +
      +
    • Create a new page named templates.
    • +
    • Press Ctrl+K and search for templates and open the page.
    • +
    • Once on the page, click on Three dots in top right corner of the screen and from the drop down menu select Open in default app +
    • +
    • Here paste the following then save and close the default app:
    • +
    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    +49
    +50
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +73
    +74
    +75
    +76
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +97
    +98
    +99
    +100
    +101
    +102
    +103
    +104
    +105
    +106
    +107
    +108
    +109
    +110
    +111
    +112
    +113
    +114
    +115
    +116
    +117
    +118
    +119
    +120
    +121
    +122
    +123
    +124
    +125
    +126
    +127
    +128
    +129
    +130
    +131
    +132
    +133
    +134
    +135
    +136
    +137
    +138
    +139
    +140
    +141
    +142
    +143
    +144
    +145
    +146
    +147
    +148
    +149
    +150
    +151
    +152
    +153
    +154
    +155
    +156
    +157
    +158
    +159
    +160
    +161
    +162
    +163
    +164
    +165
    +166
    +167
    +168
    +169
    +170
    +171
    +172
    +173
    +174
    +175
    +176
    +177
    +178
    +179
    +180
    +181
    +182
    +183
    +184
    +185
    +186
    +187
    +188
    +189
    +190
    +191
    +192
    +193
    +194
    +195
    +196
    +197
    +198
    +199
    +200
    +201
    +202
    +203
    +204
    +205
    +206
    +207
    +208
    +209
    +210
    +211
    +212
    +213
    +214
    +215
    +216
    +217
    +218
    +219
    +220
    +221
    +222
    +223
    +224
    +225
    +226
    +227
    +228
    +229
    +230
    +231
    +232
    +233
    +234
    +235
    +236
    +237
    +238
    +239
    +240
    +241
    +242
    +243
    +244
    +245
    +246
    +247
    +248
    +249
    +250
    +251
    +252
    +253
    +254
    +255
    +256
    +257
    +258
    +259
    +260
    +261
    +262
    +263
    +264
    +265
    +266
    +267
    +268
    +269
    +270
    +271
    +272
    +273
    +274
    +
    		title:: templates
    +		visibility:: false
    +		icon:: 🧾
    +		
    +		- # Circle
    +		  id:: 66b0d360-cde5-4e95-87b3-4e97a1e23bb5
    +		  template:: circle-template
    +		  template-including-parent:: false
    +		  arg-color:: red
    +			- <span class='circle' style='background: ``c.args.color``; display: inline-block; width: 15px; height: 15px; border-radius: 50%; vertical-align: middle;'></span>
    +		- # People Page
    +		  template:: people page
    +		  template-including-parent:: false
    +			- tags:: people
    +			  icon:: 👨‍💼
    +			- ## Tasks
    +				- query-table:: true
    +				     query-properties:: [:priority :deadline :block]
    +				  #+BEGIN_QUERY
    +				  {:title [:h3 "Owned"]
    +				  :query [:find (pull ?b [*])
    +				       :in $ ?tag
    +				       :where
    +				       [?b :block/marker ?marker]
    +				       [(contains? #{"TODO" "DOING" "NOW" "LATER" "WAITING"} ?marker)]
    +				       (page-ref ?b ?tag)
    +				       [?ref :block/name "project"]
    +				       (not [?b :block/refs ?ref])]
    +				  :inputs [:query-page]
    +				  :result-transform :add-task-attrs
    +				  :breadcrumb-show? true
    +				  :group-by-page? false
    +				  :collapsed? false
    +				  }
    +				  #+END_QUERY
    +				- query-table:: true
    +				  collapsed:: true
    +				     query-properties:: [:marker :deadline :block]
    +				  #+BEGIN_QUERY
    +				  {:title [:h3 "Closed or Cancelled"]
    +				  :query [:find (pull ?b [*])
    +				       :in $ ?tag
    +				       :where
    +				       [?b :block/marker ?marker]
    +				       [(contains? #{"DONE" "CANCELLED" "CANCELED" } ?marker)]
    +				       (page-ref ?b ?tag)
    +				       [?ref :block/name "project"]
    +				       (not [?b :block/refs ?ref])]
    +				  :inputs [:query-page]
    +				  :result-transform :add-task-attrs
    +				  :breadcrumb-show? true
    +				  :group-by-page? false
    +				  :collapsed? true
    +				  }
    +				  #+END_QUERY
    +		-
    +		- # Issue Table
    +		  template:: Issue_table
    +		  template-including-parent:: false
    +			- #.tabular
    +				- ## 01 ``{|}``Issue Title``{|}``
    +				  
    +				  owner::
    +				  status:: #Red 
    +				  #Issues #.v-kanban
    +					- ### **Issue Description**
    +						-
    +					- ### **Updates**
    +						- <% Today %> :
    +				- ## 02 Issue Title
    +				  owner::
    +				  status:: #Amber
    +				  #Issues #.v-kanban
    +					- ### **Issue Description**
    +						-
    +					- ### **Updates**
    +						- <% Today %> :
    +				- ## 03 Issue Title
    +				  owner:: 
    +				  status:: #Yellow
    +				  #Issues #.v-kanban
    +					- ### **Issue Description**
    +						-
    +					- ### **Updates**
    +						- <% Today %> :
    +				- ## 04 Issue Title
    +				  owner:: 
    +				  status:: #Green
    +				  #Issues #.v-kanban
    +					- ### **Issue Description**
    +						-
    +					- ### **Updates**
    +						- <% Today %> :
    +				- ## 05 Issue Title
    +				  owner:: 
    +				  status:: #on-hold 
    +				  #Issues #.v-kanban
    +					- ### **Issue Description**
    +						-
    +					- ### **Updates**
    +						- <% Today %> :
    +				- ## 06 Issue Title
    +				  owner:: 
    +				  status:: #no-go
    +				  #Issues #.v-kanban
    +					- ### **Issue Description**
    +						-
    +					- ### **Updates**
    +						- <% Today %> :
    +				- ## 07 Issue Title
    +				  owner:: 
    +				  status:: #closed
    +				  #Issues #.v-kanban
    +					- ### **Issue Description**
    +						-
    +					- ### **Updates**
    +						- <% Today %> :
    +		- # Issue
    +		  template:: Issue
    +		  template-including-parent:: false
    +			- ## ``{|}``01 Issue Title``{|}``
    +			  
    +			  owner::
    +			  status:: #Red
    +			  #Issues #.v-kanban
    +				- ### **Issue Description**
    +					-
    +				- ### **Updates**
    +					- <% Today %> :
    +		- # Date Today
    +		  template:: Date Today
    +		  template-including-parent:: false
    +			- **``today``** ``{|}``
    +		- # [[Bookmarks]]
    +		  template:: Bookmark
    +		  template-including-parent:: false
    +			- url:: ``{|}``
    +			  
    +			  topic::
    +		- # [[Project page]]
    +		  template:: project page
    +		  template-including-parent:: false
    +			- tags:: project page
    +			  icon:: 📂
    +			- ## Project Meta
    +			  collapsed:: true
    +				- DOING [#B] #project <% current page %>
    +			- ## Actions Log
    +				- query-properties:: [:deadline :priority :block]
    +				  #+BEGIN_QUERY
    +				  {:title [:h4 "On ToDo List"]
    +				  :query [:find (pull ?b [*])
    +				       :in $ ?tag
    +				       :where
    +				       [?b :block/marker ?marker]
    +				       [(contains? #{"TODO"} ?marker)]
    +				       (page-ref ?b ?tag)
    +				       [?ref :block/name "project"]
    +				       (not [?b :block/refs ?ref])]
    +				  :inputs [:query-page]
    +				  :result-transform :add-task-attrs
    +				  :breadcrumb-show? true
    +				  }
    +				  #+END_QUERY
    +				- query-table:: false
    +				  query-properties:: [:page :block]
    +				  #+BEGIN_QUERY
    +				  {:title [:h4 "Ongoing Tasks"]
    +				  :query [:find (pull ?b [*])
    +				       :in $ ?tag
    +				       :where
    +				       [?b :block/marker ?marker]
    +				       [(contains? #{"DOING" "NOW" "LATER" "WAITING"} ?marker)]
    +				       (page-ref ?b ?tag)
    +				       [?ref :block/name "project"]
    +				       (not [?b :block/refs ?ref])]
    +				  :inputs [:query-page]
    +				  :result-transform :add-task-attrs
    +				  :breadcrumb-show? true
    +				  :group-by-page? false
    +				  :collapsed? false
    +				  }
    +				  #+END_QUERY
    +				- query-properties:: [:deadline :priority :block]
    +				  collapsed:: true
    +				  #+BEGIN_QUERY
    +				  {:title [:h4 "Completed Tasks"]
    +				  :query [:find (pull ?b [*])
    +				       :in $ ?tag
    +				       :where
    +				       [?b :block/marker ?marker]
    +				       [(contains? #{"DONE"} ?marker)]
    +				       (page-ref ?b ?tag)
    +				       [?ref :block/name "project"]
    +				       (not [?b :block/refs ?ref])]
    +				  :inputs [:query-page]
    +				  :result-transform :add-task-attrs
    +				  :breadcrumb-show? true
    +				  :table-view? false
    +				  :collapsed? true
    +				  }
    +				  #+END_QUERY
    +			- ## Issues Log
    +				- query-sort-by:: status
    +				     query-table:: true
    +				     query-sort-desc:: true
    +				     query-properties:: [:block :owner :status]
    +				  #+BEGIN_QUERY
    +				  {:title [:h4 "Open Issues"]
    +				  :query [:find (pull ?b [*])
    +				       :in $ ?query-page
    +				       :where
    +				       [?p :block/name ?query-page]
    +				  	 [?tag2 :block/name "issues"]
    +				  	 [?b :block/refs ?tag2]
    +				  	 [?tag1 :block/name "closed"]
    +				  	 (not [?b :block/refs ?tag1])
    +				       [?b :block/refs ?p]
    +				       [?ref :block/name "project"]
    +				       (not [?b :block/refs ?ref])         
    +				       ]
    +				  :inputs [:query-page]
    +				  :breadcrumb-show? false
    +				  :table-view? true
    +				  :group-by-page? false
    +				  :collapsed? false
    +				  }
    +				  #+END_QUERY
    +				- query-properties:: [:block :owner :status]
    +				  collapsed:: true
    +				  #+BEGIN_QUERY
    +				  {:title [:h4 "Closed Issues"]
    +				  :query [:find (pull ?b [*])
    +				       :in $ ?query-page
    +				       :where
    +				       [?p :block/name ?query-page]
    +				  	 [?tag2 :block/name "issues"]
    +				  	 [?b :block/refs ?tag2]
    +				  	 [?tag1 :block/name "closed"]
    +				  	 [?b :block/refs ?tag1]
    +				       [?b :block/refs ?p]
    +				       [?ref :block/name "project"]
    +				       (not [?b :block/refs ?ref])         
    +				       ]
    +				  :inputs [:query-page]
    +				  :breadcrumb-show? false
    +				  :table-view? true
    +				  :group-by-page? false
    +				  :collapsed? true
    +				  }
    +				  #+END_QUERY
    +			- #+BEGIN_QUERY
    +			  {:title [:h2 "Project Notes"]
    +			  :query [:find (pull ?b [*])
    +			       :in $ ?query-page
    +			       :where
    +			       [?p :block/name ?query-page]
    +			       [?b :block/refs ?p]
    +			  	 [?tag2 :block/name "issues"]
    +			  	 (not [?b :block/refs ?tag2])
    +			       [?ref :block/name "project"]
    +			       (not [?b :block/refs ?ref])        
    +			       (not [?b :block/marker _])
    +			       ]
    +			  :inputs [:query-page]
    +			  :result-transform (fn [result]
    +			                   (sort-by (fn [b]
    +			                              (get b :block/created-at "A")) result))
    +			  :breadcrumb-show? false
    +			  :group-by-page? true
    +			  :collapsed? false
    +			  }
    +			  #+END_QUERY
    +		-
    +
    + +

    Contents page

    + +
      +
    • Press Ctrl+K and search for contents and open the page.
    • +
    • Once on the page, click on Three dots in top right corner of the screen and from the drop down menu select Open in default app +
    • +
    • Here paste the following then save and close the default app:
    • +
    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +
      - query-properties:: [:icon :page :updated-at]
    +  #+BEGIN_QUERY
    +  {:title [:h1 "⏳ Ongoing Projects"]
    +    :query [:find (pull ?page [*])
    +        :where
    +          [?block :block/page ?page]
    +          [?page :block/name ?pagename]
    +          [?block :block/marker ?marker]
    +          [(contains? #{"DOING"} ?marker)]
    +          (page-ref ?block "project")
    +          (not [?page :block/name "templates"])
    +      ]
    +      :breadcrumb-show? false
    +      :collapsed? false}
    +  #+END_QUERY
    +  
    +  - query-properties:: [:icon :page :updated-at]
    +    #+BEGIN_QUERY
    +    {
    +    :title [:h1 "👨‍💼People"]
    +    ;; ---- Get every block into variable ?block
    +    :query [:find (pull ?block [*])
    +        ;; ---- filter command
    +        :where
    +        ;; ---- get page name (lowercase) from the special page block into variable ?pagename
    +        [?block :block/name ?pagename]
    +        ;; ---- Select if block is a special page block and has a single property (arg1) with value arg2
    +        (page-property ?block :tags "people")
    +    ]
    +    }
    +#+END_QUERY
    +
    + +

    Sample Content Page

    +

    screenshot_20240813-152544

    + +

    Usage

    + +

    Sample Day to Day Capture

    +

    Screenshot_13-8-2024_151147_

    + +

    Create Project

    +
      +
    • To create a CARDIO Log for a project, first create a new page and give it name of the Project.
    • +
    • Next, open the newly created project page.
    • +
    • Once opened, press Ctrl+t and select project page template and press enter.
    • +
    • This will create all relevant sections for the Project.
    • +
    + +

    Create User Page

    +
      +
    • Create a new page with the user / resource name.
    • +
    • Open the newly created page.
    • +
    • Once opened, press Ctrl+t and select people page template and press enter.
    • +
    • This will create all relevant sections to track actions for this resource.
    • +
    + +

    Create New Issues

    +
      +
    • Now for everyday usage, each time there is a new issue or a number of issues they can simply be recorded by typing /.is for single issue and /.it for a table of issues.
    • +
    • Once the issue or issues table is created, all usual fields can be filled but most importantly the issue title must include hashtag for the project that the issue belongs to. +
        +
      • So an issue for project called North Star must have #[[North Star]] in the title.
      • +
      • As an example say the issue is to do with lack of resources then the title should be #[[North Star]]: Lack of Resources +
      • +
      • +

        The issue status or any topic to be highlighted with RAG status can be tagged with following tags:

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        TagCircle Appended
        #red🔴
        #amber🟠
        #green🟢
        #on-hold🟣
        #yellow🟡
        #Closed🔵
        #no-go🚫
        +
      • +
      +
    • +
    • +

      Circles with additional colours can be created using the template. To do so, press /.c and press enter. This should then put following text on the editor: ` {{renderer :template-view, circle-template, :color orange}} `

      +
    • +
    • Now just replace orange in above with the colour desired.
    • +
    + +

    Maintain Issues Log

    +
      +
    • Reviewing the logs is as simple as opening the Contents page, clicking the project name and scrolling down to the Issues Log section.
    • +
    • Now if there are actions identified during the review of an issue, they can be simply added in the updates section under the date of review which can be quickly added by pressing Ctrl+Shift+D and then noting down the action as a sub bullet TODO [#A] Do Something /deadline +
    • +
    • I usually log decisions against a logged issue or if they resulted from a general discussion, just as #<projectname> #decision.
    • +
    + +

    Day to day notes

    +
      +
    • All the project relevant notes will be automatically collected on the project’s page so long as they have been tagged with the project name so for the day to day notes all that is required is add entires in the Journal.
    • +
    + +

    Blockquotes

    +
      +
    • As we added css for different coloured borders of blockquotes and also created shortcode and keyboard shortcut we can do this in multiple ways: +
        +
      • Type />band press enter. This will place <blockquote class='blue'></blockquote> and the quote can be written between the tags.
      • +
      • +

        Type the following, replacing yellow with one of the predefined colours: yellow, pink, blue, green, red, grey, gray, orange or purple. + {{> yellow,Some yellow quote}}

        + +

        image

        +
      • +
      +
    • +
    + +

    Coloured Highlight

    +
      +
    • Type /=y, select Yellow Highlighter from pop-up menu and press enter. This will place <mark class='yellow'></mark> and text to be highlighted can be written between the tags. It will be presented as Yellow Highlight +
    • +
    • +

      Type the following and it will be presented as This text is highlighted. + {{== pink,pink highlight}}

      + +

      image

      +
    • +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/markdown-and-gantt-charts/index.html b/markdown-and-gantt-charts/index.html new file mode 100644 index 0000000..bc49bf3 --- /dev/null +++ b/markdown-and-gantt-charts/index.html @@ -0,0 +1,869 @@ + + + + + + +Markdown and Gantt Charts - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Markdown and Gantt Charts +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +
    + + +
    + +

    For a fairly long time, I have been looking for a simple markdown type of solution to be able to quickly draw Gantt charts but never came across what one would call a quick option. It has always been an involved process.

    + +

    To my simplistic mind, a simple solution would just be an option where I can type the action, a start date and an end date for the action line after line and it get’s displayed in the Gantt chart.

    + +

    The latest edition of Linux Format (Issue 221, Page 52) had mermaid as one of the hotpicks and highlighted the availability of Gantt charts which piqued my interest, I tried the solution and it is indeed what I was looking for. There are a few rough edges but it is a working solution and in normal circumstances will save considerable time and hence will become my tool of choice. +There are few ways of using it and because it has been made to work with MarkDown syntax, it can work on a number of tools too. I found CuteMarkEd to be the easiest one to use but the various ways to use it are listed below and their usage explained in following sections:

    + +
      +
    1. CuteMarkEd
    2. +
    3. Install Mermaid CLI
    4. +
    5. Install Markdown Plus
    6. +
    7. Mermaid Live Editor
    8. +
    + +

    Install CuteMarkEd

    +

    CuteMarkEd is not available in debian repositories nor does it have a binary for debian. Although it does have an rpm binary. As I have been playing with Debian / Ubuntu based Elementary OS lately, instead of my regular Fedora install, I figured might as well compile from source code. Steps are as below and are also available on the github page for CuteMarkEd:

    + +
    #Prepare the environment for building from source code.
    +sudo apt-get install build-essential checkinstall git
    +
    +#Get the code from github
    +git clone --recursive https://github.com/cloose/CuteMarkEd.git
    +
    +#Install dependencies for CuteMarkEd
    +sudo apt-get install libqt5webkit5-dev qttools5-dev-tools qt5-default discount libmarkdown2-dev libhunspell-dev
    +
    +#Compile and install CuteMarkEd
    +qmake CuteMarkEd.pro
    +cd CuteMarkEd/
    +qmake CuteMarkEd.pro
    +make
    +echo 'A Qt-based Markdown editor with live HTML preview and syntax highlighting of markdown document.' > description-pak
    +sudo checkinstall --requires 'libqt5webkit5, libmarkdown2, libhunspell-1.3-0, discount'
    +sudo ln -s /usr/lib/x86_64-linux-gnu/qt5/bin/cutemarked /usr/local/bin/
    +sudo mkdir -p /usr/local/share/icons
    +sudo cp app/icons/scalable/cutemarked.svg /usr/local/share/icons/cutemarked.svg
    +sudo apt-get install fcitx-libs-qt5
    +
    + +

    Now there is a bug in the tool which makes the Gantt charts appear as monochrome but fear not for there is a workaround for it.Once installed, open CuteMarkEd and add a snippet in the tool to ensure Gantt charts display properly. Steps below:

    +
      +
    1. Enable diagram support by clicking Extras->Options->Diagram Support
      +image +
    2. +
    3. Add the snippet for quick creation by clicking Extras->Options->Snippets->Add +
    4. +
    5. Then type ~~~gantt and in description something to the effect of Adds the codeblock for mermaid with the necessary javascript +
    6. +
    7. Now in the snippet box add the following code.
    8. +
    9. To invoke the snippet simply type in editor ~~~gantt followed by pressing the Ctrl+Space keys.
    10. +
    + +
    ~~~mermaid
    +gantt  
    +        dateFormat  YYYY-MM-DD
    +        title <Name of the project>
    +
    +%%	<Name of Activity>		: crit if critical else empty,done, active or empty, reference name or empty, Start Date or dependency, End Date or Duration
    +        section Phase 1 Name
    +        Activity 1			:	 done,    des1, 2017-01-06, 2017-01-08
    +        Activity 2               	:	 active,  des2, 2017-01-09, 2017-01-12
    +        Activity 3               	:        	  des3, 2017-01-12, 5d
    +        Activity 4              	:         	  des4, after des3, 5d
    +
    +        section Phase 2 Name
    +        Activity 5 			: crit, done,		2017-01-06, 24h
    +        Activity 6		        : crit, done, 		after des1, 2d
    +        Activity 7		        : crit, active, 		    3d
    +        Activity 8			: crit,			 	    5d
    +        Activity 9			:			 	    2d
    +        Activity 10			: 			 	    1d
    +
    +        section Phase 3 Name
    +        Activity 11			: 	active,   a1,	after des1, 3d
    +        Activity 12			:			after a1  , 20h
    +        Activity 13			:		 doc1, 	after a1  , 48h
    +
    +        section Phase 4 Name
    +        Activity 12			:			after doc1, 3d
    +        Activity 15			: 	  			    20h
    +        Activity 16			: 			   	    48h
    +~~~
    +<script>  
    +            mermaid.ganttConfig = {
    +                titleTopMargin:25,
    +                barHeight:20,
    +                barGap:4,
    +                topPadding:50,
    +                sidePadding:100,
    +                gridLineStartPadding:35,
    +                fontSize:11,
    +                numberSectionStyles:3,
    +                axisFormatter: [
    +                    // Within a day
    +                    ['%I:%M', function (d) {
    +                        return d.getHours();
    +                    }],
    +                    // Monday a week
    +                    ['w. %U', function (d) {
    +                        return d.getDay() == 1;
    +                    }],
    +                    // Day within a week (not monday)
    +                    ['%a %d', function (d) {
    +                        return d.getDay() &amp;&amp; d.getDate() != 1;
    +                    }],
    +                    // within a month
    +                    ['%b %d', function (d) {
    +                        return d.getDate() != 1;
    +                    }],
    +                    // Month
    +                    ['%m-%y', function (d) {
    +                        return d.getMonth();
    +                    }]
    +                ]
    +            };
    +</script>
    +
    + +

    This will then show in the preview pane following Gantt Chart and we can edit, add and modify the data as per the requirements: +

    + +
    +

    TIP: You can make as many snippets as you want and for the simple solution that I mentioned right at the top, I made one snippet called dategantt replacing the code between line 3 to line 29 of the snippet above + dateFormat DD/MM/YY + title Project Name

    + +
    Section Pre-condition
    +Activity 1			:21/12/16, 22/12/16
    +Activity 2 		:21/12/16, 22/12/16
    +Activity 3 	:16/01/17, 02/02/17
    +Activity 4 		:01/02/17, 02/02/17
    +
    +Section Kick-off
    +Activity 5	:01/02/17, 03/02/17
    +Activity 6	:01/02/17, 03/02/17
    +Project Initiated		:01/02/17, 03/02/17
    +
    +Section Tech Design
    +Technical Design		:crit, active, T1, 06/02/17, 21/03/17
    +
    +Section Delivery	
    +Activity 7		:06/02/17, 10/02/17
    +Activity 8			:10/02/17, 14/02/17
    +Order				:15/02/17, 14/03/17
    +Deployment			:crit, 15/03/17, 21/03/17
    +Activity 9			:crit,T2, after T1, 23/03/17
    +Project Close-Down		:after T2, 24/03/17 ``` Once done, just type dategantt followed by ctrl+space and you will have the framework to enter activities, start dates and end dates. Gantt Chart will be ready just like the one as cover image of this post right at the top !!!
    +
    +
    + +

    Install Mermaid CLI

    +

    One of the drawbacks of the CuteMarkEd is that the pdf export there just does not work and you will need to rely on a screenshot. Now if the Gantt chart is really huge a screen shot wont cut it and mermaid does offer the ability to generate svg file for your effort. It is ofcourse a command line tool but is actually fairly easy to use once installed.

    +
    #check if nodejs is installed. If command below results in nothing then you need to install nodejs
    +nodejs -v
    +
    +# Instal nodejs
    +sudo apt install nodejs
    +#On Ubuntu there is some link issue where node is 
    +#referenced as nodejs which results in issues 
    +#while installing npm modules. This can be avoided 
    +#by creating a symlink using following command.
    +sudo ln -s /usr/bin/nodejs /usr/bin/node
    +
    +#Install npm
    +sudo apt install npm
    +
    +#In order to install npm modules without sudo,
    +#As explained in one of [my previous posts](http://mgw.dumatics.com/ghost-on-fedora-24/#step3installnpmmodules), 
    +#it requires fixing the permissions using following steps.
    +#This needs to be done only once so if you have 
    +#done this in past, then there is no need to repeat.
    +
    +mkdir ~/.npm-global
    +npm config set prefix '~/.npm-global'
    +nano ~/.profile
    +source ~/.profile
    +
    +#Install Mermaid and phantomjs on which mermaid depends.
    +
    +npm install -g phantomjs
    +npm install -g mermaid
    +
    +#Check if mermaid is installed by issuing the command
    +mermaid --h
    +
    +#Generate a png using following command
    +mermaid -p path/to/markdown file with .md or .mmd extension.
    +
    +

    Install Markdown-plus

    +

    MarkdownPlus is a good markdown editor and can be used on their hosted solution or for offline use it can be installed as a local copy and accessed from the browser by pointing the browser to the index.html. The bug from CuteMarkEd is not present here. +To access offline, you can clone it from github using following command.

    + +
    git clone --recursive https://github.com/tylingsoft/markdown-plus.git
    +
    + +

    Then open the folder markdown-plus and right click on the index.html file and open it using your browser of choice. Alternatively, in the browser enter file:///home/path/to/markdown-plus//index.html.

    + +

    Mermaid Live Editor

    + +

    While Markdown Plus is quite a versatile browser based editor, I did not like the fact that there is no easy way to save the file you have created and all I could work out was that you will have to copy paste your text and save it using another text editor. +I felt, for this reason that the Mermaid Live editor was better compared to this option and the live editor is quite basic to be honest and can’t be used offline.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/metabase-a-bi-solution-that-just-works/index.html b/metabase-a-bi-solution-that-just-works/index.html new file mode 100644 index 0000000..2ac9351 --- /dev/null +++ b/metabase-a-bi-solution-that-just-works/index.html @@ -0,0 +1,831 @@ + + + + + + +Metabase - A BI solution that just works - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Metabase - A BI solution that just works +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +
    + + +
    + + + +

    I like exploring new solutions and anything to do with data always piques my interest. I came across this nice tool through the list of free self hosted software.

    + +
    +

    UPDATE: +I am still getting introduced to the world of docker and while most of it just works few things confuse the hell out of me.

    + +

    Anyway what happened is there was an update for Metabase and I wanted to apply it on my container and in doing so I realised that some of this post can be changed to make it a better experience from get go because after update all my config and dashboards were gone and I had to reconfigure everything.

    + +

    It could be that my lack of knowledge meant I did something wrong and faced the issue but irrespective doing it as per updated post below will ensure you dont face the issue at all because I have now tested it twice to ensure it works well.

    +
    + +

    I have also added the steps to update the metabase to latest docker image as it worked for me.

    + +

    The overall impression when I opened their site can be summed up with WOW!! Now they offer simple docker install supported officially by them and the guide to install docker image is simple enough but the few +simple steps I followed are as below:

    + +
      +
    1. Create an A-record for a subdomain for metabase
    2. +
    3. Download the official metabse docker image from dockerhub
    4. +
    5. Create an apache server conf file for metabase
    6. +
    7. Run the metabase docker image
    8. +
    9. Access metabase docker image using subdomain URL
    10. +
    11. Configure your database on metabase
    12. +
    13. Run Certbot to make the URL https
    14. +
    15. Done!!
    16. +
    + +

    Create A-record for subdomain

    + +

    Using the process for your Domain Name Regsitrar create an A-Record for +a subdomain you would want to use for metabase. For this guide we will +assume that the subdomain being created is called “metabase” so if +your domain name is say yourdomain.com then the URL to access metabase +will be metabase .yourdomain .com.

    + +

    It is important that if you use a different subdomain name, please replace metabase with the subdomain name you have chosen in all Apache conf file samples in sections below.

    + +

    More detailed steps are available on previous post HERE

    + +

    Download and Create Apache Conf

    + +
    docker pull metabase/metabase
    +cd /etc/apache2/sites-available/
    +sudo nano metabase.conf
    +
    +

    Create a conf file similar to what is shown below:

    + +
    <VirtualHost *:80>
    +    ServerName metabase.yourdomain.com
    +    ServerAlias metabase.yourdomain.com
    +
    +    ErrorLog ${APACHE_LOG_DIR}/error_metabase.log
    +    CustomLog ${APACHE_LOG_DIR}/access_metabase.log combined
    +
    +    ProxyPreserveHost On
    +    ProxyPass / http://localhost:3000/
    +    ProxyPassReverse / http:/localhost:3000/
    +    ProxyPass "/ws2/"  "ws://localhost:3000/"
    +    ProxyPass "/wss2/" "wss://localhost:3000/"        
    + </VirtualHost>
    +
    +

    Save this by pressing Ctrl+X and issue following commands to enable site and reload the apache server.

    + +
     sudo a2ensite metabase.conf
    + sudo systemctl reload apache2
    +
    + +

    Run Metabase docker image

    +
    docker run -d -p 3000:3000 --name metabase metabase/metabase
    +
    +

    If all goes well, you should have metabase already running. If you get an error to the effect saying port 3000 is already in use, you will perhaps have to run it on another port (say 12345) but as metabase container would have been created you will need to issue following commands.

    + +
    docker rm metabase
    +docker run -d -p 12345:3000 --name metabase metabase/metabase
    +
    +

    In addition to avoid running the database through the container we must anyway move it to filesystem. Using commands below:

    +
    mkdir /var/www/metabase
    +
    +

    This command will create metabase directory on the path /var/www/metabase but you can as easily chose another path as you wish. If you do, just remember to update it in next commands.

    +
    docker ps
    +
    +

    This will show you the active docker containers. Copy the container ID in first column for the row where under Names you see “metabase” which is the container name we have given using previosu commands.Now copy the +database from container to local filesystem.

    +
    cd /var/www/metabase
    +docker cp <container ID>:/metabase.db ./
    +
    +

    Now Stop and rename the container

    +
    docker stop metabase
    +docker rename metabase metabase_old
    +
    +

    Finally run Metabase Docker Image while pointing it to your filesystem for database file.

    +
    docker run -d -p 12345:3000 -v ~/metabase-data:/var/www/metabase -e "MB_DB_FILE=/var/www/metabase/metabase.db" --name metabase metabase/metabase
    +
    +

    The above docker command can be explained as below:

    + +

    docker run -d - This is telling system to start a container in detached mode. By design, containers started in detached mode exit when the root process used to run the container exits.

    + +

    ` -p 12345:3000` - This is telling docker to make the container map port 12345 to port 3000 which is the default port on which metabase starts its server.

    + +

    -v ~/metabase-data:/var/www/metabase- This part is telling docker to map the “metabase-data” directory in the container to “/var/www/metabase” on the filesystem. In other words, mounting the local volume at path “/var/www/metabase” onto the container in “metabase-data” directory.

    + +

    --name metabase metabase/metabase- Finally this part of command is telling docker to name the container “metabase” and to use the docker image “metabase/metabase” from dockerhub

    + +

    Do bear in mind if you change the port as explained above, you will also need to change the port in apache conf file in previous step. So your apache conf file in this case will look as shown below:

    +
        <VirtualHost *:80>
    +        ServerName metabase.yourdomain.com
    +        ServerAlias metabase.yourdomain.com
    +    
    +        ErrorLog ${APACHE_LOG_DIR}/error_metabase.log
    +        CustomLog ${APACHE_LOG_DIR}/access_metabase.log combined
    +    
    +        ProxyPreserveHost On
    +        ProxyPass / http://localhost:12345/
    +        ProxyPassReverse / http:/localhost:12345/
    +        ProxyPass "/ws2/"  "ws://localhost:12345/"
    +        ProxyPass "/wss2/" "wss://localhost:12345/"
    +        
    +    </VirtualHost>
    +
    +

    Access and configure on browser

    + +

    Access your metabase using the subdomain url - http:// metabase.yourdomain .com and it should walk you through the initial configuration. If you do not want to add a database and just play with the tool, they have kindly provided a sample database.

    + +

    It might be useful for you to create a “user” in your database that has “read only” access and then use that “user” when you configure metabase. Metabase will only ever need read access as it cannot be used to modify data in your database.

    + +

    Enable SSL

    + +

    Once you are happy with your configuration from step above, just run the certbot to enable https using letsencrypt certificate. On Debian this could be as simple as issuing the command

    +
    sudo certbot
    +
    +

    Once above step is created you will have an additional conf file named metabase-le-ssl.conf created in the location /etc/apache2/sites-available. Check this and once it is there you are all set to access your new functional metabase instance on https. More detailed steps are available on previous post HERE

    + +

    Update version

    + +

    Updating on docker image is pretty straight forward and provided you have followed steps above should not result in you loosing all the effort once update has completed.

    + +
    #docker rm metabase_old
    +
    +docker stop metabase
    +
    +#docker rename metabase metabase_old
    +
    +docker pull metabase/metabase
    +
    +docker start metabase
    +
    +

    From my recent messing around, I have come to realise that so long as the first start of the docker image was done using environment variables pointing to correct database directory, there is no need to run the commented lines above and a simple stop, upgrade and start should work just fine. Enjoy!!!

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mmc-convert-media-files-on-linux/index.html b/mmc-convert-media-files-on-linux/index.html new file mode 100644 index 0000000..1930e28 --- /dev/null +++ b/mmc-convert-media-files-on-linux/index.html @@ -0,0 +1,693 @@ + + + + + + +MMC Convert media files on Linux - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    MMC Convert media files on Linux +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    I have been using Mobile Media Converter for almost a year now and it is such a great tool. I think it is a must have at-least on Linux. +It not only converts almost all popular media formats into other formats but also has an inbuilt youtube video downloader which comes very handy for converting all the free official(and unofficial) song videos into MP3. +Let me spell it out, how it all works.

    +

    1. Download and Install MMC

    +

    Well you download and install MMC from this site - http://www.miksoft.net/mobileMediaConverterDown.htm +Scroll down to the part of screen that shows the following: +Alt Text +Now follow the instructions to add Medibuntu repo if you haven’t already done so.

    +

    2. Download media:

    +

    Open youtube and search for a song that you want to have on you mp3 player. Copy the link from browser and open MMC. In Linux Mint follow Menu - All Applications - Sound & Video, to open following window. +Alt Text +Now clicking on Add YouTube video will open following window: +Only in this window your link copied from youtube would be already populated. +Click on Download and wait till it downloads your song. +Alt Text

    +

    3. Convert it to MP3

    +

    Once downloaded the video gets added into the queue as shown in the following screenshot: +Alt Text +Now you can select the location where you want to store the mp3 and the conversion type as can be seen below. +Alt Text +This also shows how many other formats this nice little piece of software can handle. +Anyway, select MP3 Audio and click on Convert. +That’s it all done. +Now I don’t condone the download of any material with copyright, I do support use of anything that is legal. +There are some really good official videos (like the one in this example) that can be converted into MP3 or even video formats to enjoy on a long journey.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mysql-function-to-calculate-closing-workday-date-given-elapsed-working-time/index.html b/mysql-function-to-calculate-closing-workday-date-given-elapsed-working-time/index.html new file mode 100644 index 0000000..9ea1af7 --- /dev/null +++ b/mysql-function-to-calculate-closing-workday-date-given-elapsed-working-time/index.html @@ -0,0 +1,903 @@ + + + + + + +MySQL Function to calculate closing WorkDay date given elapsed working time - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    MySQL Function to calculate closing WorkDay date given elapsed working time +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +
    + + +
    + + + +

    On my post MySQL function to calculate elapsed working time I was asked in comments if the assumptions can be reversed such that given start date, starting time and closing time of site and the elapsed working hours, function should return the closed date. I was convinced that it will be possible to achieve this with minor tweaks to original logic and so I only concentrated on original code and how to tweak it to achieve the result. It is very likely that there might exist a more elegant solution but frankly I did not have a usecase I did find a usecase afterall - Clue was in redefining the problem description for this. Presented below is the function that with my minimal testing seems to give correct results. Feel free to try it and as always any feedback is welcome. :)

    + +

    Problem Description:

    + +

    Find out the WorkDay date by when a ticket must be closed given:

    + +
      +
    1. Country of site for which incident was logged.
    2. +
    3. Time and Date when Incident was logged.
    4. +
    5. Elapsed working hours in decimal. (Ten and a half hours as 10.5 and so on)
    6. +
    7. Opening time of the site for which the incident was logged.
    8. +
    9. Closing Time of the site for which incident was logged.
    10. +
    11. SLA Type - Is the output date to be based purely on number of hours or based on SLA in days (eg: Next Business Day).
    12. +
    + +

    Assumption: It is assumed that opening and closing times are same on all working days and that all the sites are closed on holidays and weekends.

    + +

    Example of problem

    + +

    Let’s say an incident was logged on “Friday 23rd Feb 2018 at 15:00” for a site in the “UK” which opens between 08:00 to 16:00. Now if the SLA is purely based on hours i.e. Tickets must be closed withing 16 hours then for the above incident function should calculate the closing date as:

    + +

    2018-02-27 15:00 = 1 hour on Friday, then skip Saturday and Sunday, 8 hours on Monday and 7 hours on Tuesday the 27th of Feb 2018. However, if the SLA is based on number of days, then 16 hours will translate to an SLA of next business day hence in this case function should return 2018-02-26 16:00 which is a Monday end of day and the next business day for this incident. The use case really is in creating a report that shows the target closed date based on SLA. Specifically Next Business Day SLA, which isn’t direct hours calculation and is really always the next working day but based on cut-off time when the call is logged. So a call logged on a working day 1 after cut-off will be counted as logged on working day 2 and hence will need to be closed by working day 3 whereas a call logged within cut-off of working day 1 will need to be closed by close of business working day 2. This is turn can allow for a comparison between Target Closed Date from this function and Actual Closed Date to show SLA failures where Actual Closed Date is greater than Target Closed Date.

    + +

    Pre-Requisites:

    + +

    Follow from previous post.

    + +

    Function code and explanation

    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    +49
    +50
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +
    CREATE DEFINER=`ankit`@`%` FUNCTION `get_closed_date_given_start_time`(
    +	`param_country` VARCHAR(20),
    +	`assigneddatetime` VARCHAR(20),
    +	`param_elapsedhours` VARCHAR(10),
    +	`starttime` VARCHAR(20),
    +	`endtime` VARCHAR(20),
    +	`sla_type` VARCHAR(10)
    +
    +)
    +RETURNS TEXT
    +LANGUAGE SQL
    +NOT DETERMINISTIC
    +CONTAINS SQL
    +SQL SECURITY DEFINER
    +COMMENT ''
    +BEGIN
    +Set @starttime = starttime;
    +Set @endtime = endtime;
    +Select time_to_sec(timediff(@endtime,@starttime))/3600 into @maxhoursaday;
    +Set @assigneddate = assigneddatetime;  
    +Set @timecount = param_elapsedhours;
    +Set @timevar1 = @assigneddate;
    +Set @nextdate = @assigneddate;
    +Set @timevar2 = null;
    +Set @param_country = param_country;
    +############ Check if the assigned time was before the starttime or closed time was after the endtime provided #############
    +Set @checkstart = null;
    +Set @checkend = null;
    +Set @slatype = sla_type;
    +
    +Select CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@starttime),
    +CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@endtime)  into @checkstart, @checkend;
    +
    +if (@slatype = 'H') then
    +	if (@assigneddate < @checkstart) then
    +		SET @nextdate = @checkstart;    
    +	end if;
    +else
    +	if (@assigneddate < @checkend) then
    +		SET @nextdate = @checkstart;
    +	end if;
    +end if;
    +
    +Set @count = @timecount;
    +while @count>0 do 
    +	select weekday(@nextdate) into @weekday; # Assign the weekday value to @weekday. Weekday returns o for Monday, 2 for Tuesday ...5 for Saturday and 6 for Sunday
    +	 Select sum(if(date_format(holiday_date,'%Y-%m-%d') = substring_index(@nextdate,' ',1),1,0)) from holiday_table 
    +	 where Country_codes = 'ALL' or instr(Country_codes,@param_country)>0
    +	 into @holidayflag; #Check if the date stored in nextdate (which is assigneddate on first run of while loop) is a holiday and set the holiday flag
    +	if ( @weekday<5 and @holidayflag=0) then #Proceed if the date in nextdate variable is neither weekend nor a holiday
    +		if (@count = @timecount) then #Check if it is first run.ie. if nextdate is assigneddate
    +			Set @timevar1 = @nextdate; #assign assigndate to variable timevar1
    +			SELECT CONCAT(SUBSTRING_INDEX(@nextdate, ' ', 1), ' ',@endtime) INTO @timevar2;#get site closing time on assigned date and store it on to timevar2
    +		else
    +			Select CONCAT(substring_index(@nextdate,' ',1),' ',@starttime) into @timevar1;
    +			SELECT CONCAT(substring_index(@nextdate,' ',1), ' ', @endtime) INTO @timevar2;
    +		end if;
    +		SELECT LEAST(Greatest(((TIME_TO_SEC(TIMEDIFF(@timevar2, @timevar1))) / 3600),0),@maxhoursaday) INTO @timecounttemp;   
    +		Set @count = @count - @timecounttemp;
    +	end if;
    +	
    +	if @count > 0 then
    +	 Select adddate(concat(substring_index(@nextdate,' ',1),' ',@starttime),1) INTO @nextdate;
    +	 else
    +	 Select SUBSTRING_INDEX(@nextdate, ' ', 1) INTO @nextdate;
    +   end if;
    +end while;
    +Set @finaldate = null;
    +Select concat(@nextdate,' ',substring_index(addtime(@endtime,sec_to_time(3600*@count)),':',2)) INTO @finaldate;
    +RETURN @finaldate;
    +
    +END
    +
    + +

    Calling the function

    + +

    Function will expect 6 parameters and with specific format as explained below:

    + +
      +
    • param_country - This is the country code as specified in holiday table
    • +
    • assigneddatetime - This must be provided in the format %Y-%m-%d %H-%i-%s. So for our example it will be 2018-02-23 15:00:00
    • +
    • param_elapsedhours - This must be provided in decimal hours format. +
        +
      • Bear in mind that if SLA Type is not H, you need to understand the number of working hours in allowed number of days in SLA. So for 8 working hours in a day: +
          +
        • Same day SLA will have 8 hours
        • +
        • Next Business Day will have 16 hours(2*8)
        • +
        • A 2 day SLA will have 24 hours
        • +
        • and so on
        • +
        +
      • +
      +
    • +
    • starttime - This must be in the format %H:%i. So for our example it will be 08:00
    • +
    • endtime - This must be in the format %H:%i. So for our example it will be 16:00
    • +
    • sla_type - This must be ‘H’ if the sla is based on hours or anything else (say ‘D’) if SLA is based on days.
    • +
    + +

    The call for this function will be as below:

    + +

    For hours SLA:

    + +
    Select get_closed_date_given_start_time('ALL','2018-02-23 15:00:00','16','08:00','16:00','H');
    +
    +

    This will give an output of 2018-02-27 15:00

    + +

    For Next Business Day SLA:

    +
    Select get_closed_date_given_start_time('ALL','2018-02-23 15:00:00','16','08:00','16:00','D');
    +
    +

    This will give an output of 2018-02-26 16:00

    + +

    For just calculating next working day date given number of hours:

    +
    Select get_closed_date_given_start_time('ALL','2018-02-23 15:00:00','10.5','08:00','16:00','H');
    +
    +

    This will give an output of 2018-02-27 09:30

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mysql-function-to-calculate-elapsed-working-time/index.html b/mysql-function-to-calculate-elapsed-working-time/index.html new file mode 100644 index 0000000..2af5b33 --- /dev/null +++ b/mysql-function-to-calculate-elapsed-working-time/index.html @@ -0,0 +1,1041 @@ + + + + + + +MySQL function to calculate elapsed working time - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    MySQL function to calculate elapsed working time +

    + + +

    + + + + + + + + + + + + 7 minute read + + + +

    + + +
    + + +
    + + + +
    + +
    + +

    I wrote this function to cater for a specific requirement and I don’t know if there are better ways of doing it but this saved tremendous amount of time and might have real time application elsewhere.

    + +

    Problem Statement:

    + +

    Find out the age of an incident in working minutes, given the following:

    + +
      +
    1. Time and Date of when an incident was logged
    2. +
    3. Time and date of when the same incident was closed
    4. +
    5. Opening time of the site for which the incident was logged
    6. +
    7. Closing Time of the site for which incident was logged
    8. +
    9. Country of the site for which incident has been logged
    10. +
    + +

    Assumption: It is assumed that opening and closing times are same on all working days and that all the sites are closed on holidays and weekends

    + +

    Function should take all the above five “given” as parameter and then calculate age of the incident.

    + +

    Example of problem

    + +

    Let’s say an incident was logged on “Friday 10th June 2016 at 12:00” for a site in the “UK” which opens between 09:00 to 16:00. This incident was then closed on “Tuesday 14th June 2016 at 14:00”.

    + +

    For the above incident function should calculate the age as 960 minutes = 16 hours = [4 hours on Friday (12:00 to 16:00) + 7 hours on Monday (09:00 to 16:00) + 5 hours on Tuesday (09:00 to 14:00)]

    + +

    Pre-requisites:

    + +

    A holiday table for the “Country” of the site for which incident is being provided should already be created on the database with the name holiday_table. Created using code below:

    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +
    CREATE TABLE `holiday_table` (
    +	`holiday_table_id` INT(11) NOT NULL,
    +	`holiday_date` DATETIME NULL DEFAULT NULL,
    +	`week_day` VARCHAR(12) NULL DEFAULT NULL,
    +	`holiday_name` VARCHAR(45) NULL DEFAULT NULL,
    +	`Country_codes` VARCHAR(45) NOT NULL DEFAULT 'ALL',
    +	PRIMARY KEY (`holiday_table_id`)
    +)
    +COLLATE='utf8_general_ci'
    +ENGINE=InnoDB
    +;
    +
    + +

    Sample sql-insert Data for holiday_table:

    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +
    INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (2, '2016-03-25 00:00:00', 'Friday', 'Good Friday', 'ALL');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (3, '2016-03-28 00:00:00', 'Monday', 'Easter Monday', 'ALL');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (4, '2016-05-02 00:00:00', 'Monday', 'Early May bank holiday', 'ALL');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (5, '2016-05-30 00:00:00', 'Monday', 'Spring bank holiday', 'ALL');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (6, '2016-08-29 00:00:00', 'Monday', 'Summer bank holiday', 'ALL');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (7, '2016-12-26 00:00:00', 'Monday', 'Boxing Day', 'ALL');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (8, '2016-12-27 00:00:00', 'Tuesday', 'Christmas Day (substitute day)', 'ALL');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (9, '2016-01-01 00:00:00', 'Friday', 'New Year’s Day', 'SG');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (10, '2016-02-08 00:00:00', 'Monday', 'Chinese New Year', 'SG');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (11, '2016-02-09 00:00:00', 'Tuesday', 'Chinese New Year', 'SG');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (12, '2016-05-21 00:00:00', 'Saturday', 'Vesak Day', 'SG');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (13, '2016-07-06 00:00:00', 'Wednesday', 'Hari Raya Puasa', 'SG');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (14, '2016-08-09 00:00:00', 'Tuesday', 'National Day', 'SG');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (15, '2016-09-12 00:00:00', 'Monday', 'Hari Raya Haji', 'SG');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (16, '2016-10-29 00:00:00', 'Saturday', 'Deepavali', 'SG');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (17, '2016-01-01 00:00:00', 'Friday', 'New Year’s Day', 'IN');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (18, '2016-01-26 00:00:00', 'Tuesday', 'Republic Day', 'IN');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (19, '2016-07-06 00:00:00', 'Wednesday', 'Idul Fitr', 'IN');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (20, '2016-08-15 00:00:00', 'Monday', 'Independence Day', 'IN');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (21, '2016-10-11 00:00:00', 'Tuesday', 'Dussehra/Durga Puja', 'IN');
    +INSERT INTO `holiday_table` (`holiday_table_id`, `holiday_date`, `week_day`, `holiday_name`, `Country_codes`) VALUES (22, '2016-10-31 00:00:00', 'Monday', 'Diwali Privilege Holiday/Gobardhan Puja', 'IN');
    +
    + +

    Function code and explanation

    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    +29
    +30
    +31
    +32
    +33
    +34
    +35
    +36
    +37
    +38
    +39
    +40
    +41
    +42
    +43
    +44
    +45
    +46
    +47
    +48
    +49
    +50
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +73
    +74
    +75
    +76
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +97
    +98
    +99
    +100
    +101
    +102
    +103
    +104
    +
    CREATE DEFINER=`root`@`localhost` FUNCTION `workday_time_diff_holiday_table`(
    +`param_country` varchar(10), 
    +`assigneddatetime` varchar(20), 
    +`closeddatetime` varchar(20), 
    +`starttime` varchar(20), 
    +`endtime` varchar(20)
    +)
    +	RETURNS int(11)
    +	LANGUAGE SQL
    +	NOT DETERMINISTIC
    +	CONTAINS SQL
    +	SQL SECURITY DEFINER
    +	COMMENT ''
    +BEGIN 
    +Set @starttime = starttime;
    +Set @endtime = endtime;
    +Select time_to_sec(timediff(@endtime,@starttime))/3600 into @maxhoursaday;
    +Set @assigneddate = assigneddatetime; 
    +Set @closeddate = closeddatetime; 
    +Set @timecount = 0;
    +Set @timevar1 = @assigneddate;
    +Set @nextdate = @assigneddate;
    +Set @timevar2 = null;
    +Set @param_country = param_country;
    +############ 
    +/*Check if the assigned time was before the starttime 
    +or closed time was after the endtime provided*/
    +#############
    +Set @checkstart = null;
    +Set @checkend = null;
    +Select CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@starttime),
    +CONCAT(SUBSTRING_INDEX(@closeddate, ' ', 1), ' ',@endtime)  into @checkstart, @checkend;
    +
    +if (@assigneddate > @checkstart) then
    +		if (@closeddate<@checkend) then
    +			Set @assigneddate = @assigneddate;
    +            Set @closeddate = @closeddate;
    +		else
    +			Set @assigneddate = @assigneddate;
    +			Set @closeddate = @checkend;
    +		end if;
    +    else
    +		if (@closeddate<@checkend) then
    +			SET @assigneddate = @checkstart;
    +            Set @closeddate = @closeddate;
    +		else
    +			SET @assigneddate = @checkstart;
    +            Set @closeddate = @checkend;
    +		end if;
    +    end if;
    +####################
    +/*After above check, the assigneddate and closeddate
    +variables will be reset in accordance with the checks.*/
    +####################################    
    +
    +SELECT DATEDIFF(@closeddate, @assigneddate) INTO @fixcount; # check the difference between assigned date and closed date. 
    +Set @count = @fixcount; # allocate the difference between closed date and assigned date to a counter
    +If @fixcount > 0 then #  true if line 57 resulted in more than 1 then run the while loop on next line
    +	while @count>=0 do # run the while loop until the count which is right now difference between closed and assigned becomes zero
    +		select weekday(@nextdate) into @weekday; # Assign the weekday value to @weekday. Weekday returns o for Monday, 2 for Tuesday ...5 for Saturday and 6 for Sunday
    +
    +/*Check if the date stored in nextdate 
    +(which is assigneddate on first run of while loop and closeddate on last run) 
    +is a holiday and set the holiday flag*/
    +
    +        Select sum(if(date_format(holiday_date,'%Y-%m-%d') = substring_index(@nextdate,' ',1),1,0)) 
    +        from holiday_table 
    +        where Country_codes = 'ALL' or instr(Country_codes,@param_country)>0
    +        into @holidayflag; 
    +		if ( @weekday<5 and @holidayflag=0) then #Proceed if the date in nextdate variable is neither weekend nor a holiday
    +			if (@count = @fixcount) then #Check if it is first run.ie. if nextdate is assigneddate
    +				Set @timevar1 = @assigneddate; #assign assigndate to variable timevar1
    +				SELECT CONCAT(SUBSTRING_INDEX(@assigneddate, ' ', 1), ' ',@endtime) INTO @timevar2;#get site closing time on assigned date and store it on to timevar2
    +			elseif (@count = 0) then #if the date in nextdate variable is closeddate then do the following otherwise proceed
    +				Select concat(substring_index(@closeddate,' ',1),' ',@starttime) into @timevar1; # 
    +				Set @timevar2 = @closeddate;
    +			else
    +				Select concat(@nextdate,' ',@starttime) into @timevar1;
    +				SELECT CONCAT(@nextdate, ' ', @endtime) INTO @timevar2;
    +			end if;
    +	SELECT 
    +        LEAST(Greatest(((TIME_TO_SEC(TIMEDIFF(@timevar2, @timevar1))) / 3600),0),@maxhoursaday) 
    +        INTO @timecounttemp;
    +            
    +			Set @timecount = @timecounttemp + @timecount;
    +		end if;
    +        Set @timevar1 = @nextdate;
    +        SELECT 
    +        ADDDATE(SUBSTRING_INDEX(@timevar1, ' ', 1),1) 
    +        INTO @nextdate;
    +		Set @count = @count - 1;
    +	end while;
    +else 
    +    #check if the assigned date / closed date is a holiday or weekend
    +    select weekday(@assigneddate) into @weekday; # Assign the weekday value to @weekday. Weekday returns o for Monday, 2 for Tuesday ...5 for Saturday and 6 for Sunday
    +    Select sum(if(date_format(holiday_date,'%Y-%m-%d') = substring_index(@assigneddate,' ',1),1,0)) from holiday_table where Country_codes = 'ALL' or instr(Country_codes,@param_country)>0 into @holidayflag; #Check if the date stored in assigneddate is a holiday and set the holiday flag
    +    if ( @weekday<5 and @holidayflag=0) then #Proceed if the date in assigneddate variable is neither weekend nor a holiday
    +        SELECT Least(Greatest(((TIME_TO_SEC(TIMEDIFF(@closeddate, @assigneddate))) / 3600),0),@maxhoursaday) INTO @timecount;
    +    else
    +        Set @timecount = 0;
    +    end if;
    +end if;
    +RETURN @timecount*60;
    +END
    +
    + +

    Calling the function

    + +

    Function will expect 5 parameters and with specific format as explained below:

    + +
      +
    • param_country - This is the country code as specified in holiday table
    • +
    • assigneddatetime - This must be provided in the format %Y-%m-%d %H-%i-%s. So for our example it will be 2016-06-10 12:00:00 +
    • +
    • closeddatetime - This must be provided in the format %Y-%m-%d %H-%i-%s. So for our example it will be 2016-06-14 14:00:00 +
    • +
    • starttime - This must be in the format %H:%i. So for our example it will be 09:00 +
    • +
    • endtime - This must be in the format %H:%i. So for our example it will be 16:00 +
    • +
    + +

    The call for this function will be as below:

    + +
    ## To get number of minutes
    +Select `WORKDAY_TIME_DIFF_HOLIDAY_TABLE`('UK','2016-06-10 12:00:00','2016-06-14 14:00:00','09:00','16:00');
    +
    +## To get number of hours
    +Select `WORKDAY_TIME_DIFF_HOLIDAY_TABLE`('UK','2016-06-10 12:00:00','2016-06-14 14:00:00','09:00','16:00')/60;
    +
    +## To get in number of working days
    +Select (`WORKDAY_TIME_DIFF_HOLIDAY_TABLE`('UK','2016-06-10 12:00:00','2016-06-14 14:00:00','09:00','16:00')/60)/(substring_index('16:00',':',1)-substring_index('09:00',':',1));
    +
    +
    + +

    Complete Flowchart

    +

    The plantuml code for this can be checked by copying the image link and decoding it on Plantuml Online Server

    + +

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mysql-stored-procedure-to-return-json-for-google-charts/index.html b/mysql-stored-procedure-to-return-json-for-google-charts/index.html new file mode 100644 index 0000000..7bc6d70 --- /dev/null +++ b/mysql-stored-procedure-to-return-json-for-google-charts/index.html @@ -0,0 +1,1076 @@ + + + + + + +MySQL Stored Procedure to return JSON for google charts on BIRT - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    MySQL Stored Procedure to return JSON for google charts on BIRT +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +
    + + +
    + + + +

    My requirement was to get the data in a format that google chart can use to draw the chart I want. Now gogle chart accepts data in json format where all column names separated with comma are in first square bracket set followed by values in rest of the square bracket sets and each square bracket set is separated by comma as well.

    + +

    Having searched on good old google, there did not appear to be any quick way of doing it without getting hands dirty with likes of php and as I was to plug this into a BIRT report where a simple html would do the trick, I really just needed the data-set to be returned in format that google-chart understood.

    + +

    I figured it can be easily done using a MySQL stored procedure and can be a repeatable process which resulted in creation of stored procedure presented below.

    + +

    The code

    + +
    CREATE DEFINER=`root`@`localhost` PROCEDURE `json_builder_multiple_string`(IN `var1` varchar(10000), IN `tab_name` text, IN `int_col_as_str` int)
    +	LANGUAGE SQL
    +	NOT DETERMINISTIC
    +	CONTAINS SQL
    +	SQL SECURITY DEFINER
    +	COMMENT ''
    +BEGIN
    +/*
    +- This procedure will take columns and tablename as parameter.
    +- Third parameter is to tell the procedure how many columns are to be returned as string - surrounded by quotes
    +- Third parameter must always be less than the total columns being requested.
    +- Rest of the columns must have numerical values as they won't be surrounded with quotes
    +- Procedure returns 0 for each null value.
    +​
    +SAMPLE PROCEDURE CALL
    +CALL `json_builder_multiple_string`('Year,Month,Region,Sales,Expenses', 'myjsonexample',4);
    +​
    +SAMPLE QUERY GENERATED:
    +SELECT 
    +    CONCAT('[\'Year\',\'Month\',\'Region\',\'Sales\',\'Expenses\'],',
    +            GROUP_CONCAT('[',
    +                CONCAT_WS(',',
    +                        CONCAT('\'',
    +                                IFNULL(`Year`, 0),
    +                                '\',\'',
    +                                IFNULL(`Month`, 0),
    +                                '\',\'',
    +                                IFNULL(`Region`, 0),
    +                                '\',\'',
    +                                IFNULL(`Sales`, 0),
    +                                '\''),
    +                        IFNULL(`Expenses`, 0))
    +                SEPARATOR '],'),
    +            ']') AS data_set
    +FROM
    +    myjsonexample
    +​
    +SAMPLE OUTPUT:
    +['Year','Month','Region','Sales','Expenses'],['2004','JAN','NW','1000',400],['2005','Feb','SW','1170',460],['2006','Mar','NE','2000',1210],['2007','Apr','SE','650',540],['2008','May','EC','0',0]
    +​
    +SAMPLE TABLE USED:
    +Drop Table if exists `myjsonexample`;
    +CREATE TABLE `myjsonexample` (
    +  `Year` int(11) NOT NULL,
    +  `Month` varchar(25),
    +  `Region` varchar(25),
    +  `Sales` int(11) ,
    +  `Expenses` int(11) 
    +) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    +INSERT INTO `myjsonexample`
    +(`Year`,
    +`Month`,
    +`Region`,
    +`Sales`,
    +`Expenses`)
    +VALUES
    +(2004,'JAN','NW',1000,400),
    +(2005,'Feb','SW',1170,460),
    +(2006,'Mar','NE',2000,1210),
    +(2007,'Apr','SE',650,540),
    +(2008,'May','EC',null,null);
    +*/
    +SET SESSION group_concat_max_len = 1000000;
    +Set @stmt1 = null;
    +select concat('Select concat(\'',concat('[\\\'',replace(replace(var1,'_',' '),',','\\\',\\\''),'\\\'],'),'\'',
    +', group_concat(\'[\',
    +concat_ws(\',\',
    +CONCAT(\'\\\'\', ifnull(`',replace(substring_index(var1,',',int_col_as_str),',','`,0),\'\\\',\\\'\',ifnull(`'),
    +'`,0), \'\\\'\'),',
    +(concat('ifnull(`',
    +replace(
    +                SUBSTRING(var1,LENGTH(SUBSTRING_INDEX(var1, ',', int_col_as_str)) + 2,LENGTH(var1)),
    +                ',',
    +                '`,0),ifnull(`'),
    +'`,0)')
    +)
    +,') SEPARATOR \'],\'),\']\') as data_set from ', tab_name) into @stmt1;
    +Prepare stmt2 from @stmt1;
    +Execute stmt2;
    +END</code></pre>
    +<h1 id="thealternativecode">The alternative code</h1>
    +Another variation of above stored procedure where the output is returned in an output parameter can be created simply by adding two more lines to above code after Execute stmt2 and also including the output parameter in first line. Complete code below:
    +<pre class="language-sql line-numbers"><code>CREATE DEFINER=`root`@`localhost` PROCEDURE `json_builder_outparam`(IN `var1` varchar(10000), IN `tab_name` text, OUT `varout` text)
    +	LANGUAGE SQL
    +	NOT DETERMINISTIC
    +	CONTAINS SQL
    +	SQL SECURITY DEFINER
    +	COMMENT ''
    +BEGIN
    +/*
    +- This procedure will take columns and tablename as parameter.
    +- Third parameter is to tell the procedure how many columns are to be returned as string - surrounded by quotes
    +- Third parameter must always be less than the total columns being requested.
    +- Rest of the columns must have numerical values as they won't be surrounded with quotes
    +- Procedure returns 0 for each null value.
    +​
    +SAMPLE PROCEDURE CALL
    +CALL `json_builder_multiple_string`('Year,Month,Region,Sales,Expenses', 'myjsonexample',4);
    +​
    +SAMPLE QUERY GENERATED:
    +SELECT 
    +    CONCAT('[\'Year\',\'Month\',\'Region\',\'Sales\',\'Expenses\'],',
    +            GROUP_CONCAT('[',
    +                CONCAT_WS(',',
    +                        CONCAT('\'',
    +                                IFNULL(`Year`, 0),
    +                                '\',\'',
    +                                IFNULL(`Month`, 0),
    +                                '\',\'',
    +                                IFNULL(`Region`, 0),
    +                                '\',\'',
    +                                IFNULL(`Sales`, 0),
    +                                '\''),
    +                        IFNULL(`Expenses`, 0))
    +                SEPARATOR '],'),
    +            ']') AS data_set
    +FROM
    +    myjsonexample
    +​
    +SAMPLE OUTPUT:
    +['Year','Month','Region','Sales','Expenses'],['2004','JAN','NW','1000',400],['2005','Feb','SW','1170',460],['2006','Mar','NE','2000',1210],['2007','Apr','SE','650',540],['2008','May','EC','0',0]
    +​
    +SAMPLE TABLE USED:
    +Drop Table if exists `myjsonexample`;
    +CREATE TABLE `myjsonexample` (
    +  `Year` int(11) NOT NULL,
    +  `Month` varchar(25),
    +  `Region` varchar(25),
    +  `Sales` int(11) ,
    +  `Expenses` int(11) 
    +) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    +INSERT INTO `myjsonexample`
    +(`Year`,
    +`Month`,
    +`Region`,
    +`Sales`,
    +`Expenses`)
    +VALUES
    +(2004,'JAN','NW',1000,400),
    +(2005,'Feb','SW',1170,460),
    +(2006,'Mar','NE',2000,1210),
    +(2007,'Apr','SE',650,540),
    +(2008,'May','EC',null,null);
    +*/
    +SET SESSION group_concat_max_len = 1000000;
    +Set @stmt1 = null;
    +select concat('Select concat(\'',concat('[\\\'',replace(replace(var1,'_',' '),',','\\\',\\\''),'\\\'],'),'\'',
    +', group_concat(\'[\',
    +concat_ws(\',\',
    +CONCAT(\'\\\'\', ifnull(`',replace(substring_index(var1,',',int_col_as_str),',','`,0),\'\\\',\\\'\',ifnull(`'),
    +'`,0), \'\\\'\'),',
    +(concat('ifnull(`',
    +replace(
    +                SUBSTRING(var1,LENGTH(SUBSTRING_INDEX(var1, ',', int_col_as_str)) + 2,LENGTH(var1)),
    +                ',',
    +                '`,0),ifnull(`'),
    +'`,0)')
    +)
    +,') SEPARATOR \'],\'),\']\') as data_set from ', tab_name) into @stmt1;
    +Prepare stmt2 from @stmt1;
    +Execute stmt2;
    +Set varout = @varouttemp;
    +Select varout;
    +END
    +
    + +

    The BIRT usage explained

    + +

    My sample BIRT reports are available for download HERE. +Enabling the google chart on BIRT couldn’t be simpler.

    + +
      +
    • Create a new report
    • +
    • Add a text field, open it and change it to HTML as shown in the image below
      + +
    • +
    • Copy the html[1] (modify as required - I used jsfiddle for checking the code and making changes) and paste in to the text field.
    • +
    • Run
    • +
    • Once the BIRT preview is working fine, create a dataset by calling the stored-procedure above and bind it to this text field.
    • +
    • To bind a dataset, in "Report Design" perspective, click on the field and then in "Property Editor" select "Binding" as shown below:
      + +
    • +
    • Then right click on the text field and click on edit and then click on the "Fx" symbol next to where field was changed to HTML on top of the window. This will open javascript editor.
    • +
    • On javascript editor select the static dataset and replace it by selecting the dataset like so:
      + +
    • +
    • That should change the HTML to dynamically get the data from json builder.
    • +
    + +
    <html>
    +  <head>
    +    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    +    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    +    <script type="text/javascript">
    +google.charts.load('current', {
    +        'packages': ['geochart', 'corechart', 'table', 'controls']
    +      });
    +      google.charts.setOnLoadCallback(drawRegionsMap);
    +
    +      function drawRegionsMap() {
    +				// Create our data table.
    +        	  var data = google.visualization.arrayToDataTable([
    +            ['Country', 'Period', 'RMPV', 'TARGET', 'Average'],
    +            ['Germany', 'Nov-2015', 75, 120, 97.5],
    +            ['Germany', 'Dec-2015', 55, 65, 60],
    +            ['Canada', 'Nov-2015', 200, 85,42.5],
    +            ['Canada', 'Dec-2015', 55, 65, 60],
    +            ['IN', 'Nov-2015', 328, 30, 179],
    +            ['IN', 'Dec-2015', 55, 65, 60],
    +            ['France', 'Dec-2015', 30, 100, 65],
    +            ['France', 'Nov-2015', 75, 120, 97.5],
    +            ['Brazil', 'Dec-2015', 50, 100, 75],
    +            ['Brazil', 'Nov-2015', 200, 85, 142.5],
    +            ['United States', 'Dec-2015', 55, 65, 60],
    +            ['United States', 'Nov-2015', 328, 30, 179]
    +          ]);
    +
    +        var data1 = google.visualization.data.group(data,[0],
    +        [{'column': 4, 'aggregation': google.visualization.data.avg, 'type': 'number'}]
    +        );
    +
    +        var options = {
    +          colorAxis: {
    +//          values: [0,30,90,150],
    +          colors: ['#F40EF4','#FCFC2A','#7BFC2A', '#FC4A2A']
    +          },
    +          backgroundColor: '#F1F7FD',
    +          datalessRegionColor: '#8B8E91',
    +          defaultColor: '#f5f5f5'
    +        };
    +        var chart = new google.visualization.GeoChart(document.getElementById('regions_div'));
    +
    +        function selectHandler() {
    +          var selectedItem = chart.getSelection()[0];
    +          if (selectedItem) {
    +            var message = data1.getValue(selectedItem.row, 0);
    +//            alert(message)
    +            //if(message=='Canada'){
    +            var display_value = [message];
    +           //};
    +          };
    +          /* Start of testing*/
    +          
    +          // Create a dashboard.
    +          var dashboard = new google.visualization.Dashboard(
    +            document.getElementById('dashboard_div'));
    +
    +          // Create a range slider, passing some options
    +          var donutRangeSlider = new google.visualization.ControlWrapper({
    +            'controlType': 'NumberRangeFilter',
    +            'containerId': 'filter_div',
    +            'options': {
    +              //'filterColumnLabel': 'M602'
    +              'filterColumnIndex': [2]
    +            }
    +          });
    +
    +          // Create a range slider, passing some options
    +          var donutRangeSlider1 = new google.visualization.ControlWrapper({
    +            'controlType': 'NumberRangeFilter',
    +            'containerId': 'filter_div1',
    +            'options': {
    +              //'filterColumnLabel': 'M525'
    +              'filterColumnIndex': [3]
    +            }
    +          });
    +
    +          var categoryPicker = new google.visualization.ControlWrapper({
    +            controlType: 'CategoryFilter',
    +            containerId: 'control3',
    +            state: {
    +              selectedValues: display_value
    +            },
    +            options: {
    +              //filterColumnLabel: 'Location Type',
    +              filterColumnIndex: [0],
    +              ui: {
    +                labelStacking: 'vertical',
    +                allowTyping: false,
    +                allowMultiple: false,
    +                allowNone: false
    +              }
    +            }
    +          });
    +          // Create a column chart, passing some options
    +          var ColumnChart = new google.visualization.ChartWrapper({
    +            'chartType': 'ColumnChart',
    +            'containerId': 'chart1',
    +            'options': {
    +              'width': '900px',
    +              'legend': 'bottom',
    +              'vAxes': {
    +                0: {
    +                  title: 'Average Utilisation/Target Utilisation'
    +                },
    +                1: {
    +                  title: 'RMPV Utilisation'
    +                }
    +              },
    +              'series': {
    +                1: {
    +                  targetAxisIndex: 1
    +                }
    +              }
    +            },
    +            view: {
    +              columns: [1, 2, 3, 4]
    +            }
    +
    +
    +          });
    +
    +          // Create a table chart, passing some options
    +          var tableChart = new google.visualization.ChartWrapper({
    +            'chartType': 'Table',
    +            'containerId': 'chart2',
    +          });
    +
    +          var pie = new google.visualization.ChartWrapper({
    +            chartType: 'PieChart',
    +            containerId: 'chart3',
    +            options: {
    +              legend: 'bottom',
    +              title: 'Average Utilisation per month',
    +              pieSliceText: 'value'
    +            },
    +            // Instruct the piechart to use colums 0 (Name) and 3 (Donuts Eaten)
    +            // from the 'data' DataTable.
    +            view: {
    +              columns: [1, 4]
    +            }
    +          });
    +
    +          // Establish dependencies, declaring that 'filter' drives 'pieChart',
    +          // so that the pie chart will only display entries that are let through
    +          // given the chosen slider range.
    +          dashboard.bind([donutRangeSlider, donutRangeSlider1, categoryPicker], [ColumnChart, tableChart, pie]);
    +
    +          // Draw the dashboard.
    +          dashboard.draw(data);
    +          //redraw the main chart so selection doesn't retun empty
    +					chart.draw(data1, options);
    +          // End of testing*/
    +        };
    +
    +        google.visualization.events.addListener(chart, 'select', selectHandler);
    +
    +        chart.draw(data1, options);
    +      }
    +
    +	  </script>
    +  </head>
    +  <body>
    +    <div id="regions_div" style="width: 900px; height: 500px;"></div>
    +	  <!--Div that will hold the dashboard-->
    +  <div id="dashboard_div">
    +    <!--Divs that will hold each control and chart-->
    +    <table>
    +      <tr>
    +        <td>
    +        <div id="control3" align="center"></div>
    +        </td>
    +      </tr>
    +      <tr>
    +        <td>
    +          <div id="filter_div"></div>
    +        </td>
    +        <td>
    +          <div id="filter_div1"></div>
    +        </td>
    +      </tr>
    +    </table>
    +    <div id="chart1"></div>
    +    <div id="chart3"></div>
    +    <div id="chart2" align="center"></div>
    +    </div>
    +  </body>
    +</html>
    +
    + +
    + +
    +
      +
    1. HTML to be pasted in text field: ↩︎ +
    2. +
    +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/note-7-to-oneplus-3/index.html b/note-7-to-oneplus-3/index.html new file mode 100644 index 0000000..c264b3b --- /dev/null +++ b/note-7-to-oneplus-3/index.html @@ -0,0 +1,732 @@ + + + + + + +Note 7 to OnePlus 3 - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Note 7 to OnePlus 3 +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +
    + + +
    + +

    I was a super excited owner of Note 7 in September this year and then in few +days the happiness started disappearing as the news of exploding Note 7 started +appearing all over the internet.

    + +

    Then came the notice to exchange the Note 7 which I dutifully did and assured +that nothing will now go wrong I started enjoying that gem of a device. Alas, it +was not to be and the device was recalled for good. Being a user of Note devices +for so long and yet unable to continue with my old Note 3, I decided to go for +some interim device - you know until Samsung decides to get the act right and +bring the next Note device to the market.

    + +

    After going through a number of possible options ranging from Note 7’s close +cousin Galaxy S7 edge+ all the way to newly released Pixel, I actually settled +for OnePlus 3. It was not a gamble actually as my wife is already using it and +is quite pleased with it’s performance and the price is just right for this to +qualify as the interim phone.

    + +

    Anyway, so I ordered and waited because there was a good 4 week wait period. The +ordering and delivery all went as planned while in the meantime I was back with +my trusty Note 3.

    + +

    OnePlus 3 arrived and I set it up to my taste and my oh my what an experience. +Apart from Stylus it was lacking nothing and is very smooth, very light and +camera quality is very good as well - not as good as Note 7 but definitely +better than Note 3. I have now been with this device for just over a week and +although I miss the S-Pen, I am very happy with the device in general and it’s +battery life in particular.

    + +

    I have listed below the things that I like and the things I hope it had:

    + +

    Features I Loved:

    + +
      +
    1. Battery life
    2. +
    3. Superfast Charging with Dash Charger
    4. +
    5. Display and inbuilt Night mode
    6. +
    7. Ringer Switch
    8. +
    9. Inbuilt Colour control for LED Notification
    10. +
    11. Smooth minimal interface
    12. +
    13. No bloatware
    14. +
    15. Fingerprint Scanner doubles as Home button
    16. +
    17. Gesture support for flash light
    18. +
    19. Ability to swap Capacitive button functionality
    20. +
    21. Dual Sim Card capability - Allows me to carry one phone for both work and personal numbers.
    22. +
    + +

    Sure all of the above can be pretty similar on all high end handsets but what +makes it unique for OnePlus 3 is the price point of £329. That price is +unbeatable and the phone, in my humble opinion, must be the first choice for +anyone who isn’t fussed about a stylus.

    + +

    There can be a comparison between camera results where Samsung outperforms in my +experience but only slightly and still the photo quality from OnePlus 3 is +definitely very good second only to Samsung and no other.

    + +

    Wishlist

    + +
      +
    1. Functional Stylus (Frankly no other manufacturer is tapping on this market. God knows why?)
    2. +
    3. Note 7 had a good integration of Fingerprint Scanner with browser to login to various sites. I miss that on OnePlus 3.
    4. +
    5. External Memory Card slot.
    6. +
    + +

    Here’s hoping that OnePlus 3 team will listen to my wishlist :).

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/o2-xda-serra-official-htc-upgrade-with-tf3d-of-touch-pro-2/index.html b/o2-xda-serra-official-htc-upgrade-with-tf3d-of-touch-pro-2/index.html new file mode 100644 index 0000000..a12a156 --- /dev/null +++ b/o2-xda-serra-official-htc-upgrade-with-tf3d-of-touch-pro-2/index.html @@ -0,0 +1,792 @@ + + + + + + +O2 XDA Serra - Official HTC upgrade with TF3D of Touch Pro 2 - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    O2 XDA Serra - Official HTC upgrade with TF3D of Touch Pro 2 +

    + + +

    + + + + + + + + + + + + 12 minute read + + + +

    + + +
    + + +
    + +

    Ever since I laid my hands on O2 XDA Serra aka HTC Raphael aka HTC Touch Pro , I have always loved the device despite it’s limitations on battery life and have found some really useful apps during my association with windows mobile (for last five years or so) which found their way onto Serra as well. While I have known for a while that we can flash ROMS, I was perhaps over protective when it came to Serra and never really went beyond doing a HardSPL for device but last Sunday when I was fiddling with the facebook application and SMS registration, I realised that each time I sent an SMS and then tried to open the internet explorer or opera they will just crash demanding a soft reset. I could instantly find the problem to be Kaspersky Anti-Virus(KAV) failure. It is not possible to remove KAV without a hard reset at least not to my knowledge. (A hard reset is what brings your device back to factory conditions). Now since a Hard Reset would mean lot of work in terms of reinstalling all my beloved apps etc, I figured I might as well see if there is some other ROM I could use. This is what triggered my quest. +What started as fixing the issue caused with a Kaspersky Anti Virus failure rendering crashes to internet browsers on my HTC Touch Pro ended in my pleasantly getting a complete device makeover and in the process making my gadget work way better than it was working before. Lovely GUI, great Facebook integration, faster boot time and most important of all a functional GPS, yes you read it right a functional GPS. The problem that plagued almost all handsets provided by O2. +I must start by saying that if it worked for me it must work for you but if it does not please don’t hold me responsible. Another thing you must know is doing a HardSPL strips your device of the warranty. There is a way of putting back the StockSPL for warranty reasons but I have not tried it but then it does not sound too complicated. You may also find views on forums that suggest installing a cooked ROM is better but as I said I am too much in love with the device to install flaky ROMs I actually opted for the official update ROM released by HTC and then gave it the touchflo3d of touch pro 2 and after some tweaks here and there which I will all list down, I am completely (I stress the word completely) satisfied with the final results. +Ok so now over to the process: +What are we trying to achieve?

    +
      +
    1. To install the manufacturer provided ROM
    2. +
    3. To get the handset a good look and feel.
    4. +
    5. Ensure that GPS functions
    6. +
    7. To get phone address book integrated with face-book so you can get your contacts pictures from their FB profile onto your device.
    8. +
    9. To get Weather for the local city that may not be present in the default list.
    10. +
    11. Gmail sync to ensure Gmail is configured as push mail on your device.
    12. +
    13. The Google calendar sync to your on device outlook calendar.
    14. +
    +

    What do we need?

    +
      +
    1. A windows PC / laptop.
    2. +
    3. O2 XDA Serra / HTC Raphael / HTC Touch Pro
    4. +
    5. USB cable to connect XDA to PC / laptop
    6. +
    7. At least a 1 GB Mini SD Card. Considering you have XDA, you may want to get a bigger capacity memory card (of up to 8GB or 16GB…). I have a 8GB one.
    8. +
    9. Access to internet.
    10. +
    11. A good data plan that will ensure good use of the effort you are about to put in.
    12. +
    13. Downloaded cabs and ROM. You can either download all in bulk from here (http://www.mediafire.com/?l12vaj688qwh85w) (I have uploaded them for ease as a bundle) or follow the links and download individually under each section.
    14. +
    15. and as I always ask, lot of patience.
    16. +
    +

    Steps:

    +
      +
    1. HardSPL
    2. +
    3. Install HTC ROM
    4. +
    5. Install touchflo3d
    6. +
    7. Install Dialler
    8. +
    9. Install .net 3.5 and weather database editor
    10. +
    11. Configure Device
    12. +
    13. Configure Email
    14. +
    +

    Step 1: HardSPL +Now to start, we will first need to apply HardSPL to the device. I do not know what the words SPL stand for but what I have come to understand after reading for hours on XDA-developer forums is that if you want to install ROM that is not provided by the vendor who sold you the device (O2 in my case), you will need to apply this HardSPL or you may either not be able to install the new ROM or may even be at risk of bricking your device. HardSPL is not complicated and all credit goes to developers at xda-developer community. You will have official instructions if you follow this link < http://forum.xda-developers.com/showthread.php?t=410150>. +For the sake of completeness, I am anyway including instructions here.

    +
    +

    </hr>text borrowed from XDA-developer website<hr></hr> +Instructions:

    +
      +
    1. download Hard-SPL package from attachment, extract to an empty folder. make sure it's launched from a local drive (not through network drive, etc.)
    2. +
    3. you must Have Phone Synced with PC in Windows Mobile!
    4. +
    5. run RaphaelHardSPL-Unsigned_190_1_3.exe,
    6. +
    7. follow steps in the RUU, check device for prompts after PC shows loading bar.
    8. +
    9. it should go to black screen now.
    10. +
    11. SPL flashes, device automatically reboots, job done.
    12. +
    13. to confirm you got it installed, go into bootloader mode (tricolour screen!) and verify the screen shows 1.90.OliNex.
    14. +
    +

    NOTE 1: you will not see the SPL version during normal boot, that is the OS version, not SPL!
    +to enter bootloader mode to see version: with the device turned on, press and hold the volume down button, then press the reset button with the stylus tip, then release the volume down button when bootloader tricolour screen appears. +NOTE 2: anyone having problems with the device entering SSPL automatically, please copy SSPLManual.exe from second attachment to the device and run it. then once the screen is black, run RUU manually. i.e. you run the RUU on the PC, if it isn’t obvious. +NOTE 3: this is unsigned Hard-SPL. no limitations on flashing ROMs or radio packages. also, this has overwrite protection, if someone needs to revert to stock SPL for warranty reasons, we will soon post a stock SPL downgrade package. +NOTE 4: do not use this RUU for anything other than SPL flashing (i.e. hardspl or stock spl restore)!!! if you want to flash some other rom, then use customruu from: http://forum.xda-developers.com/showthread.php?t=410761 +Step by step how to return to stock spl - for warranty reasons only!

    +
      +
    1. be sure to first restore stock OS, and stock radio. stock SPL is always to be done last!!
    2. +
    3. download the stock spl package from this post.
    4. +
    5. also download the original hardspl from the attachments in this post.
    6. +
    7. run the hardspl exe but do not click anything in RUU yet. just let the hardspl EXE extract the files for flashing.
    8. +
    9. the SPL you want to revert to is a .NBH file, put the NBH in the extracted hardspl package, overwriting the original NBH file in it!
    10. +
    11. continue with the RUU (or if needed, run SSPL-Manual.exe manually, then run RUU when it goes to black screen), it goes to 100%, reboots, done.
      ++6. to verify, volume down + reset, see version number on tricolour screen, should now just say 1.90.0000.
    12. +
    +
    +

    </hr>text borrowed from XDA-developer website<hr></hr> +Step 2: Install HTC ROM +Straight forward really. Connect your phone to the PC through the USB cable. Ensure that a connection is established with pc through active sync. Now double click on the ROM update utility (RUU) (RUU_Raphael_HTC_Europe_5.05.401.1_R2_Radio_Signed_Raphael_52.58.25.30_1.11.25.01_Ship) and it will guide you through the process. +If you want to directly download from HTC the link is http://www.htc.com/uk/SupportDownload.aspx?p_id=140&cat=2&dl_id=501 +You will have to use the non-O2 serial number. The one I used was HT833K016924. +(Now if all went well what follows is the non-risky bit. To be honest I don’t even understand why we have to do this but then that’s the process and I was not about to experiment on my lovely instrument based on my limited knowledge led beliefs so I did this nonetheless.) +Once completed it will restart XDA. Once it reaches the screen where it asks to configure Stylus, do a Hard Reset by pressing the Volume down key and the enter key (Round centre key) simultaneously and pressing the reset key with your stylus. (Reset Key is a small hole at the bottom of your handset next to the charging slot.) Once the system switches off release the reset key but continue to hold Volume Down key and enter key. +It will now show a screen cautioning you that if you continue it will result in loss of data and if you wish to continue press Vol Up key. Press the Vol up key now. I don’t distinctly remember but I think it asked me twice to press Vol Up which I did. Basically just follow the instructions. +That should sort out the new ROM installation. +Now at this stage, you would have got rid of the O2 splash screen which has given way to elegant Touch Pro splash screen. This itself was an extreme form of happiness as I was not a huge fan of the O2 splash screen anyway. Ok you may want to take a moment with the new screen and play around. If you are happy and are not overly worried about facebook integration, I will recommend that you skip to Step 5. If however you want FB integration and would want a more Touch Pro 2 kind of interface there is still some work to be done. Moving on, then to Step 3. +Step 3: Install touchflo3d

    +
    +

    </hr>text borrowed from XDA-developer website<hr></hr> +http://forum.xda-developers.com/showthread.php?t=542113 +Instructions +1) Disable “TouchFLO 3D” from your Today items. Soft reset. +2) Go to System Settings, “Power”, and uncheck all of the Options on the “Advanced” tab. +3) Install the “Gen.Y_Manila_R1_5.cab” (install may take 10 minutes or more). DO NOT RESTART. +4) Install Language Pack cab 0409 Gen.Y_Manila_R1_5. DO NOT RESTART. +5) Install the HTC Scroll_1_0_1914_2726 cab file. NOW SOFT RESET. +Done!

    +
    +

    </hr>text borrowed from XDA-developer website<hr></hr> +I did not install anything in the optional installs but if you would want to, you can follow the instructions on the post by Captain Throwback on the link above. The version I downloaded was R1.5 which is included in the pack. +Step 4: Install Dialler +I downloaded the dialler from ppcgeek forums, many thanks to them. It is not available on the link provided in xda-developer post. If you want to download from there, you will have to register on their site and do a bit of googling. Alternatively, it is included in the pack above, just the non confusing cab file named - . +Move it to your memory card on phone. Now from XDA go to the location where you saved it and click the cab to install it. It will take about 3 to 5 minutes. +Step 5: Install .net 3.5 and weather database editor +Copy the < NETCFv35.wm.armv4i> cab(.net 3.5) and < WeatherDatabaseEditor 1.1 Modified> cab(weather DB) onto the memory card on your device. Now first install .net 3.5 by clicking the cab file from the location where you copied it. +To install weather DB follow the steps below:
    +5.1. Install Weather Database Editor
    +5.2. find your locCode on <a href="http://www.accuweather.com">http://www.accuweather.com</a>. (Example: Accuweather URL for Northwich in UK is : <a href="http://www.accuweather.com/world-index-forecast.asp?partner=accuweather&traveler=0&loccode=EUR%7CUK%7CUK123%7CNorthwich">http://www.accuweather.com/world-index-forecast.asp?partner=accuweather&traveler=0&loccode=EUR|UK|UK123|Northwich</a>, The locCode is EUR|UK|UK123|Northwich)
    +5.3. Now open the weather DB on XDA, select your country and then select a city that begins with the same alphabet as your city and the one you are unlikely to use. For this example lets say - Nuneaton. Click on Edit -> Edit City.
    +5.4. Now change the name of city to the one you want and in “Accu Weather Code” Field enter the locCode obtained in Step 5.2. For our example it would be - EUR|UK|UK123|Northwich
    +5.5. Click on File and exit.
    +5.6. Go to Weather tab and add your city and update the weather.

    +
    NOTE: Steps 5.7 to 5.9 are not relevant to you if you directly jumped to this section from Step 2 and have not installed the new TF3D
    +

    5.7. Go to home page on XDA and click on watch, now click on “Add City” and add the local city. +5.8. Make this as your default city by clicking the radio button. +5.9 Now click on Menu-> Rearrange Cities and bring your local city to top of the list. +This is it, we will now move to next step. +Step 6: Configure Device +Many people don’t care to update the owner details but I think it is important to have that updated and hence my first step when I am done with initial installations and all is to update owner details. This ensures that if phone is lost and lands in honest hands you still have a chance of getting it back. It did happen to me so it isn’t a fairy tale…J +Click on Windows icon -> Settings-> All Settings ->Personal -> Owner Information and update these details. Ensure that the telephone number you enter is different than the one for this instrument else it pretty much defies the purpose of entering this information on the first place. +Next give your XDA a name. Click on Windows icon -> Settings-> All Settings -> System -> About. -> Device ID. Update your device name and description. +Personalise weather updates. Slide over to weather, add any other cities you are interested in and click update now.

    +
    I recommend that you install registry editor through the cab file provided in my bundle named and a file Explorer named but these are completely optional
    +

    Your basic configuration is now complete.

    + +

    Step 7: Configure Email +If you use hotmail or live and you have a good data plan (mine is unlimited internet usage) it may be a good idea to configure windows live as that ensures push mail so you have instant delivery of your mail as soon as it arrives into your handset. +To do this, Click on Windows icon -> All Programmes -> Windows Live. Enter details and configure.

    +
    Ensure that you change sync settings from “Manual” to “As soon as it comes” and the time schedule according to your needs.
    +

    Gmail has recently introduced Gmail Sync that can sync your email, calendar and address book. They have provided instructions but they were not exactly mapping to how it was shown on my device. I instead used the following route. +Connect your phone to your PC / laptop through ActiveSync and open Device Centre. Now click on Setup device -> Sync Setup and in server name enter m.google.com. enter your username and password. The SSL checkbox is clicked by default so let it remain. If it is not checked, please select it in the checkbox. +Save settings. Now under items to be synced, of the three : email, calendar and address book select whichever you want synced…(Remember if you set anything other than these three to be synced through exchange nothing will work on Google Sync). +I did not want my address book sync with Gmail so I left it and for other two I changed the sync settings to Exchange server. This is it your email will now be delivered into outlook mailbox on your XDA. You may want to check in your Gmail settings that you have activated IMAP or else even though this setup is correct you will not get mails delivered. +Congratulations on successful completion of the whole process. Now enjoy the new look XDA with enhanced capabilities!!!

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openvpn-on-linux-mint-to-access-us/index.html b/openvpn-on-linux-mint-to-access-us/index.html new file mode 100644 index 0000000..d63a9b7 --- /dev/null +++ b/openvpn-on-linux-mint-to-access-us/index.html @@ -0,0 +1,721 @@ + + + + + + +OpenVPN on Linux Mint to access US sites - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    OpenVPN on Linux Mint to access US sites +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +
    + + +
    + +
    + +

    UPDATE: LOOKS LIKE HOSTIZZLE IS NOT WORKING ANYMORE. THE STEPS IN THIS GUIDE WILL STILL BE RELEVANT FOR SETTING UP OPEN VPN, JUST THAT YOU WILL NEED TO FIND SOME OTHER PROVIDER.

    +
    + +

    I received a Google Music invite and as it is only available in US, I used OpenVPN on linux. Now this is a handy little trick to bypass the geographical constraints that are placed to block services on net. All this without opening the windows box to install dodgy looking services of Hotspot Shield which anyway does not work on Linux. +Ever come across "This video is not available in your country." on you tube or ever wanted to access Hulu only to be greeted by a similar message. Well then, if yes is your answer, openVPN is the solution to such sorrows. +It also comes with some other benefits such as anonymous browsing but then highlighting benefits of VPN is not exactly the aim of this post. The aim of this post is: +To configure a free VPN service that allows you to bypass country restrictions for US based sites. Keyword - US. As the site I am going to use only gives a US IP. Ofcourse if we remove the word free it opens up a range of possibilities which I will leave the readers to explore. :) +Alright, let’s get on with the business:

    +

    Step 1: Install OpenVPN

    +

    Open Synaptics and type OpenVPN in "Quick Search". Then select all the packages marked green in screenshot below but not in your synaptic window and click on "Apply". + +Then type "pptp" in "Quick Search" and select all the packages marked green in the screenshot below but not in your synaptic window and click on "Apply". +

    +

    Step 2: Register with the free OpenVPN provider "Hostizzle"

    +

    a) Open the website http://hostizzle.com/ and follow the screenshots below: +
    +
    +

    +

    Step 3: Set-up OpenVPN on Linux Mint

    +

    a) Open Terminal and type the following command: +gksudo nautilus /etc/openvpn/ +You will be asked for root password and the File Explorer will be opened with root access. This is necessary as /opt directory only gives write access to root and as you wil see next step requires some files to be extracted in this directory. This extracted directory will be some long number.

    +
    Please be extremely careful now, as you are accessing your file system as root.
    +

    b) Now copy the zip file downloaded from Hostizzle in Step 2 to this directory and extract it here.

    +

    Step 4: Configure network manager

    +

    Open the Network Manager as shown in following screenshot: +Menu > All Applications > Preferences > Network Connections
    + +This will open the following window:
    + +Now click on VPN tab and then on Import button as shown in next screenshot. +It will open the file selection box. Navigate to File System/etc/openvpn/ and now open the long numbered directory that was extracted in Step 3(a) as shown in next figure. + +Here select the .ovpn file and click Open. Following dialogue box will open.
    +Fig-9 +Just accept defaults and click "Apply".
    +Then Click on "Close" on Network Connections window. +

    +

    Step 5: Connect to the OpenVPN

    +

    Restart your system and once it is connected to your home WiFi, click on Network Indicator in the panel and select "VPN Connections". + +This will list all available VPN connections as can be seen in the screenshot. Select the VPN name with big number which is what we have just configured. + +Once done if everything was done correctly, it should connect to VPN and you should be able to browse with an american IP address. +Some key points to remember: +Free service of Hostizzle is quite generous compared to so many other options I explored, yet it is worth knowing the restrictions:
    +a) 100GB of bandwidth each month.
    +b) US IP
    +c) Limited to one month. You need to download new Keys and configure VPN using steps below each month. +Now considering that it takes less than 5 minutes to complete these steps and that it is a free alternative with a generous bandwidth, I would say it is all worth it. +Ofcourse, if you are not happy with the restrictions, there are some paid alternatives that can be implemented for as low as $4 per month from various providers, Hostizzle included. Go shopping !!!

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page10/index.html b/page10/index.html new file mode 100644 index 0000000..902e262 --- /dev/null +++ b/page10/index.html @@ -0,0 +1,683 @@ + + + + + + +Make Gadgets Work - Page 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Install Open Workbench and JRE on Wine in Linux Mint + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    What is Open Workbench? +Open Workbench is a Project Planning Software comparable to Microsoft Project. There are mixed views on whether it is truly open sour...

    +
    +
    + + + + + + +
    +
    + +

    + + How to edit PDF in Linux - The easy way. + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    Recently someone asked this question to me and after some search on Google I came across mentions of PDFedit, Scribus, flpsed, Gimp, PDFMod and even the open...

    +
    +
    + + + + + + +
    + +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page11/index.html b/page11/index.html new file mode 100644 index 0000000..6f2b6b3 --- /dev/null +++ b/page11/index.html @@ -0,0 +1,676 @@ + + + + + + +Make Gadgets Work - Page 11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Glympse + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Every now and then we come across something that has a strong potential, something that is a great phenomenon in making and Glympse to me is that idea. + +

    +
    +
    + + + + + + +
    +
    + +

    + + MMC Convert media files on Linux + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    I have been using Mobile Media Converter for almost a year now and it is such a great tool. I think it is a must have at-least on Linux. +It not only converts...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Time for a bit of show-off + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Hey Hey I made an Android app named “Sai Satcharitra”. It’s a very basic app and +all it does is helps people read Sai Satcharitra on their Android Smartphone...

    +
    +
    + + + + + + +
    + +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page12/index.html b/page12/index.html new file mode 100644 index 0000000..62aba9e --- /dev/null +++ b/page12/index.html @@ -0,0 +1,667 @@ + + + + + + +Make Gadgets Work - Page 12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Call US and several other countries free using Android + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    That’s right…with smartphones all that was possible using computers is increasingly becoming feasible through phones. I just now configured my Nexus S to mak...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Jagannath Hora on Linux - Play on Linux Magic + + +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +

    While I prefer Maitreya as it can run with Linux as native, quite a few of my friends have asked me if Jagannatha Hora will work on Linux and hence this post...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Audio problem in Wine under KDE 4.4.1 - Solved + + +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +

    I was trying to install spotify on linux which used to work perfectly on Gnome and when I tried on KDE it was giving error box. I wrote winecfg on terminal a...

    +
    +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page13/index.html b/page13/index.html new file mode 100644 index 0000000..1dc97af --- /dev/null +++ b/page13/index.html @@ -0,0 +1,663 @@ + + + + + + +Make Gadgets Work - Page 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Configure BlogTK 1.0 for blogger + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Right so I am happy with Blogilo, then why BlogTK? +It so happened that I was trying to edit my last post and for some unknown reason I was getting error in u...

    +
    +
    + + + + + + +
    +
    + +

    + + KUBUNTU Blog Entry + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    This is how my love for Kubuntu has started….and growing by the minute… +I was experimenting with different Linux distros and then I went ahead with Linux Min...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Sony VAIO N-VIDIA setup to make S-Video work + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    + +UPDATE for 9.10 +For Ubuntu 9.10 I was just not able to get it work and finally I figured it out. The problem is with the latest Nvidia driver, the one that...

    +
    +
    + + + + + + +
    +
    + +

    + + Sony VAIO FE21H Webcam on skype + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Alright friends not a huge tip but still I, being new fan of Ubuntu, installed it on my media laptop…Sony VAIO… +one might ask what is media laptop…well I had...

    +
    +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page14/index.html b/page14/index.html new file mode 100644 index 0000000..4494f77 --- /dev/null +++ b/page14/index.html @@ -0,0 +1,534 @@ + + + + + + +Make Gadgets Work - Page 14 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Get Gmail as Push Email on Sony P990i + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    While I have moved on from P990i which is now in safe hands of my dear wife, I remember doing some good amount of web searching and still had no idea how to ...

    +
    +
    + + + + + + +
    +
    + +

    + + AAO A150L Step by Step Installation Process for Linux4one + + +

    + + +

    + + + + + + + + + + + + 11 minute read + + + +

    + + +

    Linux4one is the only Linux distro other than pre-installed Linpus that worked out of the box on my and two other Acer aspire one A150L which belonged to my ...

    +
    +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page2/index.html b/page2/index.html new file mode 100644 index 0000000..fd7fec5 --- /dev/null +++ b/page2/index.html @@ -0,0 +1,661 @@ + + + + + + +Make Gadgets Work - Page 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Tech for Diabetics + + +

    + + +

    + + + + + + + + + + + + 9 minute read + + + +

    + + +

    As a diabetic there are a number of things we are not able to control but one thing we can do is keep tabs on our data +

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Converting AAX (Audible) to mp3 + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    Lately I have been a bit frustrated because while I subscribe to Audible services, Amazon and Google do not play nice with each other limiting me - the owner...

    +
    +
    + + + + + + +
    +
    + +

    + + Metabase - A BI solution that just works + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    I like exploring new solutions and anything to do with data always piques my interest. I came across this nice tool through the list of free self hosted soft...

    +
    +
    + + + + + + +
    + +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page3/index.html b/page3/index.html new file mode 100644 index 0000000..6cd817b --- /dev/null +++ b/page3/index.html @@ -0,0 +1,665 @@ + + + + + + +Make Gadgets Work - Page 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Home Networking + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    Network routing + + +Router - Router is the device at home that connects all devices in your house to the internet. It does so by assigning IP address to each o...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Ghost Upgrade errors and fixes (1.19.x) + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    I have found the recent ghost upgrades quite painless but there have been few hiccups for last two times so I kept a record of what helped and it is as liste...

    +
    +
    + + + + + + +
    +
    + +

    + + Unprotect Sheets in Libre Calc, Excel + + +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +

    A friend of mine today had an issue. He had created a template for some really complex calculations and to ensure he does not mess up with the forumlae by mi...

    +
    +
    + + + + + + +
    + +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page4/index.html b/page4/index.html new file mode 100644 index 0000000..588d371 --- /dev/null +++ b/page4/index.html @@ -0,0 +1,673 @@ + + + + + + +Make Gadgets Work - Page 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Markdown and Gantt Charts + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    For a fairly long time, I have been looking for a simple markdown type of solution to be able to quickly draw Gantt charts but never came across what one wou...

    +
    +
    + + + + + + +
    +
    + +

    + + DD-WRT firmware on TP-LINK TL-WR841N v11 + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    I have been with PlusNet for over two years now and am a happy camper as far as fiber optic broadband is concerned but as I am no longer on a broadband contr...

    +
    +
    + + + + + + +
    +
    + +

    + + Swap File to create extra memory + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    While renewing my LetsEncrypt certificate, I found myself in a strange situation where the certbot won’t run asking me to update pip and then each time I tri...

    +
    +
    + + + + + + +
    +
    + +

    + + Note 7 to OnePlus 3 + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    I was a super excited owner of Note 7 in September this year and then in few +days the happiness started disappearing as the news of exploding Note 7 started +...

    +
    +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page5/index.html b/page5/index.html new file mode 100644 index 0000000..e5758e3 --- /dev/null +++ b/page5/index.html @@ -0,0 +1,674 @@ + + + + + + +Make Gadgets Work - Page 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Grav - CMS with a difference + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    While I love Ghost as a blogging platform, it is not best placed for things other than blogs - after all that is the basic idea behind creation of this wonde...

    +
    +
    + + + + + + +
    +
    + +

    + + Windows 10 - a bucket load of pain + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    + As Windows 10 is a commercial offering, one would think it will be working as expected and it does so long as like me one has come to expect pain from Mic...

    +
    +
    + + + + + + +
    +
    + +

    + + Ethercalc + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Ethercalc is good tool which can be selfhosted. It is fairly simple to do so. Though it will be available for anyone who has the URL because there is no inbu...

    +
    +
    + + + + + + +
    +
    + +

    + + Fix for PHP Issues after upgrade to Ubuntu 16.04.1 (Xenial) + + +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +

    After updating from Ubuntu 14.04, the php and Apache stopped being friends and one of the WordPress site I maintain went all white and admin page was just sh...

    +
    +
    + + + + + + +
    +
    + +

    + + Update Ghost on Fedora + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    While the guidance on Ghost website is very clear, I did get issues that required steps in troubleshooting. Something to do with lodash and npm version 2 stu...

    +
    +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page6/index.html b/page6/index.html new file mode 100644 index 0000000..6f0ac86 --- /dev/null +++ b/page6/index.html @@ -0,0 +1,680 @@ + + + + + + +Make Gadgets Work - Page 6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + The complete walkthrough of my blogger to ghost migration + + +

    + + +

    + + + + + + + + + + + + 9 minute read + + + +

    + + +

    The 7 Year Itch +It can’t possibly be a coincidence that this is the 7th year since I started blogging on blogger and therefore it is very likely to be a stro...

    +
    +
    + + + + + + +
    +
    + +

    + + Ghost on Fedora 24 + + +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +

    To install Ghost as my blogging platform, I had to go through a number of hoops and one of them was to get the nodejs working and what not. I figured this mi...

    +
    +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page7/index.html b/page7/index.html new file mode 100644 index 0000000..3311f55 --- /dev/null +++ b/page7/index.html @@ -0,0 +1,673 @@ + + + + + + +Make Gadgets Work - Page 7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Seafile Server behind nginx on Fedora 24 Security Lab Spin + + +

    + + +

    + + + + + + + + + + + + 12 minute read + + + +

    + + +

    I have recently been intrigued by the idea of replacing the likes of “Dropbox” and “Google Drive” with a cloud set-up of my own. I had “Owncloud” set-up for ...

    +
    +
    + + + + + + +
    +
    + +

    + + Crossover + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    There are technologies that I can use in my personal life, that I am proud of and those that I play around with but reality is that as a Senior Project Manag...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Arch Linux - nearly a year on…. + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    I have been dwelling in the world of Arch Linux for just under a year now and must admit the experience is nothing less than liberating. Granted that the bar...

    +
    +
    + + + + + + +
    +
    + +

    + + Linux Mint on Android through VNC and Jump + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    Today “Jump” was available for free on Amazon as the app of the day and since it’s nearly 7 quids on google play store, I downloaded. For windows and Mac use...

    +
    +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page8/index.html b/page8/index.html new file mode 100644 index 0000000..5832948 --- /dev/null +++ b/page8/index.html @@ -0,0 +1,676 @@ + + + + + + +Make Gadgets Work - Page 8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Python, ERIC RAD IDE and QT designer + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Right, I have decided to play around a little with the most loved language of open source a. k. a. Python. + +

    +
    +
    + + + + + + +
    +
    + +

    + + Conky on my desktop - step by step + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    +My new friend Damjan recently mentioned that he liked the Conky on my desktop and asked for details as have few others so I figured a post on the topic will...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Prepare Linux Mint 13 for Android development + + +

    + + +

    + + + + + + + + + + + + 8 minute read + + + +

    + + +

    Few weeks back I updated to the latest Linux Mint offering "Maya" a.k.a Linux Mint 13. Now this is a LTS (Long Term Support) version and I wanted to be in a ...

    +
    +
    + + + + + + +
    + +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page9/index.html b/page9/index.html new file mode 100644 index 0000000..fc1719e --- /dev/null +++ b/page9/index.html @@ -0,0 +1,676 @@ + + + + + + +Make Gadgets Work - Page 9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    + + + +

    Recent posts

    + + + + +
    + + + + + +
    +
    + +

    + + Top 10 Android Apps that I use !!!! + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    I have been using Android Phones and Tablets for quite some time now and have +monitored my use for apps that I use on a daily basis and those I would not eve...

    +
    +
    + + + + + + +
    +
    + +

    + + Exchange 2007 on Thunderbird using DAVMail + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    The start of this week was like a nightmare for me. Whole family was down with flu and I had the fever that is probably the highest ever of my entire life at...

    +
    +
    + + + + + + +
    +
    + +

    + + StageVu / DivX video on Bodhi Linux + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    If you read my last post you will know I have been playing with Bodhi Linux lately. One of the selling point for this distro is it’s minimalistic approach. H...

    +
    +
    + + + + + + +
    +
    + +

    + + How to boot from USB when BIOS does not have the option. + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    I have an old Sony VAIO which is not in it’s best of health and has long been really a companion for my telly, faithfully streaming media from bbc iplayer, y...

    +
    +
    + + + + + + +
    +
    + +

    + + OpenVPN on Linux Mint to access US sites + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    + +UPDATE: LOOKS LIKE HOSTIZZLE IS NOT WORKING ANYMORE. THE STEPS IN THIS GUIDE WILL STILL BE RELEVANT FOR SETTING UP OPEN VPN, JUST THAT YOU WILL NEED TO FIN...

    +
    +
    + + +
    + + + + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/part-1-configure-epson-s515w-on-linux-mint-ubuntu-10-04/index.html b/part-1-configure-epson-s515w-on-linux-mint-ubuntu-10-04/index.html new file mode 100644 index 0000000..75cb6ef --- /dev/null +++ b/part-1-configure-epson-s515w-on-linux-mint-ubuntu-10-04/index.html @@ -0,0 +1,733 @@ + + + + + + +Part 1 - Configure Epson S515W on Linux Mint / Ubuntu 10.04 - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Part 1 - Configure Epson S515W on Linux Mint / Ubuntu 10.04 +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +
    + + +
    + +
    + +

    UPDATE +While this tutorial will get your printer up and running, you should also follow Part 2 to ensure that it continues to work even after you have restarted your printer. +-Ankit

    +
    + +

    Lately I have not done much experiments and hence a stable system. I did not require any changes and all was well and then recently I changed over to Linux Mint which by the way is a UBUNTU derivative, very slick and very cool. Now after this reinstall I was able to get back all my previous installs and everything the usual way but scanning using Epson S515W was not out of the box and below are the steps I followed to get it working:

    +
    Step 1: Configure Printer
    +Step 2: Test Print a page
    +Step 3 - Install drivers and software for scanner
    +Step 4 - Test Scanner
    +
    +

    Step 1: Configure Printer

    +

    This really works quite easily in all Ubuntu installs. Goto System > Administration > Printer +Alt Text +Now you can see my printer configured but when you will get this window, you wil only see Print_to_PDF. You should then Click on arrow next to ADD and select Printer. It will open the window below: +Alt Text +Again under Network Printer it may not show anything initially but if your printer is on and configured to your wireless router it will identify in a moment or two. If it does not, click on Find Network Printer. +Alt Text +Once your printer appears, click on it and then click on "Forward".

    +

    Step 2: Print a test Page

    +

    Open the Printer window again from System>Administration>Printer and double click on printer. It should open the following window: +Alt Text +Click on "Print Test Page". If it prints the ubuntu test page correctly your printer is configured. Mine was so I will not go more in details of alternative option. If you do want to know more let me know and I will put a post. +However if now you will try to scan you will find that scanner either is not identified or if it is it does not complete the scanning and printer gets hanged, so over to next steps then.

    +

    Step 3 - Install drivers and software for scanner

    +

    Goto the avasys site that provides downloads and navigate for your model or if you have the same as mine just click on the link below and then goto Downloads and select All In One Printers. On the next page scroll down all the way and select the radio button for your printer. +http://avasys.jp/eng/

    + +

    Alt Text +Now these installs need to happen in a sequence and we don’t need all deb files on the next page anyway so I will mention download and install in sequence as it should be done.

    +
      +
    1. From the section "Download for Epson Stylus NX510/NX515/SX510W/SX515W/TX550W data package" download the data package iscan-data_1.4.0-1_all.deb.
    2. +
    +
      +
    • Install this package now.
    • +
    • From the section "Download for Epson Stylus NX510/NX515/SX510W/SX515W/TX550W core package" download iscan_2.26.0-3.ltdl7_i386.deb.
    • +
    • Install this package now
    • +
    • From the section "Download for Epson Stylus NX510/NX515/SX510W/SX515W/TX550W network plugin package" download iscan-network-nt_1.1.0-2_i386.deb
    • +
    • Install this package now.
    • +
    • Now on your printer(actual machine not on laptop / computer) goto Settings (Press the button with Wrench and Screwdriver symbol as shown in the image Printer Control).
    • +
    +

    Alt Text

    +
      +
    • Using left arrow navigate to Network Settings (Computer and Printer icon)
    • +
    • Click OK button.
    • +
    • Using Down Arrow select "Confirm Settings" and click OK button.
    • +
    • Press Down Arrow twice and it should show the Printer IP Address. Note it Down. It will be something like 192.168.1.60.
    • +
    • Now back on your laptop open terminal and type gksudo nautilus. You will be asked your superuser password. Provide that. It will open the file explorer with admin privileges. Be extremely careful now, you do not want to delete anything in this privilege mode.
    • +
    • In the file explorer, click on "File System" and browse to the file path etc>sane.d and open the file named epkowa.conf. (Screenshot below)
    • +
    • In the file below usb and scsi add the line net 1865 so for our example this will be net 192.168.1.60 1865. (In gedit it was line 12 for me.)
    • +
    • Save and close the file.
    • +
    • Close all windows.
    • +
    +

    Alt Text

    +

    Step 4 - Test Scanner

    +

    Now open Menu>All Applications>Graphics>Image Scan! for Linux. +Alt Text +This should open the software window. Now click on preview to see that a document preview is produced. If it does you are all set. If not you must check the configuration of epkowa.conf and try again. +Alt Text +The preview of my 3.5 year olds drawing from scanner : +Alt Text +and the final scan is shown below: +Alt Text

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/part-2-configure-epson-s515w-on-linux-mint-ubuntu-10-04/index.html b/part-2-configure-epson-s515w-on-linux-mint-ubuntu-10-04/index.html new file mode 100644 index 0000000..41c2dd2 --- /dev/null +++ b/part-2-configure-epson-s515w-on-linux-mint-ubuntu-10-04/index.html @@ -0,0 +1,694 @@ + + + + + + +Part 2 - Configure Epson S515W on Linux Mint / Ubuntu 10.04 - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Part 2 - Configure Epson S515W on Linux Mint / Ubuntu 10.04 +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +
    + + +
    + +

    While the majority of settings are covered in my previous post here I found that each time I switched off my printer, it used to change it’s IP address and I had to repeat Steps 7 to 16 each time. My printing and scanning needs are pretty limited so I never bothered working out a solution until today and knowing what I now know, it was quite a simple and quick solution I should have done this on the first place. Anyway as they say better late than never. :) +So to carry out the magical transformation to your printing experience follow the steps below:

    +
      +
    1. On your printer (actual machine not on laptop / computer) go to Settings (Press the button with Wrench and Screwdriver symbol as shown in picture).
      +Alt Text +
    2. +
    +
      +
    • Using left arrow navigate to Network Settings (Computer and Printer icon)
    • +
    • Click OK button.
    • +
    • Using down arrow navigate to "General Setup" and click OK button.
    • +
    • Printer will show the question "After changing the settings, network may be disconnected. Continue?" and present options Yes and No with No selected (highlighted in Yellow.)
    • +
    • Using left arrow move to Yes and press OK.
    • +
    • Printer will now show the screen for "Printer Name Setup", do not change anything. Just Click OK to proceed to next screen.
    • +
    • Now printer will show screen for TCP/IP screen with two options Auto which is selected by default and highlighted in yellow and Manual. Here using down arrow key select Manual.
    • +
    • Click OK button.
    • +
    • The screen will show current IP address assigned to printer something like 192.168.1.66.
    • +
    • Now I am assuming that you have already followed part 1 of this tutorial so on your laptop open the file epkowa.conf from /etc/sane.d. You can also open this file by typing the following command in terminal gedit /etc/sane.d/epkowa.conf +
    • +
    • Note down the IP address you entered last time 192.168.1.60 below usb and scsi. (Line 12 on my file.)
    • +
    • Now on your printer change IP address to match the one on last step. To do so, first click on left arrow on printer to reach the last digit of displayed IP address and then adjust the number using up or down arrow.
    • +
    • Once it is same as that on epkowa.conf file (192.168.1.60 in this example), click OK button. Now continue to click OK button on all remaining screens till you reach Network Settings screen (About 5 times)
    • +
    • Press OK one final time and that's it you are done.
    • +
    +

    If you are to now switch off and switch on the printer and test scan it should work without any issues. +Hope you find this useful.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/prepare-linux-mint-13-for-android-development/index.html b/prepare-linux-mint-13-for-android-development/index.html new file mode 100644 index 0000000..b60ee6b --- /dev/null +++ b/prepare-linux-mint-13-for-android-development/index.html @@ -0,0 +1,865 @@ + + + + + + +Prepare Linux Mint 13 for Android development - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Prepare Linux Mint 13 for Android development +

    + + +

    + + + + + + + + + + + + 8 minute read + + + +

    + + +
    + + +
    + +

    Few weeks back I updated to the latest Linux Mint offering "Maya" a.k.a Linux Mint 13. Now this is a LTS (Long Term Support) version and I wanted to be in a position to install everything right just so I can keep it for a longer duration and hence have been taking my time configuring stuff. +Last time when I had set up system for Android Development I remember messing up a lot and ending up installing too many things here and there and in the process did learn how to do it properly. I did not document that as a blog as it was too fragmented an experience at that time but this time round I did it properly and everything (well, okay, almost everything) was perfect. +There is lot of material on the web but again that is what led to a less than perfect install last time as it is all disjointed, making the sequence go wrong, using one way for one thing another way for second and ending us with a not so nice experience distracting you from what you want to do, start developing something on android platform or perhaps just get the adb set-up to flash your nexus phone. +So presented below is a guide that will help you prepare your Linux Mint 13 or equivalent distro for you to start android development. +You will notice that these notes do not require downloading "android sdk" separately and that is to save time and effort, trust me. +So the high level steps are:

    +
     Step 1: Download required files
    + Step 2: Install Oracle Java (or what is called sun-java on Android webpages)
    + Step 3: Install eclipse
    + Step 4: Install ADT Plug-in for Eclipse
    + Step 5: Final Configurations
    + Step 6: Create Android Virtual Device
    +
    +

    Step 1: Download required files

    +

    Download following files: +Fig-2 +Links to original file locations: +Script to Update Java
    +(Updated link to the latest version of script as highlighted in screenshot.) +Java 7 SDK +Java 7 Samples and Demo +Java 7 API Docs +Java 6 Samples and Demo +Java 6 API Docs +Java 6 SDK +If you don’t want to move around different webpages and websites, I have also uploaded all these files on mediafire. +http://www.mediafire.com/?4x3u3if9o7dy4

    +

    Step 2: Install Oracle Java

    +

    You might ask, why do we need to do it this way? +That’s because Oracle Java 6 SDK is a pre-requisite for installing Android SDK but it’s not available in Ubuntu or Linux Mint repository so it can’t be installed using synaptic or apt-get. +Right then, I am assuming that all the files downloaded above are placed in "Downloads" directory. If not, please replace "Downloads" in all commands with whichever directory you have downloaded these file to.

    +
    
    +#1. Open terminal and type:
    +cd Downloads 
    +#2. Make the downloaded .bin Java 6 file executable and run
    +chmod a+x jdk-6u37-linux-i586.bin 
    +./jdk-6u37-linux-i586.bin 
    +###This will create a directory named "jdk1.6.0_37".
    +#3. Untar the Java 7 SDK 
    +tar -xvf jdk-7u7-linux-i586.tar.gz
    +###This will create a directory named "jdk1.7.0_07".
    +#4. Unzip the Java 6 api docs 
    +unzip jdk-6u30-apidocs.zip -d jdk1.6.0_37/
    +###This command will unzip the apidocs zip file 
    +###and place the contents in "jdk1.6.0_37"
    +#5. Unzip the Java 7 api docs
    +unzip jdk-7u6-apidocs.zip -d jdk1.7.0_07/
    +###This command will unzip the apidocs zip file 
    +###and place the contents in "jdk1.7.0_07"
    +#6. Untar the Java 6 demos and samples
    +tar -xvf jdk-6u37-linux-i586-demos.tar.gz
    +###This command will untar the demos and samples file 
    +###and place the contents in "jdk1.6.0_37"
    +#7. Untar the Java 7 demos and samples
    +tar -xvf jdk-7u9-linux-i586-demos.tar.gz
    +###This will create a folder named jdk1.7.0_09. 
    +###Copy the contents of this folder into jdk1.7.0_07.
    +#8. Move the Java 6 to it's proper location
    +sudo mv jdk1.6.0_37 /usr/lib/jvm
    +###You will be asked to provide root password.
    +#9. Now move the Java 7 to it's proper location
    +sudo mv jdk1.7.0_07 /usr/lib/jvm
    +#10. Make the script to update java update-java-0.5b executable
    +#then execute it by using following commands in terminal.
    +chmod +x update-java-0.5b
    +sudo ./update-java-0.5b
    +

    You will be presented with following selection box:
    +Fig-3 +Once you click on OK you will be presented with following screen: +Fig-4 +Select the radio button and click on OK. +Once Java 6 SDK is installed repeat step 10 and this time when you reach the selection window select Java 7 as shown below. +Fig-5 +Now Oracle Java 6 and Oracle Java 7 will both be installed on your system. To check this you can use the tool "galternatives". This can be installed by typing following command on the terminal window: +sudo apt-get install galternatives
    +followed by galternatives. +This will open the "G Alternatives" window, scroll down to "Java" in left hand pane and click on it. You should see both versions installed and radio button for highest version selected as shown below: +Fig-6

    +

    Step 3: Install eclipse

    +
      +
    1. Open Synaptic package manager and type eclipse, click on the check-box next to it and select "Mark for installation" then click on "Apply" as shown below.
    2. +
    +

    Fig-7

    +
      +
    1. Once Eclipse is installed, we need to find out whether the necessary SWT (Standard Widget Toolkit) libraries link are set correctly or not. This is to avoid Eclipse throwing a tantrum and not starting because it is unable to find the SWT library. As you can see in the screenshot below it's a pretty quick thing:
    2. +
    +

    Fig-8 +A) First type the following command to check if whether SWT directory exists or not: +ls ~/.swt/lib/linux/x86/ +If you see the same message as shown in screenshot above "ls: cannot access /home//.swt/lib/linux/x86/: No such file or directory", then it means we need to create the SWT directory so continue to sub-step B. +If this command does not result in this message nor does it show a list of files (blue text in screenshot) then skip directly to sub-step C. +If it does show the list of files in blue in above screenshot, you don’t need to do anything further and for you it’s time to move to next step. +B) Type the following command in terminal to create the SWT directory: +mkdir -p ~/.swt/lib/linux/x86/ +C) If the swt directory exist, but nothing is listed (no blue text), then run the following command in terminal: +ln -s /usr/lib/jni/libswt-* ~/.swt/lib/linux/x86/ +Finally as shown in screenshot, type the command in sub-step A once again and you should see the list shown in blue on the screenshot: +Time to move on to install ADT (Android Development Tools Plugin on eclipse.

    +

    Step 4: Install ADT Plug-in for Eclipse

    +

    OK we have so far installed Java, installed eclipse and now we are all set to install Android. To do so we will follow the screenshots below: +When you start eclipse, you will be shown a splash screen, ask you to set workspace which I leave default and finally this window will open. As shown, click on the Workbench in right hand side corner. This will lead to following window. +Fig-9 +Here Click on Help > Install New Software. This will open following window. +Fig-10 +Fig-13 +Click on "Add" button in red rectangle above. Following pop-up window will appear. +Fig-11 +In Name type "ADT Plugin" or whatever name you want to give.
    +In Location type "https://dl-ssl.google.com/android/eclipse/"
    +Then click OK. On following window it will first show pending but eventually it will look as below. +Fig-12 +Here select the first option “Developer Tools” and click OK. +Fig-14 +Click “NEXT”. +Fig-14 +Click “NEXT”. +Fig-15 +Let it run in foreground i.e. don’t do anything and just wait. +Fig-16 +Click “RESTART NOW”. You will be presented with following window on restart. +Fig-17 +Now leave the target location as is and leave everything as default and click “NEXT”. +Fig-18 +As shown above, selected the “ACCEPT ALL” radio button and click “INSTALL”. +Now the android SDK is installed on the system along with other things. Close all the open windows just to clear any screen clutter. Now open the terminal and type following commands: +Fig-19 +cd android-sdks/tools followed by ./android . +It will open the following window: +Fig-20

    +
        1. Select as shown above and optionally you can also select checkbox against Documentation and Samples. Once done click on "INSTALL x Packages". 
    +    2. On the next window, for which I did not take a screenshot but which is same as the one before previous screenshot of terminal, select radio button "ACCEPT ALL" and click on "INSTALL". 
    +    3. Now once you are back to this window select "Google APIs" and click on "Install 1 package".
    +    4. On the package lis, click on "ACCEPT" radio button and click on "INSTALL".
    +
    +

    Android Development Tools and SDK is now installed. We just need to configure few things.

    +

    Step 5: Final Configurations

    +

    adb Environmental Variables

    +
      +
    1. Go to home folder
    2. +
    3. Press Ctrl+H
      +Fig-21 +
    4. +
    5. Locate the .bashrc file, if it does not exist, create one (In home folder, right click and select "Create New Document > Empty Document").
    6. +
    7. Open the file in gedit and add export PATH=${PATH}:/tools:/platform-tools at the end, if you created the file yourself just add this line and save.
    8. +
    +

    Fig-22 +Set the PATH environment

    +
      +
    1. Go to home folder
    2. +
    3. Press Ctrl+H
    4. +
    5. Locate the .profile file
    6. +
    7. Open the file in gedit and add the following at the end.
    8. +
    +
    # set PATH so it includes user's Android SDK if it exists
    +
    +
    +if [ -d "$HOME/android-sdks" ] ; then
    +
    +  PATH="$HOME/android-sdks:$HOME/android-sdks/tools:$PATH"
    +
    +fi
    +

    Fig-23 +Set-up udev +This step is only required if you want to use your android device for development purpose or if you want to flash a custom ROM.

    +
      +
    1. Open the terminal and type following:
    2. +
    +

    gksudo nautilus /etc/udev/rules.d

    +
      +
    1. +Create new document like we did above- Right click on empty space and select "Create New Document" and then "Empty Document. +
    2. +
    3. +Name this document as "51-android.rules" +
    4. +
    5. +Open the document with Gedit and add following lines - assuming you are using "Nexus S" like me, if not see if your phone details are available on this link: http://wiki.cyanogenmod.com/wiki/Udev and if not follow the guideline under the heading "Manually create udev rules" on that link. +
    6. +
    +

    Fig-24

    +
      +
    1. Now make this 51-android.rules file executable by typing following command in the terminal window:
    2. +
    +

    sudo chmod a+r /etc/udev/rules.d/51-android.rules

    +

    Step 6: Create Android Virtual Device

    +

    Open eclipse and follow the screenshots below: +Fig-25 +Click on Window - AVD Manager to get to next screen except that in your screen there will be no entry. +Fig-26 +Click on “NEW” to create a new AVD. It will open next window. +Fig-27 +Fill details as above - You can chose name of your choice and change details as per your requirements. +Fig-28 +To Start the AVD, click on Start. +Finally, you will be able to see the AVD as below: +Fig-29 +This is it. Restart and your system is now all set for developing wonderful Android Applications.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/prosody-behind-apache-on-debian-stretch/index.html b/prosody-behind-apache-on-debian-stretch/index.html new file mode 100644 index 0000000..2ff62f7 --- /dev/null +++ b/prosody-behind-apache-on-debian-stretch/index.html @@ -0,0 +1,1068 @@ + + + + + + +Prosody behind Apache on Debian Stretch with Conversations - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Prosody behind Apache on Debian Stretch with Conversations +

    + + +

    + + + + + + + + + + + + 10 minute read + + + +

    + + +
    + + +
    + + + +

    Genreal Guidance

    + +

    This how-to assumes that:

    + +
      +
    • User has access to a registered domain - domain.name. Any reference to domain.name throughout the tutorial therefore, must be replaced with your own domain name.
    • +
    • If stuck anywhere, please do get in touch with prosody support chat - They are a very helpful lot and are very kind too.
    • +
    + +

    Network Setup:

    + +

    It is not mandatory to follow the flow of this how-to but if you do, please do it till the end before you try and access the IM server from any client to save yourself some pain.

    + +

    Obtain the IP address

    + +

    .… of the machine where your XMPP server will be installed using following command on the terminal ip -a address On the result shown, IP address against inet family is the one we are interested in as can be seen in screenshot below: +PUTTY_2018-06-15_12-49-42

    + +

    Copy this ip address, we will need it in next step.

    + +

    Port Forwarding on router if hosting from home.

    + +

    You have to ensure that following ports are opened and directed to above IP address from your router. 5222, 5269, 5280, 5281 On DD-WRT this can be done as shown in screenshot below. This will, ofcourse, vary based on router you are using. +chrome_2018-06-15_11-34-57 +Use the IP address from previous step to fill the IP address field. Basically this set-up is informing your router that if a request comes to you asking for one of the above mentioned ports direct that request to this IP address on the same port.

    + +

    Create a sub-domain record

    + +
    +
    Please note that the official Prosody Documentation recommends using Server Record and even the experts on their chat support strongly recommend that approach.
    +
    +I did not have that option unfortunately - read I was being lazy / adventurous - anyway the C-Name set-up has worked fine for me and hence the guide follows this route.
    +
    +You have been warned !!!
    +
    +
    + +

    This step will vary based on the domain name registrar you use. I use Namecheap for which you could do it as shown in screenshot below: +chrome_2018-06-15_11-43-31

    + +

    Ensure you have some sort of Dynamic DNS update set-up in place

    + +

    You can create an account here -> DNSOMATIC If you are going to use DNSOMATIC which allows you to update several places like so: +chrome_2018-06-15_11-53-35 +For namecheap in order to get the value for password, login to Namecheap dashboard, go to Advanced DNS and scroll down to "Dynamic DNS" section and copy the value in front of the field named "Dynamic DNS Password” as shown below: +chrome_2018-06-15_17-02-32.png +The setting to update DNSOMATIC on DD-WRT are as shown below: +DYDNS Server:updates.dnsomatic.com +Hostname: all.dnsomatic.com +URL: https://updates.dnsomatic.com/nic/update?hostname= +chrome_2018-06-15_11-48-32 +For all other Dynamic DNS set-ups ensure that your subdomain will always get updated with latest IP address to ensure continuity of service.

    + +

    Server Setup:

    + +

    Normally the XMPP clients on phone directly access port 5222 or 5269 for MUC (Multi User Chats aka Group Chats) but to make sure image uploads work and for being able to access IM from web we need to enable reverse proxy using Apache server. As this is fairly straight forward, lets get that out of the way.

    + +

    Apache with reverse Proxy enabled

    + +

    Make directory for subdomain:

    + +

    sudo mkdir /var/www/html/im

    + +

    Then create a server conf file and open for editing in nano using following command:\

    + +

    sudo nano /etc/apache2/sites-available/prosody.conf

    + +

    Paste the following in conf file:

    +
    <VirtualHost *:80>
    +    ServerName im.domain.name
    +    ServerAlias im.domain.name
    +    DocumentRoot /var/www/html/im
    +
    +    ErrorLog ${APACHE_LOG_DIR}/error_im.log
    +    CustomLog ${APACHE_LOG_DIR}/access_im.log combined
    +
    +    ProxyPass /http-bind/ http://im.domain.name:5280/http-bind/
    +    ProxyPassReverse /http-bind/ http://im.domain.name:5280/http-bind/
    +
    +
    +RewriteEngine on
    +RewriteCond %{SERVER_NAME} =im.domain.name
    +RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
    +</VirtualHost>
    +
    +

    Save the file with Ctrl+x, y and Enter. Finally enable site and reload server.

    +
        sudo a2ensite prosody.conf
    +    sudo systemctl reload apache2
    +
    +

    Enable SSL set-up using Let’s Encrypt {#enablesslsetupusingletsencrypt} +————————————–

    + +

    It is assumed at this point that certbot is already installed but if not, follow the tutorial here On terminal give the command sudo certbot +PUTTY_2018-06-15_12-08-21 +Then follow the wizard and once completed check that an additional conf file named prosody-le-ssl.conf is created. If not something did not go well so check again and repeat but if yes move to next step.

    + +

    Ensure certbot is renewing automatically

    + +

    Debian stretch gets the service certbot.timer already on it which when enabled will try to autorenew twice a day ensuring a continuous certificate availability. To locate and edit the certbot.timerfollowing commands are helpful:
    +PUTTY_2018-06-15_12-19-30

    +
    locate certbot.timer
    +nano /etc/systemd/system/timers.target.wants/certbot.timer
    +
    +

    The certbot.timer entry on my system is as below: +PUTTY_2018-06-15_17-23-18

    + +

    Once edited, make sure it is enabled using following commands:

    +
    sudo systemctl enable certbot.timer
    +sudo systemctl start certbot.timer
    +
    + +

    Prosody XMPP server

    +

    Assumption - Apache 2.4 web server is already installed and functioning.

    + +

    Install

    + +

    Option 1 (Not Recommended):

    + +

    sudo nano /etc/apt/sources.list +Then add this -> deb http://packages.prosody.im/debian stretch mainon the last line.

    + +

    Option 2 (Recommended):

    + +
    echo deb http://packages.prosody.im/debian \$\(lsb\_release -sc) main | sudo tee -a /etc/apt/sources.list
    +
    + +

    Above command is recommended because it takes the release of your installed version and adds it directly rather than the hard coded “stretch” in Option 1 which must be changes specific to the version of debian. Now add the key, update repository, upgrade for good measure and then install using the following commands:

    + +
        wget https://prosody.im/files/prosody-debian-packages.key -O- | sudo apt-key add
    +    sudo apt-get update
    +    sudo apt-get upgrade
    +    sudo apt-get install prosody
    +
    +

    Install plugins / modules

    + +

    Latest updated documentation is ofcourse on project website but at the time of writing these are the instructions that were followed:

    +
    cd /usr/lib/prosody/
    +sudo hg clone https://hg.prosody.im/prosody-modules/ prosody-modules
    +
    +

    To update:

    +
    cd /usr/lib/prosody/prosody-modules/
    +sudo hg pull --update
    +
    + +

    Import SSL using certbot

    + +

    Issue following commands to get the SSL activated for Prosody:

    + +
    sudo certbot renew --deploy-hook "prosodyctl --root cert import /etc/letsencrypt/live"
    +sudo prosodyctl --root cert import /etc/letsencrypt/live
    +sudo prosodyctl cert generate localhost 
    +
    + +

    Configure

    + +

    The configuration file for prosody is very well documented and only needs minor tweaks. However, it is lot of commenting and to keep this readable, I have put my configuration here that can either be directly pasted or can be compared to update the default conf file. Either way, first you would need to open the config file:

    + +

    sudo nano /etc/prosody/prosody.cfg.lua

    + +

    Now the working config file for me is as shown below and can be compared to amend or can be directly copy pasted.

    + +

    Bear in mind that all references to domain.name must be replaced with your registered domain name.

    + +
    admins = {"admin1@im.domain.name"}
    +
    +plugin_paths = {"/usr/lib/prosody/prosody-modules" }
    +consider_bosh_secure = true
    +cross_domain_bosh = true
    +modules_enabled = {
    +
    +        -- Generally required
    +                "roster"; -- Allow users to have a roster. Recommended ;)
    +                "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
    +                "tls"; -- Add support for secure TLS on c2s/s2s connections
    +                "dialback"; -- s2s dialback support
    +                "disco"; -- Service discovery
    +
    +        -- Not essential, but recommended
    +                "carbons"; -- Keep multiple clients in sync
    +                "pep"; -- Enables users to publish the1ir mood, activity, playing music and more
    +                "omemo_all_access"; -- xep-0060 for enabling omemo access to non subscribers
    +                "private"; -- Private XML storage (for room bookmarks, etc.)
    +                "blocklist"; -- Allow users to block communications with other users
    +                "vcard"; -- Allow users to set vCards
    +
    +        -- Nice to have
    +                "version"; -- Replies to server version requests
    +                "uptime"; -- Report how long server has been running
    +                "time"; -- Let others know the time here on this server
    +                "ping"; -- Replies to XMPP pings with pongs
    +                "register"; -- Allow users to register on this server using a client and change passwords
    +                "mam"; -- Store messages in an archive and allow users to access it
    +                --"mam_muc"; -- store group chat messages
    +
    +        -- Admin interfaces
    +                "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
    +                --"admin_telnet"; -- Opens telnet console interface on localhost port 5582
    +
    +        -- HTTP modules
    +                "bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
    +                "websocket"; -- XMPP over WebSockets
    +                "http_files"; -- Serve static files from a directory over HTTP
    +                "http_upload"; -- module to enable file upload in group chat
    +
    +        -- Other specific functionality
    +                "groups"; -- Shared roster support
    +                "proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
    +                "smacks";
    +                "csi";
    +                "cloud_notify";
    +                "conversejs";
    +}
    +
    +modules_disabled = {
    +        -- "offline"; -- Store offline messages
    +        -- "c2s"; -- Handle client connections
    +        -- "s2s"; -- Handle server-to-server connections
    +        -- "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
    +}
    +
    +allow_registration = false
    +
    +c2s_require_encryption = false
    +
    +s2s_require_encryption = true
    +
    +s2s_secure_auth = true
    +
    +s2s_secure_domains = { "domain.name" }
    +
    +pidfile = "/var/run/prosody/prosody.pid"
    +
    +authentication = "internal_hashed"
    +
    +archive_expires_after = "1w" -- Remove archived messages after 1 week
    +
    +log = {
    +        info = "/var/log/prosody/prosody.log"; -- Change 'info' to 'debug' for verbose logging
    +        error = "/var/log/prosody/prosody.err";
    +        -- "*syslog"; -- Uncomment this for logging to syslog
    +        -- "*console"; -- Log to the console, useful for debugging with daemonize=false
    +}
    +
    +certificates = "certs"
    +
    +----------- Virtual hosts -----------
    +-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
    +-- Settings under each VirtualHost entry apply *only* to that host.
    +
    +VirtualHost "localhost"
    +
    +VirtualHost "im.domain.name"
    +
    +------ Components ------
    +---Set up a MUC (multi-user chat) room server on conference.example.com:
    +   Component "conference.im.domain.name" "muc"
    +     restrict_room_creation = "local"
    +
    + +

    Once completed, save it and proceed to next steps.

    + +
    sudo service prosody start
    +sudo service prosody stop
    +sudo service prosody start -v
    +sudo service prosody stop
    +
    +## Enable prosody as a service so it restarts each time after system reboot
    +
    +sudo systemctl is-enabled prosody.service
    +sudo service prosody restart
    +
    +

    Add User

    + +

    Use prosodyctl command to add user like so:

    + +

    sudo prosodyctl adduser admin1@im.domain.name

    + +

    and then provide password for the user. Ofcourse you must add as many users as you need and bear in mind the conf file above has registration switched-off so the users must be manually added using this step. Check the manpage using man prosodyctl to see how to update password for an existing user and to delete a user.

    + +

    Clients Setup:

    + +

    Conversations on Android

    + +

    Conversations is the best client for XMPP on Android and is available on playstore for a small amount or it can be installed from f-droid for free. The free version has some limitations with regards to push but I have been using it with no issues what-so-ever.

    + +

    OpenPGP using OpenKeyChain

    + +

    Install the OpenKeyChain app, create the key pair and trigger Conversations to start from OpenKeyChain. The other person must also have OpenPGP Key pair and you should have the public key of other person added on openkeychain instaled on your phone. Do bear in mind Conversations developer says it is an experimental feature. Although it did work perfectly fine for me while I tested with about 4 or 5 users.

    + +

    Conversejs.org on web

    + +

    Go to the web root we defined initially and create an index.html:

    + +

    sudo nano /var/www/html/im/index.html

    + +

    Then add following html code in there:

    + +

    Bear in mind that all references to domain.name must be replaced with your registered domain name.

    +
        <!doctype html>
    +    <html class="no-js" lang="en">
    +    <head>
    +        <meta charset="utf-8"/>
    +        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    +        <title>Converse</title>
    +        <link rel="shortcut icon" type="image/ico" href="css/images/favicon.ico"/>
    +        <link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/3.3.4/css/converse.min.css" />
    +        <link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/3.3.4/css/mobile.min.css" />
    +        <script src="https://cdn.conversejs.org/3.3.4/dist/converse.min.js"></script>
    +    </head>
    +    <body class="reset">
    +        <div class="content">
    +            <div class="inner-content">
    +                <h1 class="brand-heading"><i class="icon-conversejs"></i> Converse</h1>
    +            </div>
    +        </div>
    +    <script>
    +        converse.initialize({
    +            authentication: 'login',
    +            auto_away: 300,
    +            auto_reconnect: true,
    +            bosh_service_url: 'https://im.domain.name/http-bind/',
    +            show_controlbox_by_default: true,
    +            message_archiving: 'always',
    +            allow_logout: true,
    +            allow_dragresize: true
    +        });
    +    </script>
    +    </body>
    +    </html>
    +
    +

    Save the file with Ctrl+x, y and Enter. Once done restart the webserver as well as prosody for good measure:

    +
    sudo systemctl restart apache2.service
    +sudo prosodyctl restart
    +
    + +

    ChatSecure on iOS

    + +

    Now I do not have an iphone but a few of my friends do. They were able to use ChatSecure which is also an opensource app. Unfortunately ChatSecure is unable to get the images on group chat from Conversations but I am hoping that developer of ChatSecure will fix this issue sooner rather than later.

    + +

    Desktop Client

    + +

    For desktop I found Gajim to be most polished and it can be downloaded for your OS from here All works great on it except for the http_upload which seems to fail for transferring images with an error “Unsecured”. I am not sure what the issue is a search results did not help much - I left it because I dont really need this feature from desktop. However if someone has any ideas on how to fix this, please do leave it in the comments. The plugins on Gajim that I have applied and activated can be seen below: +Gajim_2018-06-15_17-10-11.png

    + +

    Maintenance

    + +

    The service does generate a lot of logs and it will serve you well to delete generated logs daily using Cronjob like so

    +
    sudo crontab -e
    +
    +

    Then add the following on the cronjob:

    +
    ############## DELETE PROSODY LOG FILE ARCHIVES ###########################
    +MAILTO="your@email.com"
    +0 0 * * 0-6 rm  /var/log/prosody/*.gz
    +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python-eric-rad-ide-and-qt-designer/index.html b/python-eric-rad-ide-and-qt-designer/index.html new file mode 100644 index 0000000..ce14a2a --- /dev/null +++ b/python-eric-rad-ide-and-qt-designer/index.html @@ -0,0 +1,693 @@ + + + + + + +Python, ERIC RAD IDE and QT designer - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Python, ERIC RAD IDE and QT designer +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    Right, I have decided to play around a little with the most loved language of open source a. k. a. Python.

    + +

    How do we do that? By coding?

    + +

    Aha and how do I code for Python and be productive given I am a semi-part-time-hobbyist kinda programmer…I am heavily inclined to use an IDE…and an IDE that actually has some GUI editor…so began my search to find out what are my options in the exciting open source world.

    + +

    I quickly worked out that IDE and RAD IDE are two different things. RAD IDE is what I am after and well there is nothing straight forward and best bet was to use QT with PySide or PyQT and there is an argument on each side for PyQT vs PySide.

    + +

    Anyhoo, PySide is not compatible yet with QT5 and QT4.8.x is not available for download from QT official downloads. Whatever happened to providing an archive. QT itself is a world in itself with lot of modules and plugins etc and I was feeling lost.

    + +

    I diverted efforts to find what else and stumbled upon ERIC IDE for Python and from here things started tying up to a point where I could logically assimilate the available information to start preparing my system for my kind of Python Development.

    + +

    However, this information isn’t as readily available as it should be and most mentioned on trusty old Internet Google search is combination of PySide and the non existent QT4 which had it not been for Linux package managers, might as well be a lost cause.

    + +

    All it involves is downloading Eric from repository which satisfied most of the dependencies and then I downloaded qt4 designer and included qt4 creator just for good measure.

    + +

    So there now we have the tool and all I need is setting it up and getting started.

    + +

    Following links will come handy I suppose.

    + +

    http://eric-ide.python-projects.org/tutorials/MiniBrowser/

    + +

    http://anh.cs.luc.edu/python/hands-on/handsonHtml/handson.html#x1-310001.9.3

    + +

    Here’s to some exciting learning experience.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/read-excel-csv-recursive/index.html b/read-excel-csv-recursive/index.html new file mode 100644 index 0000000..c2a06c3 --- /dev/null +++ b/read-excel-csv-recursive/index.html @@ -0,0 +1,907 @@ + + + + + + +Python Function read excel / csv files from a given directory and its subdirectories - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Python Function read excel / csv files from a given directory and its subdirectories +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +
    + + +
    + + + +

    Code

    + +

    The code for the function is as shown below:

    + +
    import os
    +import pandas as pd
    +import numpy as np
    +from datetime import datetime 
    +
    +def extract_data(directory_path, columns_to_extract, date_columns=[], rows_to_skip=0, output_filename='output.csv'):
    +    """
    +    Extracts data from all Excel and CSV files in the specified directory and its subdirectories that contain all the
    +    specified columns.
    +
    +    :param directory_path: The path to the directory to search for Excel and CSV files.
    +    :param columns_to_extract: A list of column names to extract from each file.
    +    :param date_columns: A list of column names to parse as dates using pd.to_datetime().
    +    :param output_filename: The path and filename to save the extracted data in a csv
    +    :return: A DataFrame containing the extracted data from all Excel and CSV files that contain the specified columns,
    +    or None if no files contain the specified columns.
    +    """
    +    start_time = datetime.now()
    +    extracted_data = pd.DataFrame()
    +    files_with_no_columns = []
    +    sheet_read = ''
    +    extracted_columns = columns_to_extract.copy()
    +    extracted_columns.extend(['File Name','SubDir','CreatedDate','LastModifiedDate'])
    +
    +    for root, dirs, files in os.walk(directory_path):
    +        for file in files:
    +            print('Started reading ' + file + ' at: {}'.format(datetime.now()) + ' ...')
    +            if file.endswith('.xlsx') or file.endswith('.xls') or file.endswith('.csv'):
    +                file_path = os.path.join(root, file)
    +                columns_found_outer = False
    +                if file.endswith('.csv'):
    +                    df = pd.read_csv(file_path,skiprows=rows_to_skip)
    +                    for col in columns_to_extract:
    +                        if col in df.columns:
    +                            columns_found_outer = True
    +                            #print(col + 'in csv true loop')
    +                        else:
    +                            columns_found_outer = False
    +                            #print(col + 'in false loop')
    +                            break
    +                else:
    +                    dfs = pd.read_excel(file_path, sheet_name=None,skiprows=rows_to_skip)
    +                    for key in dfs.keys():
    +                        columns_found = False
    +                        for col in columns_to_extract:
    +                            if col in dfs[key].columns:
    +                                columns_found = True
    +                                #print(col + 'in true loop')
    +                            else:
    +                                columns_found = False
    +                                #print(col + 'in false loop')
    +                                break
    +                        if columns_found:
    +                            columns_found_outer = True
    +                            df = dfs[key]
    +                            sheet_read = key
    +                            break
    +                if columns_found_outer:
    +                    if len(date_columns) > 0:
    +                        for col in date_columns:
    +                            if col in df.columns:
    +                                df[col] = pd.to_datetime(df[col])
    +                    df['File Name'] = file
    +                    df['SubDir'] = os.path.basename(root)
    +
    +                    createx = modx = os.path.getctime(file_path)
    +                    xcreate = datetime.fromtimestamp(modx)
    +                    df['CreatedDate'] = xcreate
    +
    +                    modx = os.path.getmtime(file_path)
    +                    xmod = datetime.fromtimestamp(modx)
    +                    df['LastModifiedDate'] = xmod
    +                    extracted_data = pd.concat([extracted_data, df[extracted_columns]], ignore_index=True)
    +                    #print(xmod)
    +                    if file.endswith('.csv'):
    +                        print(file + ' has been read')
    +                    else:
    +                        print(file + ' has been read and it was last modified on ' + xmod.strftime('%Y-%m-%d') + '. The name of the sheet that was read is: ' + sheet_read)
    +                else:
    +                    files_with_no_columns.append(file_path)
    +    if len(files_with_no_columns) > 0:
    +        print("The following files do not contain the specified columns:")
    +        for file_path in files_with_no_columns:
    +            print(file_path)
    +    if not extracted_data.empty:
    +        extracted_data = extracted_data.applymap(lambda s: s.upper() if type(s) == str else s).fillna('')
    +        extracted_data.to_csv(output_filename)
    +        print('Started at: {}'.format(start_time) + '. \nEnded at: {}'.format(datetime.now()) + '. \nTime elapsed (hh:mm:ss.ms) {}'.format(datetime.now() - start_time))
    +        return extracted_data
    +    else:
    +        print("Specified columns do not exist in any file in the provided directory.")
    +        print('Started at: {}'.format(start_time) + '. \nEnded at: {}'.format(datetime.now()) + '. \nTime elapsed (hh:mm:ss.ms) {}'.format(datetime.now() - start_time))
    +        return None
    +
    + +

    Explanation

    + +
      +
    1. +

      The function extract_data takes in the following parameters:

      + +
        +
      • +directory_path: the path to the directory to search for Excel and CSV files
      • +
      • +columns_to_extract: a list of column names to extract from each file
      • +
      • +date_columns: a list of column names to parse as dates using pd.to_datetime()
      • +
      • +rows_to_skip: the number of rows to skip when reading each file
      • +
      • +output_filename: the path and filename to save the extracted data in a CSV file
      • +
      +
    2. +
    3. +

      The function starts by creating an empty DataFrame called extracted_data.

      +
    4. +
    5. +

      It also initializes variables files_with_no_columns and sheet_read to track files that do not contain the specified columns and to keep track of the current sheet being read when extracting data from Excel files.

      +
    6. +
    7. +

      The list extracted_columns is created by copying the input columns_to_extract list and adding additional columns for the filename, subdirectory name, file creation date, and last modified date.

      +
    8. +
    9. +

      The function then loops through all the files in the specified directory and its subdirectories using os.walk().

      +
    10. +
    11. +

      For each file, it checks if it has a “.xlsx”, “.xls”, or “.csv” extension.

      +
    12. +
    13. +

      If the file is a CSV file, the function reads it into a DataFrame using pd.read_csv(), skipping the number of rows specified by rows_to_skip.

      +
    14. +
    15. +

      It then checks if all the columns in columns_to_extract are present in the DataFrame. If so, it sets columns_found_outer to True and proceeds to the next step. If not, it sets columns_found_outer to False and moves on to the next file.

      +
    16. +
    17. +

      If the file is an Excel file, the function reads all sheets in the file into a dictionary of DataFrames using pd.read_excel() and sheet_name=None. It then loops through all the sheets and all the columns in columns_to_extract, checking if each column is present in each sheet’s DataFrame. If all the columns are present in a sheet, it sets columns_found to True and proceeds to the next step. If not, it sets columns_found to False and moves on to the next sheet in the same Excel file. If at least one sheet contains all the specified columns, the function combines the DataFrames of all sheets into one using pd.concat().

      +
    18. +
    19. +

      If columns_found_outer is True, it extracts the filename, subdirectory name, file creation date, and last modified date using os.path.basename(), os.path.getctime(), and os.path.getmtime(), and adds them as new columns to the DataFrame. It then appends the DataFrame to the extracted_data DataFrame.

      +
    20. +
    21. +

      If columns_found_outer is False, it increments the files_with_no_columns counter and prints a warning message.

      +
    22. +
    23. +

      Finally, the function checks if any files contained the specified columns. If not, it returns None. Otherwise, it sorts the extracted_data DataFrame by filename and saves it to a CSV file using to_csv(). It then prints the number of files processed, the number of files that did not contain the specified columns, and the path to the output file.

      +
    24. +
    + +

    Sample Usage

    + +

    The function can be called in python as shown below:

    + +
    # set directory path
    +directory_path = './Work' # Path for the directory where all the files containing data for  extraction are to be searched
    +
    +# set the columns you want to extract
    +columns_to_extract = ['Region','Country', 'Product Number','Quantity','Date of Sale']
    +date_col = ['Date of Sale']
    +# set the rows to skip
    +skiprows = 0 #this basically will be number of rows in begining of the files which must be skipped to reach the header row of the data
    +output_filename = 'Regional Sales Data.csv'
    +#Call function
    +df = extract_data(directory_path,columns_to_extract,date_col,skiprows,output_filename)
    +
    + +

    Now, assuming there were 12 separate files for past 12 months inside the folder then so long as all those files, irrespective of whether they are csv or excel, have the columns Region,Country, Product Number,Quantity,Date of Sale; the function will read the files and extract the data and return it to the dataframe df.

    + +

    GUI Implementation

    + +

    A very basic GUI implementation of above function using PySimpleGUI with all codeis available here

    + +

    Usage

    +

    The script can directly be copied to a Jupyter cell or can be run from terminal. Following command should ensure all dependencies are installed:

    + +
    py -m pip install pandas, numpy, pysimplegui
    +
    + +

    Some things the GUI takes care of are:

    + +
      +
    • Allows selection of columns to be extracted from a sample .csv file
    • +
    • Allows user to specify which of the selected columns should be parsed as date +
    • +
    • Gives a date based filename to output +
    • +
    • Shows colour coded log for which files were read in green and which were ignored in red background.
    • +
    + +

    Screenshots

    + +

    Some screenshots of the resulting app are as shown below:

    + +

    Empty Form

    +

    image

    + +

    Filled Form

    +

    image

    + +

    Displays extracted output

    +

    image

    + +

    Displays filename of the output and location where it is saved

    +

    image

    + +

    Colour coded log

    +

    image

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..562a8fa --- /dev/null +++ b/robots.txt @@ -0,0 +1 @@ +Sitemap: https://mgw.dumatics.com/sitemap.xml diff --git a/root-nexus-4-on-linux-mint-13-and-access-all-files-on-computer/index.html b/root-nexus-4-on-linux-mint-13-and-access-all-files-on-computer/index.html new file mode 100644 index 0000000..c055aaf --- /dev/null +++ b/root-nexus-4-on-linux-mint-13-and-access-all-files-on-computer/index.html @@ -0,0 +1,783 @@ + + + + + + +Root Nexus 4 on Linux Mint 13 and access all files on computer - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Root Nexus 4 on Linux Mint 13 and access all files on computer +

    + + +

    + + + + + + + + + + + + 7 minute read + + + +

    + + +
    + + +
    + +

    Having a rooted phone and then going to one that does not have root access is like getting used to driving a luxury car but then being forced to drive a tractor. So with arrival of my shining new nexus 4 once the novelty worn of in 8 hours or so, I sat down and rooted the device. Now there are plenty of guides out there but not many specific to Linux just yet. One reason might just be the fact the Linux Users are really smart and know how to figure it out but what about users who are new …well at least for them I am sure this post will be useful and while we are at it, I felt I will install the touch version of CWM… +Pre-requisite:

    +
      +
    1. For purpose of this tutorial, I will assume that adb set-up is in place using steps explained in my last post specifically up to Step 5. It is totally worth doing it before you proceed but if you would prefer a shorter route, please refer some guide on how to install just adb. Though I would not recommend any other approach. (Not because they will not work but because this approach will ensure clean system and something I have tested 4 times already to work and if you will follow this post, it makes sense to do it the way I did)
    2. +
    3. Clockwork Recovery Mod (Touch Version)
    4. +
    5. Super User App
    6. +
    7. Fastboot
    8. +
    9. This process will wipe out all user data and apps. Appropriate back-ups should be taken and restoring those is beyond the scope of this post.
    10. +
    +

    Bullets 2 and 3 can be downloaded directly from http://www.clockworkmod.com/rommanager and http://download.chainfire.eu/282/SuperSU/ or as before I have uploaded them in mediafire and can be downloaded from this link. +I downloaded fastboot few months back from xda using this link and am not sure if it is still there or not so I have uploaded it to mediafire as well. +Fig-1

    +
    Step 1: Prepare Nexus 4 and Linux Mint 13
    +Step 2: Unblock bootloader for Nexus 4
    +Step 3: Root Nexus 4
    +Step 4: Make Clockwork Mod Permanent
    +Step 5: Mount and Un-Mount Nexus 4 to access files from Linux Mint
    +
    +

    Step 1: Prepare Nexus 4 and Linux Mint 13

    +

    1.1. Check if fastboot is already there in the the /android-sdks/platform-tools directory. If yes skip the next step. +1.2. Extract the fastboot from downloaded zip file and place it in the /android-sdks/platform-tools directory if it is not already there and make it executable (Right Click, Select properties, Go To permissions tab and select the checkbox in front of "Execute") +1.3. Copy the downloaded Clock Work Mod file (recovery-clockwork-touch-6.0.2.3-mako.img) in the /android-sdks/platform-tools directory. +1.4.Activate debug mode in nexus 4 - to do this go to settings > About Phone and then click 7 to 8 times on "Build Number". This will activate developer mode. +1.5. Now click back and got to {} Developer Options and Click the checkbox against USB debugging. +1.6. Set up udev on linux mint:
    +a. Assuming that you have followed last post you would already have a "51-android.rules" file created. +b. Open the file with Gedit using following command

    +
     sudo gedit /etc/udev/rules.d/51-android.rules 
    +
    +

    c. Add following lines:

    +
    #LG - Nexus 4
    +SUBSYSTEM=="usb", ATTR{idVendor}=="1004", MODE="0666"
    +
    +SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="4ee1", MODE="0660", OWNER="ankit" #Normal nexus 4
    +SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="4ee2", MODE="0660", OWNER="ankit" #Debug & Recovery nexus 4
    +SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="4ee0", MODE="0660", OWNER="ankit" #Fastboot nexus 4
    +
    +

    I got the Vendor ID and Product ID by connecting the phone in different states (USB debug checked, unchecked and also after the phone was connected and rebooted into bootloader using command "adb reboot bootloader") as per the guidance given here. +d. Now Save the file, then chmod to all read using following command:

    +
    sudo chmod +x /etc/udev/rules.d/51-android.rules 
    +
    +

    Step 2: Unblock bootloader for Nexus 4

    +

    2.1. Plug your phone into the computer and type the following command in terminal:

    +
    adb reboot bootloader
    +
    +

    2.2. Once Nexus 4 has rebooted in recovery mode, type the following command in terminal:

    +
    fastboot oem unlock
    +
    +

    If terminal displays the message "waiting for device" 51-android.rules file is not set correctly and you might need to check vendor ID and product id using lsusb command in different modes and update it with appropriate data. +2.3. Phone will display a long message and ask for confirmation to unlock bootloader. Select "Yes" by using the volume keys and use power to select it. +2.4. Now, using volume keys navigate to "Recovery Mode" and select it using "Power" key. +2.5. After a while Android with blue progress bar should appear and phone should reboot but if it does not and instead shows a screen with "Android" on it’s back with an exclamation mark on it’s tummy, don’t panic. Just press "Power" and "Volume Up" till it shows recovery menu and then select "reboot". +The phone is now bootloader unlocked.

    +

    Step 3: Root Nexus 4

    +

    OK now when the phone boots, it will be fresh with factory reset, no data or apps whatsoever other than those that are there by default and when the phone boots, it will ask all details like selecting country etc. Just enter quickly without bothering to enter gmail, wifi etc. Once done: +3.1. Install mtpfs from synaptics. +3.2. Plug the phone to computer. +3.3. Type the following commands:

    +
    sudo mkdir /media/nexus4 
    +
    +

    followed by

    +
    sudo chmod 755 /media/nexus4 
    +
    +

    3.4. Now mount the nexus 4 using following command so we can transfer files:

    +
    sudo mtpfs -o allow_other /media/nexus4 
    +
    +

    3.5. Copy the downloaded file "CWM-SuperSU-v0.98.zip" on Nexus 4. +3.6. Type the following command to unmount nexus 4:

    +
    sudo umount /media/nexus4
    +
    +

    3.7. Reactivate the debug mode using Step 1.4 and Step 1.5 above. +3.8. On terminal type the command:

    +
    adb reboot bootloader 
    +
    +

    followed by:

    +
    fastboot flash recovery /android-sdks/platform-tools/recovery-clockwork-touch-6.0.2.3-mako.img
    +
    +
    Make sure in above command you replace PATH_TO with the actual path to the file. An easy way can be to right click on "recovery-clockwork-touch-6.0.2.3-mako.img" file and click on properties thene select the path and paste in this command.
    +

    Fig-2 +3.9. Once completed, on the phone navigate to "Recover Mode" using volume keys and select using power key. +3.10 The new recovery menu will be presented. Select "install zip from sd card" and "choose zip from sd card." and select the file we had put in step 3.5 - "CWM-SuperSU-v0.98.zip". +3.11 Once done, go back to reboot and "Reboot" the phone. +At this point your phone is rooted,however, you will get a message suggesting the recovery will not be permanent or something to that effect which is fine if you aren’t bothered about it but if you would rather want to keep this CWM we need to move to next step.

    +

    Step 4: Make Clockwork Mod Permanent

    +

    4.1. On your phone install ES File Explorer. +4.2. Select Settings.
    +Fig-3 +4.3. Select "Root Settings".
    +Fig-3 +4.4. Select all checkboxes. You will be asked for superuser access, say Yes.
    +Fig-3 +4.5. Now go back to main screen of ES File Explorer and select the third tab with an icon of Folder with up arrow and text Up. This should bring you to root. +4.6 Now using ES File Explorer navigate on your phone to /system/etc, find the file named "install-recovery.sh" and rename it to "install-recovery.bak" +4.7 Repeat Steps 3.7 to 3.11. +Now your clockwork mod is permanent.

    +

    Step 5: Mount and Un-Mount Nexus 4 to access files from Linux Mint

    +

    Now, with the steps 3.1 to 3.6 we have laid the foundation for being able to connect Nexus 4 and transfer files using USB. Something earlier was possible using USB Mass storage mode but is not present jelly bean onwards. +The commands in 3.4 and 3.6 are key to achieve this but rather than remembering these and typing each time, I have made a menu entries for each of these (Mount Nexus 4, Unmount Nexus 4) and after connecting phone via USB, I simply click on these, system asks root password and then connects nexus 4 as mass storage (see screenshot below): +Fig-3 +In order to get these you will follow the steps below: +5.1 Right Click on Menu and select "edit Menu".
    +5.2 Now click on "New Menu" and Enter a menu entry "Phone".
    +5.3. Select the checkbox next to the new menu entry "Phone" in middle pane.
    +5.4. Select new menu entry "Phone" in left pane.
    +5.5. Click on "New Item" in right Pane and in the dialogue box fill the fields as below and save:

    +
    Type:       Application in terminal
    +Name:       Mount Nexus 4
    +Command:    sudo mtpfs -o allow_other /media/nexus4
    +

    5.6 Again click on "New Item" in right Pane and in the dialogue box fill the fields as below and save:

    +
    Type:            Application in terminal
    +Name:            Unmount Nexus 4
    +Command:         sudo umount /media/nexus4
    +

    5.7 Make sure checkbox next to these new items is ticked. Save and Close. +That’s it !!! All Done. +Hope some will find this post useful.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rstudio-server-setup-with-ssl-behind-apache-proxy-server/index.html b/rstudio-server-setup-with-ssl-behind-apache-proxy-server/index.html new file mode 100644 index 0000000..bfb6845 --- /dev/null +++ b/rstudio-server-setup-with-ssl-behind-apache-proxy-server/index.html @@ -0,0 +1,843 @@ + + + + + + +Rstudio Server Setup with SSL behind Apache proxy server - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Rstudio Server Setup with SSL behind Apache proxy server +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +
    + + +
    + + + +

    Install R using following commands:

    + +
    sudo apt-get install r-base libapparmor1 gdebi-core
    +# Check that R is installed
    +R
    +#quit R
    +q()
    +
    + +

    Install Rstudio IDE server

    +
    cd Downloads/
    +wget https://download2.rstudio.org/rstudio-server-1.0.136-amd64.deb
    +sudo gdebi rstudio-server-1.0.136-amd64.deb
    +
    + +

    At this point if all goes well you can check the status of rstudio server by issuing the command:

    +
    sudo systemctl status rstudio-server.service
    +
    + +

    The server is started automatically at port 8787 and can be accessed using <ip_address:8787> in browser of your choice, provided all firewall settings have been taken care of.

    + +

    However, when you open the Rstudio server you will be presented with a logon screen and while you can access this using the users for the machine it is hosted on, it will be prudent to create a lower privilege user as explained in next section.

    +

    Add User to access the RStudio

    + +
    sudo adduser rstudio
    +
    +

    Set up SSL and reverse proxy for R-Studio Server

    + +

    Now important thing to note is that community version of Rstudio server does not come with SSL enabled but just to run it on a secure socket layer you don’t necessarily need the pro version. By following the steps below, your communication with the server will be on SSL.

    + +

    However to achieve the objective we need to accomplish following steps:

    + +
      +
    1. Enable modules on Apache to help set up proxy
    2. +
    +
      +
    • Configure a proxy to control access to RStudio Server
    • +
    • Use LetsEncrypt to enable SSL
    • +
    • Restrict access to Rstudio server only through proxy
    • +
    • Restart both Rstudio and Apache servers
    • +
    + +

    Step 1: Enable modules on Apache to help set up proxy

    + +

    There is guidance on how to do this on Rstudio Support. However, there was a bit of hair pulling and head scratching involved to get all the steps above work together so stick with me but keep that link in back pocket for variations or when you are stuck.

    + +

    With head scratching and hair pulling I mean that I encountered errors such as these - AH01102 error reading status line from remote server, Rstudio Proxy redirect changing the URL to localhost and many others which can be avoided by following steps as explained below. Anyway so we need to enable mod_proxy and mod_proxy_wstunnel modules. As Apache is already installed and mod_proxy already enabled I did not have to install the module itself, but if it needs to be done the commands are:

    + +
    sudo apt-get install libapache2-mod-proxy-html
    +sudo apt-get install libxml2-dev
    +
    + +

    Issuing the following commands should enable the relevant modules:

    +
    sudo a2enmod proxy
    +sudo a2enmod proxy_http
    +sudo a2enmod proxy_wstunnel
    +
    + +

    Step 2: Configure a proxy to control access to RStudio Server

    +
    # Change directory to sites-available
    +cd /etc/apache2/sites-available
    +# create a rstudio conf file
    +sudo nano rstudio.conf
    +
    + +

    Paste the following in the conf file but make sure to change details relevant to your set-up for each entry (line numbers 2, 3, 4, 15 and 16 below):

    +
    <VirtualHost *:80>;
    +        ServerAdmin user@yoursite.com
    +        ServerName yoursite.com
    +        ServerAlias whatever.yoursite.com
    +
    +#Specify path for Logs
    +        ErrorLog ${APACHE_LOG_DIR}/error.log
    +        CustomLog ${APACHE_LOG_DIR}/access.log combined
    +
    +        RewriteEngine on
    +
    +#Rewrite the url supplied to ensure https is applied
    +        RewriteCond %{SERVER_NAME} =yoursite.com [OR]
    +        RewriteCond %{SERVER_NAME} =whatever.yoursite.com
    +        RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
    +
    +# Following lines should open rstudio directly from the url
    +        RewriteCond %{HTTP:Upgrade} =websocket
    +        RewriteRule /(.*)     ws://localhost:8787/$1  [P,L]
    +        RewriteCond %{HTTP:Upgrade} !=websocket
    +        RewriteRule /(.*)     http://localhost:8787/$1 [P,L]
    +        ProxyPass / http://localhost:8787/
    +        ProxyPassReverse / http://localhost:8787/
    +
    +</VirtualHost>;
    +
    +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
    +
    + +

    Press Ctrl+x and save the file.

    + +

    TIP: If you just want reverse proxy and no SSL, you can just comment out line 15, 16 and 17 in above conf file and you are all set. If you do want to enable SSL, enabling the site with commands below won’t probably work just yet and subsequent steps will need to be completed.

    + +

    Now enable the new site by issuing the commands:

    +
    sudo a2ensite rstudio.conf
    +sudo service apache2 restart
    +
    + +

    Step 3: Use LetsEncrypt to enable SSL

    + +

    Follow the instructions here for specific usecase but one way or the other using Certbot you will be able to obtain the LetsEncrypt SSL certificate and enable it on your server.

    + +

    Once certbot has completed doing it’s thing you would find an additional conf file in /etc/apache2/sites-available named rstudio-le-ssl.conf. It will be pretty much same content as in rstudio.conf with very minor changes. The first line will be listening on 443 instead of 80 and the ssl certificates will be included. Normally you would not need to tweak anything in the resultant file but just for reference the contenst of this file will look as below:

    + +
    <IfModule mod_ssl.c>
    +<VirtualHost *:443>
    +
    +        ServerAdmin user@yoursite.com
    +        ServerName yoursite.com
    +        ServerAlias whatever.yoursite.com
    +
    +        ErrorLog ${APACHE_LOG_DIR}/error.log
    +        CustomLog ${APACHE_LOG_DIR}/access.log combined
    +
    +RewriteEngine on
    +# Following lines should open rstudio directly from the url
    +        RewriteCond %{HTTP:Upgrade} =websocket
    +        RewriteRule /(.*)     ws://localhost:8787/$1  [P,L]
    +        RewriteCond %{HTTP:Upgrade} !=websocket
    +        RewriteRule /(.*)     http://localhost:8787/$1 [P,L]
    +        ProxyPass / http://localhost:8787/
    +        ProxyPassReverse / http://localhost:8787/
    +
    +SSLCertificateFile /etc/letsencrypt/live/whatever.yoursite.com/fullchain.pem
    +SSLCertificateKeyFile /etc/letsencrypt/live/whatever.yoursite.com/privkey.pem
    +Include /etc/letsencrypt/options-ssl-apache.conf
    +</VirtualHost>
    +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
    +</IfModule>
    +
    + +

    Restrict access to Rstudio server only through proxy

    +

    Finally, we want to ensure that access to the Rstudio server is only through the proxy we configured and to do that we just need to specify this in the rstudio server configuration the attribute that tells it to only serve requests from localhost.

    + +
    sudo nano /etc/rstudio/rserver.conf
    +
    + +

    Now on the opened file type www-address=127.0.0.1 and press Ctrl+x and save the file.

    + +

    Restart both Rstudio and Apache servers

    + +

    Finally issue the following commands to restart both the servers:

    +
    sudo systemctl restart rstudio-server.service
    +sudo service apache2 restart
    +
    + +

    This is it. Now your new Rstudio server is ready to be used through secure socket layer.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/seafile-server-behind-nginx-on-fedora-24-security-lab-spin/index.html b/seafile-server-behind-nginx-on-fedora-24-security-lab-spin/index.html new file mode 100644 index 0000000..b2d9dd2 --- /dev/null +++ b/seafile-server-behind-nginx-on-fedora-24-security-lab-spin/index.html @@ -0,0 +1,1121 @@ + + + + + + +Seafile Server behind nginx on Fedora 24 Security Lab Spin - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Seafile Server behind nginx on Fedora 24 Security Lab Spin +

    + + +

    + + + + + + + + + + + + 12 minute read + + + +

    + + +
    + + +
    + + + +

    I have recently been intrigued by the idea of replacing the likes of “Dropbox” and “Google Drive” with a cloud set-up of my own. I had “Owncloud” set-up for nearly a year but was not happy with it. There were minor niggles aside form speed and thumbnails and then “Owncloud” had a recent split leading to creation of “Nextcloud”.

    + +

    While “Nextcloud” is the one that is more aligned to the general principles of community driven software, it is new and is still plagued with owncloud issues as it is essentially same stuff in new packaging at the moment.

    + +

    In the meantime, every now and then I was reading all the good stuff people had to say about “Seafile” and so I wanted to give it a try. Now for the past year and a half I have also been using Fedora Security Lab spin on my home server and I just wanted to get the Seafile set-up on it so I did a few “duckduckgo” searches on the net and finally had the steps to achieve the objective. Obviously it all worked and my Seafile server is live and kicking, hence the post. :)

    + +

    So in nutshell my objective was to:

    + +
    +Install seafile-server-5 behind nginx on Fedora 24 Security Lab spin all on a 32 bit 12 year old laptop. +
    + +

    The steps I followed are listed below with detailed notes of what I did. I do not claim these to be perfect but this is what worked for me. If you know that something can be done better, please do let me know in the comments.

    + +

    1. Install required software to support seafile with nginx:

    +
      +
    1. python
    2. +
    3. python-imaging
    4. +
    5. MySQL-python
    6. +
    7. python-setuptools
    8. +
    9. nginx
    10. +
    11. mariadb
    12. +
    13. mariadb-server
    14. +
    15. policycoreutils-python
    16. +
    17. setroubleshoot
    18. +
    + +

    All this can be done with one single command:

    + +

    sudo dnf install python python-imaging MySQL-python python-setuptools nginx mariadb mariadb-server policycoreutils-python setroubleshoot

    + +

    2. Start mariadb-server and set up basic security settings.

    +

    Now as we need to run all commands with “sudo”, it is actually easier to just go root. In other words, type “su” on the terminal and provide your password so you are logged in as root on the terminal. When you are logged in as root the $ changes to # at command prompt. All commands below are on root so if you are not logged in as root, you will need to run these with “sudo”.

    + +
    #Start mariadb: 
    +[root@localhost /] systemctl start mariadb
    +#Initiate the mariadb secure installation:
    +[root@localhost /] mysql_secure_installation
    +
    +

    Above command will trigger a set of questions around port, username, password etc. You can of course change these. If you do so, you must keep note of it and change your specific details for database in subsequent steps but if you leave it default you will basically have a user: root, with a password of your choice (we will use “sqlpasswd” for this example) and a database server running at port 3306.

    +

    3. Set up databases and privileges for "seafile"

    +

    Type following:

    +
    [root@localhost /] mysql -p
    +
    +

    You will be prompted to enter a password. Provide the password you have set-up for maria-db server in step above. So for our example it will be: sqlpasswd +Now on mysql prompt type following commands one after one along with semi-colon:

    +
    create database `ccnet-db` character set = 'utf8';
    +create database `seafile-db` character set = 'utf8';
    +create database `seahub-db` character set = 'utf8'
    +
    +

    Now we will create a user named “seafile” for mysql with a password ‘seafilepwd’. You must replace this password with one of your own. To do so use the command below and change seafilepwd with a password of your choice.

    +
    create user 'seafile'@'localhost' identified by 'seafilepwd';
    +
    +

    Once seafile user is created, we need to grant permissions to this user on the three databases we created above. To do so, use the following one by one on mysql command prompt:

    +
    GRANT ALL PRIVILEGES ON `ccnet-db`.* to `seafile`@localhost;
    +GRANT ALL PRIVILEGES ON `seafile-db`.* to `seafile`@localhost;
    +GRANT ALL PRIVILEGES ON `seahub-db`.* to `seafile`@localhost;
    +
    +

    Finally we will make sure that mariadb-server starts every-time the system is started. To do this simply copy the command below:

    + +
    [root@localhost /] systemctl enable mariadb
    +
    +

    4. Create directories to download and extract seafile server:

    + +
    
    +
    +
    +
    1
    +2
    +3
    +4
    +
    #Create a directory called seafile:
    +[root@localhost /] mkdir /opt/seafile
    +Now change directory to the newly created path
    +[root@localhost /] cd /opt/seafile
    +
    + +

    Download the latest seafile-server relevant to your machine architecture (32 bit, 64 bit etc). As I am using a 32 bit laptop, I used the link meant for that.

    + +

    TIP: You can get the relevant link by first opening the seafile site in firefox (https://www.seafile.com/en/download/) and scrolling down all the way to “Server” section. Then under linux section you will see link for 64 bit and 32 bit versions. Right click on one you need and click on “Copy Link” Location&quot;. The copied text is what you need to paste after wget in command below.

    + +
    [root@localhost /] wget https://bintray.com/artifact/download/seafile-org/seafile/seafile-server_5.1.3_i386.tar.gz
    +
    +

    Now to extract the downloaded file, type following command.

    +
    [root@localhost /] tar -xzf seafile-server_5.1.3_i386.tar.gz
    +
    +

    TIP: If downloaded version is different, the filename “seafile-server_5.1.3_i386.tar.gz” will be different in the link you would have got from previous step. Use the file name right at the end of the link you copied.

    + +

    OK, now we will create a directory named “installed” and move the downloaded file in there.

    +
    [root@localhost /] mkdir installed
    +[root@localhost /] mv seafile-server_5.1.3_i386.tar.gz installed
    +
    +

    Checkpoint

    +

    At this point, if you give the tree command, your directory structure would look as shown below

    +
    [root@localhost seafile] tree -L 2
    +
    +
    .
    +|---installed
    +|----- seafile-server_5.1.3_i386.tar.gz
    +|---seafile-server-5.1.3
    +|----- check_init_admin.py
    +|----- reset-admin.sh
    +|----- runtime
    +|----- seaf-fsck.sh
    +|----- seaf-fuse.sh
    +|----- seaf-gc.sh
    +|----- seafile
    +|----- seafile.sh
    +|----- seahub
    +|----- seahub.sh
    +|----- setup-seafile-mysql.py
    +|----- setup-seafile-mysql.sh
    +|----- setup-seafile.sh
    +|----- upgrade
    +
    +

    5. Configure the seafile-server

    + +
    #Change directory to seafile-server where the execution scripts are residing.
    +[root@localhost seafile] cd seafile-server-5.1.3
    +#Now run the execution script:
    +[root@localhost seafile-server-5.1.3] ./setup-seafile-mysql.sh
    +
    + +

    Running this script will initiate the seafile server set-up for mysql. You will need to provide answers to some questions. I am providing those below where you need to deviate from default or need to provide specifc information:

    +
      +
    1. +Name of the server. Provide a servername like "my_seafile_cloud" or "cloudy_lemon" ...you get the gist.
    2. +
    3. +IP or Domain of Server:
    4. +
    + +
    +

    TIP: Now let’s take a bit of time to understand what should go here. For my usage I wanted to use dyndns so I can access this server from outside of my home network. So I had to configure my dyndns url to a port on my home router such that router understood that incoming traffic to that particular port must be transferred to this machine where the server is hosted.

    + +

    Then on this machine, I have forwarded the traffic coming to a specific port again to whichever server I want to access thus having my unique URL for each service I am interested in. I will go into in a bit more detail later but for now, just ensure you are providing 1 the internal IP as well as dyndns IP along with the port you have configured on your router to reach this machine in particular where you are configuring the server.

    + +

    So if internal IP of this machine is 192.168.1.24 and my dyndns url is banana.dyndns.com and I have forwarded port 9994 on my router to this machine and on machine incoming traffic to 9994 is forwarded to the port relevant to seahub then I will provide following entries as answer to this question: +192.168.1.24:9994, banana.dyndns.com

    +
    + +
      +
    1. +Choose a way to initiatlise seafile databases: +Now remember we have already done this in step 2. So for this question we will need to give option 2. so just type 2 and press enter. +
    2. +
    3. +Host of mysql server: Unless you changed this in step 2, leave it as default (localhost) +
    4. +
    5. +Port for mysql server: as above leave it default (3306) unless you changed in Step 2. +
    6. +
    7. +User for seafile: seafile +
    8. +
    9. +Password for user "seafile": Use the password provided in Step 3. "seafilepwd" is what we provided for this example. +
    10. +
    11. +Database name for ccnet: ccnet-db +
    12. +
    13. +Database name for seafile: seafile-db +
    14. +
    15. +Database name for seahub: seahub-db
    16. +
    + +

    Once this wizard has completed configuring, you will get a confirmation that will tell you that seafile is now listening on port 8082 and seahub on port 8000 unless you changed these while providing answers to the wizard.

    + +

    6. Add the user and provide right access

    + +

    Use following commands to go up one directory, add a user “seafile” and provide right privileges.

    +
    [root@localhost seafile-server-5.1.3] cd ..
    +[root@localhost seafile] adduser seafile
    +[root@localhost seafile] chown -R seafile .
    +
    +

    7. Generate SSL Certificate:

    + +
    #Change directory to /etc/ssl
    +[root@localhost seafile] cd /etc/ssl
    +#Generate private and public ssl certificates
    +[root@localhost ssl] openssl genrsa -out seafile_privkey.pem 2048
    +[root@localhost ssl] openssl req -new -x509 -key seafile_privkey.pem -out seafile_cacert.pem -days 1095
    +
    +

    8. Create the nginx config file for seafile

    + +

    Use following command to create seafile.conf file:

    + +
    [root@localhost ssl] nano /etc/nginx/conf.d/seafile.conf
    +
    +

    Copy Paste the following but do make relevant changes where required for servername etc:

    + +
    ########################################################
    +server {
    +    listen      80;
    +    server_name *banana.dyndns.com*; ---&gt; Must change this as per your set-up. Notice no port 9994 here.
    +    rewrite ^ https://$http_host$request_uri? permanent;   # force redirect http to https. 
    +}
    +server {
    +    listen 443;
    +    ssl on;
    +    ssl_certificate /etc/ssl/seafile_cacert.pem;
    +    ssl_certificate_key /etc/ssl/seafile_privkey.pem;
    +    server_name *banana.dyndns.com*; ---&gt;Must change this as per your set-up. Notice no port 9994 here. 
    +    proxy_set_header X-Forwarded-For $remote_addr;
    +    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains&amp;quot;;
    +    server_tokens off;
    +    location / {
    +        fastcgi_pass   127.0.0.1:8000;
    +        fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
    +        fastcgi_param  PATH_INFO          $fastcgi_script_name;
    +        fastcgi_param  SERVER_PROTOCOL    $server_protocol;
    +        fastcgi_param  QUERY_STRING       $query_string;
    +        fastcgi_param  REQUEST_METHOD     $request_method;
    +        fastcgi_param  CONTENT_TYPE       $content_type;
    +        fastcgi_param  CONTENT_LENGTH     $content_length;
    +        fastcgi_param  SERVER_ADDR        $server_addr;
    +        fastcgi_param  SERVER_PORT        $server_port;
    +        fastcgi_param  SERVER_NAME        $server_name;
    +        fastcgi_param  HTTPS              on;
    +        fastcgi_param  HTTP_SCHEME        https;
    +        access_log     /var/log/nginx/seahub.access.log;
    +        error_log      /var/log/nginx/seahub.error.log;
    +    }
    +    location /seafhttp {
    +        rewrite ^/seafhttp(.*)$ $1 break;
    +        proxy_pass http://127.0.0.1:8082;
    +        client_max_body_size 0;
    +        proxy_connect_timeout 36000s;
    +        proxy_read_timeout 36000s;
    +        proxy_send_timeout 36000s;
    +    }
    +    location /media {
    +        root /opt/seafile/seafile-server-latest/seahub;
    +    }
    +
    +########################################################
    +
    +

    9. Apply config changes to seafile

    + +
    #Change to conf directory at /opt/seafile.
    +[root@localhost ssl] cd /opt/seafile/conf
    +#Now open the ccnet.conf file:
    +[root@localhost conf] nano ccnet.conf
    +
    +

    Amend the file as per below:

    + +
    ########################################################
    +
    +[General]
    +USER_NAME = my_seafile_cloud 
    +#**Must change this as per your setup.**
    +ID =
    +NAME = my_seafile_cloud ####&lt;--------- Must change this as per your setup.
    +SERVICE_URL = https://192.168.1.24:9994,https://banana.dyndns.com:9994 
    +#**Must change this as per your setup.**
    +#**Also note that here we have provided the 9994 port **
    +#**- the one we used as port forward on router **
    +#**to send traffic to this machine.**
    +
    +[Client]
    +PORT = 13419
    +
    +[Database]
    +ENGINE = mysql
    +HOST = 127.0.0.1
    +PORT = 3306
    +USER = seafile
    +PASSWD = sqlpasswd 
    +#**This would be same as what was provided in Step 2.**
    +DB = ccnet-db
    +CONNECTION_CHARSET = utf8
    +
    +########################################################
    +
    +

    Open the seahub_settings.py file

    +
    [root@localhost conf] nano seahub_settings.py
    +
    +

    Add the line FILE_SERVER_ROOT= 'https://banana.dyndns.com:9994/seafhttp' replacing banana.dyndns.com:9994 with url to reach your machine.

    + +
    ########################################################
    +
    +DATABASES = 
    +{
    +	'default': 
    +	{
    +	'ENGINE': 'django.db.backends.mysql',
    +	'NAME': 'seahub-db',
    +	'USER': 'seafile',
    +	'PASSWORD': 'sqlpasswd', #This will be same as password created in Step 2.
    +	'HOST': '127.0.0.1',
    +	'PORT': '3306'
    +	}
    +}
    +
    +FILE_SERVER_ROOT= 'https://banana.dyndns.com:9994/seafhttp' 
    +#**Must change this as per your setup.**
    +#**Also note that here we have provided the 9994 port **
    +#**- the one we used as port forward on router **
    +#**to send traffic to this machine.**
    +
    +

    10. Now create the seafile and seahub services and enable them so they run at system startup:

    +

    Create a seafile.service file:

    + +
    [root@localhost conf] nano /etc/systemd/system/seafile.service
    +
    + +

    Paste the below into the file:

    + +
    ########################################################
    +
    +[Unit]
    +Description=Seafile
    +After=network.target mariadb.service
    +
    +[Service]
    +Type=oneshot
    +ExecStart=/opt/seafile/seafile-server-latest/seafile.sh start
    +ExecStop=/opt/seafile/seafile-server-latest/seafile.sh stop
    +RemainAfterExit=yes
    +User=seafile
    +Group=seafile
    +
    +[Install]
    +WantedBy=multi-user.target
    +#########################################################
    +
    +
    #Now reload the systemd and enable seafile to start at system start-up.
    +[root@localhost conf] systemctl daemon-reload
    +[root@localhost conf] systemctl enable seafile
    +#Create a seahub.service file:
    +[root@localhost conf] nano /etc/systemd/system/seahub.service
    +
    + +

    Paste the following in the file and save.

    + +
    ##########################################################
    +[Unit]
    +Description=Seafile hub
    +After=network.target seafile.service
    +
    +[Service]
    +ExecStart=/opt/seafile/seafile-server-latest/seahub.sh start-fastcgi
    +ExecStop=/opt/seafile/seafile-server-latest/seahub.sh stop
    +User=seafile
    +Group=seafile
    +Type=oneshot
    +RemainAfterExit=yes
    +
    +[Install]
    +WantedBy=multi-user.target
    +##########################################################
    +
    +

    Now enable seahub to start at system start-up and start seafile and seahub services.

    + +
    [root@localhost conf] systemctl enable seahub
    +[root@localhost conf] systemctl start seafile
    +[root@localhost conf] systemctl start seahub
    +
    +

    Start nginx service and enable to run at system start

    + +
    [root@localhost conf] systemctl enable nginx.service
    +[root@localhost conf] systemctl start nginx.service
    +
    +

    12. Fedora specific steps:

    + +

    Now at this point there were few final things specific to Fedora that needed attention. I was ending up getting error 500 and what not and after a bit of searching on duckduckgo, I tried steps listed in this section that fixed the issue.

    + +

    I am not entirely sure what this does but what I understood is selinux was not allowing some access between nginx and seafile which this resolved. How? Well, you tell me :D.

    + +

    I had to install policycoreutils-python and setroubleshoot packages for these commands to work, so I have included them in Step 1 anyway.

    + +
    [root@localhost conf] cat /var/log/audit/audit.log | grep nginx | grep denied | audit2allow -M mynginx
    +[root@localhost conf] semodule -i mynginx.pp
    +[root@localhost conf] setsebool httpd_can_network_connect 1
    +
    +

    13. Re-set admin user and password for seafile:

    + +
    #Change directory to seafile
    +[root@localhost conf] cd /opt/seafile/seafile-server-5.1.3/
    +#Now run the reset-admin
    +[root@localhost seafile-server-5.1.3] ./reset-admin.sh
    +
    + +

    14. Firewall configuration and port forwarding:

    + +

    Notes below are relevant to system running Fedora Security Lab spin with XFCE but general principle will be same. +Application->Administration->Firewall will open firewall gui.

    + +


    Alt Text

    + +

    You will be asked for password and upon entering you will be presented with Zones and Services. +
    Alt Text

    + +

    In Services enable https by clicking the checkbox against “https”. +
    Alt Text

    + +

    Now click on “Ports” and click on “Add”. +
    Alt Text
    +Then add port 8082 in “Port / Port Range” and select “TCP” as “Protocol”.
    +Then add port 8082 in “Port / Port Range” and select “UDP” as “Protocol”.
    +Then add port 8000 in “Port / Port Range” and select “TCP” as “Protocol”.
    +Then add port 8000 in “Port / Port Range” and select “UDP” as “Protocol”.
    +Then add port 443 in “Port / Port Range” and select “TCP” as “Protocol”.
    +
    Alt Text

    + +

    Now click on Port Forward tab and click on “Add”. Then fill field as shown below:

    + +
    Protocol: tcp  
    +Port / Port Range: 9994 
    +# This is the port you have used on your router for port forwarding to this machine.
    +Under Destination section, tick the checkbox for "Local Forwarding"
    +Port / Port Forwarding field: 443 
    +# If you have used above configuration, SSL is enabled at 443. 
    +# If you have changed it, mention the relevant port.
    +
    +

    TIP: Once you have made these changes don’t forget to save this from run time to permanent or these changes will be lost when you restart the machine. [Options-> Runtime to Permanent]

    + +

    15. Finally, restart nginx:

    + +

    Use the following command to restart nginx services.

    +
    [root@localhost seafile-server-5.1.3]# systemctl restart nginx.service
    +
    +

    This is it. All Done !!! +If you now type your URL: (https://banana.dyndns.com:9994 from this example) on a browser, you will be presented with login page where you should use the admin login details you have created in Step 13 above.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..7aa2222 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,326 @@ + + + +https://mgw.dumatics.com/aao-a150l-step-by-step-installation-process-for-linux4one/ +2009-08-11T22:02:00+00:00 + + +https://mgw.dumatics.com/get-gmail-as-push-email-on-sony-p990i/ +2009-08-13T16:52:00+00:00 + + +https://mgw.dumatics.com/sony-vaio-fe21h-webcam-on-skype/ +2009-09-15T17:00:00+00:00 + + +https://mgw.dumatics.com/sony-vaio-n-vidia-setup-to-make-s-video-work/ +2009-09-16T17:10:00+00:00 + + +https://mgw.dumatics.com/o2-xda-serra-official-htc-upgrade-with-tf3d-of-touch-pro-2/ +2009-10-07T17:44:00+00:00 + + +https://mgw.dumatics.com/kubuntu-blog-entry/ +2010-03-07T10:22:00+00:00 + + +https://mgw.dumatics.com/configure-blogtk-1-0-for-blogger/ +2010-03-15T12:39:00+00:00 + + +https://mgw.dumatics.com/audio-problem-in-wine-under-kde-4-4-1-solved/ +2010-03-19T12:43:00+00:00 + + +https://mgw.dumatics.com/install-maitreya-vedic-astrology-software-on-ubuntu-kbuntu/ +2010-03-22T13:38:00+00:00 + + +https://mgw.dumatics.com/jagannath-hora-on-linux-play-on-linux-magic/ +2010-05-03T12:57:00+00:00 + + +https://mgw.dumatics.com/part-1-configure-epson-s515w-on-linux-mint-ubuntu-10-04/ +2010-11-13T14:50:00+00:00 + + +https://mgw.dumatics.com/call-us-and-several-other-countries-free-using-android/ +2011-02-19T15:30:00+00:00 + + +https://mgw.dumatics.com/google-voice-sip2sip-ikall-free-international-calls-to-known-contacts/ +2011-02-20T15:32:00+00:00 + + +https://mgw.dumatics.com/time-for-a-bit-of-show-off/ +2011-04-17T15:02:00+00:00 + + +https://mgw.dumatics.com/logitech-ex100-wireless-keyboard-and-mouse-not-working-read-on/ +2018-03-29T15:37:53+00:00 + + +https://mgw.dumatics.com/mmc-convert-media-files-on-linux/ +2011-04-29T14:48:00+00:00 + + +https://mgw.dumatics.com/glympse/ +2011-05-02T15:09:00+00:00 + + +https://mgw.dumatics.com/part-2-configure-epson-s515w-on-linux-mint-ubuntu-10-04/ +2011-05-04T15:12:00+00:00 + + +https://mgw.dumatics.com/how-to-edit-pdf-in-linux-the-easy-way/ +2011-05-07T15:35:00+00:00 + + +https://mgw.dumatics.com/install-open-workbench-and-jre-on-wine-in-linux-mint/ +2011-06-04T15:55:00+00:00 + + +https://mgw.dumatics.com/visio-alternative-on-linux-business-process-model-and-notation-tool/ +2011-06-20T17:41:00+00:00 + + +https://mgw.dumatics.com/access-files-folders-directories-from-linux-mint-on-android/ +2011-11-04T11:03:00+00:00 + + +https://mgw.dumatics.com/openvpn-on-linux-mint-to-access-us/ +2011-11-06T10:26:00+00:00 + + +https://mgw.dumatics.com/how-to-boot-from-usb-when-bios-does-not-have-the-option/ +2012-02-20T18:50:00+00:00 + + +https://mgw.dumatics.com/stagevu-divx-video-on-bodhi-linux/ +2012-03-04T22:25:00+00:00 + + +https://mgw.dumatics.com/exchange-2007-on-thunderbird-using-davmail/ +2012-03-16T22:39:00+00:00 + + +https://mgw.dumatics.com/top-10-android-apps-that-i-use/ +2012-08-06T22:05:00+00:00 + + +https://mgw.dumatics.com/flash-samsung-galaxy-s-gt-i9000-with-cyanogenmod-9-on-linux/ +2012-10-15T22:20:00+00:00 + + +https://mgw.dumatics.com/prepare-linux-mint-13-for-android-development/ +2012-10-17T23:12:00+00:00 + + +https://mgw.dumatics.com/root-nexus-4-on-linux-mint-13-and-access-all-files-on-computer/ +2013-01-01T12:38:00+00:00 + + +https://mgw.dumatics.com/conky-on-my-desktop-step-by-step/ +2013-01-18T13:09:00+00:00 + + +https://mgw.dumatics.com/python-eric-rad-ide-and-qt-designer/ +2013-03-01T13:30:00+00:00 + + +https://mgw.dumatics.com/linux-mint-on-android-through-vnc-and-jump/ +2013-03-17T13:37:00+00:00 + + +https://mgw.dumatics.com/arch-linux-nearly-a-year-on/ +2014-02-19T16:00:00+00:00 + + +https://mgw.dumatics.com/mysql-stored-procedure-to-return-json-for-google-charts/ +2016-02-18T13:56:00+00:00 + + +https://mgw.dumatics.com/crossover/ +2016-06-06T15:01:00+00:00 + + +https://mgw.dumatics.com/seafile-server-behind-nginx-on-fedora-24-security-lab-spin/ +2016-06-27T17:54:00+00:00 + + +https://mgw.dumatics.com/ghost-on-fedora-24/ +2016-07-01T11:37:53+00:00 + + +https://mgw.dumatics.com/the-complete-walkthrough-of-my-blogger-to-ghost-migration/ +2018-03-29T15:37:53+00:00 + + +https://mgw.dumatics.com/mysql-function-to-calculate-elapsed-working-time/ +2018-03-29T15:37:53+00:00 + + +https://mgw.dumatics.com/ddclient-on-fedora-2/ +2016-07-20T16:11:45+00:00 + + +https://mgw.dumatics.com/tomcat-on-fedora-behind-nginx/ +2016-07-25T17:52:00+00:00 + + +https://mgw.dumatics.com/update-ghost-on-fedora/ +2016-07-28T22:37:53+00:00 + + +https://mgw.dumatics.com/fix-for-php-issues-after-upgrade-to-ubuntu-16-04-1-xenial/ +2018-03-29T15:37:53+00:00 + + +https://mgw.dumatics.com/ethercalc/ +2016-08-12T15:37:53+00:00 + + +https://mgw.dumatics.com/the-pain-of-using-windows/ +2016-09-05T15:37:53+00:00 + + +https://mgw.dumatics.com/grav-cms-with-a-difference/ +2018-03-29T15:37:53+00:00 + + +https://mgw.dumatics.com/note-7-to-oneplus-3/ +2018-03-29T15:37:53+00:00 + + +https://mgw.dumatics.com/swap-file-to-create-extra-memory/ +2017-03-02T15:37:53+00:00 + + +https://mgw.dumatics.com/dd-wrt-firmware-on-tp-link-tl-wr841n-v11/ +2018-03-29T15:37:53+00:00 + + +https://mgw.dumatics.com/markdown-and-gantt-charts/ +2018-03-29T15:37:53+00:00 + + +https://mgw.dumatics.com/rstudio-server-setup-with-ssl-behind-apache-proxy-server/ +2018-03-29T15:37:54+00:00 + + +https://mgw.dumatics.com/ghost-v1-0-upgrade-related-quirks-and-fixes/ +2018-03-29T15:37:54+00:00 + + +https://mgw.dumatics.com/unprotect-sheets-in-libre-calc-excel/ +2018-03-29T15:37:54+00:00 + + +https://mgw.dumatics.com/ghost-upgrade-errors-and-fixes-1-19-x/ +2018-01-04T19:36:16+00:00 + + +https://mgw.dumatics.com/mysql-function-to-calculate-closing-workday-date-given-elapsed-working-time/ +2018-03-29T15:37:54+00:00 + + +https://mgw.dumatics.com/home-networking/ +2018-11-10T08:59:05+00:00 + + +https://mgw.dumatics.com/prosody-behind-apache-on-debian-stretch/ +2020-05-21T11:56:42+00:00 + + +https://mgw.dumatics.com/metabase-a-bi-solution-that-just-works/ +2019-04-08T17:36:09+00:00 + + +https://mgw.dumatics.com/converting-aax-audible-to-mp3/ +2020-05-21T11:48:26+00:00 + + +https://mgw.dumatics.com/upgrading-php-version-on-linux-for-apache/ +2021-02-10T00:14:40+00:00 + + +https://mgw.dumatics.com/tech-for-diabetics/ +2021-03-12T13:05:15+00:00 + + +https://mgw.dumatics.com/jupyterlite-on-github-enterprise-with-panel-enabled/ +2023-01-25T17:50:00+00:00 + + +https://mgw.dumatics.com/git-basics/ +2023-02-26T17:50:00+00:00 + + +https://mgw.dumatics.com/git-jargon/ +2023-02-28T17:50:00+00:00 + + +https://mgw.dumatics.com/read-excel-csv-recursive/ +2023-04-18T20:26:00+00:00 + + +https://mgw.dumatics.com/logseq-customisation-for-project-management-teamplate/ +2024-08-13T08:15:00+00:00 + + +https://mgw.dumatics.com/about/ +2024-08-14T08:40:00+00:00 + + +https://mgw.dumatics.com/categories/ + + +https://mgw.dumatics.com/ + + +https://mgw.dumatics.com/sitemap/ + + +https://mgw.dumatics.com/year-archive/ + + +https://mgw.dumatics.com/page2/ + + +https://mgw.dumatics.com/page3/ + + +https://mgw.dumatics.com/page4/ + + +https://mgw.dumatics.com/page5/ + + +https://mgw.dumatics.com/page6/ + + +https://mgw.dumatics.com/page7/ + + +https://mgw.dumatics.com/page8/ + + +https://mgw.dumatics.com/page9/ + + +https://mgw.dumatics.com/page10/ + + +https://mgw.dumatics.com/page11/ + + +https://mgw.dumatics.com/page12/ + + +https://mgw.dumatics.com/page13/ + + +https://mgw.dumatics.com/page14/ + + diff --git a/sitemap/index.html b/sitemap/index.html new file mode 100644 index 0000000..20c7b51 --- /dev/null +++ b/sitemap/index.html @@ -0,0 +1,3011 @@ + + + + + + +Sitemap - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + +
    + +

    Sitemap

    + +

    A list of all the posts and pages found on the site. For you robots out there is an XML version available for digesting as well.

    + +

    Pages

    + +
    +
    + +

    + + Page Not Found + +

    + + + +

    Page not found. Your pixels are in another canvas. +

    +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    + +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    + +
    + +
    + +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +
    +
    + +

    + + + +

    + + + + +
    +
    + +

    Posts

    + +
    + +
    + +
    + +
    + +
    +
    + +

    + + Git Jargon + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    Familiarisation with some Git jargon +

    +
    +
    + +
    +
    + +

    + + Git Basics + + +

    + + +

    + + + + + + + + + + + + 7 minute read + + + +

    + + +

    Basics of Git + +

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Tech for Diabetics + + +

    + + +

    + + + + + + + + + + + + 9 minute read + + + +

    + + +

    As a diabetic there are a number of things we are not able to control but one thing we can do is keep tabs on our data +

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Converting AAX (Audible) to mp3 + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    Lately I have been a bit frustrated because while I subscribe to Audible services, Amazon and Google do not play nice with each other limiting me - the owner...

    +
    +
    + +
    +
    + +

    + + Metabase - A BI solution that just works + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    I like exploring new solutions and anything to do with data always piques my interest. I came across this nice tool through the list of free self hosted soft...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Home Networking + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    Network routing + + +Router - Router is the device at home that connects all devices in your house to the internet. It does so by assigning IP address to each o...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Ghost Upgrade errors and fixes (1.19.x) + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    I have found the recent ghost upgrades quite painless but there have been few hiccups for last two times so I kept a record of what helped and it is as liste...

    +
    +
    + +
    +
    + +

    + + Unprotect Sheets in Libre Calc, Excel + + +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +

    A friend of mine today had an issue. He had created a template for some really complex calculations and to ensure he does not mess up with the forumlae by mi...

    +
    +
    + +
    + +
    + +
    + +
    + +
    +
    + +

    + + Markdown and Gantt Charts + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    For a fairly long time, I have been looking for a simple markdown type of solution to be able to quickly draw Gantt charts but never came across what one wou...

    +
    +
    + +
    +
    + +

    + + DD-WRT firmware on TP-LINK TL-WR841N v11 + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    I have been with PlusNet for over two years now and am a happy camper as far as fiber optic broadband is concerned but as I am no longer on a broadband contr...

    +
    +
    + +
    +
    + +

    + + Swap File to create extra memory + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    While renewing my LetsEncrypt certificate, I found myself in a strange situation where the certbot won’t run asking me to update pip and then each time I tri...

    +
    +
    + +
    +
    + +

    + + Note 7 to OnePlus 3 + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    I was a super excited owner of Note 7 in September this year and then in few +days the happiness started disappearing as the news of exploding Note 7 started +...

    +
    +
    + +
    +
    + +

    + + Grav - CMS with a difference + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    While I love Ghost as a blogging platform, it is not best placed for things other than blogs - after all that is the basic idea behind creation of this wonde...

    +
    +
    + +
    +
    + +

    + + Windows 10 - a bucket load of pain + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    + As Windows 10 is a commercial offering, one would think it will be working as expected and it does so long as like me one has come to expect pain from Mic...

    +
    +
    + +
    +
    + +

    + + Ethercalc + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Ethercalc is good tool which can be selfhosted. It is fairly simple to do so. Though it will be available for anyone who has the URL because there is no inbu...

    +
    +
    + +
    +
    + +

    + + Fix for PHP Issues after upgrade to Ubuntu 16.04.1 (Xenial) + + +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +

    After updating from Ubuntu 14.04, the php and Apache stopped being friends and one of the WordPress site I maintain went all white and admin page was just sh...

    +
    +
    + +
    +
    + +

    + + Update Ghost on Fedora + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    While the guidance on Ghost website is very clear, I did get issues that required steps in troubleshooting. Something to do with lodash and npm version 2 stu...

    +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    +
    + +

    + + The complete walkthrough of my blogger to ghost migration + + +

    + + +

    + + + + + + + + + + + + 9 minute read + + + +

    + + +

    The 7 Year Itch +It can’t possibly be a coincidence that this is the 7th year since I started blogging on blogger and therefore it is very likely to be a stro...

    +
    +
    + +
    +
    + +

    + + Ghost on Fedora 24 + + +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +

    To install Ghost as my blogging platform, I had to go through a number of hoops and one of them was to get the nodejs working and what not. I figured this mi...

    +
    +
    + +
    +
    + +

    + + Seafile Server behind nginx on Fedora 24 Security Lab Spin + + +

    + + +

    + + + + + + + + + + + + 12 minute read + + + +

    + + +

    I have recently been intrigued by the idea of replacing the likes of “Dropbox” and “Google Drive” with a cloud set-up of my own. I had “Owncloud” set-up for ...

    +
    +
    + +
    +
    + +

    + + Crossover + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    There are technologies that I can use in my personal life, that I am proud of and those that I play around with but reality is that as a Senior Project Manag...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Arch Linux - nearly a year on…. + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    I have been dwelling in the world of Arch Linux for just under a year now and must admit the experience is nothing less than liberating. Granted that the bar...

    +
    +
    + +
    +
    + +

    + + Linux Mint on Android through VNC and Jump + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    Today “Jump” was available for free on Amazon as the app of the day and since it’s nearly 7 quids on google play store, I downloaded. For windows and Mac use...

    +
    +
    + +
    +
    + +

    + + Python, ERIC RAD IDE and QT designer + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Right, I have decided to play around a little with the most loved language of open source a. k. a. Python. + +

    +
    +
    + +
    +
    + +

    + + Conky on my desktop - step by step + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    +My new friend Damjan recently mentioned that he liked the Conky on my desktop and asked for details as have few others so I figured a post on the topic will...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Prepare Linux Mint 13 for Android development + + +

    + + +

    + + + + + + + + + + + + 8 minute read + + + +

    + + +

    Few weeks back I updated to the latest Linux Mint offering "Maya" a.k.a Linux Mint 13. Now this is a LTS (Long Term Support) version and I wanted to be in a ...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Top 10 Android Apps that I use !!!! + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    I have been using Android Phones and Tablets for quite some time now and have +monitored my use for apps that I use on a daily basis and those I would not eve...

    +
    +
    + +
    +
    + +

    + + Exchange 2007 on Thunderbird using DAVMail + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    The start of this week was like a nightmare for me. Whole family was down with flu and I had the fever that is probably the highest ever of my entire life at...

    +
    +
    + +
    +
    + +

    + + StageVu / DivX video on Bodhi Linux + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    If you read my last post you will know I have been playing with Bodhi Linux lately. One of the selling point for this distro is it’s minimalistic approach. H...

    +
    +
    + +
    +
    + +

    + + How to boot from USB when BIOS does not have the option. + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    I have an old Sony VAIO which is not in it’s best of health and has long been really a companion for my telly, faithfully streaming media from bbc iplayer, y...

    +
    +
    + +
    +
    + +

    + + OpenVPN on Linux Mint to access US sites + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    + +UPDATE: LOOKS LIKE HOSTIZZLE IS NOT WORKING ANYMORE. THE STEPS IN THIS GUIDE WILL STILL BE RELEVANT FOR SETTING UP OPEN VPN, JUST THAT YOU WILL NEED TO FIN...

    +
    +
    + +
    + +
    + +
    + +
    + +
    +
    + +

    + + Install Open Workbench and JRE on Wine in Linux Mint + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    What is Open Workbench? +Open Workbench is a Project Planning Software comparable to Microsoft Project. There are mixed views on whether it is truly open sour...

    +
    +
    + +
    +
    + +

    + + How to edit PDF in Linux - The easy way. + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    Recently someone asked this question to me and after some search on Google I came across mentions of PDFedit, Scribus, flpsed, Gimp, PDFMod and even the open...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Glympse + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Every now and then we come across something that has a strong potential, something that is a great phenomenon in making and Glympse to me is that idea. + +

    +
    +
    + +
    +
    + +

    + + MMC Convert media files on Linux + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    I have been using Mobile Media Converter for almost a year now and it is such a great tool. I think it is a must have at-least on Linux. +It not only converts...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Time for a bit of show-off + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Hey Hey I made an Android app named “Sai Satcharitra”. It’s a very basic app and +all it does is helps people read Sai Satcharitra on their Android Smartphone...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Call US and several other countries free using Android + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    That’s right…with smartphones all that was possible using computers is increasingly becoming feasible through phones. I just now configured my Nexus S to mak...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Jagannath Hora on Linux - Play on Linux Magic + + +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +

    While I prefer Maitreya as it can run with Linux as native, quite a few of my friends have asked me if Jagannatha Hora will work on Linux and hence this post...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Audio problem in Wine under KDE 4.4.1 - Solved + + +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +

    I was trying to install spotify on linux which used to work perfectly on Gnome and when I tried on KDE it was giving error box. I wrote winecfg on terminal a...

    +
    +
    + +
    +
    + +

    + + Configure BlogTK 1.0 for blogger + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Right so I am happy with Blogilo, then why BlogTK? +It so happened that I was trying to edit my last post and for some unknown reason I was getting error in u...

    +
    +
    + +
    +
    + +

    + + KUBUNTU Blog Entry + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    This is how my love for Kubuntu has started….and growing by the minute… +I was experimenting with different Linux distros and then I went ahead with Linux Min...

    +
    +
    + +
    + +
    + +
    +
    + +

    + + Sony VAIO N-VIDIA setup to make S-Video work + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    + +UPDATE for 9.10 +For Ubuntu 9.10 I was just not able to get it work and finally I figured it out. The problem is with the latest Nvidia driver, the one that...

    +
    +
    + +
    +
    + +

    + + Sony VAIO FE21H Webcam on skype + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Alright friends not a huge tip but still I, being new fan of Ubuntu, installed it on my media laptop…Sony VAIO… +one might ask what is media laptop…well I had...

    +
    +
    + +
    +
    + +

    + + Get Gmail as Push Email on Sony P990i + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    While I have moved on from P990i which is now in safe hands of my dear wife, I remember doing some good amount of web searching and still had no idea how to ...

    +
    +
    + +
    +
    + +

    + + AAO A150L Step by Step Installation Process for Linux4one + + +

    + + +

    + + + + + + + + + + + + 11 minute read + + + +

    + + +

    Linux4one is the only Linux distro other than pre-installed Linpus that worked out of the box on my and two other Acer aspire one A150L which belonged to my ...

    +
    +
    + +

    poems

    + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sony-vaio-fe21h-webcam-on-skype/index.html b/sony-vaio-fe21h-webcam-on-skype/index.html new file mode 100644 index 0000000..80ba366 --- /dev/null +++ b/sony-vaio-fe21h-webcam-on-skype/index.html @@ -0,0 +1,687 @@ + + + + + + +Sony VAIO FE21H Webcam on skype - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Sony VAIO FE21H Webcam on skype +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    Alright friends not a huge tip but still I, being new fan of Ubuntu, installed it on my media laptop…Sony VAIO… +one might ask what is media laptop…well I had a bit of problem with my Sony VAIO some 1.5 years back and a key got stuck and as it goes with any Sony product there was no cheap solution at hand… +At that time for some reason it appeared like a motherboard issue and I just bought another Dell laptop…later I realised that it was a keyboard issue so I invested in a wireless keyboard and had two lappies at home just like that :)…I figured that with wireless keyboard VAIO laptop will make perfect companion to my 32" Samsung and hence it got the name of media laptop. +I use this laptop to browse without fear as I will never ever do any important work on it so am never worried of loosing..not that I ended up having any virus at all….and now with Linux I doubt if there will ever be any virus on this laptop….but anyway I have gone on a tangent… +What I started to tell was a tip on how we can make VAIO’s inbuilt motion-eye webcam to behave and the tip goes as below… +Incase your webcam is not giving good images on ur Sony VAIO, you may want to try this:

    +
      +
    1. Goto System->Preferences->Main Menu
    2. +
    3. In Main Menu window click on Internet in left hand navigation under "Menu" and then click on Skype" in right hand under "Items".
    4. +
    5. Now click on "Properties" button.
    6. +
    7. It will open a Launcher Properties dialogue box.
    8. +
    9. In third field "Command" replace "Skype" with this line:
      +bash -c 'LD_PRELOAD=/usr/lib/libv4l/v4l1compat.so skype' +
    10. +
    +

    That is it. You should now have a properly working motion-eye with skype. +There is more info on this link: +https://help.ubuntu.com/community/Webcam +Ciao !!!

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sony-vaio-n-vidia-setup-to-make-s-video-work/index.html b/sony-vaio-n-vidia-setup-to-make-s-video-work/index.html new file mode 100644 index 0000000..6ff3b29 --- /dev/null +++ b/sony-vaio-n-vidia-setup-to-make-s-video-work/index.html @@ -0,0 +1,988 @@ + + + + + + +Sony VAIO N-VIDIA setup to make S-Video work - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Sony VAIO N-VIDIA setup to make S-Video work +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +
    + + +
    + +
    + +

    UPDATE for 9.10
    +For Ubuntu 9.10 I was just not able to get it work and finally I figured it out. The problem is with the latest Nvidia driver, the one that ubuntu says recommended. In order to make your TV Out work, you must uninstall the latest release version and install the one just lower than that. (The working version for me was 173.) Then follow these steps: +sudo nvidia-xconfig +sudo reboot +sudo nvidia-settings +Then follow the steps shown below. If you are lucky you wont need to refer Section A of this post at all. I did not have to do that !!! +Cheers,
    +Ankit
    +UPDATE for 9.10

    +
    + +

    S-Video did not work out of the box on ubuntu Sony Vaio combination but it was not difficult to have it sorted once the following steps were taken. +I am giving the steps to set-up TV-Out assuming your n-vidia file is working fine, if not follow steps in Section A before completing steps below.

    +
      +
    • Step 1: Connect laptop and TV through S-Video Cable.
    • +
    • Step 2. System->Administration->Nvidia X-Server Settings
    • +
    • Step 3: Click on X-Server Display Configuration and then click on "Detect Displays". If you have connected fine, the screen should be something like this:
    • +
    +

    http://4.bp.blogspot.com/_-KsGlJDRxDw/SrFWWRqfjhI/AAAAAAAAAHY/Ze7d7J7dKT8/s400/Screenshot-NVIDIA+X+Server+Settings-1.png

    +
      +
    • Step 4: Click on the TV icon, following screen will appear.
    • +
    +

    http://2.bp.blogspot.com/_-KsGlJDRxDw/SrFY6ndgpeI/AAAAAAAAAHg/Tkskm1BYebM/s400/Screenshot-NVIDIA+X+Server+Settings-2.png

    +
      +
    • Step 5: Click on "Configure" and then select "Twinview"
    • +
    +

    http://1.bp.blogspot.com/_-KsGlJDRxDw/SrFY7MnhQzI/AAAAAAAAAHo/hw7mVXTa4PI/s400/Screenshot-NVIDIA+X+Server+Settings-3.png

    +
      +
    • Step 6: Now I found that keeping TV on left ensured that while using dailymotion full screen it will come on TV but keeping on right it came on laptop monitor so you may want to depending on your preference change the location to left as shown below.
    • +
    +

    http://4.bp.blogspot.com/_-KsGlJDRxDw/SrFY7bt7sCI/AAAAAAAAAHw/pGeyp1_GfeU/s400/Screenshot-NVIDIA+X+Server+Settings-4.png +This is it.You should now be able to watch streaming video on your TV…:)

    +

    Section A:

    +

    I found the most useful instructions below, but I did have to make few tweaks. +Source: http://ubuntuforums.org/showthread.php?t=98456 +Right, I will walk step by step based on the guide from the source mentioned above and mention the changes / tweaks I did to make it work on my laptop. I am sure it should work with same conf on other VAIO as well.

    +
      +
    • +Step 1: Aplication-> Accessories-> Terminal +
    • +
    • +Step 2: Type sudo cp /etc/X11/xorg.conf /etc/X11/xorg.conf.backup +
    • +
    • +Step 3: You will need to enter password at this stage. Enter the password. +
    • +
    • +Step 4: Now Type: sudo gedit /etc/X11/xorg.conf +
    • +
    +

    Difference in old and new xorg.conf files:
    +I used the following command on terminal to compare the old and new .conf files: +diff -u /etc/X11/xorg.conf.backup /etc/X11/xorg.conf +The numbers between @@ represent Row number and column number. - stands for old and + for new.

    +
    +@@ -61,9 +61,9 @@
    +Section "Monitor"
    +Identifier "Monitor0"
    +VendorName "Unknown"
    +- ModelName "TV-0"
    +- HorizSync 28.0 - 55.0
    +- VertRefresh 43.0 - 72.0
    ++ ModelName "Nvidia Default Flat Panel"
    ++ HorizSync 29.0 - 49.0
    ++ VertRefresh 0.0 - 60.0
    +EndSection
    +
    +Section "Monitor"
    +@@ -110,12 +110,14 @@
    +
    +# Removed Option "TwinView" "0"
    +# Removed Option "metamodes" "DFP: nvidia-auto-select +0+0"
    ++# Removed Option "TwinView" "1"
    ++# Removed Option "metamodes" "TV: nvidia-auto-select +0+0, DFP: nvidia-auto-select +1024+0"
    +Identifier "Screen0"
    +Device "Device0"
    +Monitor "Monitor0"
    +DefaultDepth 24
    +- Option "TwinView" "1"
    +- Option "metamodes" "TV: nvidia-auto-select +0+0, DFP: nvidia-auto-select +1024+0"
    ++ Option "TwinView" "0"
    ++ Option "metamodes" "DFP: nvidia-auto-select +0+0"
    +SubSection "Display"
    +Depth 24
    +EndSubSection
    +
    +
    +

    Copy of my xorg.conf and xorg.conf.backup +xorg.conf - CURRENT - WORKING VERSION

    +
    +Section "ServerLayout"
    +Identifier "Default Layout"
    +Screen 0 "Screen0" 0 0
    +InputDevice "Keyboard0" "CoreKeyboard"
    +InputDevice "Mouse0" "CorePointer"
    +EndSection
    +
    +Section "Module"
    +Load "glx"
    +EndSection
    +
    +Section "ServerFlags"
    +Option "Xinerama" "0"
    +EndSection
    +
    +Section "InputDevice"
    +
    +# generated from default
    +Identifier "Keyboard0"
    +Driver "kbd"
    +EndSection
    +
    +Section "InputDevice"
    +
    +# generated from default
    +Identifier "Mouse0"
    +Driver "mouse"
    +Option "Protocol" "auto"
    +Option "Device" "/dev/psaux"
    +Option "Emulate3Buttons" "no"
    +Option "ZAxisMapping" "4 5"
    +EndSection
    +
    +Section "Monitor"
    +Identifier "Configured Monitor"
    +EndSection
    +
    +Section "Monitor"
    +Identifier "Monitor0"
    +VendorName "Unknown"
    +ModelName "Nvidia Default Flat Panel"
    +HorizSync 29.0 - 49.0
    +VertRefresh 0.0 - 60.0
    +EndSection
    +
    +Section "Monitor"
    +Identifier "Monitor1"
    +VendorName "Unknown"
    +ModelName "TV-0"
    +HorizSync 28.0 - 55.0
    +VertRefresh 43.0 - 72.0
    +EndSection
    +
    +Section "Device"
    +Identifier "Configured Video Device"
    +Driver "nvidia"
    +Option "NoLogo" "True"
    +EndSection
    +
    +Section "Device"
    +Identifier "Device0"
    +Driver "nvidia"
    +VendorName "NVIDIA Corporation"
    +BoardName "GeForce Go 7400"
    +EndSection
    +
    +Section "Device"
    +Identifier "Device1"
    +Driver "nvidia"
    +VendorName "NVIDIA Corporation"
    +BoardName "GeForce Go 7400"
    +Option "TVOutFormat" "SVIDEO"
    +Option "TVStandard" "PAL-G"
    +Option "ConnectedMonitor" "Monitor[1]"
    +BusID "PCI:1:0:0"
    +Screen 1
    +EndSection
    +
    +Section "Screen"
    +Identifier "Default Screen"
    +Device "Configured Video Device"
    +Monitor "Configured Monitor"
    +DefaultDepth 24
    +EndSection
    +
    +Section "Screen"
    +
    +# Removed Option "TwinView" "0"
    +# Removed Option "metamodes" "DFP: nvidia-auto-select +0+0"
    +# Removed Option "TwinView" "1"
    +# Removed Option "metamodes" "TV: nvidia-auto-select +0+0, DFP: nvidia-auto-select +1024+0"
    +Identifier "Screen0"
    +Device "Device0"
    +Monitor "Monitor0"
    +DefaultDepth 24
    +Option "TwinView" "0"
    +Option "metamodes" "DFP: nvidia-auto-select +0+0"
    +SubSection "Display"
    +Depth 24
    +EndSubSection
    +EndSection
    +
    +Section "Screen"
    +Identifier "Screen1"
    +Device "Device1"
    +Monitor "Monitor1"
    +DefaultDepth 24
    +Option "TwinView" "0"
    +Option "metamodes" "TV: nvidia-auto-select +0+0"
    +SubSection "Display"
    +Depth 24
    +EndSubSection
    +EndSection
    +
    +

    xorg.conf.backup - OLD COPY

    +
    +Section "ServerLayout"
    +Identifier "Default Layout"
    +Screen 0 "Screen0" 0 0
    +InputDevice "Keyboard0" "CoreKeyboard"
    +InputDevice "Mouse0" "CorePointer"
    +EndSection
    +
    +Section "Module"
    +Load "glx"
    +EndSection
    +
    +Section "ServerFlags"
    +Option "Xinerama" "0"
    +EndSection
    +
    +Section "InputDevice"
    +
    +# generated from default
    +Identifier "Keyboard0"
    +Driver "kbd"
    +EndSection
    +
    +Section "InputDevice"
    +
    +# generated from default
    +Identifier "Mouse0"
    +Driver "mouse"
    +Option "Protocol" "auto"
    +Option "Device" "/dev/psaux"
    +Option "Emulate3Buttons" "no"
    +Option "ZAxisMapping" "4 5"
    +EndSection
    +
    +Section "Monitor"
    +Identifier "Configured Monitor"
    +EndSection
    +
    +Section "Monitor"
    +Identifier "Monitor0"
    +VendorName "Unknown"
    +ModelName "TV-0"
    +HorizSync 28.0 - 55.0
    +VertRefresh 43.0 - 72.0
    +EndSection
    +
    +Section "Monitor"
    +Identifier "Monitor1"
    +VendorName "Unknown"
    +ModelName "TV-0"
    +HorizSync 28.0 - 55.0
    +VertRefresh 43.0 - 72.0
    +EndSection
    +
    +Section "Device"
    +Identifier "Configured Video Device"
    +Driver "nvidia"
    +Option "NoLogo" "True"
    +EndSection
    +
    +Section "Device"
    +Identifier "Device0"
    +Driver "nvidia"
    +VendorName "NVIDIA Corporation"
    +BoardName "GeForce Go 7400"
    +EndSection
    +
    +Section "Device"
    +Identifier "Device1"
    +Driver "nvidia"
    +VendorName "NVIDIA Corporation"
    +BoardName "GeForce Go 7400"
    +Option "TVOutFormat" "SVIDEO"
    +Option "TVStandard" "PAL-G"
    +Option "ConnectedMonitor" "Monitor[1]"
    +BusID "PCI:1:0:0"
    +Screen 1
    +EndSection
    +
    +Section "Screen"
    +Identifier "Default Screen"
    +Device "Configured Video Device"
    +Monitor "Configured Monitor"
    +DefaultDepth 24
    +EndSection
    +
    +Section "Screen"
    +
    +# Removed Option "TwinView" "0"
    +# Removed Option "metamodes" "DFP: nvidia-auto-select +0+0"
    +Identifier "Screen0"
    +Device "Device0"
    +Monitor "Monitor0"
    +DefaultDepth 24
    +Option "TwinView" "1"
    +Option "metamodes" "TV: nvidia-auto-select +0+0, DFP: nvidia-auto-select +1024+0"
    +SubSection "Display"
    +Depth 24
    +EndSubSection
    +EndSection
    +
    +Section "Screen"
    +Identifier "Screen1"
    +Device "Device1"
    +Monitor "Monitor1"
    +DefaultDepth 24
    +Option "TwinView" "0"
    +Option "metamodes" "TV: nvidia-auto-select +0+0"
    +SubSection "Display"
    +Depth 24
    +EndSubSection
    +EndSection
    +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/stagevu-divx-video-on-bodhi-linux/index.html b/stagevu-divx-video-on-bodhi-linux/index.html new file mode 100644 index 0000000..6bfa45c --- /dev/null +++ b/stagevu-divx-video-on-bodhi-linux/index.html @@ -0,0 +1,701 @@ + + + + + + +StageVu / DivX video on Bodhi Linux - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    StageVu / DivX video on Bodhi Linux +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    If you read my last post you will know I have been playing with Bodhi Linux lately. One of the selling point for this distro is it’s minimalistic approach. However, that also means it doesn’t come with some of the media packages that help stream all type of media - stagevu (DivX) included. +Fortunately it does not require lot of effort to get them all working. At-least for me it was quite quick. +The steps I followed are listed below:

    +
      +
    • Install non-free codecs:
    • +
    +

    http://appcenter.bodhilinux.com/software/showDesc/Non-Free_Codecs

    +
      +
    • Install dvd codecs:
    • +
    +

    http://appcenter.bodhilinux.com/software/showDesc/Dvd_Playback

    +
      +
    • Install VLC Player:
    • +
    +

    http://appcenter.bodhilinux.com/software/showDesc/VLC

    +
      +
    • Install SMplayer:
    • +
    +

    http://appcenter.bodhilinux.com/software/showDesc/SMPlayer

    +
      +
    • Install totem, totem-mozilla, mozilla-plugin-vlc, mencoder using Synaptic Package Manager:
    • +
    +

    You can find Synaptic Package Manager under Applications or simply by typing the following command in terminal: +sudo synaptic

    +
      +
    • Install divx4linux package:
    • +
    +

    This can be downloaded from following link: +http://www.mediafire.com/?71fs4c9cy8xy4q4 +Double click on the downloaded file and it will launch the installer. +Once installed, restart the system and then open media sites in firefox and check that the videos are working. +For stagevu though divx4linux should work out of the box on firefox but if not click on "Tools" > "Manage Content Plugin" and select divx to play the plugin. Restart browser and that should be it. +Let me know your experiences and tips in comments.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/staticman.yml b/staticman.yml new file mode 100644 index 0000000..61b9592 --- /dev/null +++ b/staticman.yml @@ -0,0 +1,104 @@ +# Name of the property. You can have multiple properties with completely +# different config blocks for different sections of your site. +# For example, you can have one property to handle comment submission and +# another one to handle posts. +# To encrypt strings use the following endpoint: +# https://{your Staticman API URL}/v[2|3]/encrypt/{TEXT TO BE ENCRYPTED} + +comments: + # (*) REQUIRED + # + # Names of the fields the form is allowed to submit. If a field that is + # not here is part of the request, an error will be thrown. + allowedFields: ["name", "email", "url", "message"] + + # (*) REQUIRED WHEN USING NOTIFICATIONS + # + # When allowedOrigins is defined, only requests sent from one of the domains + # listed will be accepted. The origin is sent as part as the `options` object + # (e.g. + + + + +Swap File to create extra memory - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Swap File to create extra memory +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    While renewing my LetsEncrypt certificate, I found myself in a strange situation where the certbot won’t run asking me to update pip and then each time I tried updating pip it failed with the error error: command 'x86_64-linux-gnu-gcc' failed with exit status 4.

    + +

    It turns out that this happens due to low memory and with my digitalocean droplet being the cheapest one this was bound to happen sooner rather than later. Fortunately there is a way around it as explained below.

    + +

    Use of following commands will ensure that the swap file is created which in turn will help avoid the "error: command ‘x86_64-linux-gnu-gcc’ failed with exit status 4".

    + +

    Following commands will create a swap file:

    + +
    sudo dd if=/dev/zero of=/swapfile1 bs=1024 count=524288
    +sudo mkswap /swapfile1
    +sudo chown root:root /swapfile1
    +sudo chmod 0600 /swapfile1
    +sudo swapon /swapfile1
    +
    +

    The swap file will now be activated but will be gone after the reboot. It can be reactivated using the last command (I hope so know as I have now tried it).

    + +

    Anyway, after creating the swapfile, you will be able to upgrade pip without the aforementioned error. :)

    + +

    Update: 02/03/2017 +I ran into memory issues yet again and I thought instead of increasing the memory for swapfile1, what if I can create another swapfile. I tried this and it works. Infact I felt quite nice uncovering a concept of multiple swapfiles purely based on my whim ;). All I really had to do was repeat above code replacing swapfile1 with swapfile2 and I had two swapfiles working together increasing available memory for my server.

    + +
    sudo dd if=/dev/zero of=/swapfile2 bs=1024 count=524288
    +sudo mkswap /swapfile2
    +sudo chown root:root /swapfile2
    +sudo chmod 0600 /swapfile2
    +sudo swapon /swapfile2
    +
    + +

    Thing is after it worked I was a bit intrigued by the concept and read a bit more on swapon / swapoff and few useful commands are listed below:

    +
    # To enable all swapfiles 
    +swapon -a
    +# To disable all swapfiles
    +swapoff -a
    +# To see all available swapfiles 
    +swapon -s
    +# To enable a particular swapfile
    +swapon &lt;filename&gt;
    +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tech-for-diabetics/index.html b/tech-for-diabetics/index.html new file mode 100644 index 0000000..c952b33 --- /dev/null +++ b/tech-for-diabetics/index.html @@ -0,0 +1,984 @@ + + + + + + +Tech for Diabetics - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Tech for Diabetics +

    + + +

    + + + + + + + + + + + + 9 minute read + + + +

    + + +
    + + +
    + + + +

    Background

    + +

    As a diabetic there are a number of things we are not able to control +but one thing we can do is keep tabs on our data and while in past it +would have meant meticulously noting down BG readings in a diary, today +it is much much easier with all the apps and connected services. In this +article, I aim to go through the list of technologies that I am aware of +and use to keep on top of my diabetic data right from my phone.

    + +

    Blood Glucose Meter

    + +

    While there are a number of options available, I settled for CareSens +Dual. The +biggest advantage with this model is that it plays very nicely with +xdrip+ app although lately I am relying more on Diabox so the point is +kind of moot. Apart from that, one factor we must bear in mind while +chosing the device is not just the cost of the device itself but also +that of testing strips as it will be a recurring expense and this device +has one of the most reasonably priced testing strips.

    + +

    Freestyle Libre 2 (FSL2)

    + +

    FSL2 became available in the UK only from Jan’21 and this or it’s +previous version allow for a more regular check of the BG trends. FSL1 +in my experience was a bit less accurate than FSL2. However these +sensors along with the official app from manufacturer require manual +scan, don’t give all datapoints and can not be calibrated which is again +where xdrip+ / Diabox apps come into the picture.

    + +
    +

    Freestyle Libre 1 (FSL1) with Transmitter (Miaomiao / Bubble)

    + +

    Before Jan’21, in the UK we only had the option of FSL1 and as this version does not have bluetooth communication; the possibility to +calibrate and get automated data points required usage of a transmitter device such as Miaomiao or Bubble. I ordered a Miaomiao +transmitter back then and I still can use it with FSL2 albiet with slightly modified approach. I do it because I have the device but with +FSL2 a transmitter is not needed as explained later.

    +
    + +

    Freestyle Libre App

    + +

    Now the fact is I dont really use this app for anything other than +activating the sensor but it is for this reason alone an app you can not +avoid or do without. In order to use the sensors to feed data directly +and automatically to Diabox the sensor at the time of activation should +not be paired through bluetooth to this app. Now there are several ways +to achieve this. One is to install the app on a completely separate +phone and turn off Bluetooth on that phone. Another is just do not give +this app the “Location” permission which would then not allow app access +to Bluetooth. Either scenario should work in theory but I use the safest +approach of activating sensor through this app on a compltely different +phone (that of my daughter actually :))

    + +

    +Diabox (Especially with FSL2)

    + +

    I have been using this app only for 10 days at the time of writing but +it works really well with FSL2 and as it gets the data directly from the +sensor it removes the additional cost of the transmitter and that makes +this a very useful app indeed. The app is available for both iOS as well +as Android but from what I gathered at some point Abbot Laboratories +(Manufacturers of FSL sensor) complained about the app to Google and so +google was obligated to takedown this app from playstore. App therefore +is available as an apk file on github and the latest release for it can +be downloaded from this GitHub +Link The developer +has a Diabox Dev +website +with lot more information on user interface and setup etc. but I will +explain my setup below.

    + +
    + +
      +
    1. Download the latest version of the app from GitHub +Link +
    2. +
    3. Install the app - As it is not from playstore, the permission to +install from unknown sources1 should be +activated in Android Settings.
    4. +
    5. Once installed, open the app. It will ask to scan the sensor. Once +you scan, the phone will automatically pair the sensor with the +Bluetooth of the Libre.
    6. +
    7. Now to enable calibration tap on the sensor image as shown in +screenshot below: +adfd44c4c0cf4e91aeeb8792970dbf67 +
    8. +
    9. Now tap 5 times on the text “Factory” in the last entry “Calibration +Mode” as shown below: +00aec12e25c74fed8e124e788850090e +
    10. +
    11. This will present a dialogbox “Sensor Magic +Code”. Type “GODMODE” in the +dialogbox field and press “OK”. This should activate the calibration +mode and screen should look as shown below: +4aa44c106c724c16b27129ccc3acfb3a +
    12. +
    13. Go back to homepage and you will now see a red coloured circular +button with “+” symbol on it as you can see on the first screenshot +above.
    14. +
    15. Tapping on the add button will reveal an icon with a glucometer as +shown below: +d417b24684524a05985e737b656b55a9 +
    16. +
    17. Tapping on the glucometer icon will open a sliding scale to provide +the calibration value. +8f671fb4efa542d08bdf60c1ead2756d +
    18. +
    19. Add your calibration and click on Save button.
    20. +
    + +
    + +

    Now, the app also allows uploading the data it collects to LibreView and +Nighscout. I am not so keen on LibreView web interface so I never tried +uploading on that service but have it syncing with Nightscout. For +Nightscout sync, you will need to have a Nightscout instance up and +running2 but assuming it is in place, the steps +will be simple to follow:

    + +
      +
    1. Click on “Settings”
    2. +
    3. Click on “Integration”
    4. +
    5. Enable “Nightscout Share Server Upload”
    6. +
    7. Enter the URL for Nightscout instance, something like +https://your_nightscout.herokuapp.com/api/v1/ +
    8. +
    9. Enter the password for your instance.
    10. +
    11. Click on Connect Test +
    12. +
    13. If the test is a success, click on Save button.
    14. +
    + +

    Nightscout

    + +

    Nightscout is a fairly detailed open source solution with loads and +loads of functionalities but as a type 2 user I merely use it as a +backup of my data.

    + +
    +

    One key thing to note is that once you setup this server your BG data +is available to anyone with access to your URL as there is no password +block or similar which in my eyes is no big deal. What can anyone do +with just my blood glucose data and how or why will they get access to +an obscure URL anyway but if this makes you worried perhaps you can +skip this part. If this is not of concern then I must say that while +the setup guide2 looks very long and complex, +it really isn’t. They have taken care to make it so very simple that +its infact easier than even completing the registration form for some +of the online services we have got accustomed to and it will just +work.

    +
    + +

    To set it up you can follow the official set-up and installation +guide.

    + +

    Diabetes:M

    + +

    Diabetes-M is a very good app and creates some very useful and pretty +reports that can be helpful during discussion with Diabetic Nurse / GP. +One big advantage of using this app is that it is able to import data +from Nightscout directly and keep it in sync every 15 minutes and then +that data is also backed-up in Diabetes-M servers. The app also allows +for automatic data sync with a number of popular Glucometers but that is +a paid feature and I do not use it so I cannot comment on its +reliability. For my usecase I anyway calibrate Diabox each time manually +when I take the reading from finger prick and that is synched to +Nightscout which is synched to Diabetes-M so the data is anyway there +when I need it. Steps to sync with Nightscout are as below:

    + +
      +
    1. Open your nightscout instance in a browser on a laptop / PC .
    2. +
    3. Click on the site in sequence as shown below:
      +2707fa8797d94ed08f1d6c44fc04ef85 +
    4. +
    5. On resulting screen - Administration panel, check if the role +“readable” exists.
      +cc9cdbfe94474be4b87515de72d9f98c +
    6. +
    7. If not create it by clicking on the button Add new Role and in +“Permissions” type *.*.read as shown below.
      +053fbb025cc74ebc880f729d3dc3b6b2 +
    8. +
    9. If it exists, go to the section “Subjects - People, Devices, etc” +and click on “Add new Subject” and fill the resulting dialogbox like +shown below and click Save:
      +64ed999845d644239615c3b0e2aab454 +
    10. +
    11. This will generate the Access Token for the app and it will be shown +in the table in the section:
      +8c623ef74cdc41d8a6ba5fd05e30b32d +
    12. +
    13. Open the Diabetes:M app and click on the hamburger menu on top left +corner of the screen. Then on the panel locate and tap on the Data +Sync icon.
      +5229542f7a364b07bbed0356ec8f3565 +
    14. +
    15. On resulting screen under section “Link external sources” locate +Nightscout and click on setting icon.
      +4c718c71ade84cb7a2c9eeb3596e32c1 +
    16. +
    17. Now fill the URL for your nightscout instance, something like +https://your_nightscout.herokuapp.com +
    18. +
    19. Leave “Secret” empty and in in the field “Access token” type the +access token shown generated in step 6 above from the browser and +click Save.
    20. +
    + +

    Direct links to appstore:

    + +
      +
    1. Google +Playstore
    2. +
    3. Apple Appstore
    4. +
    + +

    XDRIP+

    + +

    xdrip+ is an opensource Android app (an iOS variant exists but I havent +read much about it so have no knowledge to share) and as it is not +available from playstore it can be obtained directly from developer +site: https://jamorham.github.io/#xdrip-plus To use xdrip, you will +also need to order a transmitter called miaomiao - It usually comes +within 2 to 3 days - for me atleast it came in 2 days back in October +when I started using FSL1. It is a bit of investment at about £162 but +it pays for itself in long run. I ordered from their +site3. Once you get the transmitter, the process +will be like so:

    + +
      +
    1. It will come with a USB charging cable so place it on charger. It +should start blinking blue but if it does not there is a very small +hole and you will need a safety pin to press and reset it. Insert +the safety pin and keep it pressed for 10 seconds. Then place it on +charge.
    2. +
    3. Once the transmitter is charged it will display green light.
    4. +
    5. Now there is also a strap they provide with the transmitter which +you can use to place it over the libre sensor on your arm.
    6. +
    7. Open xdrip app and follow the xdrip+ installation +guide4.
    8. +
    + +

    Once connected, it will ask you to calibrate the app with readings from +two finger prick tests. You can do one and provide twice or you can do +twice. I usually do it twice for each new sensor.

    + +

    Tidepool

    + +

    This is a not-for-profit site which xdrip+ can directly communicate with +and push the readings to every 15 minutes thus creating a backup for +your data in the cloud. As I still use the miaomiao transmitter paired +with xdrip+ I have created a sync from xdrip+ to tidepool. Now the good +thing with tidepool is that it shows glucometer readings in a separate +table and while for Diabox I have to manually enter the finger prick +test data, Caresens Dual is paired perfectly with xdrip and every single +time I take a finger prick test, xdrip+ automatically fetches the +reading from meter via Bluetooth and then that reading is fed into +Tidepool which allows me to see my BG data at one place like so: +tidepool

    + +

    Eufy Smartscales

    + +

    Eufy smartscale is very reasonably priced and I ordered it from Amazon. +The scale is reliable, uses standard 3x “AAA” battery which last for +years not months. The app interface is good too. One drawback perhaps is +that data is only accessible through app on the phone and not through +any website. It does sync weight with google fit so that can be accessed +and shared across other services if one is so inclined but rest of it +will need to be manually captured for any statistical fun. However, if I +knew before what I know now I would have perhaps opted for a device from +this +list +as then I could have had my data collected through the open source app +openScale +and would have had better freedom on how to move it around. Hope this +list is useful. If there are any questions, please do not hesitate to +ask. :)

    + +
    + + + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/the-complete-walkthrough-of-my-blogger-to-ghost-migration/index.html b/the-complete-walkthrough-of-my-blogger-to-ghost-migration/index.html new file mode 100644 index 0000000..34fa5a2 --- /dev/null +++ b/the-complete-walkthrough-of-my-blogger-to-ghost-migration/index.html @@ -0,0 +1,902 @@ + + + + + + +The complete walkthrough of my blogger to ghost migration - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    The complete walkthrough of my blogger to ghost migration +

    + + +

    + + + + + + + + + + + + 9 minute read + + + +

    + + +
    + + +
    + +

    The 7 Year Itch

    +

    It can’t possibly be a coincidence that this is the 7th year since I started blogging on blogger and therefore it is very likely to be a strong case of the 7 year itch syndrome but whichever way you look at it, divorce was inevitable given blogger had just stopped inspiring me. +I have been fiddling with different blogging platforms while getting accused of neglecting my sweet and loving family…😢. Ghost caught my fancy three weeks back. The last post was the beginning of our courtship and this post tells the tale of how a casual fling turned into marital commitment. 😂

    + +

    To start a fresh blog, choosing any platform is easy and straight forward but to move from one platform to another is - umm… lets just say a very involved process - rewarding but involved.

    + +

    Love can move mountains!!!

    +

    A complete migration from blogger to WordPress would have been way simpler. I know this as I have done it in past and it appeared like moving to Ghost would require migrating to a WordPress instance anyway. There was - I must admit - a temptation to call WordPress the home but that wouldn’t have made a great love-story now - would it? +However, the much publicised WordPress route to Ghost migration did not work for me and eventually after a lot of manual copying, pasting, cleaning, pruning, hiding, reading and learning later, the self-hosted blog is all complete. My first baby with my new found love - Ghost - all ready and set to meet the world at large !!! 😃

    + +

    So story below will take you through ups and downs of a love affair that I hope will last for a life-time (or for a very long foreseeable future at the very least…😍).

    + +
      +
    1. +Install Ghost +
    2. +
    3. +Install a theme +
    4. +
    5. +Configure the theme +
    6. +
    7. +Enable commenting on the blog with DISQUS +
    8. +
    9. +Migrate content from blogger +
    10. +
    11. +Redirect Traffic from old blog +
    12. +
    13. +Migrate comments from old blog +
    14. +
    15. +Enable Search for your blog +
    16. +
    17. +Enable Social Links +
    18. +
    + +

    Install Ghost

    +

    This is covered in my last post. Once it was installed, I took some time exploring and learning Markdown. +Last post was my first one using Markdown and it was a very pleasing experience indeed. That nice experience paved way as well as helped me finalise the decision to go the whole nine yards.

    + +

    Install a theme

    +

    There are some very beautiful themes available on Ghost Marketplace. I have used the theme called scrawl and then tweaked it a bit. +Once I found the theme I liked, I downloaded the zip file, unzipped it and deleted the zip file like so.

    + +
    #Download
    +curl -L https://github.com/ktweeden/scrawl/archive/master.zip -o master.zip
    +#unzip
    +unzip -o master.zip
    +#Delete zip file
    +rm master.zip
    +#restart Ghost
    +pm2 restart Ghost
    +
    + +

    Theme is now installed.

    + +

    Configure the theme

    + +
      +
    1. The first thing I wanted to configure on my new theme was the code block. Prismjs is the way to go and it is already included in the theme I downloaded but the line numbering was not there. After reading a bit on PrismJS website, I understood that core css file from prism did not have this and also I wanted the dark theme so I downloaded the "okadia" theme css along with line number plugin.
    2. +
    +

    I then replaced the content of /var/www/html/site-name/content/themes/scrawl-master/assets/css/prism.css with the content in downloaded CSS.

    +
      +
    1. Next thing I wanted to change was the header background colour and also the link colour. While it was very close to what I wanted, my actual liking is for the colour #F2C20F and a bit darker link colour #B710EF. To do this I edited the _global_styles.scss like so:
    2. +
    + +
    #change directory
    +cd /var/www/html/site-name/content/themes/scrawl-master/sass/partials 
    +/#Edit _global_styles.scss
    +nano _global_styles.scss 
    +
    + +

    Now change the colour of $primary-colour and also add $link-colour. After this your /* Colour */ section will look as shown below:

    +
    /* Colours */
    +$primary-colour: #F2C20F;
    +$secondary-colour: #254E70;
    +$tertiary-colour: #FF4B3E;
    +$font-colour: #011627;
    +$background-colour: #EFEFEF;
    +$link-colour: #B710EF;
    +
    + +

    Then I changed directory using cd /var/www/html/site-name/content/themes/scrawl-master/assets/css and opened index.css and post.css where I changed the background-color property to #F2C20F as shown below:

    + +

    nano index.css

    + +
    /**
    +  MIXINS
    +**/
    +.blog-title-background {
    +  background-color: #F2C20F;
    +  width: 100%;
    +  display: -webkit-box;
    +  display: -ms-flexbox;
    +  display: flex;
    +  -webkit-box-orient: vertical;
    +  -webkit-box-direction: normal;
    +      -ms-flex-direction: column;
    +          flex-direction: column; }
    +
    + +

    nano post.css

    + +
    /**line 86/434 (19%), col 22/29**/
    +.blog-title-background {
    +  background-color: #F2C20F;
    +  width: 100%; }
    +
    + +

    Enable commenting on the blog with DISQUS

    + +

    Using DISQUS to enable comments is extremely simple especially on scrawl - the theme I have used and is as explained under the DISQUS section of the theme website:

    + +

    To enable commenting:

    + +
      +
    • I created an account on Disqus.
    • +
    • Then I created a "channel" for this blog in DISQUS using steps below:
    • +
    • Open DISQUS and click on "Get Started" to sign-up
    • +
    • Once the sign-up was completed, email verified etc, I went to channels link on home page of disqus and created a channel say thetestchannel +
    • +
    • Copied the following code into the blog header code injection:
    • +
    + +
    <script>window.__themeCfg.disqusUsername = 'thetestchannel';</script>
    +
    +

    thetestchannel in code above must be replaced with the name of the channel created on DISQUS.

    + +

    Migrate content from blogger

    + +

    Now, this was the most painful of all the things I had to do - the moving of mountain if you like - because the automated solution using Blogger2Ghost just wouldn’t work for me. So I basically copy pasted most of the content from blogger over to Ghost and placed the various screenshots manually.

    + +

    If my posts were in 3 digits, I would have persevered and tried contacting someone for help but as it was relatively less content, I just went ahead and did it manually and am glad I did so because that way I was able to do a bit of clean-up too.

    + +

    Redirect Traffic from old blog

    + +

    There are a huge number of posts on www for 301 redirect and what not but I felt, it is only fair to let the redirects land on old blog and users be told there of the new destination so like last step, I manually updated the posts on blogger to just let the reader know that the post they are looking for has moved to new location and link for that post on new site. +Not most elegant and efficient approach but I am happy that way.

    + +

    If there are huge entries and one still has to go the manual route, it will save a lot of pain if the permanent link for new posts on Ghost is same as that on old blog and this can be achieved by clicking on clog next to “Save Draft” on the post and changing the “Post URL”

    + +

    Migrate comments from old blog

    + +

    Migrating comments from blogger to DISQUS is very easy.

    + +
      +
    • I opened the blogger importing tool which can be accessed using a URL similar to this - `https://thetestchannel.disqus.com/admin/discussions/import/platform/blogger/`
    • +
    + +

    However, getting them reflected on new ghost instance has three approaches dependent on the route one takes for redirecting from old blog. As I chose no redirection as such, I had to go for a CSV mapping file between my old and new blog URL per post.

    + +

    Once again, for the number of posts I have this was not a challenge at all but I can’t imagine doing such a thing for a big content transfer and it may be worth paying attention to the note in step above if one has huge content to transfer as it will reduce the pain for comments transfer.

    + +

    Enable Search for your blog

    + +

    Ghost recommends Swifttype and the popular site Ghost for beginners provides guidance for Google CSE (Custom Search Engine). I did not like the idea of paying for Swifttype for my small site and Google Search Engine was taking it’s sweet time to crawl my site - unlike in past now we cant order / request a crawl and there was ofcourse Google Custom Search watermark which I am not all that fond of.

    + +

    I updated the theme I am using to include search feature.

    + + +

    This is, much like many other customisations I did, a very theme specific step and for the theme I have chosen it is fortunately very easy to achieve except for linkedin and google+ for which I had to add few lines of code. I achieved this using steps below:

    + +
      +
    • Adding already available social icons is very simple and most of the social links are actually available out of the box. So for Tumblr and Instagram all I had to do was open the admin panel of Ghost, click on Code Injection link and paste the following two lines replacing yourusername with my username on that platform.
    • +
    + +
    <script>window.__themeCfg.tumblrUsername = 'yourusername';</script>
    +<script>window.__themeCfg.instagramUsername = 'yourusername';</script>
    +
    + +
      +
    • For LinkedIn and Google+:
      +Open the footer.hbs using nano /var/www/html/site-name/content/themes/scrawl-master/partials/footer.hbs and pasted the following code under div class footer-container around line 14.
    • +
    + +
    <a class="social-button linkedin hidden"><i class="icon-linkedin"></i></a>
    +<a class="social-button gplus hidden"><i class="icon-gplus"></i></a>
    +
    +

    Then under scripts I added following code around line number 42

    + +
    <ul>
    +<li>
    +case "Linkedin":
    +return "https://uk.linkedin.com/in/" + username;
    +</li>
    +</ul>
    +<ul>
    +<li>
    +case "Gplus":<br>
    +return `<a href="https://plus.google.com/">https://plus.google.com/</a>` + username;<br>
    +</li>
    +</ul>
    +
    +

    Finally under the function revealSocialLinks around line number 69, I pasted the following code:

    + +
    revealPlatform('Linkedin');
    +revealPlatform('Gplus');
    +
    + +

    Finally I added the following in the header part of the code injection in front end admin panel of Ghost instance; replacing yourusername with my username for the relevant platform.

    + +
    <script>window.__themeCfg.linkedinUsername = 'yourusername';</script>
    +<script>window.__themeCfg.gplusUsername = 'yourusername';</script>
    +
    + +

    My Learning Curve

    +

    While there were many insights and learnings, the top 5 I think on the list of learning for me while migrating from blogger to ghost are:

    + +
      +
    1. +Clarity on using Nginx (refer last post.) - Never before have I played around with nginx but having read so many good things about it, I was keen to try and I can say it is indeed living up to all the praise I have read. It is fast and lightweight. Will I use it for production? Ofcourse I will. Will I favour it over Apache - hmm...Maybe... Maybe not. I think I will weigh my options based on what is it I want to achieve but to know about both nginx as well as Apache Server can't hurt. +
    2. +
    3. +Better Idea of Networking Concepts - With Seafile and Ghost implementation in succession, I actually learnt about quite a few concepts on configuring firewall, DNS, reverse proxy, port forwarding to list a few but more importantly I read a lot about networking and this helped me understand the core concepts involved. +
    4. +
    5. +Deeper understanding of Web Technologies and inner workings of Ghost - While trying to modify my blog's look and feel to my liking, I had to mess around with CSS, HTML. I have used the theme scrawl by ktweeden and modified it a bit. What attracted me most to it was the fact that it used a default colour theme similar to what I had in mind. I modified it but in doing so got a bit more understanding of how it all ties together - the default.hbs, index.hbs, post.hbs and the underlying handlebar scripts. +
    6. +
    7. +Markdown - I had some basic idea before I started using Ghost but if I were to use Ghost full-time, I needed to know all there is to know about markdown and while there isn't a lot - it is damn straight forward and simple - it is a very good tool indeed. I am glad I learnt it. +
    8. +
    9. +CSS plugins - Prismjs comes pre-loaded with scrawl theme but the default skin and plugins were not enough for me so I had to replace these. Now this might be very simple for people in the know. For me it was new and exciting and end result was so cool with numbers in code box and all that. I am obviously very happy with the end result and it did help me learn yet again how things interact within the Ghost platform. +
    10. +
    + +

    The Beginning !!! 😍😃😂

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/the-pain-of-using-windows/index.html b/the-pain-of-using-windows/index.html new file mode 100644 index 0000000..4db20df --- /dev/null +++ b/the-pain-of-using-windows/index.html @@ -0,0 +1,699 @@ + + + + + + +Windows 10 - a bucket load of pain - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Windows 10 - a bucket load of pain +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +
    + + +
    + +
    +

    As Windows 10 is a commercial offering, one would think it will be working as expected and it does so long as like me one has come to expect pain from Microsoft in general and Windows in particular - because pain is what you get when you use Windows 10.

    +
    + +

    When we work in office environment we are not as heavily exposed to the pain that accompanies Windows usage, mainly because everything is configured and dealt with through a central desktop provisioning team and there is usually a dedicated IT Help-desk in a big corporate.

    + +

    However, using Windows at home is not as smooth a sailing (I am really being liberal with usage of word smooth) and if you don’t trust me just try searching for issues that accompany upgrade to windows 10 and look at the range of issues.

    + +

    Now I, ofcourse, don’t blindly believe these search based opinions and have my own take based on my very own personal trauma. The story goes like so:

    + +
    +

    Why - one may ask - do I even bother with windows at home if it is so painful? This is because as much as my daughter loves the penguin, the windows is what her teacher uses and so she does need to be aware of the necessary evil.

    +
    + +

    So, there is this laptop I have which is exclusively for Windows and is only ever used by my daughter for her schoolwork. It isn’t some cheap stuff but a state of the art touch screen detachable monitor kinda laptop from HP with intel core i5 inside.

    + +

    However, as it is a detachable tablet, it has two HDD - 50 GB for monitor and 500 GB on keyboard that acts as extended memory.

    + +

    Now, it originally came with Windows 8 and then came Windows 10 followed by Windows 10 anniversary edition. When I upgraded from Windows 8 to Windows 10, I suddenly found that the whole of 50 gig was fully utilised making it impossible to do anything at all.

    + +

    Now a quick duckduckgo search later it was as simple as removing the "previous installs" which windows itself removes after a month or so they say.

    + +

    In my case the self-removal-a-month-later thing just did not happen and disk clean did not help either so I was basically left with no choice but to reformat the whole damn thing.

    + +

    This as I recollected from my old days with windows is quite a long process with lots of downloads and waits…and boy has that continued to be the same… Microsoft is nothing if not consistent. Finally after several hours of ordeal, the system was refreshed and my storage space reclaimed and a semi-functional laptop was there or thereabouts.

    + +

    I say semi-functional because unlike on Linux not all drivers are there and you have to individually download the drivers from HP website, install and then keep your fingers and toes crossed while you hope it works…finally sound was sorted but WiFi is still flaky.

    + +

    Now compare this to the seamless upgrade from one major version of Fedora 23 to Fedora 24…it did take a while to download and install but the actual effort involved was typing 4 lines on the terminal - that’s it. When I logged into Fedora 24 upgraded machine, everything just worked as before - no driver issues, no WiFi problems, nothing.

    + +

    RANT OVER

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/time-for-a-bit-of-show-off/index.html b/time-for-a-bit-of-show-off/index.html new file mode 100644 index 0000000..a6a7c0e --- /dev/null +++ b/time-for-a-bit-of-show-off/index.html @@ -0,0 +1,719 @@ + + + + + + +Time for a bit of show-off - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Time for a bit of show-off +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    Hey Hey I made an Android app named “Sai Satcharitra”. It’s a very basic app and +all it does is helps people read Sai Satcharitra on their Android Smartphones, +something that can also be achieved by maybe converting the text ino a pdf, epub +file etc.

    + +

    Anyhoo, all I wanted was to experiment with Android coding to see how much of +that developer is still alive in this business project manager. It took me about +5 hours to complete it after understanding the nuances of Android SDK and +refreshing my java coding skills which were quite minimal to start with ;-). I +used to be more of a PL/SQL / Lotus Script / C++ kind of developer in my prime.

    + +

    So well long story short, I paid the amount to be registered as developer so I +can publish this app on android market all the while thinking that if people do +find it useful, I will assume this amount and effort is my way of giving +something as Charity in name of Sai Baba.

    + +

    I published the app on Market on 26/03/2011 and then I was busy with all the +work that spring brings with it. Then today when I checked my account I was +happily surprised to see the stats. The app has been downloaded over 200 times +with about 180 active installs.

    + +

    It felt very good to see that many people are finding my work useful.I was a bit +disappointed that of the 200+ downloads only 2 have given feedback but hey, I +wasn’t there fishing for reviews so as long as it helps someone, I am a happy +dude.

    + +
    + + + + Image 1 + + + + + + Image 2 + + + + + + Image 3 + + + + +
    Check out some screenshots +
    + +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tomcat-on-fedora-behind-nginx/index.html b/tomcat-on-fedora-behind-nginx/index.html new file mode 100644 index 0000000..d29181e --- /dev/null +++ b/tomcat-on-fedora-behind-nginx/index.html @@ -0,0 +1,847 @@ + + + + + + +Tomcat 8.5.4 on Fedora behind Nginx - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Tomcat 8.5.4 on Fedora behind Nginx +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + + + +

    Install Oracle Java

    + +
    #install jdk
    +wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u102-b14/jdk-8u102-linux-i586.rpm"
    +#install jre
    +wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http://www.oracle.com/ oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u102-b14/jre-8u102-linux-i586.rpm"
    +#enable firefox plugin
    +alternatives --install /usr/lib/mozilla/plugins/libjavaplugin.so libjavaplugin.so /usr/java/jdk1.8.0_102/jre/lib/i386/libnpjp2.so 20000
    +
    + +

    URL for JDK and JRE is best obtained directly from oracle website - http://www.oracle.com/technetwork/java/javase/downloads/index.html

    + +

    Download Tomcat

    + +
    su
    +mkdir /opt/tomcat/ && cd /opt/tomcat
    +wget http://mirror.ox.ac.uk/sites/rsync.apache.org/tomcat/tomcat-8/v8.5.4/bin/apache-tomcat-8.5.4.zip
    +wget https://www.apache.org/dist/tomcat/tomcat-8/v8.5.4/bin/apache-tomcat-8.5.4.zip.md5
    +
    + +

    Check MD5

    +
    cat apache-tomcat-8.5.4.zip.md5
    +md5sum apache-tomcat-8.5.4.zip
    +unzip apache-tomcat-8.5.4.zip
    +
    + +

    Create a TOMCAT Group and User then grant access

    + +
    groupadd tomcat
    +useradd -M -s /bin/nologin -g tomcat -d /opt/tomcat tomcat
    +cd /opt/tomcat
    +chgrp -R tomcat conf
    +chmod g+rwx conf
    +chmod g+r conf/*
    +chown -R tomcat bin/ webapps/ work/ temp/ logs/
    +
    + +

    Create Service for Tomcat

    + +
    nano /etc/systemd/system/tomcat.service
    +
    + +
    # Systemd unit file for tomcat
    +[Unit]
    +Description=Apache Tomcat Web Application Container
    +After=syslog.target network.target
    +
    +[Service]
    +Type=forking
    +
    +ExecStart=/opt/tomcat/apache-tomcat-8.5.4/bin/startup.sh
    +ExecStop=/opt/tomcat/apache-tomcat-8.5.4/bin/shutdown.sh
    +
    +User=tomcat
    +Group=tomcat
    +
    +[Install]
    +WantedBy=multi-user.target
    +
    +
    + +
    systemctl start tomcat.service
    +systemctl enable tomcat.service
    +
    + +

    Alternative start and stop

    + +
    cd apache-tomcat-8.5.4/bin
    +chmod 700 /opt/tomcat/apache-tomcat-8.5.4/bin/*.sh
    +ln -s /opt/tomcat/apache-tomcat-8.5.4/bin/startup.sh /usr/bin/tomcatup
    +ln -s /opt/tomcat/apache-tomcat-8.5.4/bin/shutdown.sh /usr/bin/tomcatdown
    +tomcatup
    +tomcatdown
    +
    + +

    Change port

    +
    nano /opt/tomcat/apache-tomcat-8.5.4/conf/server.xml
    +
    + +

    Around line 69 is the connector tag where the port=8080 is specified. For this example lets change it to 8081. After change the connector tag in server.xml will look as below:

    + +
    <Connector port="8081" protocol="HTTP/1.1"
    +               connectionTimeout="20000"
    +               redirectPort="8443" >;
    +
    + +

    Add tomcat Users

    + +

    Open tomcat-users.xml and add new users before tag

    + +
    nano /opt/tomcat/apache-tomcat-8.5.4/conf/tomcat-users.xml
    +
    +

    sample user:

    +
    <role rolename="admin-gui"/><br>
    +<user username="admin" password="some admin password" roles="admin-gui"/><br>
    +<role rolename="manager-gui"/><br>
    +<user username="jhondoe" password="some password" roles="manager-gui"/>
    +
    +

    Test

    +
    touch /opt/tomcat/apache-tomcat-8.5.4/webapps/ROOT/testankit.jsp
    +nano /opt/tomcat/apache-tomcat-8.5.4/webapps/ROOT/testankit.jsp
    +#restart tomcat
    +systemctl restart tomcat.service
    +
    + +

    Open the browser and enter http://localhost:8080 (or whatever port you have configured Tomcat on.)

    + +

    Configure Nginx Reverse Proxy for Tomcat

    + +
      +
    • Configure the dynamic DNS. Steps will be as per my previous post. For purpose of this step I will be assuming you created a DDNS named tomcat.yoursite.com +
    • +
    • +

      Update /etc/hosts to include tomcat.yoursite.com

      + +
      +
      sudo nano /etc/hosts
      +#Make an entry in your hosts
      +127.0.0.1  localhost.localdomain localhost your.seafile.com your.blog.com tomcat.yoursite.com
      +
      +
    • +
    • +

      Now create nginx conf file using sudo nano /etc/nginx/conf.d/tomcat.conf as shown below:

      + +
      +
      upstream tomcat {
      +server 127.0.0.1:8081;
      +}
      +
      +server {
      +     listen      80;
      +     server_name tomcat.yoursite.com;
      +     access_log  /var/log/nginx/tomcat.access.log;
      +     error_log   /var/log/nginx/tomcat.error.log;
      +     proxy_buffers 16 64k;
      +     proxy_buffer_size 128k;
      +
      +        location / {
      +         proxy_pass  http://tomcat;
      +         proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
      +         proxy_redirect off;
      +         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;
      +     }
      + }
      +
      +
    • +
    • +

      Finally reload and restart services

      + +
      +
      sudo systemctl daemon-reload
      +sudo systemctl start nginx.service
      +sudo systemctl start tomcat.service
      +
      +
    • +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/top-10-android-apps-that-i-use/index.html b/top-10-android-apps-that-i-use/index.html new file mode 100644 index 0000000..57e7f18 --- /dev/null +++ b/top-10-android-apps-that-i-use/index.html @@ -0,0 +1,763 @@ + + + + + + +Top 10 Android Apps that I use !!!! - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Top 10 Android Apps that I use !!!! +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +
    + + +
    + +

    I have been using Android Phones and Tablets for quite some time now and have +monitored my use for apps that I use on a daily basis and those I would not even +come to know if they were removed without my knowledge. So clearly there is a +personal perspective on the list below:

    + +
      +
    • +

      Gmail The most obvious and the most heavily used app. No introduction required. +** What I like best? +** Clean Interface +** Push Notification +** What can improve? +*** Ability to zoom messages.

      +
    • +
    • +

      AppyGeek / AppyGeek for Tablet I use this app on a daily basis to get my daily dose of news in technical world. +** What I like best? +** It pulls news based on keywords you can select. So my feed includes news on tags such as “Linux”, “Smartphones”, “Galaxy Tab”, “Nexus”, “Raspberry Pi” …you get the gist. +** What can improve? +** Ability to access to stories in offline mode.

      +
    • +
    • +

      Guardian Anywhere Fine, I admit wholeheartedly, I am strong left wing supporter. Now with that out of the way, I prefer this app over the official Guardian app as this downloads content overnight. +** What I like best? +** Offline Access to stories. +** You can schedule the time for download, I prefer 4:00 AM so I have all fresh news first thing in the morning. +** “Picks” tab which learns my taste over time and provides stories to match it. +** What can improve? +** Ability to copy a small section of text from the story.

      +
    • +
    • +

      Financisto is an opensource app to manage your budget and track your expenses. It’s very comprehensive and one of the best app available on any mobile platform in my opinion. It does require initial set-up and perhaps something I will write about in another post soon but generally I find using it a very easy affair. +** What I like best? +** Complete Package equivalent to any desktop finance management software. +** What can improve? +** App can do with a basic list of category to reduce the burden of creating list of categories from scratch. +*** Improved Documentation

      +
    • +
    • +

      QuickPic It is a photo gallery which have got accustomed to as initially the stock gallery app on Android was not very good. However, recent gallery app has seen some very good enhancements yet I still feel more at home using QuickPic. +** What I like best? +** Ability to hide folders that have downloaded photos from other apps and are not taken by phone camera. +** Ability to share a photo right from within the app through gmail, email, bluetooth, G+, FB etc. +** What can improve? +*** I don’t think there is much to improve. It’s a pretty well rounded app in my opinion.

      +
    • +
    • +

      Android Assistant It’s a android system utility for monitoring system tasks, killing apps etc. All these are not what make it interesting though as there are several apps out there doing the same stuff. What I like most is listed below: +** What I like best? +** With latest version, it prompts to move any newly installed/updated app to SD card if it is possible. +** It shows the total start-up time and allows you to select apps that should be silent at start-up. +** It allows for batch uninstall, which comes quite handy when on a system clean-up spree. +** What can improve? +** Can do with a more glossy makeover.

      +
    • +
    • +

      ES File Explorer File Manager The primary function of this app is to allow user to browse the file system but it really has loads of features under the hood. +** What I like best? +** Simplicity. +** Cloud Connection to support likes of Dropbox, Google Drive, Ubuntu One etc. +** FTP +** Everything really. +** What can improve? +*** This app covers the area of accessing files, network and so on. It has a lots to offer but I don’t think it’s used to it’s full potential by many. I think this app can do with a strong tutorial for each of it’s feature.

      +
    • +
    • +

      Profile Scheduler: This is better app than Smart Profile and I have found that it’s ability to switch profile based on WiFi saves me a complex set-up that would have been based on time but even so the time based set-up is much easier and straight forward and it does allow priority over rules which makes things super easy. It’s not a drain on battery and I am very satisfied with this app. Nothing to improve really.

      +
    • +
    • +

      Smart Profile This app is a profile management tool that helps in switching to a profile based on user defined time slots. I have FOUR profiles that I switch between Sleep (Completely Silent Mode every day from 23:45), Work (Vibrate mode Monday to Friday from 09:00), Normal (Monday to Friday from 17:00), 0600 Weekends + Mornings (Normal Profile everyday from 06:00). How it works then is Starting Monday morning 06:00 phone goes on normal mode, then at 09:00 turns to vibrate mode, then at 17:00 switches to Normal profile and in night at 23:45 goes to sleep mode. This continues till Saturday morning but as “Work Profile” is scheduled only from Monday to Friday so from Saturday 06:00 onwards phone continues on normal mode whole day till 23:45 in night when it switches to sleep mode, only to get back to normal mode on Sunday at 06:00. Then again remains on normal mode whole of Sunday and in night 23:45 goes back to sleep mode. Then the cycle starts again. +** What I like best? +** Free yet effective. +** What can improve? +** As you can see from my explanation above, it is a bit complex to set-up. There was an app called audioguru which stopped working on ICS forcing me to switch but it had a simpler interface. That said in free version of audioguru there was no ability to schedule weekend so this is much better at the lovely price tag of “free”. :-)

      +
    • +
    • +

      HulloMail It’s a visual voicemail app that works with most of the providers in the UK for their pay-monthly customers. +** What I like best? +** Ability to select the voicemail I want to listen to first. +** Replay the important part of voicemail +** Store VM as mp3 for future refrence. +** What can improve? +** I don’t want much else from this app in it’s free version. So it’s alright as is.

      +
    • +
    • +

      Go SMS Pro Go SMS Pro is a SMS client which is better than the stock client as it provides many useful additional features +** What I like best? +** Ability to schedule SMS. +** Ability to send SMS in batches / groups +** Cool skins +** Go Chat is another useful feature as long as your mates are using it too. +** What can improve? +*** They are improving it all the time but I can’t think of any complaints at the moment.

      +
    • +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unprotect-sheets-in-libre-calc-excel/index.html b/unprotect-sheets-in-libre-calc-excel/index.html new file mode 100644 index 0000000..a119bfa --- /dev/null +++ b/unprotect-sheets-in-libre-calc-excel/index.html @@ -0,0 +1,687 @@ + + + + + + +Unprotect Sheets in Libre Calc, Excel - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Unprotect Sheets in Libre Calc, Excel +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +
    + + +
    + +

    A friend of mine today had an issue. He had created a template for some really complex calculations and to ensure he does not mess up with the forumlae by mistake he had password protected sheets and cells. He had done this back in 2012 and now he wanted change something in there but he had forgotten the password so he asked me if I can help. +Now I do not know much about how it could be done on windows or on excel but I knew a small trick on LibreOffice so I asked him to send me that excel file by email. I then took following steps:

    + +
      +
    1. Opened the excel file Locked.xls in LibreCalc.
    2. +
    3. Saved it as an Locked.ods file.
    4. +
    5. From file browser, right click on newly saved Locked.ods file -> Open With -> Archive Manager as shown below.
      +Menu_001 +
    6. +
    7. Now open the content.xml +
    8. +
    9. Find table:protected="true" and Replace All with table:protected="false" +
    10. +
    11. Save the xml file.
    12. +
    13. Now open the Locked.ods in LibreCalc and save it as Unlocked.xlsx +
    14. +
    + +

    This should do the trick and unlock all password protected sheets and cells to be freely modified.

    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/update-ghost-on-fedora/index.html b/update-ghost-on-fedora/index.html new file mode 100644 index 0000000..4fd8382 --- /dev/null +++ b/update-ghost-on-fedora/index.html @@ -0,0 +1,724 @@ + + + + + + +Update Ghost on Fedora - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Update Ghost on Fedora +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +
    + + +
    + +

    While the guidance on Ghost website is very clear, I did get issues that required steps in troubleshooting. Something to do with lodash and npm version 2 stuff (node_modules/knex requires lodash@'^3.7.0') that I read on one of the forums specifically the comment from ErisDS on 13/06. +Anyway, reading this I deleted node_modules followed by npm install and it worked. All commands in order as I did are listed below. If my previous posts were used to create the blog nothing here will require sudo or root privileges. +As before all this was done on Fedora 24 Linux OS and following commands will need to be changed where it mentions yoursite and username. If the path is different then obviously entire path needs to be replaced.

    + +
    #Copy the entire site as backup. It will be a verbose copy an all access rights will be preserved. 
    +cp -avr /var/www/html/yoursite /home/<username>/
    +
    +#Now in the site directory create a directory ghostlatest
    +mkdir /var/www/html/yoursite/ghostlatest
    +
    +#change directory to ghostlatest
    +cd /var/www/html/yoursite/ghostlatest
    +
    +#now download the latest ghost zip file
    +curl -LOk https://ghost.org/zip/ghost-latest.zip
    +
    +#unzip the downloaded file
    +unzip ghost-latest.zip
    +
    +#Stop your Ghost instance (assuming Ghost is the alias
    +#created as per my previous post else replace with 
    +#whatever alias was used with pm2). 
    +pm2 stop Ghost
    +
    +#Change directory and delete old folders and files
    +cd /var/www/html/yoursite
    +rm -rf core
    +rm -rf index.js
    +rm -rf *.md
    +rm -rf *.json
    +rm -rf /var/www/html/yoursite/content/themes/casper
    +
    +#Remove node_modules because anyway the lodash issue will hit later on.
    +rm -rf node_modules
    +
    +#Copy from ghost latest to site directory new folders
    +cp -avr /var/www/html/yoursite/ghostlatest/core /var/www/html/yoursite
    +cp -avr /var/www/html/yoursite/ghostlatest/index.js /var/www/html/yoursite
    +cp -avr /var/www/html/yoursite/ghostlatest/*.md /var/www/html/yoursite
    +cp -avr /var/www/html/yoursite/ghostlatest/*.json /var/www/html/yoursite
    +
    +#Optional if you haven't made customisation to default theme.
    +cp -avr /var/www/html/yoursite/ghostlatest/content/themes/casper /var/www/html/yoursite/content/themes
    +
    +#Install Latest Version
    +npm cache clean
    +npm update
    +npm install --production
    +
    +#Start to update dependencies
    +npm start --production
    +
    +#Once above command is complete, stop the server and restart using pm2
    +Ctrl+C
    +pm2 start Ghost
    +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/upgrading-php-version-on-linux-for-apache/index.html b/upgrading-php-version-on-linux-for-apache/index.html new file mode 100644 index 0000000..372133a --- /dev/null +++ b/upgrading-php-version-on-linux-for-apache/index.html @@ -0,0 +1,705 @@ + + + + + + +Upgrading PHP version on Linux for Apache - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Upgrading PHP version on Linux for Apache +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +
    + + +
    + +
      +
    1. +

      Install the Apache module for specific php version

      + +
      +
      sudo apt install libapache2-mod-php7.3
      +
      +
    2. +
    3. +

      Copy the php.ini from previous version to newer version after making +a backup of the original php.ini for new version.

      + +
      +
      sudo cp /etc/php/7.3/apache2/php.ini php.ini.original
      +sudo cp /etc/php/7.2/apache2/php.ini /etc/php/7.3/apache2/php.ini
      +
      +
    4. +
    5. +

      Install specific php modules for Apache and enable the php modules +on new version of php.

      + +
      +
      sudo apt install php7.3-curl php7.3-gd php7.3-gmp php7.3-intl php7.3-mbstring php7.3-simplexml php7.3-soap php7.3-wddx php7.3-xmlreader php7.3-xmlrpc php7.3-xmlwriter php7.3-xsl php7.3-zip php7.3-xml php7.3-mysql
      +sudo phpenmod -v 7.3 pdo_mysql soap wddx xmlreader xmlrpc xsl zip intl gd dom curl mysqlnd gmp simplexml mysqli mbstring
      +
      +
    6. +
    7. +

      Disable old php version, enable new version and restart Apache +server.

      + +
      +
      sudo a2dismod php7.2
      +sudo a2enmod php7.3
      +sudo systemctl restart apache2
      +
      +
    8. +
    + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visio-alternative-on-linux-business-process-model-and-notation-tool/index.html b/visio-alternative-on-linux-business-process-model-and-notation-tool/index.html new file mode 100644 index 0000000..3d67ab0 --- /dev/null +++ b/visio-alternative-on-linux-business-process-model-and-notation-tool/index.html @@ -0,0 +1,716 @@ + + + + + + +Visio Alternative on Linux - Business Process Model and Notation Tool - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + + + + + +
    + +
    +

    Visio Alternative on Linux - Business Process Model and Notation Tool +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +
    + + +
    + +

    UPDATE: PENCIL is an opensource software which is a pretty good alternative as well with a smaller learning curve and is Opensource. +I was recently looking for Visio alternatives on Linux and while Dia is good it lacks the oomph of Visio and hence not an easy sell for new and potential Linux converts. +Now good news for those open to enter the wonderful world of Linux is that any OS need not have an alternative for it to be usable to you, all it needs is the ability to let you run the software you find useful. +So this post isn’t about why Linux is better. It is about knowing that there is something better than the software you have got accustomed to. If someone is adamant on continuing with the software they have fallen in love with, there is always WINE / Virtualbox but if you have opened to the idea of new OS but you are taking baby steps software like yEd can help you take a giant leap. yEd is written in Java and can run on Windows, Mac and of-course Linux. +Before we move into the steps of how to get it and use it let me bore you little with what are the other alternatives I explored. +During my quest I came across two other useful candidates Activiti 5.6, ARIS Express 2.3. What went against these and in favour of yEd? Read on:

    +
      +
    1. +Ease of installation +
        +
      • +First on the list is ARIS Express 2.3. Now this requires registration which is my first gripe. I could have lived with that but as claimed on their website the product is meant for Windwos and is reported to be working fine for Linux. What they forgot to mention is on linux using Wine or atleast that is what it did on my set-up. Now am not sure if it has something to do with my installing JRE in wine for using Open Workbench (see last post) or it is because the product works on similar lines as Open Workbench installer but eighther way it does not run natively on Linux and for that reason alone it is unreliable and looses points even to Activiti. +
      • +
      • +For easiest installation, Activiti 5.6 can be added into Eclipse but that results in a bit complicated and not so user friendly user-experience. You will at-least need to have one Activiti project created in eclipse before you can draw using Activiti Diagram. (Check Screenshot) + Alt Text +
      • +
      • +Above might be circumvented by installing standalone Activiti bit that involves configuring ANT and other stuff. While guide is there, it is not very straight forward and considering we want to win new users not scare them, it is best left as untouched topic. +
      • +
      +
    2. +
    +
      +
    • Ease of Use - While all the three products yEd, Activiti and ARIS are superior to Visio, in terms of ease of use I think it's yEd which wins hands down even to Visio. The one aspect of this software that won me over was it's ability to auto align the whole flowchart / diagram with just one click and it can do it in several layouts.
    • +
    +

    OK, now that I have covered comparison, let’s get down to how we install it on our beloved Linux Mint. It’s really very simple. +Step 1: Go to this website - http://www.yworks.com/en/products_yed_download.html +Step 2: Download yEd for your platform. In our case for Linux Mint the 43 MB sh file in front of Linux. +Step 3:

    +
      +
    • a) Go to the folder where it is downloaded, select the yEd-3.7.sh file and right click on it.
    • +
    • b) In the right click menu select Properties (last entry). This will open the yEd-3.7.sh properties box.
    • +
    • c) On the Properties box, click on Permissions tab.
    • +
    • d) Click the checkbox in front of Execute Allow executing file as program.
    • +
    • e) Click Close.
    • +
    • f) Double click on the yEd-3.7.sh file. It will display a dialogue box with four options Run in terminal, Display, Cancel, Run. Select Run in terminal
    • +
    • g) yEd will now install, it may give one small error but it did not affect anything at all so ignore it if you get it.
    • +
    • h) Follow the wizard and leave default options.
    • +
    • i) Once done, you will have yEd successfully installed and can be found in MENU ALL APPLICATIONS-OTHER.
    • +
    • j) Create a shortcut of yEd by dragging yEd from MENU ALL APPLICATIONS-OTHER onto desktop. You may need to make this dragged link executable by right clicking the link and following steps b,c and d.
    • +
    +

    All Done !!! +A quick tutorial from yWorks:

    + +

    Find more on http://www.yworks.com/en/products_yed_videos.html +Having seen, how nice and easy it is to create the flowchart, let’s also see the best feature of the product that is one click alignment.

    + + + +
    + +
    + + + + + + +

    Updated:

    + + +
    + + + + + + + +
    + + +
    + + +

    Leave a comment

    +
    + +
    + + +
    + + + + + + +
    + +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/year-archive/index.html b/year-archive/index.html new file mode 100644 index 0000000..9d202ef --- /dev/null +++ b/year-archive/index.html @@ -0,0 +1,3242 @@ + + + + + + +Posts by Year - Make Gadgets Work + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + +

    Posts by Year

    + + + + + + + + + +
    +

    2024

    +
    + + + + + +
    + +
    + + +
    + Back to top ↑ +
    + +
    +

    2023

    +
    + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Git Jargon + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    Familiarisation with some Git jargon +

    +
    +
    + + + + + + +
    +
    + +

    + + Git Basics + + +

    + + +

    + + + + + + + + + + + + 7 minute read + + + +

    + + +

    Basics of Git + +

    +
    +
    + + + + + + +
    + +
    + + +
    + Back to top ↑ +
    + +
    +

    2021

    +
    + + + + + +
    +
    + +

    + + Tech for Diabetics + + +

    + + +

    + + + + + + + + + + + + 9 minute read + + + +

    + + +

    As a diabetic there are a number of things we are not able to control but one thing we can do is keep tabs on our data +

    +
    +
    + + + + + + +
    + +
    + + +
    + Back to top ↑ +
    + +
    +

    2020

    +
    + + + + + +
    +
    + +

    + + Converting AAX (Audible) to mp3 + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    Lately I have been a bit frustrated because while I subscribe to Audible services, Amazon and Google do not play nice with each other limiting me - the owner...

    +
    +
    + + +
    + Back to top ↑ +
    + +
    +

    2018

    +
    + + + + + +
    +
    + +

    + + Metabase - A BI solution that just works + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    I like exploring new solutions and anything to do with data always piques my interest. I came across this nice tool through the list of free self hosted soft...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Home Networking + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    Network routing + + +Router - Router is the device at home that connects all devices in your house to the internet. It does so by assigning IP address to each o...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Ghost Upgrade errors and fixes (1.19.x) + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    I have found the recent ghost upgrades quite painless but there have been few hiccups for last two times so I kept a record of what helped and it is as liste...

    +
    +
    + + +
    + Back to top ↑ +
    + +
    +

    2017

    +
    + + + + + +
    +
    + +

    + + Unprotect Sheets in Libre Calc, Excel + + +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +

    A friend of mine today had an issue. He had created a template for some really complex calculations and to ensure he does not mess up with the forumlae by mi...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Markdown and Gantt Charts + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    For a fairly long time, I have been looking for a simple markdown type of solution to be able to quickly draw Gantt charts but never came across what one wou...

    +
    +
    + + + + + + +
    +
    + +

    + + DD-WRT firmware on TP-LINK TL-WR841N v11 + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    I have been with PlusNet for over two years now and am a happy camper as far as fiber optic broadband is concerned but as I am no longer on a broadband contr...

    +
    +
    + + +
    + Back to top ↑ +
    + +
    +

    2016

    +
    + + + + + +
    +
    + +

    + + Swap File to create extra memory + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    While renewing my LetsEncrypt certificate, I found myself in a strange situation where the certbot won’t run asking me to update pip and then each time I tri...

    +
    +
    + + + + + + +
    +
    + +

    + + Note 7 to OnePlus 3 + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    I was a super excited owner of Note 7 in September this year and then in few +days the happiness started disappearing as the news of exploding Note 7 started +...

    +
    +
    + + + + + + +
    +
    + +

    + + Grav - CMS with a difference + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    While I love Ghost as a blogging platform, it is not best placed for things other than blogs - after all that is the basic idea behind creation of this wonde...

    +
    +
    + + + + + + +
    +
    + +

    + + Windows 10 - a bucket load of pain + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    + As Windows 10 is a commercial offering, one would think it will be working as expected and it does so long as like me one has come to expect pain from Mic...

    +
    +
    + + + + + + +
    +
    + +

    + + Ethercalc + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Ethercalc is good tool which can be selfhosted. It is fairly simple to do so. Though it will be available for anyone who has the URL because there is no inbu...

    +
    +
    + + + + + + +
    +
    + +

    + + Fix for PHP Issues after upgrade to Ubuntu 16.04.1 (Xenial) + + +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +

    After updating from Ubuntu 14.04, the php and Apache stopped being friends and one of the WordPress site I maintain went all white and admin page was just sh...

    +
    +
    + + + + + + +
    +
    + +

    + + Update Ghost on Fedora + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    While the guidance on Ghost website is very clear, I did get issues that required steps in troubleshooting. Something to do with lodash and npm version 2 stu...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + The complete walkthrough of my blogger to ghost migration + + +

    + + +

    + + + + + + + + + + + + 9 minute read + + + +

    + + +

    The 7 Year Itch +It can’t possibly be a coincidence that this is the 7th year since I started blogging on blogger and therefore it is very likely to be a stro...

    +
    +
    + + + + + + +
    +
    + +

    + + Ghost on Fedora 24 + + +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +

    To install Ghost as my blogging platform, I had to go through a number of hoops and one of them was to get the nodejs working and what not. I figured this mi...

    +
    +
    + + + + + + +
    +
    + +

    + + Seafile Server behind nginx on Fedora 24 Security Lab Spin + + +

    + + +

    + + + + + + + + + + + + 12 minute read + + + +

    + + +

    I have recently been intrigued by the idea of replacing the likes of “Dropbox” and “Google Drive” with a cloud set-up of my own. I had “Owncloud” set-up for ...

    +
    +
    + + + + + + +
    +
    + +

    + + Crossover + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    There are technologies that I can use in my personal life, that I am proud of and those that I play around with but reality is that as a Senior Project Manag...

    +
    +
    + + + + + + +
    + +
    + + +
    + Back to top ↑ +
    + +
    +

    2014

    +
    + + + + + +
    +
    + +

    + + Arch Linux - nearly a year on…. + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    I have been dwelling in the world of Arch Linux for just under a year now and must admit the experience is nothing less than liberating. Granted that the bar...

    +
    +
    + + +
    + Back to top ↑ +
    + +
    +

    2013

    +
    + + + + + +
    +
    + +

    + + Linux Mint on Android through VNC and Jump + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    Today “Jump” was available for free on Amazon as the app of the day and since it’s nearly 7 quids on google play store, I downloaded. For windows and Mac use...

    +
    +
    + + + + + + +
    +
    + +

    + + Python, ERIC RAD IDE and QT designer + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Right, I have decided to play around a little with the most loved language of open source a. k. a. Python. + +

    +
    +
    + + + + + + +
    +
    + +

    + + Conky on my desktop - step by step + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    +My new friend Damjan recently mentioned that he liked the Conky on my desktop and asked for details as have few others so I figured a post on the topic will...

    +
    +
    + + + + + + +
    + +
    + + +
    + Back to top ↑ +
    + +
    +

    2012

    +
    + + + + + +
    +
    + +

    + + Prepare Linux Mint 13 for Android development + + +

    + + +

    + + + + + + + + + + + + 8 minute read + + + +

    + + +

    Few weeks back I updated to the latest Linux Mint offering "Maya" a.k.a Linux Mint 13. Now this is a LTS (Long Term Support) version and I wanted to be in a ...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Top 10 Android Apps that I use !!!! + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    I have been using Android Phones and Tablets for quite some time now and have +monitored my use for apps that I use on a daily basis and those I would not eve...

    +
    +
    + + + + + + +
    +
    + +

    + + Exchange 2007 on Thunderbird using DAVMail + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    The start of this week was like a nightmare for me. Whole family was down with flu and I had the fever that is probably the highest ever of my entire life at...

    +
    +
    + + + + + + +
    +
    + +

    + + StageVu / DivX video on Bodhi Linux + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    If you read my last post you will know I have been playing with Bodhi Linux lately. One of the selling point for this distro is it’s minimalistic approach. H...

    +
    +
    + + + + + + +
    +
    + +

    + + How to boot from USB when BIOS does not have the option. + + +

    + + +

    + + + + + + + + + + + + 6 minute read + + + +

    + + +

    I have an old Sony VAIO which is not in it’s best of health and has long been really a companion for my telly, faithfully streaming media from bbc iplayer, y...

    +
    +
    + + +
    + Back to top ↑ +
    + +
    +

    2011

    +
    + + + + + +
    +
    + +

    + + OpenVPN on Linux Mint to access US sites + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    + +UPDATE: LOOKS LIKE HOSTIZZLE IS NOT WORKING ANYMORE. THE STEPS IN THIS GUIDE WILL STILL BE RELEVANT FOR SETTING UP OPEN VPN, JUST THAT YOU WILL NEED TO FIN...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Install Open Workbench and JRE on Wine in Linux Mint + + +

    + + +

    + + + + + + + + + + + + 3 minute read + + + +

    + + +

    What is Open Workbench? +Open Workbench is a Project Planning Software comparable to Microsoft Project. There are mixed views on whether it is truly open sour...

    +
    +
    + + + + + + +
    +
    + +

    + + How to edit PDF in Linux - The easy way. + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    Recently someone asked this question to me and after some search on Google I came across mentions of PDFedit, Scribus, flpsed, Gimp, PDFMod and even the open...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Glympse + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Every now and then we come across something that has a strong potential, something that is a great phenomenon in making and Glympse to me is that idea. + +

    +
    +
    + + + + + + +
    +
    + +

    + + MMC Convert media files on Linux + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    I have been using Mobile Media Converter for almost a year now and it is such a great tool. I think it is a must have at-least on Linux. +It not only converts...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Time for a bit of show-off + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Hey Hey I made an Android app named “Sai Satcharitra”. It’s a very basic app and +all it does is helps people read Sai Satcharitra on their Android Smartphone...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Call US and several other countries free using Android + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    That’s right…with smartphones all that was possible using computers is increasingly becoming feasible through phones. I just now configured my Nexus S to mak...

    +
    +
    + + +
    + Back to top ↑ +
    + +
    +

    2010

    +
    + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Jagannath Hora on Linux - Play on Linux Magic + + +

    + + +

    + + + + + + + + + + + + 4 minute read + + + +

    + + +

    While I prefer Maitreya as it can run with Linux as native, quite a few of my friends have asked me if Jagannatha Hora will work on Linux and hence this post...

    +
    +
    + + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Audio problem in Wine under KDE 4.4.1 - Solved + + +

    + + +

    + + + + + + + + + + + + less than 1 minute read + + + +

    + + +

    I was trying to install spotify on linux which used to work perfectly on Gnome and when I tried on KDE it was giving error box. I wrote winecfg on terminal a...

    +
    +
    + + + + + + +
    +
    + +

    + + Configure BlogTK 1.0 for blogger + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Right so I am happy with Blogilo, then why BlogTK? +It so happened that I was trying to edit my last post and for some unknown reason I was getting error in u...

    +
    +
    + + + + + + +
    +
    + +

    + + KUBUNTU Blog Entry + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    This is how my love for Kubuntu has started….and growing by the minute… +I was experimenting with different Linux distros and then I went ahead with Linux Min...

    +
    +
    + + +
    + Back to top ↑ +
    + +
    +

    2009

    +
    + + + + + +
    + +
    + + + + + + +
    +
    + +

    + + Sony VAIO N-VIDIA setup to make S-Video work + + +

    + + +

    + + + + + + + + + + + + 5 minute read + + + +

    + + +

    + +UPDATE for 9.10 +For Ubuntu 9.10 I was just not able to get it work and finally I figured it out. The problem is with the latest Nvidia driver, the one that...

    +
    +
    + + + + + + +
    +
    + +

    + + Sony VAIO FE21H Webcam on skype + + +

    + + +

    + + + + + + + + + + + + 1 minute read + + + +

    + + +

    Alright friends not a huge tip but still I, being new fan of Ubuntu, installed it on my media laptop…Sony VAIO… +one might ask what is media laptop…well I had...

    +
    +
    + + + + + + +
    +
    + +

    + + Get Gmail as Push Email on Sony P990i + + +

    + + +

    + + + + + + + + + + + + 2 minute read + + + +

    + + +

    While I have moved on from P990i which is now in safe hands of my dear wife, I remember doing some good amount of web searching and still had no idea how to ...

    +
    +
    + + + + + + +
    +
    + +

    + + AAO A150L Step by Step Installation Process for Linux4one + + +

    + + +

    + + + + + + + + + + + + 11 minute read + + + +

    + + +

    Linux4one is the only Linux distro other than pre-installed Linpus that worked out of the box on my and two other Acer aspire one A150L which belonged to my ...

    +
    +
    + + +
    + Back to top ↑ +
    + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +