├── .coveragerc ├── .gitignore ├── .travis.yml ├── MANIFEST.in ├── README.rst ├── bin ├── everpad ├── everpad-lens └── everpad-provider ├── data ├── editor-icons │ ├── everpad-checkbox.png │ ├── everpad-insert-image.png │ ├── everpad-insert-table.png │ ├── everpad-justify-center.png │ ├── everpad-justify-fill.png │ ├── everpad-justify-left.png │ ├── everpad-justify-right.png │ ├── everpad-link.png │ ├── everpad-list-ordered.png │ ├── everpad-list-unordered.png │ ├── everpad-pin.png │ ├── everpad-share.png │ ├── everpad-text-bold.png │ ├── everpad-text-italic.png │ ├── everpad-text-strikethrough.png │ └── everpad-text-underline.png ├── everpad-app.service ├── everpad-black.png ├── everpad-file.png ├── everpad-lens.png ├── everpad-mono.png ├── everpad-note.png ├── everpad-provider.service ├── everpad.desktop ├── everpad.lens ├── everpad.png ├── everpad.xcf ├── metadata.desktop ├── plasma-runner-everpad.desktop └── unity-lens-everpad.service ├── dev_requirements.txt ├── docs ├── everpad.1 └── license.txt ├── evernote ├── __init__.py └── edam │ ├── __init__.py │ ├── error │ ├── __init__.py │ ├── constants.py │ └── ttypes.py │ ├── limits │ ├── __init__.py │ ├── constants.py │ └── ttypes.py │ ├── notestore │ ├── NoteStore-remote │ ├── NoteStore.py │ ├── __init__.py │ ├── constants.py │ └── ttypes.py │ ├── type │ ├── __init__.py │ ├── constants.py │ └── ttypes.py │ └── userstore │ ├── UserStore-remote │ ├── UserStore.py │ ├── __init__.py │ ├── constants.py │ └── ttypes.py ├── everpad.pro ├── everpad ├── __init__.py ├── basetypes.py ├── const.py ├── interface │ ├── __init__.py │ ├── editor.py │ ├── editor.ui │ ├── findbar.py │ ├── findbar.ui │ ├── image.py │ ├── image.ui │ ├── list.py │ ├── list.ui │ ├── management.py │ ├── management.ui │ ├── notebook.py │ ├── notebook.ui │ ├── share_note.py │ ├── share_note.ui │ ├── tableinsert.py │ └── tableinsert.ui ├── monkey.py ├── pad │ ├── __init__.py │ ├── editor │ │ ├── __init__.py │ │ ├── actions.py │ │ ├── content.py │ │ ├── editor.html │ │ ├── resources.py │ │ └── widgets.py │ ├── indicator.py │ ├── list.py │ ├── management.py │ ├── share_note.py │ ├── tools.py │ └── treeview.py ├── provider │ ├── __init__.py │ ├── daemon.py │ ├── exceptions.py │ ├── models.py │ ├── service.py │ ├── sync │ │ ├── __init__.py │ │ ├── agent.py │ │ ├── base.py │ │ ├── note.py │ │ ├── notebook.py │ │ └── tag.py │ └── tools.py ├── specific │ ├── __init__.py │ ├── kde │ │ ├── __init__.py │ │ └── everpad_runner.py │ └── unity │ │ ├── __init__.py │ │ ├── launcher.py │ │ └── lens.py └── tools.py ├── i18n ├── ar │ └── LC_MESSAGES │ │ ├── everpad.mo │ │ └── everpad.po ├── ar_EG.qm ├── ar_EG.ts ├── de │ └── LC_MESSAGES │ │ ├── everpad.mo │ │ └── everpad.po ├── de_AT.qm ├── de_AT.ts ├── de_CH.qm ├── de_CH.ts ├── de_DE.qm ├── de_DE.ts ├── es.qm ├── es.ts ├── es │ └── LC_MESSAGES │ │ ├── everpad.mo │ │ └── everpad.po ├── ja.qm ├── ja.ts ├── ja │ └── LC_MESSAGES │ │ ├── everpad.mo │ │ └── everpad.po ├── messages.pot ├── nl.qm ├── nl.ts ├── ru │ └── LC_MESSAGES │ │ ├── everpad.mo │ │ └── everpad.po ├── ru_RU.qm ├── ru_RU.ts ├── zh_CN.qm ├── zh_CN.ts ├── zh_CN │ └── LC_MESSAGES │ │ ├── everpad.mo │ │ └── everpad.po ├── zh_TW.qm ├── zh_TW.ts └── zh_TW │ └── LC_MESSAGES │ ├── everpad.mo │ └── everpad.po ├── scripts └── install_dbus_services.py ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── factories.py ├── pad │ ├── __init__.py │ └── test_editor.py ├── provider │ ├── __init__.py │ ├── test_service.py │ └── test_sync.py ├── settings │ ├── __init__.py │ ├── ci_local.py │ └── dist.py ├── test.png └── test_basetypes.py └── thrift ├── TSCons.py ├── TSerialization.py ├── Thrift.py ├── __init__.py ├── protocol ├── TBase.py ├── TBinaryProtocol.py ├── TCompactProtocol.py ├── TProtocol.py ├── __init__.py └── fastbinary.c ├── server ├── THttpServer.py ├── TNonblockingServer.py ├── TProcessPoolServer.py ├── TServer.py └── __init__.py └── transport ├── THttpClient.py ├── TSSLSocket.py ├── TSocket.py ├── TTransport.py ├── TTwisted.py ├── TZlibTransport.py ├── __init__.py └── httpslib.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | include = everpad* 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | *.swp 4 | *.pyc 5 | *pip-delete-this-directory.txt 6 | /build 7 | /.idea 8 | /dist 9 | *.egg 10 | *.egg-info 11 | tests/settings/local.py 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | before_install: 5 | - "export TRAVIS_CI=True" 6 | - "export DISPLAY=:99.0" 7 | - sudo apt-get update -qq 8 | - sudo apt-get install -qq python-pyside python-dbus dbus python-magic 9 | - "sh -e /etc/init.d/xvfb start" 10 | install: 11 | - pip install . --use-mirrors 12 | - pip install -r dev_requirements.txt --use-mirrors 13 | before_script: 14 | - ln -s /usr/lib/python2.7/dist-packages/PySide /home/travis/virtualenv/python2.7/lib/python2.7/site-packages/PySide -v 15 | - ln -s /usr/lib/python2.7/dist-packages/dbus /home/travis/virtualenv/python2.7/lib/python2.7/site-packages/dbus -v 16 | - ln -s /usr/lib/python2.7/dist-packages/_dbus_* /home/travis/virtualenv/python2.7/lib/python2.7/site-packages/ -v 17 | - ln -s /usr/lib/python2.7/dist-packages/magic.py /home/travis/virtualenv/python2.7/lib/python2.7/site-packages/ -v 18 | - cp tests/settings/ci_local.py tests/settings/local.py 19 | - mkdir -p /home/travis/.everpad/data/1/ 20 | script: 21 | - nosetests --with-coverage 22 | after_success: 23 | - coverage report 24 | - pip install --quiet python-coveralls 25 | - coveralls 26 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-exclude evernote *.pyc 2 | recursive-exclude everpad *.pyc 3 | recursive-exclude thrift *.pyc 4 | recursive-include everpad *html 5 | recursive-include data * 6 | recursive-include i18n * 7 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Everpad 2 | ======= 3 | .. image:: https://travis-ci.org/nvbn/django-bower.png 4 | :alt: Build Status 5 | :target: https://travis-ci.org/nvbn/everpad 6 | .. image:: https://coveralls.io/repos/nvbn/everpad/badge.png?branch=develop 7 | :alt: Coverage Status 8 | :target: https://coveralls.io/r/nvbn/everpad 9 | 10 | Evernote client well integrated with linux desktop. 11 | 12 | .. image:: http://ubuntuone.com/4ABojaepuBiaDVv2VsDB7o 13 | 14 | Client has: 15 | - unity lens 16 | - indicator applet 17 | - unity launcher 18 | 19 | Client support: 20 | - notes 21 | - tags 22 | - notebooks 23 | - resources 24 | - places 25 | - en-* tags 26 | 27 | Security information: The client store notes in a SQLite database and communicates with the Evernote API via Thrift via https. 28 | 29 | Installation 30 | ============ 31 | Ubuntu 12.04+ users can use `ppa `_: 32 | 33 | ``sudo add-apt-repository ppa:nvbn-rm/ppa`` 34 | 35 | ``sudo apt-get update`` 36 | 37 | ``sudo apt-get install everpad`` 38 | 39 | If you use gnome shell please `read wiki `_ 40 | 41 | You can see more about everpad installation, including method for other linux, in `wiki `_ 42 | 43 | Some errors? 44 | ============ 45 | `Get debug information `_, check your distributive name and version, check your DE version and post bug report. 46 | 47 | Want to help? 48 | ============= 49 | `Write code here `_ 50 | 51 | Or create bug reports. 52 | 53 | Or donate: 54 | 55 | - **PayPal**: nvbn.rm@gmail.com 56 | - **Yandex Money**: 410011244953574 57 | -------------------------------------------------------------------------------- /bin/everpad: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys 3 | sys.path.append('/opt/extras.ubuntu.com/everpad/') 4 | from everpad.pad import indicator 5 | 6 | 7 | if __name__ == '__main__': 8 | indicator.main() 9 | -------------------------------------------------------------------------------- /bin/everpad-lens: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys 3 | sys.path.append('/opt/extras.ubuntu.com/everpad/') 4 | from everpad.specific.unity import lens 5 | 6 | 7 | if __name__ == '__main__': 8 | lens.main() 9 | -------------------------------------------------------------------------------- /bin/everpad-provider: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys 3 | sys.path.append('/opt/extras.ubuntu.com/everpad/') 4 | from everpad.provider import daemon 5 | 6 | 7 | if __name__ == '__main__': 8 | daemon.main() 9 | -------------------------------------------------------------------------------- /data/editor-icons/everpad-checkbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-checkbox.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-insert-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-insert-image.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-insert-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-insert-table.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-justify-center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-justify-center.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-justify-fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-justify-fill.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-justify-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-justify-left.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-justify-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-justify-right.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-link.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-list-ordered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-list-ordered.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-list-unordered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-list-unordered.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-pin.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-share.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-text-bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-text-bold.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-text-italic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-text-italic.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-text-strikethrough.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-text-strikethrough.png -------------------------------------------------------------------------------- /data/editor-icons/everpad-text-underline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/editor-icons/everpad-text-underline.png -------------------------------------------------------------------------------- /data/everpad-app.service: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=com.everpad.App 3 | Exec=/usr/bin/everpad -------------------------------------------------------------------------------- /data/everpad-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/everpad-black.png -------------------------------------------------------------------------------- /data/everpad-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/everpad-file.png -------------------------------------------------------------------------------- /data/everpad-lens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/everpad-lens.png -------------------------------------------------------------------------------- /data/everpad-mono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/everpad-mono.png -------------------------------------------------------------------------------- /data/everpad-note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/everpad-note.png -------------------------------------------------------------------------------- /data/everpad-provider.service: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=com.everpad.Provider 3 | Exec=/usr/bin/everpad-provider -------------------------------------------------------------------------------- /data/everpad.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=0.1 3 | Name=Everpad 4 | GenericName=Everpad 5 | Comment=Evernote client 6 | Comment[ru]=Клиент для Evernote 7 | Exec=everpad %U 8 | Terminal=false 9 | Icon=everpad.png 10 | Type=Application 11 | Categories=Network; 12 | X-Ayatana-Desktop-Shortcuts=NewNote;Settings 13 | MimeType=image/bmp;image/gif;image/jpeg;image/jpg;image/pjpeg;image/png;image/tiff;image/x-bmp;image/x-gray;image/x-icb;image/x-ico;image/x-png;image/x-portable-anymap;image/x-portable-bitmap;image/x-portable-graymap;image/x-portable-pixmap;image/x-xbitmap;image/x-xpixmap;image/x-pcx;image/svg+xml;image/svg+xml-compressed;image/vnd.wap.wbmp;application/pdf;application/x-bzpdf;application/x-gzpdf;application/x-xzpdf;application/postscript;application/x-bzpostscript;application/x-gzpostscript;image/x-eps;image/x-bzeps;image/x-gzeps;application/x-dvi;application/x-bzdvi;application/x-gzdvi;image/vnd.djvu;image/tiff;application/x-cbr;application/x-cbz;application/x-cb7;application/x-cbt;audio/wav;audio/mpeg; 14 | 15 | [Settings Shortcut Group] 16 | Name=Show all notes 17 | Name[ru]=Все заметки 18 | Exec=everpad --all-notes 19 | TargetEnvironment=Unity 20 | 21 | [NewNote Shortcut Group] 22 | Name=Create Note 23 | Name[ru]=Создать заметку 24 | Exec=everpad --create 25 | TargetEnvironment=Unity 26 | 27 | [Settings Shortcut Group] 28 | Name=Settings and Management 29 | Name[ru]=Настройки и Управление 30 | Exec=everpad --settings 31 | TargetEnvironment=Unity 32 | -------------------------------------------------------------------------------- /data/everpad.lens: -------------------------------------------------------------------------------- 1 | [Lens] 2 | DBusName=net.launchpad.Unity.Lens.EverpadLens 3 | DBusPath=/net/launchpad/unity/lens/everpad 4 | Name=Everpad 5 | Icon=everpad-lens 6 | Description=Everpad notes lens 7 | SearchHint=Search everpad notes 8 | Shortcut=e 9 | 10 | [Desktop Entry] 11 | X-Ubuntu-Gettext-Domain=unity-lens-everpad -------------------------------------------------------------------------------- /data/everpad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/everpad.png -------------------------------------------------------------------------------- /data/everpad.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/data/everpad.xcf -------------------------------------------------------------------------------- /data/metadata.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Everpad runner 3 | Comment=Find and create notes directly from krunner 4 | Type=Service 5 | Icon=everpad 6 | ServiceTypes=Plasma/Runner 7 | X-Plasma-API=python 8 | X-Plasma-MainScript=code/everpad_runner.py 9 | X-KDE-PluginInfo-Author=Vladimir Iakovlev 10 | X-KDE-PluginInfo-Email=nvbn.rm@gmail.com 11 | X-KDE-PluginInfo-Name=everpad 12 | X-KDE-PluginInfo-Version=1 13 | X-KDE-PluginInfo-Website=https://github.com/nvbn/everpad/ 14 | X-KDE-PluginInfo-License=BSD 15 | X-KDE-PluginInfo-EnabledByDefault=true 16 | -------------------------------------------------------------------------------- /data/plasma-runner-everpad.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Everpad runner 3 | Comment=Find and create notes directly from krunner 4 | Type=Service 5 | Icon=everpad 6 | ServiceTypes=Plasma/Runner 7 | X-Plasma-API=python 8 | X-Plasma-MainScript=code/everpad_runner.py 9 | X-KDE-PluginInfo-Author=Vladimir Iakovlev 10 | X-KDE-PluginInfo-Email=nvbn.rm@gmail.com 11 | X-KDE-PluginInfo-Name=everpad 12 | X-KDE-PluginInfo-Version=1 13 | X-KDE-PluginInfo-Website=https://github.com/nvbn/everpad/ 14 | X-KDE-PluginInfo-License=BSD 15 | X-KDE-PluginInfo-EnabledByDefault=true 16 | -------------------------------------------------------------------------------- /data/unity-lens-everpad.service: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=net.launchpad.Unity.Lens.EverpadLens 3 | Exec=/usr/bin/everpad-lens -------------------------------------------------------------------------------- /dev_requirements.txt: -------------------------------------------------------------------------------- 1 | mock 2 | factory_boy 3 | sqlalchemy==0.7.9 4 | coverage 5 | -------------------------------------------------------------------------------- /docs/everpad.1: -------------------------------------------------------------------------------- 1 | .TH EVERPAD "1" "February 20, 2014" 2 | .\" Hey, EMACS: -*- nroff -*- 3 | .\" Process this file with 4 | .\" groff -man -Tascii everpad.1 5 | .\" First parameter, NAME, should be all caps 6 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 7 | .\" other parameters are allowed: see man(7), man(1) 8 | .\" 9 | .\" Please adjust this date whenever revising the manpage. 10 | .\" 11 | .\" Some roff macros, for reference: 12 | .\" .nh disable hyphenation 13 | .\" .hy enable hyphenation 14 | .\" .ad l left justify 15 | .\" .ad b justify to both left and right margins 16 | .\" .nf disable filling 17 | .\" .fi enable filling 18 | .\" .br insert line break 19 | .\" .sp insert n+1 empty lines 20 | .\" for manpage-specific macros, see man(7) 21 | .SH NAME 22 | everpad \- Evernote client for GNU/Linux 23 | .SH SYNOPSIS 24 | \fBeverpad\fP [\-\-open OPEN] [\-\-create] [\-\-all\-notes] [\-\-settings] 25 | .br 26 | [\-\-replace] [\-\-version] [\-h] [attachments [attachments ...]] 27 | .SH DESCRIPTION 28 | .\" TeX users may be more comfortable with the \fB\fP and 29 | .\" \fI\fP escape sequences to invode bold face and italics, 30 | .\" respectively. 31 | \fBeverpad\fP allows to interact with the Evernote service 32 | right from your desktop. 33 | .PP 34 | It supports notes, tags, notebooks, resources, places and en-* tags. 35 | .\" Options section originally created using help2man 36 | .SH OPTIONS 37 | .SS "positional arguments:" 38 | .TP 39 | attachments 40 | attach files to new note 41 | .SS "optional arguments:" 42 | .TP 43 | \fB\-\-open\fR OPEN 44 | open note 45 | .TP 46 | \fB\-\-create\fR 47 | create new note 48 | .TP 49 | \fB\-\-all\-notes\fR 50 | show all notes window 51 | .TP 52 | \fB\-\-settings\fR 53 | settings and management 54 | .TP 55 | \fB\-\-replace\fR 56 | replace already running instance 57 | .TP 58 | \fB\-\-version\fR, \fB\-v\fR 59 | show version 60 | .TP 61 | \fB\-h\fR, \fB\-\-help\fR 62 | show this help message and exit 63 | .SH BUGS 64 | To report a bug, please visit https://github.com/nvbn/everpad/issues and/or report bugs to the Debian Bug Tracking System, as usual. 65 | .SH AUTHOR 66 | everpad was written by Vladimir Iakovlev . 67 | .br 68 | Manpage written for Debian by Emilien Klein . 69 | -------------------------------------------------------------------------------- /docs/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007 Vladimir Yakovlev and Contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /evernote/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/evernote/__init__.py -------------------------------------------------------------------------------- /evernote/edam/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/evernote/edam/__init__.py -------------------------------------------------------------------------------- /evernote/edam/error/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants'] 2 | -------------------------------------------------------------------------------- /evernote/edam/error/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | -------------------------------------------------------------------------------- /evernote/edam/limits/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants'] 2 | -------------------------------------------------------------------------------- /evernote/edam/limits/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | EDAM_ATTRIBUTE_LEN_MIN = 1 13 | EDAM_ATTRIBUTE_LEN_MAX = 4096 14 | EDAM_ATTRIBUTE_REGEX = "^[^\\p{Cc}\\p{Zl}\\p{Zp}]{1,4096}$" 15 | EDAM_ATTRIBUTE_LIST_MAX = 100 16 | EDAM_ATTRIBUTE_MAP_MAX = 100 17 | EDAM_GUID_LEN_MIN = 36 18 | EDAM_GUID_LEN_MAX = 36 19 | EDAM_GUID_REGEX = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" 20 | EDAM_EMAIL_LEN_MIN = 6 21 | EDAM_EMAIL_LEN_MAX = 255 22 | EDAM_EMAIL_LOCAL_REGEX = "^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(\\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*$" 23 | EDAM_EMAIL_DOMAIN_REGEX = "^[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*\\.([A-Za-z]{2,})$" 24 | EDAM_EMAIL_REGEX = "^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(\\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*\\.([A-Za-z]{2,})$" 25 | EDAM_TIMEZONE_LEN_MIN = 1 26 | EDAM_TIMEZONE_LEN_MAX = 32 27 | EDAM_TIMEZONE_REGEX = "^([A-Za-z_-]+(/[A-Za-z_-]+)*)|(GMT(-|\\+)[0-9]{1,2}(:[0-9]{2})?)$" 28 | EDAM_MIME_LEN_MIN = 3 29 | EDAM_MIME_LEN_MAX = 255 30 | EDAM_MIME_REGEX = "^[A-Za-z]+/[A-Za-z0-9._+-]+$" 31 | EDAM_MIME_TYPE_GIF = "image/gif" 32 | EDAM_MIME_TYPE_JPEG = "image/jpeg" 33 | EDAM_MIME_TYPE_PNG = "image/png" 34 | EDAM_MIME_TYPE_WAV = "audio/wav" 35 | EDAM_MIME_TYPE_MP3 = "audio/mpeg" 36 | EDAM_MIME_TYPE_AMR = "audio/amr" 37 | EDAM_MIME_TYPE_MP4_VIDEO = "video/mp4" 38 | EDAM_MIME_TYPE_INK = "application/vnd.evernote.ink" 39 | EDAM_MIME_TYPE_PDF = "application/pdf" 40 | EDAM_MIME_TYPE_DEFAULT = "application/octet-stream" 41 | EDAM_MIME_TYPES = set([ 42 | "image/gif", 43 | "image/jpeg", 44 | "image/png", 45 | "audio/wav", 46 | "audio/mpeg", 47 | "audio/amr", 48 | "application/vnd.evernote.ink", 49 | "application/pdf", 50 | "video/mp4", 51 | ]) 52 | EDAM_COMMERCE_SERVICE_GOOGLE = "Google" 53 | EDAM_COMMERCE_SERVICE_PAYPAL = "Paypal" 54 | EDAM_COMMERCE_SERVICE_GIFT = "Gift" 55 | EDAM_COMMERCE_SERVICE_TRIALPAY = "TrialPay" 56 | EDAM_COMMERCE_SERVICE_TRIAL = "Trial" 57 | EDAM_COMMERCE_SERVICE_GROUP = "Group" 58 | EDAM_COMMERCE_SERVICE_CYBERSOURCE = "CYBERSRC" 59 | EDAM_COMMERCE_DEFAULT_CURRENCY_COUNTRY_CODE = "USD" 60 | EDAM_SEARCH_QUERY_LEN_MIN = 0 61 | EDAM_SEARCH_QUERY_LEN_MAX = 1024 62 | EDAM_SEARCH_QUERY_REGEX = "^[^\\p{Cc}\\p{Zl}\\p{Zp}]{0,1024}$" 63 | EDAM_HASH_LEN = 16 64 | EDAM_USER_USERNAME_LEN_MIN = 1 65 | EDAM_USER_USERNAME_LEN_MAX = 64 66 | EDAM_USER_USERNAME_REGEX = "^[a-z0-9]([a-z0-9_-]{0,62}[a-z0-9])?$" 67 | EDAM_USER_NAME_LEN_MIN = 1 68 | EDAM_USER_NAME_LEN_MAX = 255 69 | EDAM_USER_NAME_REGEX = "^[^\\p{Cc}\\p{Zl}\\p{Zp}]{1,255}$" 70 | EDAM_TAG_NAME_LEN_MIN = 1 71 | EDAM_TAG_NAME_LEN_MAX = 100 72 | EDAM_TAG_NAME_REGEX = "^[^,\\p{Cc}\\p{Z}]([^,\\p{Cc}\\p{Zl}\\p{Zp}]{0,98}[^,\\p{Cc}\\p{Z}])?$" 73 | EDAM_NOTE_TITLE_LEN_MIN = 1 74 | EDAM_NOTE_TITLE_LEN_MAX = 255 75 | EDAM_NOTE_TITLE_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,253}[^\\p{Cc}\\p{Z}])?$" 76 | EDAM_NOTE_CONTENT_LEN_MIN = 0 77 | EDAM_NOTE_CONTENT_LEN_MAX = 5242880 78 | EDAM_APPLICATIONDATA_NAME_LEN_MIN = 3 79 | EDAM_APPLICATIONDATA_NAME_LEN_MAX = 32 80 | EDAM_APPLICATIONDATA_VALUE_LEN_MIN = 0 81 | EDAM_APPLICATIONDATA_VALUE_LEN_MAX = 4092 82 | EDAM_APPLICATIONDATA_ENTRY_LEN_MAX = 4095 83 | EDAM_APPLICATIONDATA_NAME_REGEX = "^[A-Za-z0-9_.-]{3,32}$" 84 | EDAM_APPLICATIONDATA_VALUE_REGEX = "^[^\\p{Cc}]{0,4092}$" 85 | EDAM_NOTEBOOK_NAME_LEN_MIN = 1 86 | EDAM_NOTEBOOK_NAME_LEN_MAX = 100 87 | EDAM_NOTEBOOK_NAME_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,98}[^\\p{Cc}\\p{Z}])?$" 88 | EDAM_NOTEBOOK_STACK_LEN_MIN = 1 89 | EDAM_NOTEBOOK_STACK_LEN_MAX = 100 90 | EDAM_NOTEBOOK_STACK_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,98}[^\\p{Cc}\\p{Z}])?$" 91 | EDAM_PUBLISHING_URI_LEN_MIN = 1 92 | EDAM_PUBLISHING_URI_LEN_MAX = 255 93 | EDAM_PUBLISHING_URI_REGEX = "^[a-zA-Z0-9.~_+-]{1,255}$" 94 | EDAM_PUBLISHING_URI_PROHIBITED = set([ 95 | "..", 96 | ]) 97 | EDAM_PUBLISHING_DESCRIPTION_LEN_MIN = 1 98 | EDAM_PUBLISHING_DESCRIPTION_LEN_MAX = 200 99 | EDAM_PUBLISHING_DESCRIPTION_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,198}[^\\p{Cc}\\p{Z}])?$" 100 | EDAM_SAVED_SEARCH_NAME_LEN_MIN = 1 101 | EDAM_SAVED_SEARCH_NAME_LEN_MAX = 100 102 | EDAM_SAVED_SEARCH_NAME_REGEX = "^[^\\p{Cc}\\p{Z}]([^\\p{Cc}\\p{Zl}\\p{Zp}]{0,98}[^\\p{Cc}\\p{Z}])?$" 103 | EDAM_USER_PASSWORD_LEN_MIN = 6 104 | EDAM_USER_PASSWORD_LEN_MAX = 64 105 | EDAM_USER_PASSWORD_REGEX = "^[A-Za-z0-9!#$%&'()*+,./:;<=>?@^_`{|}~\\[\\]\\\\-]{6,64}$" 106 | EDAM_NOTE_TAGS_MAX = 100 107 | EDAM_NOTE_RESOURCES_MAX = 1000 108 | EDAM_USER_TAGS_MAX = 100000 109 | EDAM_USER_SAVED_SEARCHES_MAX = 100 110 | EDAM_USER_NOTES_MAX = 100000 111 | EDAM_USER_NOTEBOOKS_MAX = 250 112 | EDAM_USER_RECENT_MAILED_ADDRESSES_MAX = 10 113 | EDAM_USER_MAIL_LIMIT_DAILY_FREE = 50 114 | EDAM_USER_MAIL_LIMIT_DAILY_PREMIUM = 200 115 | EDAM_USER_UPLOAD_LIMIT_FREE = 62914560 116 | EDAM_USER_UPLOAD_LIMIT_PREMIUM = 1073741824 117 | EDAM_NOTE_SIZE_MAX_FREE = 26214400 118 | EDAM_NOTE_SIZE_MAX_PREMIUM = 52428800 119 | EDAM_RESOURCE_SIZE_MAX_FREE = 26214400 120 | EDAM_RESOURCE_SIZE_MAX_PREMIUM = 52428800 121 | EDAM_USER_LINKED_NOTEBOOK_MAX = 100 122 | EDAM_NOTEBOOK_SHARED_NOTEBOOK_MAX = 250 123 | EDAM_NOTE_CONTENT_CLASS_LEN_MIN = 3 124 | EDAM_NOTE_CONTENT_CLASS_LEN_MAX = 32 125 | EDAM_HELLO_APP_CONTENT_CLASS_PREFIX = "evernote.hello." 126 | EDAM_FOOD_APP_CONTENT_CLASS_PREFIX = "evernote.food." 127 | EDAM_NOTE_CONTENT_CLASS_REGEX = "^[A-Za-z0-9_.-]{3,32}$" 128 | EDAM_CONTENT_CLASS_HELLO_ENCOUNTER = "evernote.hello.encounter" 129 | EDAM_CONTENT_CLASS_HELLO_PROFILE = "evernote.hello.profile" 130 | EDAM_CONTENT_CLASS_FOOD_MEAL = "evernote.food.meal" 131 | -------------------------------------------------------------------------------- /evernote/edam/limits/ttypes.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | 11 | from thrift.transport import TTransport 12 | from thrift.protocol import TBinaryProtocol, TProtocol 13 | try: 14 | from thrift.protocol import fastbinary 15 | except: 16 | fastbinary = None 17 | 18 | 19 | -------------------------------------------------------------------------------- /evernote/edam/notestore/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants', 'NoteStore'] 2 | -------------------------------------------------------------------------------- /evernote/edam/notestore/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | -------------------------------------------------------------------------------- /evernote/edam/type/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants'] 2 | -------------------------------------------------------------------------------- /evernote/edam/type/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | EDAM_NOTE_SOURCE_WEB_CLIP = "web.clip" 13 | EDAM_NOTE_SOURCE_MAIL_CLIP = "mail.clip" 14 | EDAM_NOTE_SOURCE_MAIL_SMTP_GATEWAY = "mail.smtp" 15 | -------------------------------------------------------------------------------- /evernote/edam/userstore/UserStore-remote: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 4 | # 5 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 6 | # 7 | # options string: py:new_style 8 | # 9 | 10 | import sys 11 | import pprint 12 | from urlparse import urlparse 13 | from thrift.transport import TTransport 14 | from thrift.transport import TSocket 15 | from thrift.transport import THttpClient 16 | from thrift.protocol import TBinaryProtocol 17 | 18 | import UserStore 19 | from ttypes import * 20 | 21 | if len(sys.argv) <= 1 or sys.argv[1] == '--help': 22 | print '' 23 | print 'Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] function [arg1 [arg2...]]' 24 | print '' 25 | print 'Functions:' 26 | print ' bool checkVersion(string clientName, i16 edamVersionMajor, i16 edamVersionMinor)' 27 | print ' BootstrapInfo getBootstrapInfo(string locale)' 28 | print ' AuthenticationResult authenticate(string username, string password, string consumerKey, string consumerSecret)' 29 | print ' AuthenticationResult refreshAuthentication(string authenticationToken)' 30 | print ' User getUser(string authenticationToken)' 31 | print ' PublicUserInfo getPublicUserInfo(string username)' 32 | print ' PremiumInfo getPremiumInfo(string authenticationToken)' 33 | print ' string getNoteStoreUrl(string authenticationToken)' 34 | print '' 35 | sys.exit(0) 36 | 37 | pp = pprint.PrettyPrinter(indent = 2) 38 | host = 'localhost' 39 | port = 9090 40 | uri = '' 41 | framed = False 42 | http = False 43 | argi = 1 44 | 45 | if sys.argv[argi] == '-h': 46 | parts = sys.argv[argi+1].split(':') 47 | host = parts[0] 48 | if len(parts) > 1: 49 | port = int(parts[1]) 50 | argi += 2 51 | 52 | if sys.argv[argi] == '-u': 53 | url = urlparse(sys.argv[argi+1]) 54 | parts = url[1].split(':') 55 | host = parts[0] 56 | if len(parts) > 1: 57 | port = int(parts[1]) 58 | else: 59 | port = 80 60 | uri = url[2] 61 | if url[4]: 62 | uri += '?%s' % url[4] 63 | http = True 64 | argi += 2 65 | 66 | if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed': 67 | framed = True 68 | argi += 1 69 | 70 | cmd = sys.argv[argi] 71 | args = sys.argv[argi+1:] 72 | 73 | if http: 74 | transport = THttpClient.THttpClient(host, port, uri) 75 | else: 76 | socket = TSocket.TSocket(host, port) 77 | if framed: 78 | transport = TTransport.TFramedTransport(socket) 79 | else: 80 | transport = TTransport.TBufferedTransport(socket) 81 | protocol = TBinaryProtocol.TBinaryProtocol(transport) 82 | client = UserStore.Client(protocol) 83 | transport.open() 84 | 85 | if cmd == 'checkVersion': 86 | if len(args) != 3: 87 | print 'checkVersion requires 3 args' 88 | sys.exit(1) 89 | pp.pprint(client.checkVersion(args[0],eval(args[1]),eval(args[2]),)) 90 | 91 | elif cmd == 'getBootstrapInfo': 92 | if len(args) != 1: 93 | print 'getBootstrapInfo requires 1 args' 94 | sys.exit(1) 95 | pp.pprint(client.getBootstrapInfo(args[0],)) 96 | 97 | elif cmd == 'authenticate': 98 | if len(args) != 4: 99 | print 'authenticate requires 4 args' 100 | sys.exit(1) 101 | pp.pprint(client.authenticate(args[0],args[1],args[2],args[3],)) 102 | 103 | elif cmd == 'refreshAuthentication': 104 | if len(args) != 1: 105 | print 'refreshAuthentication requires 1 args' 106 | sys.exit(1) 107 | pp.pprint(client.refreshAuthentication(args[0],)) 108 | 109 | elif cmd == 'getUser': 110 | if len(args) != 1: 111 | print 'getUser requires 1 args' 112 | sys.exit(1) 113 | pp.pprint(client.getUser(args[0],)) 114 | 115 | elif cmd == 'getPublicUserInfo': 116 | if len(args) != 1: 117 | print 'getPublicUserInfo requires 1 args' 118 | sys.exit(1) 119 | pp.pprint(client.getPublicUserInfo(args[0],)) 120 | 121 | elif cmd == 'getPremiumInfo': 122 | if len(args) != 1: 123 | print 'getPremiumInfo requires 1 args' 124 | sys.exit(1) 125 | pp.pprint(client.getPremiumInfo(args[0],)) 126 | 127 | elif cmd == 'getNoteStoreUrl': 128 | if len(args) != 1: 129 | print 'getNoteStoreUrl requires 1 args' 130 | sys.exit(1) 131 | pp.pprint(client.getNoteStoreUrl(args[0],)) 132 | 133 | else: 134 | print 'Unrecognized method %s' % cmd 135 | sys.exit(1) 136 | 137 | transport.close() 138 | -------------------------------------------------------------------------------- /evernote/edam/userstore/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants', 'UserStore'] 2 | -------------------------------------------------------------------------------- /evernote/edam/userstore/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.5.0-en-262021) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py:new_style 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TException, TApplicationException 10 | from ttypes import * 11 | 12 | EDAM_VERSION_MAJOR = 1 13 | EDAM_VERSION_MINOR = 21 14 | -------------------------------------------------------------------------------- /everpad.pro: -------------------------------------------------------------------------------- 1 | SOURCES = everpad/tools.py \ 2 | everpad/__init__.py \ 3 | everpad/specific/kde/__init__.py \ 4 | everpad/specific/kde/everpad_runner.py \ 5 | everpad/specific/__init__.py \ 6 | everpad/specific/unity/__init__.py \ 7 | everpad/specific/unity/launcher.py \ 8 | everpad/specific/unity/lens.py \ 9 | everpad/basetypes.py \ 10 | everpad/pad/tools.py \ 11 | everpad/pad/list.py \ 12 | everpad/pad/__init__.py \ 13 | everpad/pad/treeview.py \ 14 | everpad/pad/editor/widgets.py \ 15 | everpad/pad/editor/actions.py \ 16 | everpad/pad/editor/__init__.py \ 17 | everpad/pad/editor/resources.py \ 18 | everpad/pad/editor/content.py \ 19 | everpad/pad/indicator.py \ 20 | everpad/pad/management.py \ 21 | everpad/const.py \ 22 | everpad/provider/tools.py \ 23 | everpad/provider/daemon.py \ 24 | everpad/provider/__init__.py \ 25 | everpad/provider/models.py \ 26 | everpad/provider/sync.py \ 27 | everpad/provider/service.py \ 28 | everpad/interface/list.py \ 29 | everpad/interface/__init__.py \ 30 | everpad/interface/notebook.py \ 31 | everpad/interface/editor.py \ 32 | everpad/interface/findbar.py \ 33 | everpad/interface/tableinsert.py \ 34 | everpad/interface/image.py \ 35 | everpad/interface/management.py \ 36 | everpad/monkey.py 37 | 38 | TRANSLATIONS = i18n/ru_RU.ts \ 39 | i18n/ar_EG.ts \ 40 | i18n/zh_CN.ts \ 41 | i18n/zh_TW.ts \ 42 | i18n/ja.ts \ 43 | i18n/nl.ts \ 44 | i18n/de_DE.ts \ 45 | i18n/de_AT.ts \ 46 | i18n/de_CH.ts 47 | 48 | -------------------------------------------------------------------------------- /everpad/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/everpad/__init__.py -------------------------------------------------------------------------------- /everpad/basetypes.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.orm.exc import NoResultFound 2 | 3 | 4 | NONE_ID = 0 5 | NONE_VAL = 0 6 | 7 | 8 | class DbusSendableList(object): 9 | """Dbus sendable list""" 10 | 11 | def __init__(self, cls): 12 | self._cls = cls 13 | 14 | def __rshift__(self, other): 15 | """Shortcut to from_obj and struct""" 16 | return [self._cls.from_obj(item).struct for item in other] 17 | 18 | def __lshift__(self, other): 19 | """Shortcut to from_tuple""" 20 | return [self._cls.from_tuple(item) for item in other] 21 | 22 | 23 | class BaseDbusSendable(type): 24 | @property 25 | def signature(cls): 26 | return '(' + ''.join(map( 27 | lambda field: field[1], 28 | cls.fields, 29 | )) + ')' 30 | 31 | def __rshift__(cls, other): 32 | """Shortcut to from_obj and struct""" 33 | return cls.from_obj(other).struct 34 | 35 | def __lshift__(cls, other): 36 | """Shortcut to from_tuple""" 37 | return cls.from_tuple(other) 38 | 39 | @property 40 | def list(cls): 41 | """Return shortcut for list mapping""" 42 | return DbusSendableList(cls) 43 | 44 | 45 | class DbusSendable(object): 46 | __metaclass__ = BaseDbusSendable 47 | fields = tuple() 48 | 49 | def __init__(self, **kwargs): 50 | for key, val in kwargs.items(): 51 | setattr(self, key, val) 52 | 53 | @classmethod 54 | def from_obj(cls, data): 55 | inst = cls() 56 | for field in cls.fields: 57 | if hasattr(data, field[0] + '_dbus'): 58 | val = getattr(data, field[0] + '_dbus') 59 | else: 60 | val = getattr(data, field[0], None) 61 | if hasattr(val, '__call__'): 62 | val = val() 63 | setattr(inst, field[0], val) 64 | return inst 65 | 66 | @classmethod 67 | def from_tuple(cls, data): 68 | inst = cls() 69 | for num, field in enumerate(cls.fields): 70 | setattr(inst, field[0], data[num]) 71 | return inst 72 | 73 | @property 74 | def struct(self): 75 | result = [] 76 | for field in self.fields: 77 | result.append(getattr(self, field[0], None)) 78 | return tuple(result) 79 | 80 | def give_to_obj(self, obj): 81 | for field in self.fields: 82 | val = getattr(self, field[0]) 83 | try: 84 | # check exists, hasattr fails with fresh sqlalchemy 85 | # with object has no attribute '_sa_instance_state' 86 | try: 87 | getattr(obj, field[0] + '_dbus') 88 | except NoResultFound: 89 | # pass when fields is one-to-one relation 90 | pass 91 | 92 | setattr(obj, field[0] + '_dbus', val) 93 | except AttributeError: 94 | setattr(obj, field[0], val) 95 | 96 | def __repr__(self): 97 | return "<%s:\n%s>" % ( 98 | type(self).__name__, 99 | "\n".join(map( 100 | lambda field: '%s: %s' % ( 101 | field[0], str(getattr(self, field[0], '')), 102 | ), self.fields, 103 | )) 104 | ) 105 | 106 | 107 | class Note(DbusSendable): 108 | ORDER_TITLE = 0 109 | ORDER_UPDATED = 1 110 | ORDER_TITLE_DESC = 2 111 | ORDER_UPDATED_DESC = 3 112 | 113 | fields = ( 114 | ('id', 'i'), 115 | ('title', 's'), 116 | ('content', 's'), 117 | ('created', 'x'), 118 | ('updated', 'x'), 119 | ('notebook', 'i'), 120 | ('tags', 'as'), 121 | ('place', 's'), 122 | ('pinnded', 'b'), 123 | ('conflict_parent', 'i'), 124 | ('conflict_items', 'ai'), 125 | ('share_date', 'x'), 126 | ('share_url', 's'), 127 | ) 128 | 129 | 130 | class Notebook(DbusSendable): 131 | fields = ( 132 | ('id', 'i'), 133 | ('name', 's'), 134 | ('default', 'i'), 135 | ('stack', 's') 136 | ) 137 | 138 | 139 | class Tag(DbusSendable): 140 | fields = ( 141 | ('id', 'i'), 142 | ('name', 's'), 143 | ) 144 | 145 | 146 | class Resource(DbusSendable): 147 | fields = ( 148 | ('id', 'i'), 149 | ('file_name', 's'), 150 | ('file_path', 's'), 151 | ('mime', 's'), 152 | ('hash', 's'), 153 | ) 154 | 155 | 156 | class Place(DbusSendable): 157 | fields = ( 158 | ('id', 'i'), 159 | ('name', 's'), 160 | ) 161 | -------------------------------------------------------------------------------- /everpad/const.py: -------------------------------------------------------------------------------- 1 | CONSUMER_KEY = 'nvbn-1422' 2 | CONSUMER_SECRET = 'c17c0979d0054310' 3 | HOST = 'www.evernote.com' 4 | STATUS_NONE = 0 5 | STATUS_SYNC = 1 6 | DEFAULT_SYNC_DELAY = 30000 * 60 7 | SYNC_STATE_START = 0 8 | SYNC_STATE_NOTEBOOKS_LOCAL = 1 9 | SYNC_STATE_TAGS_LOCAL = 2 10 | SYNC_STATE_NOTES_LOCAL = 3 11 | SYNC_STATE_NOTEBOOKS_REMOTE = 4 12 | SYNC_STATE_TAGS_REMOTE = 5 13 | SYNC_STATE_NOTES_REMOTE = 6 14 | SYNC_STATE_SHARE = 7 15 | SYNC_STATE_STOP_SHARE = 8 16 | SYNC_STATE_FINISH = 9 17 | SYNC_MANUAL = -1 18 | SYNC_STATES = ( 19 | SYNC_STATE_START, SYNC_STATE_NOTEBOOKS_LOCAL, 20 | SYNC_STATE_TAGS_LOCAL, SYNC_STATE_NOTES_LOCAL, 21 | SYNC_STATE_NOTEBOOKS_REMOTE, SYNC_STATE_TAGS_REMOTE, 22 | SYNC_STATE_NOTES_REMOTE, SYNC_STATE_FINISH, 23 | ) 24 | DEFAULT_FONT = 'Sans' 25 | DEFAULT_FONT_SIZE = 14 26 | DEFAULT_INDICATOR_LAYOUT = [ 27 | 'create_note', 'pin_notes', 'notes', 'all_notes', 'sync', 28 | ] 29 | 30 | SCHEMA_VERSION = 5 31 | API_VERSION = 6 32 | VERSION = '2.5' 33 | DB_PATH = "~/.everpad/everpad.%s.db" % SCHEMA_VERSION 34 | 35 | ACTION_NONE = 0 36 | ACTION_CREATE = 1 37 | ACTION_DELETE = 2 38 | ACTION_CHANGE = 3 39 | ACTION_NOEXSIST = 4 40 | ACTION_CONFLICT = 5 41 | ACTION_DUPLICATE = 6 42 | 43 | DISABLED_ACTIONS = (ACTION_DELETE, ACTION_NOEXSIST, ACTION_CONFLICT) 44 | 45 | SHARE_NONE = 0 46 | SHARE_NEED_SHARE = 1 47 | SHARE_SHARED = 2 48 | SHARE_NEED_STOP = 3 49 | 50 | NONE_ID = 0 51 | NONE_VAL = 0 52 | 53 | ORDER_TITLE = 0 54 | ORDER_UPDATED = 1 55 | ORDER_TITLE_DESC = 2 56 | ORDER_UPDATED_DESC = 3 57 | 58 | DEFAULT_LIMIT = 100 59 | NOT_PINNDED = -1 60 | -------------------------------------------------------------------------------- /everpad/interface/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/everpad/interface/__init__.py -------------------------------------------------------------------------------- /everpad/interface/editor.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Editor 4 | 5 | 6 | 7 | 0 8 | 0 9 | 565 10 | 381 11 | 12 | 13 | 14 | Everpad 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 0 25 | 0 26 | 27 | 28 | 29 | 30 | about:blank 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Select notebook 43 | 44 | 45 | 46 | 47 | 48 | 49 | Note tags 50 | 51 | 52 | Commas separated tags 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | %d attached files, <a href='#'>show</a> / <a href='#'> add another</a> 62 | 63 | 64 | Qt::AutoText 65 | 66 | 67 | 68 | 69 | 70 | 71 | This note has alternative versions: %s 72 | 73 | 74 | true 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 0 83 | 0 84 | 85 | 86 | 87 | Qt::ScrollBarAlwaysOff 88 | 89 | 90 | Qt::ScrollBarAsNeeded 91 | 92 | 93 | true 94 | 95 | 96 | 97 | 98 | 0 99 | 0 100 | 545 101 | 76 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | toolBar 112 | 113 | 114 | false 115 | 116 | 117 | TopToolBarArea 118 | 119 | 120 | false 121 | 122 | 123 | 124 | 125 | 126 | 0 127 | 0 128 | 565 129 | 25 130 | 131 | 132 | 133 | 134 | Note 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | Edit 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | Save 155 | 156 | 157 | 158 | 159 | Save and close 160 | 161 | 162 | 163 | 164 | Delete 165 | 166 | 167 | 168 | 169 | Close 170 | 171 | 172 | 173 | 174 | Cut 175 | 176 | 177 | 178 | 179 | Copy 180 | 181 | 182 | 183 | 184 | Paste 185 | 186 | 187 | 188 | 189 | Find 190 | 191 | 192 | 193 | 194 | 195 | QWebView 196 | QWidget 197 |
QtWebKit/QWebView
198 |
199 |
200 | 201 | 202 |
203 | -------------------------------------------------------------------------------- /everpad/interface/findbar.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'everpad/interface/findbar.ui' 4 | # 5 | # Created: Sun Oct 21 07:01:56 2012 6 | # by: pyside-uic 0.2.13 running on PySide 1.1.0 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide import QtCore, QtGui 11 | 12 | class Ui_FindBar(object): 13 | def setupUi(self, FindBar): 14 | FindBar.setObjectName("FindBar") 15 | FindBar.resize(750, 33) 16 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Minimum) 17 | sizePolicy.setHorizontalStretch(0) 18 | sizePolicy.setVerticalStretch(0) 19 | sizePolicy.setHeightForWidth(FindBar.sizePolicy().hasHeightForWidth()) 20 | FindBar.setSizePolicy(sizePolicy) 21 | self.horizontalLayout = QtGui.QHBoxLayout(FindBar) 22 | self.horizontalLayout.setContentsMargins(3, 3, 3, 3) 23 | self.horizontalLayout.setObjectName("horizontalLayout") 24 | self.btnClose = QtGui.QPushButton(FindBar) 25 | self.btnClose.setText("") 26 | icon = QtGui.QIcon() 27 | icon.addPixmap(QtGui.QPixmap("../../../../.designer/backup"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 28 | self.btnClose.setIcon(icon) 29 | self.btnClose.setObjectName("btnClose") 30 | self.horizontalLayout.addWidget(self.btnClose) 31 | self.lblFind = QtGui.QLabel(FindBar) 32 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) 33 | sizePolicy.setHorizontalStretch(0) 34 | sizePolicy.setVerticalStretch(0) 35 | sizePolicy.setHeightForWidth(self.lblFind.sizePolicy().hasHeightForWidth()) 36 | self.lblFind.setSizePolicy(sizePolicy) 37 | self.lblFind.setObjectName("lblFind") 38 | self.horizontalLayout.addWidget(self.lblFind) 39 | self.edtFindText = QtGui.QLineEdit(FindBar) 40 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) 41 | sizePolicy.setHorizontalStretch(0) 42 | sizePolicy.setVerticalStretch(0) 43 | sizePolicy.setHeightForWidth(self.edtFindText.sizePolicy().hasHeightForWidth()) 44 | self.edtFindText.setSizePolicy(sizePolicy) 45 | self.edtFindText.setMinimumSize(QtCore.QSize(240, 0)) 46 | self.edtFindText.setObjectName("edtFindText") 47 | self.horizontalLayout.addWidget(self.edtFindText) 48 | self.btnPrevious = QtGui.QPushButton(FindBar) 49 | icon1 = QtGui.QIcon() 50 | icon1.addPixmap(QtGui.QPixmap("../../../../../../.designer/backup"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 51 | self.btnPrevious.setIcon(icon1) 52 | self.btnPrevious.setObjectName("btnPrevious") 53 | self.horizontalLayout.addWidget(self.btnPrevious) 54 | self.btnNext = QtGui.QPushButton(FindBar) 55 | self.btnNext.setIcon(icon1) 56 | self.btnNext.setObjectName("btnNext") 57 | self.horizontalLayout.addWidget(self.btnNext) 58 | self.btnHighlight = QtGui.QPushButton(FindBar) 59 | self.btnHighlight.setCheckable(True) 60 | self.btnHighlight.setObjectName("btnHighlight") 61 | self.horizontalLayout.addWidget(self.btnHighlight) 62 | self.chkMatchCase = QtGui.QCheckBox(FindBar) 63 | self.chkMatchCase.setObjectName("chkMatchCase") 64 | self.horizontalLayout.addWidget(self.chkMatchCase) 65 | spacerItem = QtGui.QSpacerItem(0, 0, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) 66 | self.horizontalLayout.addItem(spacerItem) 67 | 68 | self.retranslateUi(FindBar) 69 | QtCore.QMetaObject.connectSlotsByName(FindBar) 70 | 71 | def retranslateUi(self, FindBar): 72 | FindBar.setWindowTitle(QtGui.QApplication.translate("FindBar", "Form", None, QtGui.QApplication.UnicodeUTF8)) 73 | self.lblFind.setText(QtGui.QApplication.translate("FindBar", "Find:", None, QtGui.QApplication.UnicodeUTF8)) 74 | self.btnPrevious.setText(QtGui.QApplication.translate("FindBar", "Previous", None, QtGui.QApplication.UnicodeUTF8)) 75 | self.btnNext.setText(QtGui.QApplication.translate("FindBar", "Next", None, QtGui.QApplication.UnicodeUTF8)) 76 | self.btnHighlight.setText(QtGui.QApplication.translate("FindBar", "Highlight All", None, QtGui.QApplication.UnicodeUTF8)) 77 | self.chkMatchCase.setText(QtGui.QApplication.translate("FindBar", "Match Case", None, QtGui.QApplication.UnicodeUTF8)) 78 | 79 | -------------------------------------------------------------------------------- /everpad/interface/findbar.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | FindBar 4 | 5 | 6 | 7 | 0 8 | 0 9 | 750 10 | 33 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | Form 21 | 22 | 23 | 24 | 3 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ../../../../.designer/backup../../../../.designer/backup 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 0 42 | 0 43 | 44 | 45 | 46 | Find: 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 0 55 | 0 56 | 57 | 58 | 59 | 60 | 240 61 | 0 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | Previous 70 | 71 | 72 | 73 | ../../../../../../.designer/backup../../../../../../.designer/backup 74 | 75 | 76 | 77 | 78 | 79 | 80 | Next 81 | 82 | 83 | 84 | ../../../../../../.designer/backup../../../../../../.designer/backup 85 | 86 | 87 | 88 | 89 | 90 | 91 | Highlight All 92 | 93 | 94 | true 95 | 96 | 97 | 98 | 99 | 100 | 101 | Match Case 102 | 103 | 104 | 105 | 106 | 107 | 108 | Qt::Horizontal 109 | 110 | 111 | 112 | 0 113 | 0 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /everpad/interface/image.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'image.ui' 4 | # 5 | # Created: Sat Sep 29 16:13:53 2012 6 | # by: pyside-uic 0.2.13 running on PySide 1.1.0 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide import QtCore, QtGui 11 | 12 | class Ui_ImageDialog(object): 13 | def setupUi(self, ImageDialog): 14 | ImageDialog.setObjectName("ImageDialog") 15 | ImageDialog.setWindowModality(QtCore.Qt.WindowModal) 16 | ImageDialog.resize(248, 164) 17 | ImageDialog.setModal(True) 18 | self.gridLayout = QtGui.QGridLayout(ImageDialog) 19 | self.gridLayout.setObjectName("gridLayout") 20 | self.label = QtGui.QLabel(ImageDialog) 21 | self.label.setObjectName("label") 22 | self.gridLayout.addWidget(self.label, 0, 0, 1, 1) 23 | self.widthBox = QtGui.QSpinBox(ImageDialog) 24 | self.widthBox.setMinimum(1) 25 | self.widthBox.setMaximum(99999) 26 | self.widthBox.setObjectName("widthBox") 27 | self.gridLayout.addWidget(self.widthBox, 0, 1, 1, 1) 28 | self.label_2 = QtGui.QLabel(ImageDialog) 29 | self.label_2.setObjectName("label_2") 30 | self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) 31 | self.heightBox = QtGui.QSpinBox(ImageDialog) 32 | self.heightBox.setMinimum(1) 33 | self.heightBox.setMaximum(99999) 34 | self.heightBox.setObjectName("heightBox") 35 | self.gridLayout.addWidget(self.heightBox, 1, 1, 1, 1) 36 | self.checkBox = QtGui.QCheckBox(ImageDialog) 37 | self.checkBox.setChecked(True) 38 | self.checkBox.setObjectName("checkBox") 39 | self.gridLayout.addWidget(self.checkBox, 2, 0, 1, 2) 40 | self.buttonBox = QtGui.QDialogButtonBox(ImageDialog) 41 | self.buttonBox.setOrientation(QtCore.Qt.Horizontal) 42 | self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) 43 | self.buttonBox.setObjectName("buttonBox") 44 | self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2) 45 | 46 | self.retranslateUi(ImageDialog) 47 | QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), ImageDialog.accept) 48 | QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), ImageDialog.reject) 49 | QtCore.QMetaObject.connectSlotsByName(ImageDialog) 50 | 51 | def retranslateUi(self, ImageDialog): 52 | ImageDialog.setWindowTitle(QtGui.QApplication.translate("ImageDialog", "Everpad / Image Preferences", None, QtGui.QApplication.UnicodeUTF8)) 53 | self.label.setText(QtGui.QApplication.translate("ImageDialog", "Width", None, QtGui.QApplication.UnicodeUTF8)) 54 | self.label_2.setText(QtGui.QApplication.translate("ImageDialog", "Height", None, QtGui.QApplication.UnicodeUTF8)) 55 | self.checkBox.setText(QtGui.QApplication.translate("ImageDialog", "Discard ratio", None, QtGui.QApplication.UnicodeUTF8)) 56 | 57 | -------------------------------------------------------------------------------- /everpad/interface/image.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ImageDialog 4 | 5 | 6 | Qt::WindowModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 248 13 | 164 14 | 15 | 16 | 17 | Everpad / Image Preferences 18 | 19 | 20 | true 21 | 22 | 23 | 24 | 25 | 26 | Width 27 | 28 | 29 | 30 | 31 | 32 | 33 | 1 34 | 35 | 36 | 99999 37 | 38 | 39 | 40 | 41 | 42 | 43 | Height 44 | 45 | 46 | 47 | 48 | 49 | 50 | 1 51 | 52 | 53 | 99999 54 | 55 | 56 | 57 | 58 | 59 | 60 | Discard ratio 61 | 62 | 63 | true 64 | 65 | 66 | 67 | 68 | 69 | 70 | Qt::Horizontal 71 | 72 | 73 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | buttonBox 83 | accepted() 84 | ImageDialog 85 | accept() 86 | 87 | 88 | 248 89 | 254 90 | 91 | 92 | 157 93 | 274 94 | 95 | 96 | 97 | 98 | buttonBox 99 | rejected() 100 | ImageDialog 101 | reject() 102 | 103 | 104 | 316 105 | 260 106 | 107 | 108 | 286 109 | 274 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /everpad/interface/list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'list.ui' 4 | # 5 | # Created: Sun Feb 24 10:42:09 2013 6 | # by: pyside-uic 0.2.13 running on PySide 1.1.1 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide import QtCore, QtGui 11 | 12 | class Ui_List(object): 13 | def setupUi(self, List): 14 | List.setObjectName("List") 15 | List.resize(800, 600) 16 | self.centralwidget = QtGui.QWidget(List) 17 | self.centralwidget.setObjectName("centralwidget") 18 | self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget) 19 | self.horizontalLayout.setObjectName("horizontalLayout") 20 | self.splitter = QtGui.QSplitter(self.centralwidget) 21 | self.splitter.setOrientation(QtCore.Qt.Horizontal) 22 | self.splitter.setChildrenCollapsible(False) 23 | self.splitter.setObjectName("splitter") 24 | self.layoutWidget = QtGui.QWidget(self.splitter) 25 | self.layoutWidget.setObjectName("layoutWidget") 26 | self.verticalLayout_2 = QtGui.QVBoxLayout(self.layoutWidget) 27 | self.verticalLayout_2.setSizeConstraint(QtGui.QLayout.SetMinimumSize) 28 | self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) 29 | self.verticalLayout_2.setObjectName("verticalLayout_2") 30 | self.horizontalLayout_2 = QtGui.QHBoxLayout() 31 | self.horizontalLayout_2.setSizeConstraint(QtGui.QLayout.SetMinimumSize) 32 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 33 | self.newNotebookBtn = QtGui.QPushButton(self.layoutWidget) 34 | self.newNotebookBtn.setObjectName("newNotebookBtn") 35 | self.horizontalLayout_2.addWidget(self.newNotebookBtn) 36 | self.newNoteBtn = QtGui.QPushButton(self.layoutWidget) 37 | self.newNoteBtn.setObjectName("newNoteBtn") 38 | self.horizontalLayout_2.addWidget(self.newNoteBtn) 39 | self.verticalLayout_2.addLayout(self.horizontalLayout_2) 40 | self.notebooksList = EverpadTreeView(self.layoutWidget) 41 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) 42 | sizePolicy.setHorizontalStretch(0) 43 | sizePolicy.setVerticalStretch(0) 44 | sizePolicy.setHeightForWidth(self.notebooksList.sizePolicy().hasHeightForWidth()) 45 | self.notebooksList.setSizePolicy(sizePolicy) 46 | self.notebooksList.setMinimumSize(QtCore.QSize(200, 0)) 47 | self.notebooksList.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) 48 | self.notebooksList.setHeaderHidden(True) 49 | self.notebooksList.setObjectName("notebooksList") 50 | self.verticalLayout_2.addWidget(self.notebooksList) 51 | self.tagsList = EverpadTreeView(self.layoutWidget) 52 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) 53 | sizePolicy.setHorizontalStretch(0) 54 | sizePolicy.setVerticalStretch(0) 55 | sizePolicy.setHeightForWidth(self.tagsList.sizePolicy().hasHeightForWidth()) 56 | self.tagsList.setSizePolicy(sizePolicy) 57 | self.tagsList.setMinimumSize(QtCore.QSize(200, 0)) 58 | self.tagsList.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) 59 | self.tagsList.setHeaderHidden(True) 60 | self.tagsList.setObjectName("tagsList") 61 | self.verticalLayout_2.addWidget(self.tagsList) 62 | self.notesList = EverpadTreeView(self.splitter) 63 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) 64 | sizePolicy.setHorizontalStretch(1) 65 | sizePolicy.setVerticalStretch(0) 66 | sizePolicy.setHeightForWidth(self.notesList.sizePolicy().hasHeightForWidth()) 67 | self.notesList.setSizePolicy(sizePolicy) 68 | self.notesList.setMinimumSize(QtCore.QSize(300, 0)) 69 | self.notesList.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) 70 | self.notesList.setSortingEnabled(True) 71 | self.notesList.setObjectName("notesList") 72 | self.notesList.header().setDefaultSectionSize(200) 73 | self.notesList.header().setSortIndicatorShown(True) 74 | self.horizontalLayout.addWidget(self.splitter) 75 | List.setCentralWidget(self.centralwidget) 76 | self.menubar = QtGui.QMenuBar(List) 77 | self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25)) 78 | self.menubar.setObjectName("menubar") 79 | List.setMenuBar(self.menubar) 80 | self.statusbar = QtGui.QStatusBar(List) 81 | self.statusbar.setObjectName("statusbar") 82 | List.setStatusBar(self.statusbar) 83 | 84 | self.retranslateUi(List) 85 | QtCore.QMetaObject.connectSlotsByName(List) 86 | 87 | def retranslateUi(self, List): 88 | List.setWindowTitle(QtGui.QApplication.translate("List", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) 89 | self.newNotebookBtn.setToolTip(QtGui.QApplication.translate("List", "Create Notebook", None, QtGui.QApplication.UnicodeUTF8)) 90 | self.newNotebookBtn.setText(QtGui.QApplication.translate("List", "Notebook", None, QtGui.QApplication.UnicodeUTF8)) 91 | self.newNoteBtn.setToolTip(QtGui.QApplication.translate("List", "Create Note", None, QtGui.QApplication.UnicodeUTF8)) 92 | self.newNoteBtn.setText(QtGui.QApplication.translate("List", "Note", None, QtGui.QApplication.UnicodeUTF8)) 93 | 94 | from everpad.pad.treeview import EverpadTreeView 95 | -------------------------------------------------------------------------------- /everpad/interface/list.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | List 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 600 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | Qt::Horizontal 22 | 23 | 24 | false 25 | 26 | 27 | 28 | 29 | QLayout::SetMinimumSize 30 | 31 | 32 | 33 | 34 | QLayout::SetMinimumSize 35 | 36 | 37 | 38 | 39 | Create Notebook 40 | 41 | 42 | Notebook 43 | 44 | 45 | 46 | 47 | 48 | 49 | Create Note 50 | 51 | 52 | Note 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 0 63 | 0 64 | 65 | 66 | 67 | 68 | 200 69 | 0 70 | 71 | 72 | 73 | QAbstractItemView::NoEditTriggers 74 | 75 | 76 | true 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 0 85 | 0 86 | 87 | 88 | 89 | 90 | 200 91 | 0 92 | 93 | 94 | 95 | QAbstractItemView::NoEditTriggers 96 | 97 | 98 | true 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 1 108 | 0 109 | 110 | 111 | 112 | 113 | 300 114 | 0 115 | 116 | 117 | 118 | QAbstractItemView::NoEditTriggers 119 | 120 | 121 | true 122 | 123 | 124 | 200 125 | 126 | 127 | true 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 0 138 | 0 139 | 800 140 | 25 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | EverpadTreeView 149 | QTreeView 150 |
everpad/pad/treeview
151 |
152 |
153 | 154 | 155 |
156 | -------------------------------------------------------------------------------- /everpad/interface/notebook.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'notebook.ui' 4 | # 5 | # Created: Sun Aug 19 18:09:52 2012 6 | # by: pyside-uic 0.2.13 running on PySide 1.1.0 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide import QtCore, QtGui 11 | 12 | class Ui_Notebook(object): 13 | def setupUi(self, Notebook): 14 | Notebook.setObjectName("Notebook") 15 | Notebook.resize(296, 60) 16 | self.horizontalLayout = QtGui.QHBoxLayout(Notebook) 17 | self.horizontalLayout.setObjectName("horizontalLayout") 18 | self.verticalLayout = QtGui.QVBoxLayout() 19 | self.verticalLayout.setObjectName("verticalLayout") 20 | self.name = QtGui.QLabel(Notebook) 21 | font = QtGui.QFont() 22 | font.setWeight(75) 23 | font.setBold(True) 24 | self.name.setFont(font) 25 | self.name.setObjectName("name") 26 | self.verticalLayout.addWidget(self.name) 27 | self.content = QtGui.QLabel(Notebook) 28 | font = QtGui.QFont() 29 | font.setWeight(50) 30 | font.setBold(False) 31 | self.content.setFont(font) 32 | self.content.setObjectName("content") 33 | self.verticalLayout.addWidget(self.content) 34 | self.horizontalLayout.addLayout(self.verticalLayout) 35 | spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) 36 | self.horizontalLayout.addItem(spacerItem) 37 | self.actionBtn = QtGui.QPushButton(Notebook) 38 | self.actionBtn.setText("") 39 | self.actionBtn.setObjectName("actionBtn") 40 | self.horizontalLayout.addWidget(self.actionBtn) 41 | 42 | self.retranslateUi(Notebook) 43 | QtCore.QMetaObject.connectSlotsByName(Notebook) 44 | 45 | def retranslateUi(self, Notebook): 46 | Notebook.setWindowTitle(QtGui.QApplication.translate("Notebook", "Form", None, QtGui.QApplication.UnicodeUTF8)) 47 | self.name.setText(QtGui.QApplication.translate("Notebook", "Notebook name", None, QtGui.QApplication.UnicodeUTF8)) 48 | self.content.setText(QtGui.QApplication.translate("Notebook", "Contains 5 notes", None, QtGui.QApplication.UnicodeUTF8)) 49 | 50 | -------------------------------------------------------------------------------- /everpad/interface/notebook.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Notebook 4 | 5 | 6 | 7 | 0 8 | 0 9 | 296 10 | 60 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 75 24 | true 25 | 26 | 27 | 28 | Notebook name 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 50 37 | false 38 | 39 | 40 | 41 | Contains 5 notes 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Qt::Horizontal 51 | 52 | 53 | 54 | 40 55 | 20 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /everpad/interface/share_note.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'share_note.ui' 4 | # 5 | # Created: Tue Jan 22 22:46:18 2013 6 | # by: pyside-uic 0.2.13 running on PySide 1.1.1 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide import QtCore, QtGui 11 | 12 | class Ui_ShareNote(object): 13 | def setupUi(self, ShareNote): 14 | ShareNote.setObjectName("ShareNote") 15 | ShareNote.resize(442, 197) 16 | ShareNote.setModal(True) 17 | self.verticalLayout = QtGui.QVBoxLayout(ShareNote) 18 | self.verticalLayout.setObjectName("verticalLayout") 19 | self.waitText = QtGui.QLabel(ShareNote) 20 | font = QtGui.QFont() 21 | font.setPointSize(18) 22 | self.waitText.setFont(font) 23 | self.waitText.setScaledContents(False) 24 | self.waitText.setAlignment(QtCore.Qt.AlignCenter) 25 | self.waitText.setObjectName("waitText") 26 | self.verticalLayout.addWidget(self.waitText) 27 | self.sharedWidget = QtGui.QWidget(ShareNote) 28 | self.sharedWidget.setObjectName("sharedWidget") 29 | self.horizontalLayout_2 = QtGui.QHBoxLayout(self.sharedWidget) 30 | self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) 31 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 32 | self.sharedBox = QtGui.QVBoxLayout() 33 | self.sharedBox.setObjectName("sharedBox") 34 | self.horizontalLayout = QtGui.QHBoxLayout() 35 | self.horizontalLayout.setObjectName("horizontalLayout") 36 | self.label = QtGui.QLabel(self.sharedWidget) 37 | self.label.setObjectName("label") 38 | self.horizontalLayout.addWidget(self.label) 39 | self.shareLink = QtGui.QLineEdit(self.sharedWidget) 40 | self.shareLink.setReadOnly(True) 41 | self.shareLink.setObjectName("shareLink") 42 | self.horizontalLayout.addWidget(self.shareLink) 43 | self.sharedBox.addLayout(self.horizontalLayout) 44 | self.horizontalLayout_3 = QtGui.QHBoxLayout() 45 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 46 | self.copyButton = QtGui.QPushButton(self.sharedWidget) 47 | self.copyButton.setObjectName("copyButton") 48 | self.horizontalLayout_3.addWidget(self.copyButton) 49 | self.cancelButton = QtGui.QPushButton(self.sharedWidget) 50 | self.cancelButton.setObjectName("cancelButton") 51 | self.horizontalLayout_3.addWidget(self.cancelButton) 52 | self.sharedBox.addLayout(self.horizontalLayout_3) 53 | self.horizontalLayout_2.addLayout(self.sharedBox) 54 | self.verticalLayout.addWidget(self.sharedWidget) 55 | 56 | self.retranslateUi(ShareNote) 57 | QtCore.QMetaObject.connectSlotsByName(ShareNote) 58 | 59 | def retranslateUi(self, ShareNote): 60 | ShareNote.setWindowTitle(QtGui.QApplication.translate("ShareNote", "Dialog", None, QtGui.QApplication.UnicodeUTF8)) 61 | self.waitText.setText(QtGui.QApplication.translate("ShareNote", "wait_text", None, QtGui.QApplication.UnicodeUTF8)) 62 | self.label.setText(QtGui.QApplication.translate("ShareNote", "You can share note with link: ", None, QtGui.QApplication.UnicodeUTF8)) 63 | self.copyButton.setText(QtGui.QApplication.translate("ShareNote", "Copy url", None, QtGui.QApplication.UnicodeUTF8)) 64 | self.cancelButton.setText(QtGui.QApplication.translate("ShareNote", "Cancel sharing", None, QtGui.QApplication.UnicodeUTF8)) 65 | 66 | -------------------------------------------------------------------------------- /everpad/interface/share_note.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ShareNote 4 | 5 | 6 | 7 | 0 8 | 0 9 | 442 10 | 197 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | 18 25 | 26 | 27 | 28 | wait_text 29 | 30 | 31 | false 32 | 33 | 34 | Qt::AlignCenter 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | You can share note with link: 49 | 50 | 51 | 52 | 53 | 54 | 55 | true 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Copy url 67 | 68 | 69 | 70 | 71 | 72 | 73 | Cancel sharing 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /everpad/interface/tableinsert.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file './tableinsert.ui' 4 | # 5 | # Created: Sun Oct 7 06:22:18 2012 6 | # by: pyside-uic 0.2.13 running on PySide 1.1.0 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide import QtCore, QtGui 11 | 12 | class Ui_TableInsertDialog(object): 13 | def setupUi(self, TableInsertDialog): 14 | TableInsertDialog.setObjectName("TableInsertDialog") 15 | TableInsertDialog.setWindowModality(QtCore.Qt.WindowModal) 16 | TableInsertDialog.resize(433, 191) 17 | self.buttonBox = QtGui.QDialogButtonBox(TableInsertDialog) 18 | self.buttonBox.setGeometry(QtCore.QRect(80, 150, 341, 32)) 19 | self.buttonBox.setOrientation(QtCore.Qt.Horizontal) 20 | self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) 21 | self.buttonBox.setObjectName("buttonBox") 22 | self.gridLayoutWidget = QtGui.QWidget(TableInsertDialog) 23 | self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 10, 411, 121)) 24 | self.gridLayoutWidget.setObjectName("gridLayoutWidget") 25 | self.gridLayout = QtGui.QGridLayout(self.gridLayoutWidget) 26 | self.gridLayout.setContentsMargins(0, 0, 0, 0) 27 | self.gridLayout.setObjectName("gridLayout") 28 | self.width = QtGui.QLineEdit(self.gridLayoutWidget) 29 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) 30 | sizePolicy.setHorizontalStretch(0) 31 | sizePolicy.setVerticalStretch(0) 32 | sizePolicy.setHeightForWidth(self.width.sizePolicy().hasHeightForWidth()) 33 | self.width.setSizePolicy(sizePolicy) 34 | self.width.setObjectName("width") 35 | self.gridLayout.addWidget(self.width, 2, 1, 1, 1) 36 | self.label_2 = QtGui.QLabel(self.gridLayoutWidget) 37 | self.label_2.setObjectName("label_2") 38 | self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) 39 | self.rows = QtGui.QLineEdit(self.gridLayoutWidget) 40 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) 41 | sizePolicy.setHorizontalStretch(0) 42 | sizePolicy.setVerticalStretch(0) 43 | sizePolicy.setHeightForWidth(self.rows.sizePolicy().hasHeightForWidth()) 44 | self.rows.setSizePolicy(sizePolicy) 45 | self.rows.setObjectName("rows") 46 | self.gridLayout.addWidget(self.rows, 0, 1, 1, 1) 47 | self.label = QtGui.QLabel(self.gridLayoutWidget) 48 | self.label.setObjectName("label") 49 | self.gridLayout.addWidget(self.label, 0, 0, 1, 1) 50 | self.columns = QtGui.QLineEdit(self.gridLayoutWidget) 51 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) 52 | sizePolicy.setHorizontalStretch(0) 53 | sizePolicy.setVerticalStretch(0) 54 | sizePolicy.setHeightForWidth(self.columns.sizePolicy().hasHeightForWidth()) 55 | self.columns.setSizePolicy(sizePolicy) 56 | self.columns.setObjectName("columns") 57 | self.gridLayout.addWidget(self.columns, 1, 1, 1, 1) 58 | self.label_3 = QtGui.QLabel(self.gridLayoutWidget) 59 | self.label_3.setObjectName("label_3") 60 | self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1) 61 | self.widthType = QtGui.QComboBox(self.gridLayoutWidget) 62 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed) 63 | sizePolicy.setHorizontalStretch(0) 64 | sizePolicy.setVerticalStretch(0) 65 | sizePolicy.setHeightForWidth(self.widthType.sizePolicy().hasHeightForWidth()) 66 | self.widthType.setSizePolicy(sizePolicy) 67 | self.widthType.setObjectName("widthType") 68 | self.widthType.addItem("") 69 | self.widthType.addItem("") 70 | self.gridLayout.addWidget(self.widthType, 2, 2, 1, 1) 71 | 72 | self.retranslateUi(TableInsertDialog) 73 | QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), TableInsertDialog.accept) 74 | QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), TableInsertDialog.reject) 75 | QtCore.QMetaObject.connectSlotsByName(TableInsertDialog) 76 | 77 | def retranslateUi(self, TableInsertDialog): 78 | TableInsertDialog.setWindowTitle(QtGui.QApplication.translate("TableInsertDialog", "Everpad / Insert Table", None, QtGui.QApplication.UnicodeUTF8)) 79 | self.width.setText(QtGui.QApplication.translate("TableInsertDialog", "100", None, QtGui.QApplication.UnicodeUTF8)) 80 | self.label_2.setText(QtGui.QApplication.translate("TableInsertDialog", "Columns:", None, QtGui.QApplication.UnicodeUTF8)) 81 | self.rows.setText(QtGui.QApplication.translate("TableInsertDialog", "2", None, QtGui.QApplication.UnicodeUTF8)) 82 | self.label.setText(QtGui.QApplication.translate("TableInsertDialog", "Rows:", None, QtGui.QApplication.UnicodeUTF8)) 83 | self.columns.setText(QtGui.QApplication.translate("TableInsertDialog", "2", None, QtGui.QApplication.UnicodeUTF8)) 84 | self.label_3.setText(QtGui.QApplication.translate("TableInsertDialog", "Width:", None, QtGui.QApplication.UnicodeUTF8)) 85 | self.widthType.setItemText(0, QtGui.QApplication.translate("TableInsertDialog", "% of page", None, QtGui.QApplication.UnicodeUTF8)) 86 | self.widthType.setItemText(1, QtGui.QApplication.translate("TableInsertDialog", "pixels", None, QtGui.QApplication.UnicodeUTF8)) 87 | 88 | -------------------------------------------------------------------------------- /everpad/interface/tableinsert.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | TableInsertDialog 4 | 5 | 6 | Qt::WindowModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 433 13 | 191 14 | 15 | 16 | 17 | Everpad / Insert Table 18 | 19 | 20 | 21 | 22 | 80 23 | 150 24 | 341 25 | 32 26 | 27 | 28 | 29 | Qt::Horizontal 30 | 31 | 32 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 33 | 34 | 35 | 36 | 37 | 38 | 10 39 | 10 40 | 411 41 | 121 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 0 50 | 0 51 | 52 | 53 | 54 | 100 55 | 56 | 57 | 58 | 59 | 60 | 61 | Columns: 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 0 70 | 0 71 | 72 | 73 | 74 | 2 75 | 76 | 77 | 78 | 79 | 80 | 81 | Rows: 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 0 90 | 0 91 | 92 | 93 | 94 | 2 95 | 96 | 97 | 98 | 99 | 100 | 101 | Width: 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 0 110 | 0 111 | 112 | 113 | 114 | 115 | % of page 116 | 117 | 118 | 119 | 120 | pixels 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | buttonBox 132 | accepted() 133 | TableInsertDialog 134 | accept() 135 | 136 | 137 | 248 138 | 254 139 | 140 | 141 | 157 142 | 274 143 | 144 | 145 | 146 | 147 | buttonBox 148 | rejected() 149 | TableInsertDialog 150 | reject() 151 | 152 | 153 | 316 154 | 260 155 | 156 | 157 | 286 158 | 274 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /everpad/monkey.py: -------------------------------------------------------------------------------- 1 | import httplib2 2 | import ssl 3 | 4 | 5 | def _ssl_wrap_socket(sock, key_file, cert_file, 6 | disable_validation, ca_certs): 7 | if disable_validation: 8 | cert_reqs = ssl.CERT_NONE 9 | else: 10 | cert_reqs = ssl.CERT_REQUIRED 11 | # We should be specifying SSL version 3 or TLS v1, but the ssl module 12 | # doesn't expose the necessary knobs. So we need to go with the default 13 | # of SSLv23. 14 | return ssl.wrap_socket(sock, keyfile=key_file, certfile=cert_file, 15 | cert_reqs=cert_reqs, ca_certs=ca_certs, ssl_version=ssl.PROTOCOL_TLSv1) 16 | httplib2._ssl_wrap_socket = _ssl_wrap_socket 17 | -------------------------------------------------------------------------------- /everpad/pad/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/everpad/pad/__init__.py -------------------------------------------------------------------------------- /everpad/pad/editor/actions.py: -------------------------------------------------------------------------------- 1 | from PySide.QtGui import QIcon, QDialog, QWidget, QApplication 2 | from PySide.QtCore import Slot 3 | from PySide.QtWebKit import QWebPage 4 | from everpad.interface.tableinsert import Ui_TableInsertDialog 5 | from everpad.interface.image import Ui_ImageDialog 6 | from everpad.interface.findbar import Ui_FindBar 7 | from everpad.pad.tools import get_icon 8 | 9 | 10 | class ImagePrefs(QDialog): 11 | def __init__(self, res, *args, **kwargs): 12 | QDialog.__init__(self, *args, **kwargs) 13 | self.app = QApplication.instance() 14 | self.res = res 15 | self.ui = Ui_ImageDialog() 16 | self.ui.setupUi(self) 17 | self.setWindowIcon(get_icon()) 18 | self.ui.widthBox.setValue(self.res.w) 19 | self.ui.heightBox.setValue(self.res.h) 20 | self.ui.widthBox.valueChanged.connect(self.width_changed) 21 | self.ui.heightBox.valueChanged.connect(self.height_changed) 22 | self._auto_change = False 23 | 24 | def get_size(self): 25 | return self.ui.widthBox.value(), self.ui.heightBox.value() 26 | 27 | @Slot() 28 | def width_changed(self): 29 | if self.ui.checkBox.isChecked() and not self._auto_change: 30 | self._auto_change = True 31 | self.ui.heightBox.setValue( 32 | self.ui.widthBox.value() * self.res.h / self.res.w, 33 | ) 34 | else: 35 | self._auto_change = False 36 | 37 | @Slot() 38 | def height_changed(self): 39 | if self.ui.checkBox.isChecked() and not self._auto_change: 40 | self._auto_change = True 41 | self.ui.widthBox.setValue( 42 | self.ui.heightBox.value() * self.res.w / self.res.h, 43 | ) 44 | else: 45 | self._auto_change = False 46 | 47 | 48 | class TableWidget(QDialog): 49 | def __init__(self, parent, rows=None, cells=None, *args, **kwargs): 50 | QDialog.__init__(self, parent, *args, **kwargs) 51 | self.ui = Ui_TableInsertDialog() 52 | self.ui.setupUi(self) 53 | self.setWindowIcon(get_icon()) 54 | if rows: # typecasting sucks 55 | self.ui.rows.setText(str(int(rows))) 56 | if cells: 57 | self.ui.columns.setText(str(int(cells))) 58 | 59 | def get_width(self): 60 | result = self.ui.width.text() 61 | # 0 is %, 1 is px. 62 | if self.ui.widthType.currentIndex() == 0: 63 | result += '%' 64 | return result 65 | 66 | 67 | class FindBar(QWidget): 68 | def __init__(self, editor, *args, **kwargs): 69 | QWidget.__init__(self, *args, **kwargs) 70 | self.editor = editor 71 | self.ui = Ui_FindBar() 72 | self.ui.setupUi(self) 73 | 74 | # pyside-uic doesn't translate icons from themes correctly, so we have 75 | # to re-set the icons manually here 76 | self.ui.btnPrevious.setIcon(QIcon.fromTheme('go-previous')) 77 | self.ui.btnNext.setIcon(QIcon.fromTheme('go-next')) 78 | self.ui.btnClose.setIcon(QIcon.fromTheme('window-close')) 79 | self.visible = False 80 | 81 | self.ui.btnClose.clicked.connect(self.hide) 82 | self.ui.edtFindText.returnPressed.connect(self.find_next) 83 | self.ui.edtFindText.textChanged.connect(self.find_text_updated) 84 | 85 | self.ui.btnNext.clicked.connect(self.find_next) 86 | self.ui.btnPrevious.clicked.connect(self.find_previous) 87 | 88 | self.ui.btnHighlight.clicked.connect(self.update_highlight) 89 | self.ui.chkMatchCase.clicked.connect(self.match_case_updated) 90 | 91 | def set_search_term(self, search_term): 92 | self.ui.edtFindText.setText(search_term) 93 | 94 | def get_flags(self, default_flags=None): 95 | flags = QWebPage.FindFlag.FindWrapsAroundDocument 96 | if default_flags is not None: 97 | flags |= default_flags 98 | if self.ui.chkMatchCase.isChecked(): 99 | flags |= QWebPage.FindFlag.FindCaseSensitively 100 | return flags 101 | 102 | @Slot() 103 | def match_case_updated(self): 104 | flags = self.get_flags() 105 | 106 | # We need the *old* flags value; clear this flag if it's checked 107 | if self.ui.chkMatchCase.isChecked(): 108 | flags &= ~QWebPage.FindFlag.FindCaseSensitively 109 | else: 110 | flags |= QWebPage.FindFlag.FindCaseSensitively 111 | self.update_highlight(flags=flags, clear=True) 112 | self.update_highlight() 113 | 114 | @Slot(str) 115 | def find_text_updated(self, text): 116 | self.update_highlight(text=text, clear=True) 117 | self.find() 118 | 119 | @Slot() 120 | def find_next(self, text=None): 121 | self.find() 122 | 123 | @Slot() 124 | def find_previous(self): 125 | self.find(QWebPage.FindFlag.FindBackward) 126 | 127 | def find(self, flags=None): 128 | if not self.visible: 129 | self.show(focus=False) 130 | return 131 | 132 | flags = self.get_flags(flags) 133 | self.editor.note_edit.page.findText(self.ui.edtFindText.text(), flags) 134 | self.update_highlight() 135 | 136 | @Slot() 137 | def update_highlight(self, flags=None, text=None, clear=False): 138 | flags = flags or self.get_flags() 139 | flags |= QWebPage.FindFlag.HighlightAllOccurrences 140 | text = text or self.ui.edtFindText.text() 141 | if clear or not self.ui.btnHighlight.isChecked(): 142 | text = '' 143 | self.editor.note_edit.page.findText(text, flags) 144 | 145 | def show(self, flags=None, focus=True): 146 | QWidget.show(self) 147 | if self.visible: 148 | self.find(flags) 149 | else: 150 | self.editor.ui.centralwidget.layout().addWidget(self) 151 | self.visible = True 152 | 153 | self.find(flags) 154 | self.update_highlight() 155 | 156 | if focus: 157 | self.ui.edtFindText.setFocus() 158 | 159 | self.editor.find_action.setChecked(True) 160 | 161 | @Slot() 162 | def hide(self): 163 | QWidget.hide(self) 164 | if not self.visible: 165 | return 166 | self.update_highlight(clear=True) 167 | self.editor.ui.centralwidget.layout().removeWidget(self) 168 | self.setParent(None) 169 | self.visible = False 170 | self.editor.find_action.setChecked(False) 171 | 172 | @Slot() 173 | def toggle_visible(self): 174 | if self.visible: 175 | self.hide() 176 | else: 177 | self.show() 178 | -------------------------------------------------------------------------------- /everpad/pad/editor/widgets.py: -------------------------------------------------------------------------------- 1 | from PySide.QtGui import QCompleter, QStringListModel, QApplication 2 | from PySide.QtCore import Slot 3 | from everpad.basetypes import Tag, Notebook 4 | import re 5 | 6 | 7 | class TagEdit(object): 8 | """Abstraction for tag edit""" 9 | 10 | def __init__(self, parent, widget, on_change): 11 | """Init and connect signals""" 12 | self.parent = parent 13 | self.app = QApplication.instance() 14 | self.widget = widget 15 | self.tags_list = map(lambda tag: 16 | Tag.from_tuple(tag).name, 17 | self.app.provider.list_tags(), 18 | ) 19 | self.completer = QCompleter() 20 | self.completer_model = QStringListModel() 21 | self.completer.setModel(self.completer_model) 22 | self.completer.activated.connect(self.update_completion) 23 | self.update_completion() 24 | self.widget.setCompleter(self.completer) 25 | self.widget.textChanged.connect(Slot()(on_change)) 26 | self.widget.textEdited.connect(self.update_completion) 27 | 28 | @property 29 | def tags(self): 30 | """Get tags""" 31 | # Split on comma and Arabic comma 32 | # 0x060c is the Arabic comma 33 | return map(lambda tag: tag.strip(), 34 | re.split(u',|\u060c', self.widget.text())) 35 | 36 | @tags.setter 37 | def tags(self, val): 38 | """Set tags""" 39 | self.widget.setText(', '.join(val)) 40 | 41 | @Slot() 42 | def update_completion(self): 43 | """Update completion model with exist tags""" 44 | orig_text = self.widget.text() 45 | text = ', '.join(orig_text.replace(', ', ',').split(',')[:-1]) 46 | tags = [] 47 | for tag in self.tags_list: 48 | if ',' in orig_text: 49 | if orig_text[-1] not in (',', ' '): 50 | tags.append('%s,%s' % (text, tag)) 51 | tags.append('%s, %s' % (text, tag)) 52 | else: 53 | tags.append(tag) 54 | if tags != self.completer_model.stringList(): 55 | self.completer_model.setStringList(tags) 56 | 57 | 58 | class NotebookEdit(object): 59 | """Abstraction for notebook edit""" 60 | 61 | def __init__(self, parent, widget, on_change): 62 | """Init and connect signals""" 63 | self.parent = parent 64 | self.app = QApplication.instance() 65 | self.widget = widget 66 | for notebook_struct in self.app.provider.list_notebooks(): 67 | notebook = Notebook.from_tuple(notebook_struct) 68 | self.widget.addItem(notebook.name, userData=notebook.id) 69 | self.widget.currentIndexChanged.connect(Slot()(on_change)) 70 | 71 | @property 72 | def notebook(self): 73 | """Get notebook""" 74 | notebook_index = self.widget.currentIndex() 75 | return self.widget.itemData(notebook_index) 76 | 77 | @notebook.setter 78 | def notebook(self, val): 79 | """Set notebook""" 80 | notebook_index = self.widget.findData(val) 81 | self.widget.setCurrentIndex(notebook_index) 82 | -------------------------------------------------------------------------------- /everpad/pad/share_note.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('../..') 3 | from PySide.QtGui import QDialog, QApplication 4 | from everpad.basetypes import Note 5 | from everpad.interface.share_note import Ui_ShareNote 6 | from everpad.pad.tools import get_icon 7 | 8 | 9 | class ShareNoteDialog(QDialog): 10 | """Share note dialog""" 11 | 12 | def __init__(self, note, *args, **kwargs): 13 | """init dialog and connect signals""" 14 | QDialog.__init__(self, *args, **kwargs) 15 | self.app = QApplication.instance() 16 | self.canceled = False 17 | self.ui = Ui_ShareNote() 18 | self.ui.setupUi(self) 19 | self.setWindowIcon(get_icon()) 20 | self.note = Note.from_tuple( 21 | self.app.provider.get_note(note.id), 22 | ) 23 | self.app.data_changed.connect(self.data_changed) 24 | self.ui.cancelButton.clicked.connect(self.cancel) 25 | self.ui.copyButton.clicked.connect(self.copy_url) 26 | if not self.note.share_url: 27 | self.start_sharing() 28 | self.update() 29 | 30 | def start_sharing(self): 31 | """Start sharing note""" 32 | self.app.provider.share_note(self.note.id) 33 | 34 | def copy_url(self): 35 | """Copy sharing url""" 36 | url = self.ui.shareLink.text() 37 | self.app.clipboard().setText(url) 38 | 39 | def cancel(self): 40 | """Cancel sharing note""" 41 | self.canceled = True 42 | self.app.provider.stop_sharing_note(self.note.id) 43 | self.update() 44 | 45 | def data_changed(self): 46 | """On data changed slot""" 47 | self.note = Note.from_tuple( 48 | self.app.provider.get_note(self.note.id), 49 | ) 50 | self.update() 51 | 52 | def update(self): 53 | """Update dialog behavior""" 54 | self.update_title() 55 | if self.canceled: 56 | self.render_canceled() 57 | elif self.note.share_url: 58 | self.render_shared() 59 | else: 60 | self.render_wait() 61 | 62 | def update_title(self): 63 | """Update dialog title""" 64 | self.setWindowTitle(self.tr( 65 | 'Everpad / sharing "%s"' % self.note.title, 66 | )) 67 | 68 | def render_shared(self): 69 | """Render for already shared note""" 70 | self.ui.waitText.hide() 71 | self.ui.sharedWidget.show() 72 | self.ui.shareLink.setText(self.note.share_url) 73 | 74 | def render_canceled(self): 75 | """Render for canceled sharing""" 76 | self.ui.waitText.show() 77 | self.ui.sharedWidget.hide() 78 | self.ui.waitText.setText(self.tr('Note sharing canceled')) 79 | 80 | def render_wait(self): 81 | """Render for wait sharing""" 82 | self.ui.waitText.show() 83 | self.ui.sharedWidget.hide() 84 | self.ui.waitText.setText(self.tr('Sharing in proccess...')) 85 | -------------------------------------------------------------------------------- /everpad/pad/tools.py: -------------------------------------------------------------------------------- 1 | from PySide.QtGui import QIcon 2 | import os 3 | import sys 4 | from everpad.tools import resource_filename 5 | 6 | 7 | def get_icon(): 8 | return QIcon.fromTheme('everpad', QIcon('../../everpad.png')) 9 | 10 | 11 | def get_file_icon_path(): 12 | """ 13 | Get path of icon for file 14 | foe embedding in html. 15 | """ 16 | paths = ( 17 | os.path.join( 18 | os.path.dirname(__file__), 19 | '../../data/everpad-file.png', 20 | ), 21 | resource_filename('share/icons/hicolor/48x48/actions/everpad-file.png'), 22 | '/usr/local/share/icons/hicolor/48x48/actions/everpad-file.png', 23 | '/usr/share/icons/hicolor/48x48/actions/everpad-file.png', 24 | ) 25 | for path in paths: 26 | if os.path.isfile(path): 27 | return 'file://%s' % path 28 | file_icon_path = get_file_icon_path() 29 | -------------------------------------------------------------------------------- /everpad/pad/treeview.py: -------------------------------------------------------------------------------- 1 | from PySide.QtGui import QTreeView, QItemSelection 2 | from PySide.QtCore import Signal 3 | 4 | 5 | class EverpadTreeView(QTreeView): 6 | selection = Signal(QItemSelection, QItemSelection) 7 | 8 | def selectionChanged(self, selected, deselected): 9 | QTreeView.selectionChanged(self, selected, deselected) 10 | self.selection.emit(selected, deselected) 11 | -------------------------------------------------------------------------------- /everpad/provider/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/everpad/provider/__init__.py -------------------------------------------------------------------------------- /everpad/provider/daemon.py: -------------------------------------------------------------------------------- 1 | from .service import ProviderService 2 | from .sync.agent import SyncThread 3 | from .tools import set_auth_token, get_auth_token, get_db_session 4 | from ..specific import AppClass 5 | from ..tools import print_version 6 | from . import models 7 | from PySide.QtCore import Slot, QSettings 8 | import dbus 9 | import dbus.mainloop.glib 10 | import signal 11 | import fcntl 12 | import os 13 | import getpass 14 | import argparse 15 | import sys 16 | import logging 17 | 18 | 19 | class ProviderApp(AppClass): 20 | 21 | def __init__(self, verbose, *args, **kwargs): 22 | AppClass.__init__(self, *args, **kwargs) 23 | self.settings = QSettings('everpad', 'everpad-provider') 24 | self.verbose = verbose 25 | session_bus = dbus.SessionBus() 26 | self.bus = dbus.service.BusName("com.everpad.Provider", session_bus) 27 | self.service = ProviderService(session_bus, '/EverpadProvider') 28 | self.sync_thread = SyncThread() 29 | self.sync_thread.sync_state_changed.connect( 30 | Slot(int)(self.service.sync_state_changed), 31 | ) 32 | self.sync_thread.data_changed.connect( 33 | Slot()(self.service.data_changed), 34 | ) 35 | if get_auth_token(): 36 | self.sync_thread.start() 37 | self.service.qobject.authenticate_signal.connect( 38 | self.on_authenticated, 39 | ) 40 | self.service.qobject.remove_authenticate_signal.connect( 41 | self.on_remove_authenticated, 42 | ) 43 | self.service.qobject.terminate.connect(self.terminate) 44 | # Configure logger. 45 | self.logger = logging.getLogger('everpad-provider') 46 | self.logger.setLevel(logging.DEBUG) 47 | fh = logging.FileHandler( 48 | os.path.expanduser('~/.everpad/logs/everpad-provider.log')) 49 | fh.setLevel(logging.DEBUG) 50 | formatter = logging.Formatter( 51 | '%(asctime)s - %(name)s - %(levelname)s - %(message)s') 52 | fh.setFormatter(formatter) 53 | self.logger.addHandler(fh) 54 | self.logger.debug('Provider started.') 55 | 56 | @Slot(str) 57 | def on_authenticated(self, token): 58 | set_auth_token(token) 59 | self.sync_thread.start() 60 | 61 | @Slot() 62 | def on_remove_authenticated(self): 63 | self.sync_thread.quit() 64 | self.sync_thread.sync_state.update_count = 0 65 | set_auth_token('') 66 | session = get_db_session() 67 | session.query(models.Note).delete( 68 | synchronize_session='fetch', 69 | ) 70 | session.query(models.Resource).delete( 71 | synchronize_session='fetch', 72 | ) 73 | session.query(models.Notebook).delete( 74 | synchronize_session='fetch', 75 | ) 76 | session.query(models.Tag).delete( 77 | synchronize_session='fetch', 78 | ) 79 | session.commit() 80 | 81 | def log(self, data): 82 | self.logger.debug(data) 83 | if self.verbose: 84 | print data 85 | 86 | @Slot() 87 | def terminate(self): 88 | self.sync_thread.quit() 89 | self.quit() 90 | 91 | 92 | def _create_dirs(dirs): 93 | """Create everpad dirs""" 94 | for path in dirs: 95 | try: 96 | os.mkdir(os.path.expanduser(path)) 97 | except OSError: 98 | continue 99 | 100 | 101 | def main(): 102 | signal.signal(signal.SIGINT, signal.SIG_DFL) 103 | _create_dirs(['~/.everpad/', '~/.everpad/data/', '~/.everpad/logs/']) 104 | parser = argparse.ArgumentParser() 105 | parser.add_argument('--verbose', action='store_true', help='verbose output') 106 | parser.add_argument('--version', '-v', action='store_true', help='show version') 107 | args = parser.parse_args(sys.argv[1:]) 108 | if args.version: 109 | print_version() 110 | fp = open('/tmp/everpad-provider-%s.lock' % getpass.getuser(), 'w') 111 | try: 112 | fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB) 113 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 114 | dbus.mainloop.glib.threads_init() 115 | app = ProviderApp(args.verbose, sys.argv) 116 | app.exec_() 117 | except IOError: 118 | print("everpad-provider already ran") 119 | except Exception as e: 120 | logging.exception("failed to start everpad-provider") 121 | 122 | if __name__ == '__main__': 123 | main() 124 | -------------------------------------------------------------------------------- /everpad/provider/exceptions.py: -------------------------------------------------------------------------------- 1 | class TTypeValidationFailed(Exception): 2 | """TType validation failed""" 3 | 4 | -------------------------------------------------------------------------------- /everpad/provider/sync/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'nvbn' 2 | -------------------------------------------------------------------------------- /everpad/provider/sync/agent.py: -------------------------------------------------------------------------------- 1 | from PySide import QtCore 2 | from datetime import datetime 3 | from ... import const 4 | from ...specific import AppClass 5 | from .. import tools 6 | from . import note, notebook, tag 7 | from .. import models 8 | import time 9 | import traceback 10 | import socket 11 | 12 | 13 | class SyncThread(QtCore.QThread): 14 | """Sync notes with evernote thread""" 15 | force_sync_signal = QtCore.Signal() 16 | sync_state_changed = QtCore.Signal(int) 17 | data_changed = QtCore.Signal() 18 | 19 | def __init__(self, *args, **kwargs): 20 | """Init default values""" 21 | QtCore.QThread.__init__(self, *args, **kwargs) 22 | self.app = AppClass.instance() 23 | self._init_timer() 24 | self._init_locks() 25 | 26 | def _init_sync(self): 27 | """Init sync""" 28 | self.status = const.STATUS_NONE 29 | self.last_sync = datetime.now() 30 | self.sync_state = self.session.query(models.Sync).first() 31 | if not self.sync_state: 32 | self.sync_state = models.Sync( 33 | update_count=0, last_sync=self.last_sync) 34 | self.session.add(self.sync_state) 35 | self.session.commit() 36 | 37 | def _init_timer(self): 38 | """Init timer""" 39 | self.timer = QtCore.QTimer() 40 | self.timer.timeout.connect(self.sync) 41 | self.update_timer() 42 | 43 | def _init_locks(self): 44 | """Init locks""" 45 | self.wait_condition = QtCore.QWaitCondition() 46 | self.mutex = QtCore.QMutex() 47 | 48 | def update_timer(self): 49 | """Update sync timer""" 50 | self.timer.stop() 51 | delay = int(self.app.settings.value('sync_delay') or 0) 52 | if not delay: 53 | delay = const.DEFAULT_SYNC_DELAY 54 | 55 | if delay != const.SYNC_MANUAL: 56 | self.timer.start(delay) 57 | 58 | def run(self): 59 | """Run thread""" 60 | self._init_db() 61 | self._init_network() 62 | self._init_sync() 63 | while True: 64 | self.mutex.lock() 65 | self.wait_condition.wait(self.mutex) 66 | self.perform() 67 | self.mutex.unlock() 68 | time.sleep(1) # prevent cpu eating 69 | 70 | def _init_db(self): 71 | """Init database""" 72 | self.session = tools.get_db_session() 73 | 74 | def _init_network(self): 75 | """Init connection to remote server""" 76 | while True: 77 | try: 78 | self.auth_token = tools.get_auth_token() 79 | self.note_store = tools.get_note_store(self.auth_token) 80 | self.user_store = tools.get_user_store(self.auth_token) 81 | break 82 | except socket.error: 83 | time.sleep(30) 84 | 85 | def _need_to_update(self): 86 | """Check need for update notes""" 87 | self.app.log('Checking need for update notes.') 88 | # Try to update_count. 89 | try: 90 | update_count = self.note_store.getSyncState( 91 | self.auth_token).updateCount 92 | except socket.error, e: 93 | self.app.log( 94 | "Couldn't connect to remote server. Got: %s" % 95 | traceback.format_exc()) 96 | # This is most likely a network failure. Return False so 97 | # everpad-provider won't lock up and can try to sync up in the 98 | # next run. 99 | return False 100 | #XXX: matsubara probably innefficient as it does a SQL each time it 101 | # accesses the update_count attr? 102 | self.app.log("Local update count: %s Remote update count: %s" % ( 103 | self.sync_state.update_count, update_count)) 104 | reason = update_count != self.sync_state.update_count 105 | self.sync_state.update_count = update_count 106 | return reason 107 | 108 | def force_sync(self): 109 | """Start sync""" 110 | self.timer.stop() 111 | self.sync() 112 | self.update_timer() 113 | 114 | @QtCore.Slot() 115 | def sync(self): 116 | """Do sync""" 117 | self.wait_condition.wakeAll() 118 | 119 | def perform(self): 120 | """Perform all sync""" 121 | self.app.log("Performing sync") 122 | self.status = const.STATUS_SYNC 123 | self.last_sync = datetime.now() 124 | self.sync_state_changed.emit(const.SYNC_STATE_START) 125 | 126 | need_to_update = self._need_to_update() 127 | 128 | try: 129 | if need_to_update: 130 | self.remote_changes() 131 | self.local_changes() 132 | except Exception, e: # maybe log this 133 | self.session.rollback() 134 | self._init_db() 135 | self.app.log(e) 136 | finally: 137 | self.sync_state_changed.emit(const.SYNC_STATE_FINISH) 138 | self.status = const.STATUS_NONE 139 | self.all_notes = None 140 | 141 | self.data_changed.emit() 142 | self.app.log("Sync performed.") 143 | 144 | def _get_sync_args(self): 145 | """Get sync arguments""" 146 | return self.auth_token, self.session, self.note_store, self.user_store 147 | 148 | def local_changes(self): 149 | """Send local changes to evernote server""" 150 | self.app.log('Running local_changes()') 151 | self.sync_state_changed.emit(const.SYNC_STATE_NOTEBOOKS_LOCAL) 152 | notebook.PushNotebook(*self._get_sync_args()).push() 153 | 154 | self.sync_state_changed.emit(const.SYNC_STATE_TAGS_LOCAL) 155 | tag.PushTag(*self._get_sync_args()).push() 156 | 157 | self.sync_state_changed.emit(const.SYNC_STATE_NOTES_LOCAL) 158 | note.PushNote(*self._get_sync_args()).push() 159 | 160 | def remote_changes(self): 161 | """Receive remote changes from evernote""" 162 | self.app.log('Running remote_changes()') 163 | self.sync_state_changed.emit(const.SYNC_STATE_NOTEBOOKS_REMOTE) 164 | notebook.PullNotebook(*self._get_sync_args()).pull() 165 | 166 | self.sync_state_changed.emit(const.SYNC_STATE_TAGS_REMOTE) 167 | tag.PullTag(*self._get_sync_args()).pull() 168 | 169 | self.sync_state_changed.emit(const.SYNC_STATE_NOTES_REMOTE) 170 | note.PullNote(*self._get_sync_args()).pull() 171 | -------------------------------------------------------------------------------- /everpad/provider/sync/base.py: -------------------------------------------------------------------------------- 1 | from ...specific import AppClass 2 | 3 | 4 | class BaseSync(object): 5 | """Base class for sync""" 6 | 7 | def __init__(self, auth_token, session, note_store, user_store): 8 | """Set shortcuts""" 9 | self.auth_token = auth_token 10 | self.session = session 11 | self.note_store = note_store 12 | self.user_store = user_store 13 | self.app = AppClass.instance() 14 | -------------------------------------------------------------------------------- /everpad/provider/sync/notebook.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.orm.exc import NoResultFound 2 | from evernote.edam.error.ttypes import EDAMUserException 3 | from evernote.edam.limits import constants as limits 4 | from evernote.edam.type import ttypes 5 | from ... import const 6 | from ..exceptions import TTypeValidationFailed 7 | from .. import models 8 | from .base import BaseSync 9 | import regex 10 | 11 | 12 | class PushNotebook(BaseSync): 13 | """Notebook sync""" 14 | 15 | def push(self): 16 | """Push notebook changes to server""" 17 | for notebook in self.session.query(models.Notebook).filter( 18 | models.Notebook.action != const.ACTION_NONE, 19 | ): 20 | self.app.log( 21 | 'Pushing notebook "%s" to remote server.' % notebook.name) 22 | 23 | try: 24 | notebook_ttype = self._create_ttype(notebook) 25 | except TTypeValidationFailed: 26 | self.app.log('notebook %s skipped' % notebook.name) 27 | notebook.action = const.ACTION_NONE 28 | continue 29 | 30 | if notebook.action == const.ACTION_CREATE: 31 | self._push_new_notebook(notebook, notebook_ttype) 32 | elif notebook.action == const.ACTION_CHANGE: 33 | self._push_changed_notebook(notebook, notebook_ttype) 34 | 35 | self.session.commit() 36 | self._merge_duplicates() 37 | 38 | def _create_ttype(self, notebook): 39 | """Create notebook ttype""" 40 | kwargs = dict( 41 | name=notebook.name[ 42 | :limits.EDAM_NOTEBOOK_NAME_LEN_MAX 43 | ].strip().encode('utf8'), 44 | defaultNotebook=notebook.default, 45 | ) 46 | 47 | if notebook.stack: 48 | kwargs['stack'] = notebook.stack[ 49 | :limits.EDAM_NOTEBOOK_STACK_LEN_MAX 50 | ].strip().encode('utf8') 51 | 52 | if not regex.search(limits.EDAM_NOTEBOOK_NAME_REGEX, notebook.name): 53 | raise TTypeValidationFailed() 54 | 55 | if notebook.guid: 56 | kwargs['guid'] = notebook.guid 57 | 58 | return ttypes.Notebook(**kwargs) 59 | 60 | def _push_new_notebook(self, notebook, notebook_ttype): 61 | """Push new notebook to server""" 62 | try: 63 | notebook_ttype = self.note_store.createNotebook( 64 | self.auth_token, notebook_ttype, 65 | ) 66 | notebook.guid = notebook_ttype.guid 67 | notebook.action = const.ACTION_NONE 68 | except EDAMUserException: 69 | notebook.action = const.ACTION_DUPLICATE 70 | self.app.log('Duplicate %s' % notebook_ttype.name) 71 | 72 | def _push_changed_notebook(self, notebook, notebook_ttype): 73 | """Push changed notebook""" 74 | try: 75 | notebook_ttype = self.note_store.updateNotebook( 76 | self.auth_token, notebook_ttype, 77 | ) 78 | notebook.action = const.ACTION_NONE 79 | except EDAMUserException: 80 | notebook.action = const.ACTION_DUPLICATE 81 | self.app.log('Duplicate %s' % notebook_ttype.name) 82 | 83 | def _merge_duplicates(self): 84 | """Merge and remove duplicates""" 85 | for notebook in self.session.query(models.Notebook).filter( 86 | models.Notebook.action == const.ACTION_DUPLICATE, 87 | ): 88 | try: 89 | original = self.session.query(models.Notebook).filter( 90 | (models.Notebook.action != const.ACTION_DUPLICATE) 91 | & (models.Notebook.name == notebook.name) 92 | ).one() 93 | except NoResultFound: 94 | original = self.session.query(models.Notebook).filter( 95 | models.Notebook.default == True, 96 | ).one() 97 | 98 | for note in self.session.query(models.Note).filter( 99 | models.Note.notebook_id == notebook.id, 100 | ): 101 | note.notebook = original 102 | 103 | self.session.delete(notebook) 104 | self.session.commit() 105 | 106 | 107 | class PullNotebook(BaseSync): 108 | """Pull notebook from server""" 109 | 110 | def __init__(self, *args, **kwargs): 111 | super(PullNotebook, self).__init__(*args, **kwargs) 112 | self._exists = [] 113 | 114 | def pull(self): 115 | """Receive notebooks from server""" 116 | for notebook_ttype in self.note_store.listNotebooks(self.auth_token): 117 | self.app.log( 118 | 'Pulling notebook "%s" from remote server.' % notebook_ttype.name) 119 | try: 120 | notebook = self._update_notebook(notebook_ttype) 121 | except NoResultFound: 122 | notebook = self._create_notebook(notebook_ttype) 123 | self._exists.append(notebook.id) 124 | 125 | self.session.commit() 126 | self._remove_notebooks() 127 | 128 | def _create_notebook(self, notebook_ttype): 129 | """Create notebook from ttype""" 130 | notebook = models.Notebook(guid=notebook_ttype.guid) 131 | notebook.from_api(notebook_ttype) 132 | self.session.add(notebook) 133 | self.session.commit() 134 | return notebook 135 | 136 | def _update_notebook(self, notebook_ttype): 137 | """Try to update notebook from ttype""" 138 | notebook = self.session.query(models.Notebook).filter( 139 | models.Notebook.guid == notebook_ttype.guid, 140 | ).one() 141 | if notebook.service_updated < notebook_ttype.serviceUpdated: 142 | notebook.from_api(notebook_ttype) 143 | return notebook 144 | 145 | def _remove_notebooks(self): 146 | """Remove not received notebooks""" 147 | if self._exists: 148 | q = (~models.Notebook.id.in_(self._exists) 149 | & (models.Notebook.action != const.ACTION_CREATE) 150 | & (models.Notebook.action != const.ACTION_CHANGE)) 151 | else: 152 | q = ((models.Notebook.action != const.ACTION_CREATE) 153 | & (models.Notebook.action != const.ACTION_CHANGE)) 154 | 155 | self.session.query(models.Notebook).filter( 156 | q).delete(synchronize_session='fetch') 157 | -------------------------------------------------------------------------------- /everpad/provider/sync/tag.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.orm.exc import NoResultFound 2 | from evernote.edam.error.ttypes import EDAMUserException 3 | from evernote.edam.limits import constants as limits 4 | from evernote.edam.type import ttypes 5 | from ... import const 6 | from ..exceptions import TTypeValidationFailed 7 | from .. import models 8 | from .base import BaseSync 9 | import regex 10 | 11 | 12 | class PushTag(BaseSync): 13 | """Push tags to server""" 14 | 15 | def push(self): 16 | """Push tags""" 17 | for tag in self.session.query(models.Tag).filter( 18 | models.Tag.action != const.ACTION_NONE, 19 | ): 20 | self.app.log('Pushing tag "%s" to remote server.' % tag.name) 21 | 22 | try: 23 | tag_ttype = self._create_ttype(tag) 24 | except TTypeValidationFailed: 25 | tag.action = const.ACTION_NONE 26 | self.app.log('tag %s skipped' % tag.name) 27 | continue 28 | 29 | if tag.action == const.ACTION_CREATE: 30 | self._push_new_tag(tag, tag_ttype) 31 | elif tag.action == const.ACTION_CHANGE: 32 | self._push_changed_tag(tag, tag_ttype) 33 | 34 | self.session.commit() 35 | 36 | def _create_ttype(self, tag): 37 | """Create tag ttype""" 38 | if not regex.search(limits.EDAM_TAG_NAME_REGEX, tag.name): 39 | raise TTypeValidationFailed() 40 | 41 | kwargs = dict( 42 | name=tag.name[:limits.EDAM_TAG_NAME_LEN_MAX].strip().encode('utf8'), 43 | ) 44 | 45 | if tag.guid: 46 | kwargs['guid'] = tag.guid 47 | 48 | return ttypes.Tag(**kwargs) 49 | 50 | def _push_new_tag(self, tag, tag_ttype): 51 | """Push new tag""" 52 | try: 53 | tag_ttype = self.note_store.createTag( 54 | self.auth_token, tag_ttype, 55 | ) 56 | tag.guid = tag_ttype.guid 57 | tag.action = const.ACTION_NONE 58 | except EDAMUserException as e: 59 | self.app.log(e) 60 | 61 | def _push_changed_tag(self, tag, tag_ttype): 62 | """Push changed tag""" 63 | try: 64 | self.note_store.updateTag( 65 | self.auth_token, tag_ttype, 66 | ) 67 | tag.action = const.ACTION_NONE 68 | except EDAMUserException as e: 69 | self.app.log(e) 70 | 71 | 72 | class PullTag(BaseSync): 73 | """Pull tags from server""" 74 | 75 | def __init__(self, *args, **kwargs): 76 | super(PullTag, self).__init__(*args, **kwargs) 77 | self._exists = [] 78 | 79 | def pull(self): 80 | """Pull tags from server""" 81 | for tag_ttype in self.note_store.listTags(self.auth_token): 82 | self.app.log( 83 | 'Pulling tag "%s" from remote server.' % tag_ttype.name) 84 | try: 85 | tag = self._update_tag(tag_ttype) 86 | except NoResultFound: 87 | tag = self._create_tag(tag_ttype) 88 | self._exists.append(tag.id) 89 | 90 | self.session.commit() 91 | self._remove_tags() 92 | 93 | def _create_tag(self, tag_ttype): 94 | """Create tag from server""" 95 | tag = models.Tag(guid=tag_ttype.guid) 96 | tag.from_api(tag_ttype) 97 | self.session.add(tag) 98 | self.session.commit() 99 | return tag 100 | 101 | def _update_tag(self, tag_ttype): 102 | """Update tag if exists""" 103 | tag = self.session.query(models.Tag).filter( 104 | models.Tag.guid == tag_ttype.guid, 105 | ).one() 106 | if tag.name != tag_ttype.name.decode('utf8'): 107 | tag.from_api(tag_ttype) 108 | return tag 109 | 110 | def _remove_tags(self): 111 | """Remove not exist tags""" 112 | if self._exists: 113 | q = (~models.Tag.id.in_(self._exists) 114 | & (models.Tag.action != const.ACTION_CREATE)) 115 | else: 116 | q = (models.Tag.action != const.ACTION_CREATE) 117 | self.session.query(models.Tag).filter(q).delete( 118 | synchronize_session='fetch') 119 | -------------------------------------------------------------------------------- /everpad/provider/tools.py: -------------------------------------------------------------------------------- 1 | from thrift.protocol import TBinaryProtocol 2 | from thrift.transport import THttpClient 3 | from evernote.edam.userstore import UserStore 4 | from evernote.edam.notestore import NoteStore 5 | from sqlalchemy import create_engine 6 | from sqlalchemy.orm import sessionmaker 7 | from urlparse import urlparse 8 | from .models import Base 9 | from ..const import HOST, DB_PATH 10 | from ..tools import get_proxy_config 11 | from ..specific import get_keyring 12 | import os 13 | import logging 14 | 15 | 16 | def _nocase_lower(item): 17 | return unicode(item).lower() 18 | 19 | 20 | def set_auth_token(token): 21 | get_keyring().set_password('everpad', 'oauth_token', token) 22 | 23 | 24 | def get_auth_token(): 25 | try: 26 | return get_keyring().get_password('everpad', 'oauth_token') 27 | except Exception as e: 28 | logging.exception("Error getting token from keyring") 29 | 30 | 31 | def get_db_session(db_path=None): 32 | if not db_path: 33 | db_path = os.path.expanduser(DB_PATH) 34 | engine = create_engine('sqlite:///%s' % db_path) 35 | Base.metadata.create_all(engine) 36 | Session = sessionmaker(bind=engine) 37 | session = Session() 38 | conn = session.connection() 39 | conn.connection.create_function('lower', 1, _nocase_lower) 40 | return session 41 | 42 | 43 | def get_user_store(auth_token=None): 44 | if not auth_token: 45 | auth_token = get_auth_token() 46 | user_store_uri = "https://" + HOST + "/edam/user" 47 | 48 | user_store_http_client = THttpClient.THttpClient(user_store_uri, 49 | http_proxy=get_proxy_config(urlparse(user_store_uri).scheme)) 50 | user_store_protocol = TBinaryProtocol.TBinaryProtocol(user_store_http_client) 51 | return UserStore.Client(user_store_protocol) 52 | 53 | 54 | def get_note_store(auth_token=None): 55 | if not auth_token: 56 | auth_token = get_auth_token() 57 | user_store = get_user_store(auth_token) 58 | note_store_url = user_store.getNoteStoreUrl(auth_token) 59 | note_store_http_client = THttpClient.THttpClient(note_store_url, 60 | http_proxy=get_proxy_config(urlparse(note_store_url).scheme)) 61 | note_store_protocol = TBinaryProtocol.TBinaryProtocol(note_store_http_client) 62 | return NoteStore.Client(note_store_protocol) 63 | -------------------------------------------------------------------------------- /everpad/specific/__init__.py: -------------------------------------------------------------------------------- 1 | from PySide.QtGui import QIcon 2 | from everpad.specific.unity.launcher import UnityLauncher 3 | import os 4 | 5 | 6 | launchers = { 7 | 'ubuntu': UnityLauncher, 8 | 'default': UnityLauncher, 9 | } 10 | 11 | 12 | def get_launcher(*args, **kwargs): 13 | de = os.environ.get('DESKTOP_SESSION', 'default') 14 | launcher = launchers.get(de, launchers['default']) 15 | return launcher(*args, **kwargs) 16 | 17 | 18 | def get_tray_icon(is_black=False): 19 | if os.environ.get('DESKTOP_SESSION', 'default') == 'gnome': 20 | return QIcon.fromTheme('everpad', QIcon('../../data/everpad.png')) 21 | if is_black: 22 | return QIcon.fromTheme('everpad-black', QIcon('../../data/everpad-black.png')) 23 | else: 24 | return QIcon.fromTheme('everpad-mono', QIcon('../../data/everpad-mono.png')) 25 | 26 | 27 | if 'kde' in os.environ.get('DESKTOP_SESSION', '') or os.environ.get('KDE_FULL_SESSION') == 'true': # kde init qwidget for wallet access 28 | from PySide.QtGui import QApplication 29 | AppClass = QApplication 30 | else: 31 | from PySide.QtCore import QCoreApplication 32 | AppClass = QCoreApplication 33 | 34 | 35 | class QSettingsKeyringAdpdater(object): 36 | def __init__(self, settings): 37 | self._settings = settings 38 | 39 | def _prepare_name(self, app, name): 40 | return '%s_%s' % (app, name) 41 | 42 | def set_password(self, app, name, password): 43 | self._settings.setValue(self._prepare_name(app, name), password) 44 | 45 | def get_password(self, app, name): 46 | self._settings.value(self._prepare_name(app, name)) 47 | 48 | 49 | def get_keyring(): 50 | if os.environ.get('DESKTOP_SESSION', 'default') in ('Lubuntu', 'LXDE'): 51 | # keyring fails on initialisation in lxde 52 | return QSettingsKeyringAdpdater(AppClass.instance().settings) 53 | else: 54 | import keyring 55 | return keyring 56 | -------------------------------------------------------------------------------- /everpad/specific/kde/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/everpad/specific/kde/__init__.py -------------------------------------------------------------------------------- /everpad/specific/kde/everpad_runner.py: -------------------------------------------------------------------------------- 1 | from PyKDE4 import plasmascript 2 | from PyKDE4.plasma import Plasma 3 | from PyKDE4.kdeui import KIcon 4 | from html2text import html2text 5 | from everpad.basetypes import Note 6 | from everpad.tools import get_provider, get_pad 7 | import dbus 8 | 9 | 10 | CREATE = -1 11 | SETTINGS = -2 12 | provider = get_provider() 13 | 14 | 15 | class EverpadRunner(plasmascript.Runner): 16 | 17 | def match(self, context): 18 | if not context.isValid(): 19 | return 20 | query = context.query() 21 | search = query.__str__() # PyQt is shit 22 | if len(search) < 3: 23 | return 24 | if search.lower() in 'create note': 25 | action = Plasma.QueryMatch(self.runner) 26 | action.setText("Create new note in everpad") 27 | action.setType(Plasma.QueryMatch.ExactMatch) 28 | action.setIcon(KIcon("everpad")) 29 | action.setData(str(CREATE)) 30 | context.addMatch(query, action) 31 | if search.lower() in 'settings and management': 32 | action = Plasma.QueryMatch(self.runner) 33 | action.setText("Open everpad settings") 34 | action.setType(Plasma.QueryMatch.ExactMatch) 35 | action.setIcon(KIcon("everpad")) 36 | action.setData(str(SETTINGS)) 37 | context.addMatch(query, action) 38 | blank = dbus.Array([], signature='i') 39 | for note_struct in provider.find_notes( 40 | search, blank, blank, 0, 41 | 1000, Note.ORDER_TITLE, -1, 42 | ): 43 | note = Note.from_tuple(note_struct) 44 | action = Plasma.QueryMatch(self.runner) 45 | action.setText(note.title) 46 | content = html2text(note.content) 47 | content = content[:200] 48 | action.setSubtext(content) 49 | action.setType(Plasma.QueryMatch.ExactMatch) 50 | action.setIcon(KIcon("everpad")) 51 | action.setData(str(note.id)) 52 | context.addMatch(query, action) 53 | 54 | def run(self, context, match): 55 | data = match.data().toInt()[0] 56 | pad = get_pad() 57 | if data == CREATE: 58 | pad.create() 59 | elif data == SETTINGS: 60 | pad.settings() 61 | else: 62 | pad.open(data) 63 | 64 | 65 | def CreateRunner(parent): 66 | return EverpadRunner(parent) 67 | -------------------------------------------------------------------------------- /everpad/specific/unity/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/everpad/specific/unity/__init__.py -------------------------------------------------------------------------------- /everpad/specific/unity/launcher.py: -------------------------------------------------------------------------------- 1 | import dbus 2 | import dbus.service 3 | 4 | 5 | class UnityLauncher(dbus.service.Object): 6 | def __init__(self, app_uri, *args, **kwargs): 7 | self.app_uri = app_uri 8 | self.data = {} 9 | dbus.service.Object.__init__(self, *args, **kwargs) 10 | 11 | def update(self, data): 12 | self.data = data 13 | self.Update(self.app_uri, data) 14 | 15 | @dbus.service.signal( 16 | dbus_interface='com.canonical.Unity.LauncherEntry', 17 | signature=("sa{sv}") 18 | ) 19 | def Update(self, app_uri, properties): 20 | return 21 | 22 | @dbus.service.method( 23 | dbus_interface='com.canonical.Unity.LauncherEntry', 24 | in_signature="", out_signature="sa{sv}", 25 | ) 26 | def Query(self): 27 | return self.app_uri, self.data 28 | -------------------------------------------------------------------------------- /everpad/specific/unity/lens.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.insert(0, '..') 3 | from singlet.lens import SingleScopeLens, ListViewCategory 4 | from gi.repository import Gio, Unity, Notify 5 | from singlet.utils import run_lens 6 | from everpad.tools import get_provider, get_pad, resource_filename 7 | from everpad.basetypes import Note, Tag, Notebook, Place, Resource 8 | from everpad.const import API_VERSION 9 | from html2text import html2text 10 | from datetime import datetime 11 | import dbus 12 | import dbus.mainloop.glib 13 | import sys 14 | import os 15 | import gettext 16 | import json 17 | 18 | 19 | path = os.path.join(os.path.dirname(__file__), '../../../i18n') 20 | if not os.path.isdir(path): 21 | path = resource_filename('share/locale/') 22 | gettext.bindtextdomain('everpad', path) 23 | gettext.textdomain('everpad') 24 | _ = gettext.gettext 25 | 26 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 27 | provider = get_provider() 28 | 29 | 30 | class EverpadLens(SingleScopeLens): 31 | 32 | class Meta: 33 | name = 'everpad' 34 | description = _('Everpad Lens') 35 | search_hint = _('Search Everpad') 36 | icon = 'everpad-lens' 37 | search_on_blank = True 38 | bus_name = 'net.launchpad.Unity.Lens.EverpadLens' 39 | bus_path = '/net/launchpad/unity/lens/everpad' 40 | 41 | def __init__(self): 42 | SingleScopeLens.__init__(self) 43 | provider.connect_to_signal( 44 | 'data_changed', 45 | self.update_props, 46 | dbus_interface="com.everpad.provider", 47 | ) 48 | provider.connect_to_signal( 49 | 'settings_changed', 50 | self.settings_changed, 51 | dbus_interface="com.everpad.provider", 52 | ) 53 | self.update_props() 54 | self._scope.connect('preview-uri', self.preview) 55 | 56 | def settings_changed(self, name, value): 57 | if name == 'search-on-home': 58 | self.update_props() 59 | 60 | def update_props(self): 61 | icon = Gio.ThemedIcon.new(resource_filename( 62 | "share/icons/unity-icon-theme/places/svg/group-recent.svg", 63 | )) 64 | tags = Unity.CheckOptionFilter.new('tags', _('Tags'), icon, True) 65 | for tag_struct in provider.list_tags(): 66 | tag = Tag.from_tuple(tag_struct) 67 | tags.add_option(str(tag.id), tag.name, icon) 68 | notebooks = Unity.RadioOptionFilter.new( 69 | 'notebooks', _('Notebooks'), icon, True, 70 | ) 71 | for notebook_struct in provider.list_notebooks(): 72 | notebook = Notebook.from_tuple(notebook_struct) 73 | notebooks.add_option(str(notebook.id), notebook.name, icon) 74 | places = Unity.RadioOptionFilter.new('places', _('Places'), icon, True) 75 | for place_struct in provider.list_places(): 76 | place = Place.from_tuple(place_struct) 77 | places.add_option(str(place.id), place.name, icon) 78 | self._lens.props.filters = [notebooks, tags, places] 79 | self._lens.props.search_in_global = True 80 | 81 | def can_search_on_home_lens(self): 82 | return bool(int(provider.get_settings_value('search-on-home') or 1)) 83 | 84 | pin_notes = ListViewCategory(_("Pin Notes"), 'everpad-lens') 85 | all_notes = ListViewCategory(_("All Notes"), 'everpad-lens') 86 | 87 | def search(self, search, results): 88 | try: 89 | version = provider.get_api_version() 90 | except ( # dbus raise some magic 91 | dbus.exceptions.UnknownMethodException, 92 | dbus.exceptions.DBusException, 93 | ): 94 | version = -1 95 | if version < API_VERSION: 96 | dim = datetime.now() - getattr(self, 'last_api_notify', datetime.now()) 97 | if dim.seconds > 600: 98 | Notify.init("everpad") 99 | Notify.Notification.new( 100 | 'everpad', 101 | _('Please restart everpad via indicator'), 102 | '').show() 103 | self.last_api_notify = datetime.now() 104 | return 105 | elif version > API_VERSION: 106 | sys.exit(0) 107 | if self.notebook_filter_id: 108 | notebooks = [self.notebook_filter_id] 109 | else: 110 | notebooks = dbus.Array([], signature='i') 111 | if self.place_filter_id: 112 | place = self.place_filter_id 113 | else: 114 | place = 0 115 | tags = dbus.Array(self.tag_filter_ids, signature='i') 116 | for note_struct in provider.find_notes( 117 | search, notebooks, tags, place, 118 | 1000, Note.ORDER_TITLE, -1, 119 | ): 120 | note = Note.from_tuple(note_struct) 121 | results.append(json.dumps({'id': note.id, 'search': search}), 122 | 'everpad-note', self.pin_notes if note.pinnded else self.all_notes, 123 | "text/html", note.title, html2text(note.content), 124 | '') 125 | 126 | def global_search(self, phrase, results): 127 | if self.can_search_on_home_lens(): 128 | return super(EverpadLens, self).global_search(phrase, results) 129 | else: 130 | results.clear() 131 | 132 | 133 | def preview(self, scope, uri): 134 | obj = json.loads(uri) 135 | note = Note.from_tuple(provider.get_note(obj['id'])) 136 | preview = Unity.GenericPreview.new( 137 | note.title, html2text(note.content), None, 138 | ) 139 | edit = Unity.PreviewAction.new("edit", "Edit", None) 140 | image = None 141 | for _res in provider.get_note_resources(note.id): 142 | res = Resource.from_tuple(_res) 143 | if 'image' in res.mime: 144 | image = 'file://%s' % res.file_path 145 | if image: 146 | preview.props.image_source_uri = image 147 | edit.connect('activated', self.handle_uri) 148 | preview.add_action(edit) 149 | return preview 150 | 151 | def handle_uri(self, scope, uri): 152 | obj = json.loads(uri) 153 | get_pad().open_with_search_term(int(obj['id']), obj.get('search', '')) 154 | return self.hide_dash_response() 155 | 156 | def on_filtering_changed(self, scope): 157 | tags = scope.get_filter('tags') 158 | self.tag_filter_ids = map(lambda tag: int(tag.props.id), 159 | filter(lambda tag: tag.props.active, tags.options)) 160 | notebook = scope.get_filter('notebooks').get_active_option() 161 | if notebook: 162 | self.notebook_filter_id = int(notebook.props.id) 163 | else: 164 | self.notebook_filter_id = None 165 | place = scope.get_filter('places').get_active_option() 166 | if place: 167 | self.place_filter_id = int(place.props.id) 168 | else: 169 | self.place_filter_id = None 170 | SingleScopeLens.on_filtering_changed(self, scope) 171 | 172 | 173 | def main(): 174 | run_lens(EverpadLens, sys.argv) 175 | 176 | if __name__ == '__main__': 177 | main() 178 | -------------------------------------------------------------------------------- /everpad/tools.py: -------------------------------------------------------------------------------- 1 | from functools import wraps, partial 2 | from BeautifulSoup import BeautifulSoup 3 | from HTMLParser import HTMLParser 4 | from everpad.const import API_VERSION, SCHEMA_VERSION, VERSION 5 | import dbus 6 | import re 7 | import sys 8 | import os 9 | import pkg_resources 10 | 11 | 12 | class InterfaceWrapper(object): 13 | def __init__(self, get): 14 | self.__get = get 15 | self.__load() 16 | 17 | def __load(self): 18 | self.__interface = self.__get() 19 | 20 | def __getattr__(self, name): 21 | attr = getattr(self.__interface, name) 22 | if hasattr(attr, '__call__'): 23 | attr = self.__reconnect_on_fail(attr, name) 24 | return attr 25 | 26 | def __reconnect_on_fail(self, fnc, name): 27 | def wrapper(*args, **kwargs): 28 | try: 29 | return fnc(*args, **kwargs) 30 | except dbus.DBusException: 31 | self.__load() 32 | return getattr(self.__interface, name)(*args, **kwargs) 33 | return wrapper 34 | 35 | 36 | def wrapper_functor(fnc): 37 | @wraps(fnc) 38 | def wrapper(*args, **kwrags): 39 | return InterfaceWrapper(partial(fnc, *args, **kwrags)) 40 | return wrapper 41 | 42 | 43 | @wrapper_functor 44 | def get_provider(bus=None): 45 | if not bus: 46 | bus = dbus.SessionBus() 47 | provider = bus.get_object("com.everpad.Provider", '/EverpadProvider') 48 | return dbus.Interface(provider, "com.everpad.Provider") 49 | 50 | 51 | @wrapper_functor 52 | def get_pad(bus=None): 53 | if not bus: 54 | bus = dbus.SessionBus() 55 | pad = bus.get_object("com.everpad.App", "/EverpadService") 56 | return dbus.Interface(pad, "com.everpad.App") 57 | 58 | 59 | def clean(text): # from http://stackoverflow.com/questions/1707890/fast-way-to-filter-illegal-xml-unicode-chars-in-python 60 | illegal_unichrs = [ 61 | (0x00, 0x08), (0x0B, 0x1F), (0x7F, 0x84), (0x86, 0x9F), 62 | (0xD800, 0xDFFF), (0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF), 63 | (0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF), (0x3FFFE, 0x3FFFF), 64 | (0x4FFFE, 0x4FFFF), (0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF), 65 | (0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF), (0x9FFFE, 0x9FFFF), 66 | (0xAFFFE, 0xAFFFF), (0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF), 67 | (0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF), (0xFFFFE, 0xFFFFF), 68 | (0x10FFFE, 0x10FFFF) 69 | ] 70 | 71 | illegal_ranges = [ 72 | "%s-%s" % (unichr(low), unichr(high)) 73 | for (low, high) in illegal_unichrs 74 | if low < sys.maxunicode 75 | ] 76 | illegal_xml_re = re.compile(u'[%s]' % u''.join(illegal_ranges)) 77 | return illegal_xml_re.sub('', text) 78 | 79 | 80 | def sanitize(soup=None, html=None): 81 | _allowed_tags = ( 82 | 'a', 'abbr', 'acronym', 'address', 'area', 'b', 'bdo', 83 | 'big', 'blockquote', 'br', 'caption', 'center', 'cite', 84 | 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'div', 85 | 'dl', 'dt', 'em', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 86 | 'h6', 'hr', 'i', 'img', 'ins', 'kbd', 'li', 'map', 'ol', 87 | 'p', 'pre', 'q', 's', 'samp', 'small', 'span', 'strike', 88 | 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 89 | 'th', 'thead', 'title', 'tr', 'tt', 'u', 'ul', 'var', 'xmp', 90 | 'en-media', 'en-todo', 'en-crypt', 91 | ) 92 | _disallowed_attrs = ( 93 | 'id', 'class', 'onclick', 'ondblclick', 'rel', 94 | 'accesskey', 'data', 'dynsrc', 'tabindex', 'typeof', 95 | 'property', 96 | ) 97 | _protocols = ( 98 | 'http', 'https', 'file', 'evernote', 99 | ) 100 | if not soup: 101 | soup = BeautifulSoup(html) 102 | for tag in soup.findAll(True): 103 | if tag.name in _allowed_tags: 104 | for attr in _disallowed_attrs: 105 | try: 106 | del tag[attr] 107 | except KeyError: 108 | pass 109 | try: 110 | if not sum(map( 111 | lambda proto: tag['href'].find(proto + '://') == 0, 112 | _protocols)): 113 | del tag['href'] 114 | except KeyError: 115 | pass 116 | else: 117 | tag.hidden = True 118 | return clean(reduce( 119 | lambda txt, cur: txt + unicode(cur), soup.contents, 120 | u'')) 121 | 122 | 123 | def html_unescape(html): 124 | return HTMLParser().unescape(html) 125 | 126 | 127 | def print_version(): 128 | print 'Everpad version: %s' % VERSION 129 | print 'API version: %d' % API_VERSION 130 | print 'Schema version: %d' % SCHEMA_VERSION 131 | sys.exit(0) 132 | 133 | 134 | def get_proxy_config(scheme): 135 | for fmt in ('%s_proxy', '%s_PROXY'): 136 | proxy = os.environ.get(fmt % scheme) 137 | if proxy is not None: 138 | return proxy 139 | return None 140 | 141 | 142 | def prepare_file_path(dest, file_name): 143 | file_path = os.path.join(dest, file_name) 144 | iteration = 0 145 | while os.path.isfile(file_path): 146 | file_path = os.path.join(dest, '%d_%s' % ( 147 | iteration, file_name, 148 | )) 149 | iteration += 1 150 | return file_path 151 | 152 | 153 | def resource_filename(file_name): 154 | paths = map( 155 | lambda path: os.path.join(path, file_name), 156 | ( 157 | '/opt/extras.ubuntu.com/', 158 | '/usr/local/', 159 | '/usr/', 160 | ), 161 | ) 162 | for path in paths: 163 | if os.path.isfile(path): 164 | return path 165 | return pkg_resources.resource_filename( 166 | pkg_resources.Requirement.parse("everpad"), file_name) 167 | -------------------------------------------------------------------------------- /i18n/ar/LC_MESSAGES/everpad.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/ar/LC_MESSAGES/everpad.mo -------------------------------------------------------------------------------- /i18n/ar/LC_MESSAGES/everpad.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2012-11-08 18:19+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: everpad/specific/unity/lens.py:34 21 | msgid "Everpad Lens" 22 | msgstr "عدسة إفَرْباد" 23 | 24 | #: everpad/specific/unity/lens.py:35 25 | msgid "Search Everpad" 26 | msgstr "ابحث في إفَرْباد" 27 | 28 | #: everpad/specific/unity/lens.py:55 29 | msgid "Tags" 30 | msgstr "وسوم" 31 | 32 | #: everpad/specific/unity/lens.py:59 33 | msgid "Notebooks" 34 | msgstr "دفاتر" 35 | 36 | #: everpad/specific/unity/lens.py:63 37 | msgid "Places" 38 | msgstr "أماكن" 39 | 40 | #: everpad/specific/unity/lens.py:69 41 | msgid "Pin Notes" 42 | msgstr "مُذَكِّرَات مثبتة" 43 | 44 | #: everpad/specific/unity/lens.py:70 45 | msgid "All Notes" 46 | msgstr "كل المُذَكِّرَات" 47 | 48 | #: everpad/specific/unity/lens.py:86 49 | msgid "Please restart everpad via indicator" 50 | msgstr "رجاءً أعِدْ تشغيل إفَرْباد من منطقة التنبيهات" 51 | -------------------------------------------------------------------------------- /i18n/ar_EG.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/ar_EG.qm -------------------------------------------------------------------------------- /i18n/de/LC_MESSAGES/everpad.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/de/LC_MESSAGES/everpad.mo -------------------------------------------------------------------------------- /i18n/de/LC_MESSAGES/everpad.po: -------------------------------------------------------------------------------- 1 | # German translation Everpad (DASH) 2 | # Copyright (C) 2013 3 | # This file is distributed under the same license as the Everpad package. 4 | # Simon Allendörfer , 2013 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: 1.0\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2012-11-08 18:19+0200\n" 12 | "PO-Revision-Date: 2013-03-30 15:30+0100\n" 13 | "Last-Translator: Simon Allendörfer \n" 14 | "Language-Team: \n" 15 | "Language: de\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: everpad/specific/unity/lens.py:34 21 | msgid "Everpad Lens" 22 | msgstr "Everpad-Linse" 23 | 24 | #: everpad/specific/unity/lens.py:35 25 | msgid "Search Everpad" 26 | msgstr "Everpad durchsuchen" 27 | 28 | #: everpad/specific/unity/lens.py:55 29 | msgid "Tags" 30 | msgstr "Schlagwörter" 31 | 32 | #: everpad/specific/unity/lens.py:59 33 | msgid "Notebooks" 34 | msgstr "Notizbücher" 35 | 36 | #: everpad/specific/unity/lens.py:63 37 | msgid "Places" 38 | msgstr "Orte" 39 | 40 | #: everpad/specific/unity/lens.py:69 41 | msgid "Pin Notes" 42 | msgstr "Notizbuch anpinnen" 43 | 44 | #: everpad/specific/unity/lens.py:70 45 | msgid "All Notes" 46 | msgstr "Alle Notizen" 47 | 48 | #: everpad/specific/unity/lens.py:86 49 | msgid "Please restart everpad via indicator" 50 | msgstr "Bitte Everpad mit dem Indikator neustarten" 51 | -------------------------------------------------------------------------------- /i18n/de_AT.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/de_AT.qm -------------------------------------------------------------------------------- /i18n/de_CH.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/de_CH.qm -------------------------------------------------------------------------------- /i18n/de_DE.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/de_DE.qm -------------------------------------------------------------------------------- /i18n/es.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/es.qm -------------------------------------------------------------------------------- /i18n/es/LC_MESSAGES/everpad.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/es/LC_MESSAGES/everpad.mo -------------------------------------------------------------------------------- /i18n/es/LC_MESSAGES/everpad.po: -------------------------------------------------------------------------------- 1 | # Spanish translation for Everpad (DASH) 2 | # Copyright (C) 2013 3 | # This file is distributed under the same license as the Everpad package. 4 | # Jesus M. Castagnetto , 2013 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: 1.0\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2012-11-08 18:19+0200\n" 12 | "PO-Revision-Date: 2013-03-03 09:54-0500\n" 13 | "Last-Translator: Jesus M. Castagnetto \n" 14 | "Language-Team: \n" 15 | "Language: es\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: everpad/specific/unity/lens.py:34 21 | msgid "Everpad Lens" 22 | msgstr "Lente de Everpad" 23 | 24 | #: everpad/specific/unity/lens.py:35 25 | msgid "Search Everpad" 26 | msgstr "Busca en Everpad" 27 | 28 | #: everpad/specific/unity/lens.py:55 29 | msgid "Tags" 30 | msgstr "Etiquetas" 31 | 32 | #: everpad/specific/unity/lens.py:59 33 | msgid "Notebooks" 34 | msgstr "Cuadernos de notas" 35 | 36 | #: everpad/specific/unity/lens.py:63 37 | msgid "Places" 38 | msgstr "Lugares" 39 | 40 | #: everpad/specific/unity/lens.py:69 41 | msgid "Pin Notes" 42 | msgstr "Fijar Notas" 43 | 44 | #: everpad/specific/unity/lens.py:70 45 | msgid "All Notes" 46 | msgstr "Todas las Notas" 47 | 48 | #: everpad/specific/unity/lens.py:86 49 | msgid "Please restart everpad via indicator" 50 | msgstr "Por favor, reiniciar everpad usando el indicador" 51 | -------------------------------------------------------------------------------- /i18n/ja.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/ja.qm -------------------------------------------------------------------------------- /i18n/ja/LC_MESSAGES/everpad.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/ja/LC_MESSAGES/everpad.mo -------------------------------------------------------------------------------- /i18n/ja/LC_MESSAGES/everpad.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2012-11-08 18:19+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: everpad/specific/unity/lens.py:34 21 | msgid "Everpad Lens" 22 | msgstr "Everpadレンズ" 23 | 24 | #: everpad/specific/unity/lens.py:35 25 | msgid "Search Everpad" 26 | msgstr "Everpadの検索" 27 | 28 | #: everpad/specific/unity/lens.py:55 29 | msgid "Tags" 30 | msgstr "タグ" 31 | 32 | #: everpad/specific/unity/lens.py:59 33 | msgid "Notebooks" 34 | msgstr "ノートブック" 35 | 36 | #: everpad/specific/unity/lens.py:63 37 | msgid "Places" 38 | msgstr "場所" 39 | 40 | #: everpad/specific/unity/lens.py:69 41 | msgid "Pin Notes" 42 | msgstr "ピン留めされたノート" 43 | 44 | #: everpad/specific/unity/lens.py:70 45 | msgid "All Notes" 46 | msgstr "全てのノート" 47 | 48 | #: everpad/specific/unity/lens.py:86 49 | msgid "Please restart everpad via indicator" 50 | msgstr "インジケーターからEverpadを再起動してください" 51 | -------------------------------------------------------------------------------- /i18n/messages.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2012-11-08 18:19+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: everpad/specific/unity/lens.py:34 21 | msgid "Everpad Lens" 22 | msgstr "" 23 | 24 | #: everpad/specific/unity/lens.py:35 25 | msgid "Search Everpad" 26 | msgstr "" 27 | 28 | #: everpad/specific/unity/lens.py:55 29 | msgid "Tags" 30 | msgstr "" 31 | 32 | #: everpad/specific/unity/lens.py:59 33 | msgid "Notebooks" 34 | msgstr "" 35 | 36 | #: everpad/specific/unity/lens.py:63 37 | msgid "Places" 38 | msgstr "" 39 | 40 | #: everpad/specific/unity/lens.py:69 41 | msgid "Pin Notes" 42 | msgstr "" 43 | 44 | #: everpad/specific/unity/lens.py:70 45 | msgid "All Notes" 46 | msgstr "" 47 | 48 | #: everpad/specific/unity/lens.py:86 49 | msgid "Please restart everpad via indicator" 50 | msgstr "" 51 | -------------------------------------------------------------------------------- /i18n/nl.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/nl.qm -------------------------------------------------------------------------------- /i18n/ru/LC_MESSAGES/everpad.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/ru/LC_MESSAGES/everpad.mo -------------------------------------------------------------------------------- /i18n/ru/LC_MESSAGES/everpad.po: -------------------------------------------------------------------------------- 1 | # Russian translations for PACKAGE package. 2 | # Copyright (C) 2012 ORGANIZATION 3 | # Владимир Яковлев , 2012. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "POT-Creation-Date: 2012-09-09 19:47+MSK\n" 9 | "PO-Revision-Date: 2012-09-09 19:49+0400\n" 10 | "Last-Translator: \n" 11 | "Language-Team: Russian\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | "Language: ru\n" 17 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 18 | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 19 | 20 | #: lens.py:29 21 | msgid "Everpad Lens" 22 | msgstr "Линза Everpad" 23 | 24 | #: lens.py:30 25 | msgid "Search Everpad" 26 | msgstr "Искать заметки" 27 | 28 | #: lens.py:39 29 | msgid "Tags" 30 | msgstr "Тэги" 31 | 32 | #: lens.py:43 33 | msgid "Notebooks" 34 | msgstr "Блокноты" 35 | 36 | #: lens.py:47 37 | msgid "Places" 38 | msgstr "Места" 39 | 40 | #: lens.py:54 41 | msgid "Notes" 42 | msgstr "Заметки" 43 | 44 | -------------------------------------------------------------------------------- /i18n/ru_RU.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/ru_RU.qm -------------------------------------------------------------------------------- /i18n/zh_CN.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/zh_CN.qm -------------------------------------------------------------------------------- /i18n/zh_CN/LC_MESSAGES/everpad.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/zh_CN/LC_MESSAGES/everpad.mo -------------------------------------------------------------------------------- /i18n/zh_CN/LC_MESSAGES/everpad.po: -------------------------------------------------------------------------------- 1 | # Simplified Chinese translations for PACKAGE package. 2 | # Copyright (C) 2012 ORGANIZATION 3 | # 玛格丽特 苏 , 2012. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "POT-Creation-Date: 2012-09-09 19:47+MSK\n" 9 | "PO-Revision-Date: 2012-09-09 19:49+0400\n" 10 | "Last-Translator: 玛格丽特苏 \n" 11 | "Language-Team: Simplified Chinese\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | "Language: zh_CN\n" 17 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 18 | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 19 | 20 | #: lens.py:29 21 | msgid "Everpad Lens" 22 | msgstr "Everpad 透镜" 23 | 24 | #: lens.py:30 25 | msgid "Search Everpad" 26 | msgstr "搜索 Everpad" 27 | 28 | #: lens.py:39 29 | msgid "Tags" 30 | msgstr "标签" 31 | 32 | #: lens.py:43 33 | msgid "Notebooks" 34 | msgstr "笔记本" 35 | 36 | #: lens.py:47 37 | msgid "Places" 38 | msgstr "位置" 39 | 40 | #: lens.py:54 41 | msgid "Notes" 42 | msgstr "笔记" 43 | 44 | -------------------------------------------------------------------------------- /i18n/zh_TW.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/zh_TW.qm -------------------------------------------------------------------------------- /i18n/zh_TW/LC_MESSAGES/everpad.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/i18n/zh_TW/LC_MESSAGES/everpad.mo -------------------------------------------------------------------------------- /i18n/zh_TW/LC_MESSAGES/everpad.po: -------------------------------------------------------------------------------- 1 | # Traditional Chinese translations for PACKAGE package. 2 | # Copyright (C) 2012 ORGANIZATION 3 | # 瑪格麗特 蘇 , 2012. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "POT-Creation-Date: 2012-09-09 19:47+MSK\n" 9 | "PO-Revision-Date: 2012-09-09 19:49+0400\n" 10 | "Last-Translator: 瑪格麗特蘇 \n" 11 | "Language-Team: Traditional Chinese\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | "Language: zh_TW\n" 17 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 18 | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 19 | 20 | #: lens.py:29 21 | msgid "Everpad Lens" 22 | msgstr "Everpad 透鏡" 23 | 24 | #: lens.py:30 25 | msgid "Search Everpad" 26 | msgstr "搜索 Everpad" 27 | 28 | #: lens.py:39 29 | msgid "Tags" 30 | msgstr "標籤" 31 | 32 | #: lens.py:43 33 | msgid "Notebooks" 34 | msgstr "筆記本" 35 | 36 | #: lens.py:47 37 | msgid "Places" 38 | msgstr "位置" 39 | 40 | #: lens.py:54 41 | msgid "Notes" 42 | msgstr "筆記" 43 | 44 | -------------------------------------------------------------------------------- /scripts/install_dbus_services.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Install dbus service files in a custom prefix. 5 | This is needed when installing in user directory, as the standard search 6 | path is not aware of the virtualenv. 7 | """ 8 | 9 | from __future__ import with_statement 10 | import sys 11 | import os 12 | 13 | if len(sys.argv) > 1: 14 | services_dir = sys.argv[1] 15 | else: 16 | services_dir = "~/.local/share/dbus-1/services/" 17 | 18 | services_dir = os.path.expanduser(services_dir) 19 | if not os.path.isdir(services_dir): 20 | os.makedirs(services_dir) 21 | 22 | 23 | service_files = {} 24 | 25 | service_files["everpad-app.service"] = """\ 26 | [D-BUS Service] 27 | Name=com.everpad.App 28 | Exec={prefix}/bin/everpad 29 | """ 30 | 31 | service_files["everpad-provider.service"] = """\ 32 | [D-BUS Service] 33 | Name=com.everpad.Provider 34 | Exec={prefix}/bin/everpad-provider 35 | """ 36 | 37 | service_files["unity-lens-everpad.service"] = """\ 38 | [D-BUS Service] 39 | Name=net.launchpad.Unity.Lens.EverpadLens 40 | Exec={prefix}/bin/everpad-lens 41 | """ 42 | 43 | if __name__ == '__main__': 44 | for filename, filetpl in service_files.iteritems(): 45 | print "Installing {} -> {}".format(filename, services_dir) 46 | with open(os.path.join(services_dir, filename), 'w') as f: 47 | f.write(filetpl.format(prefix=sys.prefix)) 48 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = dev 3 | tag_svn_revision = true 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | 4 | version = '2.5' 5 | requirements = [ 6 | "BeautifulSoup", 7 | "html2text", 8 | "httplib2", 9 | "keyring", 10 | "py-oauth2 ", 11 | "pysqlite ", 12 | "regex", 13 | "sqlalchemy", 14 | ] 15 | if not 'TRAVIS_CI' in os.environ: 16 | requirements.append('PySide') 17 | 18 | 19 | setup( 20 | name='everpad', 21 | version=version, 22 | description="Ubuntu integrated evernote client", 23 | long_description=open('README.rst').read(), 24 | classifiers=[ 25 | 'Programming Language :: Python', 26 | ], 27 | keywords='ubuntu python evernote', 28 | author='Vladimir Yakovlev', 29 | author_email='nvbn.rm@gmail.com', 30 | url='https://github.com/nvbn/everpad/', 31 | license='X11', 32 | packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), 33 | include_package_data=True, 34 | zip_safe=True, 35 | install_requires=requirements, 36 | entry_points={ 37 | 'gui_scripts': [ 38 | 'everpad=everpad.pad.indicator:main' 39 | ], 'console_scripts': [ 40 | 'everpad-lens=everpad.specific.unity.lens:main', 41 | 'everpad-provider=everpad.provider.daemon:main', 42 | ] 43 | }, 44 | data_files=[ 45 | ('share/icons/hicolor/24x24/actions', [ 46 | 'data/editor-icons/everpad-text-bold.png', 47 | 'data/editor-icons/everpad-list-unordered.png', 48 | 'data/editor-icons/everpad-text-strikethrough.png', 49 | 'data/editor-icons/everpad-text-italic.png', 50 | 'data/editor-icons/everpad-list-ordered.png', 51 | 'data/editor-icons/everpad-justify-center.png', 52 | 'data/editor-icons/everpad-justify-left.png', 53 | 'data/editor-icons/everpad-justify-fill.png', 54 | 'data/editor-icons/everpad-text-underline.png', 55 | 'data/editor-icons/everpad-justify-right.png', 56 | 'data/editor-icons/everpad-checkbox.png', 57 | 'data/editor-icons/everpad-link.png', 58 | 'data/editor-icons/everpad-insert-table.png', 59 | 'data/editor-icons/everpad-insert-image.png', 60 | 'data/editor-icons/everpad-pin.png', 61 | ]), 62 | ('share/icons/hicolor/32x32/apps', [ 63 | 'data/everpad-black.png', 'data/everpad-mono.png', 64 | ]), 65 | ('share/icons/hicolor/48x48/actions', [ 66 | 'data/everpad-file.png', 67 | ]), 68 | ('share/icons/hicolor/64x64/apps', [ 69 | 'data/everpad-note.png', 70 | ]), 71 | ('share/icons/hicolor/128x128/apps', [ 72 | 'data/everpad.png', 'data/everpad-lens.png', 73 | ]), 74 | ('share/pixmaps', [ 75 | 'data/everpad.png', 'data/everpad-mono.png', 76 | 'data/everpad-lens.png', 'data/everpad-note.png', 77 | 'data/everpad-black.png', 78 | ]), 79 | ('share/applications', ['data/everpad.desktop']), 80 | ('share/everpad/i18n/', [ 81 | 'i18n/ru_RU.qm', 82 | 'i18n/ar_EG.qm', 83 | 'i18n/zh_CN.qm', 84 | 'i18n/zh_TW.qm', 85 | 'i18n/ja.qm', 86 | 'i18n/es.qm', 87 | 'i18n/de_DE.qm', 88 | 'i18n/de_AT.qm', 89 | 'i18n/de_CH.qm', 90 | ]), 91 | ('share/everpad/', [ 92 | 'everpad/pad/editor/editor.html', 93 | ]), 94 | ('share/locale/ru/LC_MESSAGES', ['i18n/ru/LC_MESSAGES/everpad.mo']), 95 | ('share/locale/ar/LC_MESSAGES', ['i18n/ar/LC_MESSAGES/everpad.mo']), 96 | ('share/locale/zh_CN/LC_MESSAGES', ['i18n/zh_CN/LC_MESSAGES/everpad.mo']), 97 | ('share/locale/zh_TW/LC_MESSAGES', ['i18n/zh_TW/LC_MESSAGES/everpad.mo']), 98 | ('share/locale/ja/LC_MESSAGES', ['i18n/ja/LC_MESSAGES/everpad.mo']), 99 | ('share/locale/es/LC_MESSAGES', ['i18n/es/LC_MESSAGES/everpad.mo']), 100 | ('share/locale/de/LC_MESSAGES', ['i18n/de/LC_MESSAGES/everpad.mo']), 101 | ('share/unity/lenses/everpad', ['data/everpad.lens']), 102 | ('share/dbus-1/services', [ 103 | 'data/unity-lens-everpad.service', 104 | 'data/everpad-provider.service', 105 | 'data/everpad-app.service', 106 | ]), 107 | ('share/kde4/services/', [ 108 | 'data/plasma-runner-everpad.desktop', 109 | ]), 110 | ('share/kde4/apps/plasma/runners/everpad/', [ 111 | 'data/metadata.desktop', 112 | ]), 113 | ('share/kde4/apps/plasma/runners/everpad/contents/code/', [ 114 | 'everpad/specific/kde/everpad_runner.py', 115 | ]), 116 | ] 117 | ) 118 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/tests/__init__.py -------------------------------------------------------------------------------- /tests/factories.py: -------------------------------------------------------------------------------- 1 | from everpad.provider import models 2 | from everpad import const 3 | from factory.alchemy import SQLAlchemyModelFactory 4 | import factory 5 | 6 | 7 | class NotebookFactory(SQLAlchemyModelFactory): 8 | """Note factory""" 9 | FACTORY_FOR = models.Notebook 10 | 11 | guid = factory.Sequence(lambda n: 'guid{}'.format(n)) 12 | name = factory.Sequence(lambda n: 'name{}'.format(n)) 13 | default = False 14 | service_created = factory.Sequence(lambda n: n) 15 | service_updated = factory.Sequence(lambda n: n) 16 | action = const.ACTION_NONE 17 | stack = '' 18 | 19 | 20 | class TagFactory(SQLAlchemyModelFactory): 21 | """Tag factory""" 22 | FACTORY_FOR = models.Tag 23 | 24 | guid = factory.Sequence(lambda n: 'guid{}'.format(n)) 25 | name = factory.Sequence(lambda n: 'name{}'.format(n)) 26 | action = const.ACTION_NONE 27 | 28 | 29 | class ResourceFactory(SQLAlchemyModelFactory): 30 | """Resource factory""" 31 | FACTORY_FOR = models.Resource 32 | 33 | guid = factory.Sequence(lambda n: 'guid{}'.format(n)) 34 | hash = factory.Sequence(lambda n: 'hash{}'.format(n)) 35 | mime = 'text/plain' 36 | action = const.ACTION_NONE 37 | 38 | 39 | class NoteFactory(SQLAlchemyModelFactory): 40 | """Note factory""" 41 | FACTORY_FOR = models.Note 42 | 43 | guid = factory.Sequence(lambda n: 'guid{}'.format(n)) 44 | title = factory.Sequence(lambda n: 'title{}'.format(n)) 45 | content = factory.Sequence(lambda n: 'content{}'.format(n)) 46 | created = factory.Sequence(lambda n: n) 47 | updated = factory.Sequence(lambda n: n) 48 | updated_local = factory.Sequence(lambda n: n) 49 | 50 | 51 | class PlaceFactory(SQLAlchemyModelFactory): 52 | """Place factory""" 53 | FACTORY_FOR = models.Place 54 | 55 | name = factory.Sequence(lambda n: 'name{}'.format(n)) 56 | 57 | 58 | def invoke_session(session): 59 | """Invoke sqlalchemy sessions""" 60 | for _factory in ( 61 | NotebookFactory, 62 | TagFactory, 63 | ResourceFactory, 64 | NoteFactory, 65 | PlaceFactory, 66 | ): 67 | _factory.FACTORY_SESSION = session 68 | -------------------------------------------------------------------------------- /tests/pad/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/tests/pad/__init__.py -------------------------------------------------------------------------------- /tests/pad/test_editor.py: -------------------------------------------------------------------------------- 1 | from .. import settings 2 | from mock import MagicMock 3 | from PySide.QtCore import QSettings, Signal, QUrl 4 | from PySide.QtGui import QApplication 5 | from everpad.provider.service import ProviderService 6 | from everpad.provider.tools import get_db_session 7 | from everpad.basetypes import ( 8 | Note, Notebook, Tag, Resource, Place, 9 | NONE_ID, NONE_VAL, 10 | ) 11 | from everpad.provider import models 12 | from everpad.pad.editor import Editor 13 | from everpad.pad.editor.content import set_links 14 | from datetime import datetime 15 | import unittest 16 | import sys 17 | import os 18 | 19 | 20 | class FakeApp(QApplication): 21 | data_changed = Signal() 22 | 23 | def update(self, service): 24 | self.provider = service 25 | self.settings = QSettings('everpad-test', str(datetime.now())) 26 | 27 | 28 | app = FakeApp(sys.argv) 29 | 30 | 31 | CONTENTS = [ 32 | u"
  • 23
  • 567
", 33 | u"

123

\xa0\xa0ok", 34 | u"

\xa0\xa0123

\xa0\xa0\xa0\xa0ok

", 35 | u"

hello, i'am fat

", 36 | u"
  • 1
    • 2
    • 3
  • 4
", 37 | ] 38 | 39 | CHANGING_CONTENTS = [ 40 | (u"

< a b cd

", u"

< a b cd

"), 41 | (u"> a b cd", u"> a b cd"), 42 | (u"

ok

ok

"), 43 | ] 44 | 45 | TITLES = [ 46 | u"<<ok ok ok", 47 | ''.join([u"verybigtitle"] * 50), 48 | u"ok

https://github.com/nvbn/'), 54 | (u"https://github.com/nvbn/ http://ya.ru/", u'https://github.com/nvbn/ http://ya.ru/'), 55 | (u"

https://github.com/nvbn/

", u"

https://github.com/nvbn/

"), 56 | ] 57 | 58 | 59 | if 'test_editor' in os.environ: 60 | class EditorTestCase(unittest.TestCase): 61 | def setUp(self): 62 | self.service = ProviderService() 63 | self.service._session = get_db_session() 64 | models.Note.session = self.service._session 65 | self.app = app 66 | self.app.update(self.service) 67 | notebook = Notebook.from_tuple( 68 | self.service.create_notebook('test', None), 69 | ) 70 | self.note = Note.from_tuple(self.service.create_note(Note( 71 | id=NONE_ID, 72 | title='New note', 73 | content="New note content", 74 | tags=[], 75 | notebook=notebook.id, 76 | created=NONE_VAL, 77 | updated=NONE_VAL, 78 | place='', 79 | ).struct)) 80 | 81 | def tearDown(self): 82 | if hasattr(self, 'editor'): 83 | self.app.data_changed.disconnect( 84 | self.editor.init_alternatives, 85 | ) 86 | del self.app.provider 87 | del self.app 88 | 89 | def test_content_nochange(self): 90 | """Test content nochange""" 91 | self.editor = Editor(self.note) 92 | self.assertEqual( 93 | self.editor.note_edit.content, 94 | "New note content", 95 | ) 96 | for content in CONTENTS: 97 | self.editor.note_edit.content = content 98 | self.assertEqual( 99 | self.editor.note_edit.content, 100 | content, 101 | ) 102 | 103 | def test_content_changing(self): 104 | """Test content changing""" 105 | self.editor = Editor(self.note) 106 | for prev, current in CHANGING_CONTENTS: 107 | self.editor.note_edit.content = prev 108 | self.assertEqual( 109 | self.editor.note_edit.content, 110 | current, 111 | ) 112 | 113 | def test_title_nochange(self): 114 | """Test title nochange""" 115 | self.editor = Editor(self.note) 116 | self.assertEqual( 117 | self.editor.note_edit.title, 118 | "New note", 119 | ) 120 | for title in TITLES: 121 | self.editor.note_edit.title = title 122 | self.assertEqual( 123 | self.editor.note_edit.title, 124 | title, 125 | ) 126 | 127 | def test_set_links(self): 128 | """Test set links""" 129 | for orig, result in SET_LINKS: 130 | self.assertEqual( 131 | set_links(orig), result, 132 | ) 133 | 134 | def test_not_broken_note_links(self): 135 | """Test content nochange""" 136 | content = 'note link' 137 | self.note.content = content 138 | self.editor = Editor(self.note) 139 | self.assertEqual( 140 | self.editor.note_edit.content, 141 | content, 142 | ) 143 | 144 | def test_open_note_links(self): 145 | """Test open note links""" 146 | guid = 'guid' 147 | note = Note( 148 | id=123, 149 | ) 150 | 151 | self.app.open = MagicMock() 152 | self.service.get_note_by_guid = MagicMock( 153 | return_value=note.struct, 154 | ) 155 | 156 | link = "evernote:///view/123/123/{guid}/123/".format( 157 | guid=guid, 158 | ) 159 | self.editor = Editor(self.note) 160 | self.editor.note_edit.link_clicked(QUrl(link)) 161 | 162 | self.assertEqual( 163 | self.service.get_note_by_guid.call_args[0][0], guid, 164 | ) 165 | self.assertEqual( 166 | self.app.open.call_args[0][0].id, note.id, 167 | ) 168 | del self.app.open 169 | -------------------------------------------------------------------------------- /tests/provider/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/tests/provider/__init__.py -------------------------------------------------------------------------------- /tests/settings/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | import local 3 | except ImportError: 4 | raise ImportError('Copy dist.py to local.py') 5 | 6 | 7 | from everpad import const 8 | 9 | 10 | const.HOST = local.HOST 11 | const.CONSUMER_KEY = local.CONSUMER_KEY 12 | const.CONSUMER_SECRET = local.CONSUMER_SECRET 13 | const.DB_PATH = local.DB_PATH 14 | TOKEN = local.TOKEN 15 | -------------------------------------------------------------------------------- /tests/settings/ci_local.py: -------------------------------------------------------------------------------- 1 | HOST = 'sandbox.evernote.com' 2 | CONSUMER_KEY = 'nvbn-1422' 3 | CONSUMER_SECRET = 'c17c0979d0054310' 4 | DB_PATH = ':memory:' 5 | TOKEN = 'S=s1:U=6604d:E=145758f3852:C=13e1dde0c54:P=1cd:A=en-devtoken:V=2:H=d2b720cdb06c99a105f937e669ebfc67' 6 | -------------------------------------------------------------------------------- /tests/settings/dist.py: -------------------------------------------------------------------------------- 1 | HOST = 'sandbox.evernote.com' 2 | CONSUMER_KEY = '' 3 | CONSUMER_SECRET = '' 4 | DB_PATH = ':memory:' 5 | TOKEN = '' 6 | -------------------------------------------------------------------------------- /tests/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvbn/everpad/5db96c0f9b7c30ce4f900274f3826fdfa55cbaac/tests/test.png -------------------------------------------------------------------------------- /tests/test_basetypes.py: -------------------------------------------------------------------------------- 1 | from everpad.basetypes import Tag, DbusSendable 2 | import unittest 3 | 4 | 5 | class TestBaseTypes(unittest.TestCase): 6 | def test_signature(self): 7 | class Fake(DbusSendable): 8 | fields = ( 9 | ('id', 'i'), 10 | ('name', 's'), 11 | ) 12 | self.assertEqual( 13 | Fake.signature, '(is)', 14 | 'generate signature', 15 | ) 16 | 17 | def test_serialise(self): 18 | class Fake(object): 19 | id = 0 20 | name = '123' 21 | tag = Tag.from_obj(Fake()) 22 | self.assertEqual( 23 | tag.struct, (0, '123'), 24 | 'serialise to struct', 25 | ) 26 | 27 | def test_load(self): 28 | tag = Tag.from_tuple((0, '123')) 29 | self.assertEqual( 30 | tag.name, '123', 31 | 'load from struct', 32 | ) 33 | 34 | def test_give(self): 35 | class Fake(object): 36 | id = 0 37 | @property 38 | def id_dbus(self): 39 | return self.id 40 | 41 | @id_dbus.setter 42 | def id_dbus(self, val): 43 | self.id = val + 12 44 | tag = Tag.from_tuple((0, '123')) 45 | obj = Fake() 46 | tag.give_to_obj(obj) 47 | self.assertEqual( 48 | obj.id, 12, 49 | 'give data to object', 50 | ) 51 | -------------------------------------------------------------------------------- /thrift/TSCons.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from os import path 21 | from SCons.Builder import Builder 22 | 23 | def scons_env(env, add=''): 24 | opath = path.dirname(path.abspath('$TARGET')) 25 | lstr = 'thrift --gen cpp -o ' + opath + ' ' + add + ' $SOURCE' 26 | cppbuild = Builder(action = lstr) 27 | env.Append(BUILDERS = {'ThriftCpp' : cppbuild}) 28 | 29 | def gen_cpp(env, dir, file): 30 | scons_env(env) 31 | suffixes = ['_types.h', '_types.cpp'] 32 | targets = map(lambda s: 'gen-cpp/' + file + s, suffixes) 33 | return env.ThriftCpp(targets, dir+file+'.thrift') 34 | -------------------------------------------------------------------------------- /thrift/TSerialization.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from protocol import TBinaryProtocol 21 | from transport import TTransport 22 | 23 | def serialize(thrift_object, protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()): 24 | transport = TTransport.TMemoryBuffer() 25 | protocol = protocol_factory.getProtocol(transport) 26 | thrift_object.write(protocol) 27 | return transport.getvalue() 28 | 29 | def deserialize(base, buf, protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()): 30 | transport = TTransport.TMemoryBuffer(buf) 31 | protocol = protocol_factory.getProtocol(transport) 32 | base.read(protocol) 33 | return base 34 | 35 | -------------------------------------------------------------------------------- /thrift/Thrift.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | import sys 21 | 22 | class TType: 23 | STOP = 0 24 | VOID = 1 25 | BOOL = 2 26 | BYTE = 3 27 | I08 = 3 28 | DOUBLE = 4 29 | I16 = 6 30 | I32 = 8 31 | I64 = 10 32 | STRING = 11 33 | UTF7 = 11 34 | STRUCT = 12 35 | MAP = 13 36 | SET = 14 37 | LIST = 15 38 | UTF8 = 16 39 | UTF16 = 17 40 | 41 | _VALUES_TO_NAMES = ( 'STOP', 42 | 'VOID', 43 | 'BOOL', 44 | 'BYTE', 45 | 'DOUBLE', 46 | None, 47 | 'I16', 48 | None, 49 | 'I32', 50 | None, 51 | 'I64', 52 | 'STRING', 53 | 'STRUCT', 54 | 'MAP', 55 | 'SET', 56 | 'LIST', 57 | 'UTF8', 58 | 'UTF16' ) 59 | 60 | class TMessageType: 61 | CALL = 1 62 | REPLY = 2 63 | EXCEPTION = 3 64 | ONEWAY = 4 65 | 66 | class TProcessor: 67 | 68 | """Base class for procsessor, which works on two streams.""" 69 | 70 | def process(iprot, oprot): 71 | pass 72 | 73 | class TException(Exception): 74 | 75 | """Base class for all thrift exceptions.""" 76 | 77 | # BaseException.message is deprecated in Python v[2.6,3.0) 78 | if (2,6,0) <= sys.version_info < (3,0): 79 | def _get_message(self): 80 | return self._message 81 | def _set_message(self, message): 82 | self._message = message 83 | message = property(_get_message, _set_message) 84 | 85 | def __init__(self, message=None): 86 | Exception.__init__(self, message) 87 | self.message = message 88 | 89 | class TApplicationException(TException): 90 | 91 | """Application level thrift exceptions.""" 92 | 93 | UNKNOWN = 0 94 | UNKNOWN_METHOD = 1 95 | INVALID_MESSAGE_TYPE = 2 96 | WRONG_METHOD_NAME = 3 97 | BAD_SEQUENCE_ID = 4 98 | MISSING_RESULT = 5 99 | INTERNAL_ERROR = 6 100 | PROTOCOL_ERROR = 7 101 | 102 | def __init__(self, type=UNKNOWN, message=None): 103 | TException.__init__(self, message) 104 | self.type = type 105 | 106 | def __str__(self): 107 | if self.message: 108 | return self.message 109 | elif self.type == self.UNKNOWN_METHOD: 110 | return 'Unknown method' 111 | elif self.type == self.INVALID_MESSAGE_TYPE: 112 | return 'Invalid message type' 113 | elif self.type == self.WRONG_METHOD_NAME: 114 | return 'Wrong method name' 115 | elif self.type == self.BAD_SEQUENCE_ID: 116 | return 'Bad sequence ID' 117 | elif self.type == self.MISSING_RESULT: 118 | return 'Missing result' 119 | else: 120 | return 'Default (unknown) TApplicationException' 121 | 122 | def read(self, iprot): 123 | iprot.readStructBegin() 124 | while True: 125 | (fname, ftype, fid) = iprot.readFieldBegin() 126 | if ftype == TType.STOP: 127 | break 128 | if fid == 1: 129 | if ftype == TType.STRING: 130 | self.message = iprot.readString(); 131 | else: 132 | iprot.skip(ftype) 133 | elif fid == 2: 134 | if ftype == TType.I32: 135 | self.type = iprot.readI32(); 136 | else: 137 | iprot.skip(ftype) 138 | else: 139 | iprot.skip(ftype) 140 | iprot.readFieldEnd() 141 | iprot.readStructEnd() 142 | 143 | def write(self, oprot): 144 | oprot.writeStructBegin('TApplicationException') 145 | if self.message != None: 146 | oprot.writeFieldBegin('message', TType.STRING, 1) 147 | oprot.writeString(self.message) 148 | oprot.writeFieldEnd() 149 | if self.type != None: 150 | oprot.writeFieldBegin('type', TType.I32, 2) 151 | oprot.writeI32(self.type) 152 | oprot.writeFieldEnd() 153 | oprot.writeFieldStop() 154 | oprot.writeStructEnd() 155 | -------------------------------------------------------------------------------- /thrift/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | __all__ = ['Thrift', 'TSCons'] 21 | -------------------------------------------------------------------------------- /thrift/protocol/TBase.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from thrift.Thrift import * 21 | from thrift.protocol import TBinaryProtocol 22 | from thrift.transport import TTransport 23 | 24 | try: 25 | from thrift.protocol import fastbinary 26 | except: 27 | fastbinary = None 28 | 29 | class TBase(object): 30 | __slots__ = [] 31 | 32 | def __repr__(self): 33 | L = ['%s=%r' % (key, getattr(self, key)) 34 | for key in self.__slots__ ] 35 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 36 | 37 | def __eq__(self, other): 38 | if not isinstance(other, self.__class__): 39 | return False 40 | for attr in self.__slots__: 41 | my_val = getattr(self, attr) 42 | other_val = getattr(other, attr) 43 | if my_val != other_val: 44 | return False 45 | return True 46 | 47 | def __ne__(self, other): 48 | return not (self == other) 49 | 50 | def read(self, iprot): 51 | if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None: 52 | fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec)) 53 | return 54 | iprot.readStruct(self, self.thrift_spec) 55 | 56 | def write(self, oprot): 57 | if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None: 58 | oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec))) 59 | return 60 | oprot.writeStruct(self, self.thrift_spec) 61 | 62 | class TExceptionBase(Exception): 63 | # old style class so python2.4 can raise exceptions derived from this 64 | # This can't inherit from TBase because of that limitation. 65 | __slots__ = [] 66 | 67 | __repr__ = TBase.__repr__.im_func 68 | __eq__ = TBase.__eq__.im_func 69 | __ne__ = TBase.__ne__.im_func 70 | read = TBase.read.im_func 71 | write = TBase.write.im_func 72 | 73 | -------------------------------------------------------------------------------- /thrift/protocol/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | __all__ = ['TProtocol', 'TBinaryProtocol', 'fastbinary', 'TBase'] 21 | -------------------------------------------------------------------------------- /thrift/server/THttpServer.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | import BaseHTTPServer 21 | 22 | from thrift.server import TServer 23 | from thrift.transport import TTransport 24 | 25 | class ResponseException(Exception): 26 | """Allows handlers to override the HTTP response 27 | 28 | Normally, THttpServer always sends a 200 response. If a handler wants 29 | to override this behavior (e.g., to simulate a misconfigured or 30 | overloaded web server during testing), it can raise a ResponseException. 31 | The function passed to the constructor will be called with the 32 | RequestHandler as its only argument. 33 | """ 34 | def __init__(self, handler): 35 | self.handler = handler 36 | 37 | 38 | class THttpServer(TServer.TServer): 39 | """A simple HTTP-based Thrift server 40 | 41 | This class is not very performant, but it is useful (for example) for 42 | acting as a mock version of an Apache-based PHP Thrift endpoint.""" 43 | 44 | def __init__(self, processor, server_address, 45 | inputProtocolFactory, outputProtocolFactory = None, 46 | server_class = BaseHTTPServer.HTTPServer): 47 | """Set up protocol factories and HTTP server. 48 | 49 | See BaseHTTPServer for server_address. 50 | See TServer for protocol factories.""" 51 | 52 | if outputProtocolFactory is None: 53 | outputProtocolFactory = inputProtocolFactory 54 | 55 | TServer.TServer.__init__(self, processor, None, None, None, 56 | inputProtocolFactory, outputProtocolFactory) 57 | 58 | thttpserver = self 59 | 60 | class RequestHander(BaseHTTPServer.BaseHTTPRequestHandler): 61 | def do_POST(self): 62 | # Don't care about the request path. 63 | itrans = TTransport.TFileObjectTransport(self.rfile) 64 | otrans = TTransport.TFileObjectTransport(self.wfile) 65 | itrans = TTransport.TBufferedTransport(itrans, int(self.headers['Content-Length'])) 66 | otrans = TTransport.TMemoryBuffer() 67 | iprot = thttpserver.inputProtocolFactory.getProtocol(itrans) 68 | oprot = thttpserver.outputProtocolFactory.getProtocol(otrans) 69 | try: 70 | thttpserver.processor.process(iprot, oprot) 71 | except ResponseException, exn: 72 | exn.handler(self) 73 | else: 74 | self.send_response(200) 75 | self.send_header("content-type", "application/x-thrift") 76 | self.end_headers() 77 | self.wfile.write(otrans.getvalue()) 78 | 79 | self.httpd = server_class(server_address, RequestHander) 80 | 81 | def serve(self): 82 | self.httpd.serve_forever() 83 | -------------------------------------------------------------------------------- /thrift/server/TProcessPoolServer.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | 21 | import logging 22 | from multiprocessing import Process, Value, Condition, reduction 23 | 24 | from TServer import TServer 25 | from thrift.transport.TTransport import TTransportException 26 | 27 | class TProcessPoolServer(TServer): 28 | 29 | """ 30 | Server with a fixed size pool of worker subprocesses which service requests. 31 | Note that if you need shared state between the handlers - it's up to you! 32 | Written by Dvir Volk, doat.com 33 | """ 34 | 35 | def __init__(self, * args): 36 | TServer.__init__(self, *args) 37 | self.numWorkers = 10 38 | self.workers = [] 39 | self.isRunning = Value('b', False) 40 | self.stopCondition = Condition() 41 | self.postForkCallback = None 42 | 43 | def setPostForkCallback(self, callback): 44 | if not callable(callback): 45 | raise TypeError("This is not a callback!") 46 | self.postForkCallback = callback 47 | 48 | def setNumWorkers(self, num): 49 | """Set the number of worker threads that should be created""" 50 | self.numWorkers = num 51 | 52 | def workerProcess(self): 53 | """Loop around getting clients from the shared queue and process them.""" 54 | 55 | if self.postForkCallback: 56 | self.postForkCallback() 57 | 58 | while self.isRunning.value == True: 59 | try: 60 | client = self.serverTransport.accept() 61 | self.serveClient(client) 62 | except (KeyboardInterrupt, SystemExit): 63 | return 0 64 | except Exception, x: 65 | logging.exception(x) 66 | 67 | def serveClient(self, client): 68 | """Process input/output from a client for as long as possible""" 69 | itrans = self.inputTransportFactory.getTransport(client) 70 | otrans = self.outputTransportFactory.getTransport(client) 71 | iprot = self.inputProtocolFactory.getProtocol(itrans) 72 | oprot = self.outputProtocolFactory.getProtocol(otrans) 73 | 74 | try: 75 | while True: 76 | self.processor.process(iprot, oprot) 77 | except TTransportException, tx: 78 | pass 79 | except Exception, x: 80 | logging.exception(x) 81 | 82 | itrans.close() 83 | otrans.close() 84 | 85 | 86 | def serve(self): 87 | """Start a fixed number of worker threads and put client into a queue""" 88 | 89 | #this is a shared state that can tell the workers to exit when set as false 90 | self.isRunning.value = True 91 | 92 | #first bind and listen to the port 93 | self.serverTransport.listen() 94 | 95 | #fork the children 96 | for i in range(self.numWorkers): 97 | try: 98 | w = Process(target=self.workerProcess) 99 | w.daemon = True 100 | w.start() 101 | self.workers.append(w) 102 | except Exception, x: 103 | logging.exception(x) 104 | 105 | #wait until the condition is set by stop() 106 | 107 | while True: 108 | 109 | self.stopCondition.acquire() 110 | try: 111 | self.stopCondition.wait() 112 | break 113 | except (SystemExit, KeyboardInterrupt): 114 | break 115 | except Exception, x: 116 | logging.exception(x) 117 | 118 | self.isRunning.value = False 119 | 120 | def stop(self): 121 | self.isRunning.value = False 122 | self.stopCondition.acquire() 123 | self.stopCondition.notify() 124 | self.stopCondition.release() 125 | 126 | -------------------------------------------------------------------------------- /thrift/server/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | __all__ = ['TServer', 'TNonblockingServer'] 21 | -------------------------------------------------------------------------------- /thrift/transport/THttpClient.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | import httplib 21 | import os 22 | import socket 23 | import sys 24 | import urllib 25 | import urlparse 26 | import warnings 27 | from thrift.transport import httpslib # fix ssl issue 28 | from cStringIO import StringIO 29 | 30 | from TTransport import * 31 | 32 | 33 | class THttpClient(TTransportBase): 34 | """Http implementation of TTransport base.""" 35 | 36 | def __init__(self, uri_or_host, port=None, path=None, http_proxy=None): 37 | """THttpClient supports two different types constructor parameters. 38 | 39 | THttpClient(host, port, path) - deprecated 40 | THttpClient(uri) 41 | 42 | Only the second supports https. 43 | """ 44 | if port is not None: 45 | warnings.warn( 46 | "Please use the THttpClient('http://host:port/path') syntax", 47 | DeprecationWarning, 48 | stacklevel=2) 49 | self.host = uri_or_host 50 | self.port = port 51 | assert path 52 | self.path = path 53 | self.scheme = 'http' 54 | else: 55 | parsed = urlparse.urlparse(uri_or_host) 56 | self.scheme = parsed.scheme 57 | assert self.scheme in ('http', 'https') 58 | if self.scheme == 'http': 59 | self.port = parsed.port or httplib.HTTP_PORT 60 | elif self.scheme == 'https': 61 | self.port = parsed.port or httplib.HTTPS_PORT 62 | self.host = parsed.hostname 63 | self.path = parsed.path 64 | if parsed.query: 65 | self.path += '?%s' % parsed.query 66 | if http_proxy is not None: 67 | http_proxy = urlparse.urlparse(http_proxy) 68 | if http_proxy.scheme == 'http': 69 | if http_proxy.port is None: 70 | http_proxy.port = 8080 71 | else: 72 | raise ValueError("Unsupported Proxy Scheme, %s" % http_proxy.scheme) 73 | self.http_proxy = http_proxy 74 | self.__wbuf = StringIO() 75 | self.__http = None 76 | self.__timeout = None 77 | self.__custom_headers = None 78 | 79 | def open(self): 80 | http_proxy = getattr(self, 'http_proxy', None) 81 | if self.scheme == 'http': 82 | if http_proxy is not None: 83 | self.__http = httplib.HTTP(self.http_proxy.hostname, 84 | self.http_proxy.port) 85 | else: 86 | self.__http = httplib.HTTP(self.host, self.port) 87 | else: 88 | if http_proxy is not None: 89 | self.__http = httpslib.HTTPS(self.http_proxy.hostname, 90 | self.http_proxy.port) 91 | self.__http._conn.set_tunnel(self.host, self.port) 92 | else: 93 | self.__http = httpslib.HTTPS(self.host, self.port) 94 | 95 | def close(self): 96 | self.__http.close() 97 | self.__http = None 98 | 99 | def isOpen(self): 100 | return self.__http is not None 101 | 102 | def setTimeout(self, ms): 103 | if not hasattr(socket, 'getdefaulttimeout'): 104 | raise NotImplementedError 105 | 106 | if ms is None: 107 | self.__timeout = None 108 | else: 109 | self.__timeout = ms / 1000.0 110 | 111 | def setCustomHeaders(self, headers): 112 | self.__custom_headers = headers 113 | 114 | def read(self, sz): 115 | return self.__http.file.read(sz) 116 | 117 | def write(self, buf): 118 | self.__wbuf.write(buf) 119 | 120 | def __withTimeout(f): 121 | def _f(*args, **kwargs): 122 | orig_timeout = socket.getdefaulttimeout() 123 | socket.setdefaulttimeout(args[0].__timeout) 124 | result = f(*args, **kwargs) 125 | socket.setdefaulttimeout(orig_timeout) 126 | return result 127 | return _f 128 | 129 | def flush(self): 130 | if self.isOpen(): 131 | self.close() 132 | self.open() 133 | 134 | # Pull data out of buffer 135 | data = self.__wbuf.getvalue() 136 | self.__wbuf = StringIO() 137 | 138 | # HTTP request 139 | if self.scheme == 'http' and self.http_proxy is not None: 140 | # Instead of using CONNECT semantics for HTTP requests, use standard 141 | # http proxy full url semantics. 142 | self.__http.putrequest('POST', 'http://%s:%d%s' % 143 | (self.host, self.port, self.path)) 144 | else: 145 | self.__http.putrequest('POST', self.path) 146 | 147 | # Write headers 148 | self.__http.putheader('Host', self.host) 149 | self.__http.putheader('Content-Type', 'application/x-thrift') 150 | self.__http.putheader('Content-Length', str(len(data))) 151 | 152 | if not self.__custom_headers or 'User-Agent' not in self.__custom_headers: 153 | user_agent = 'Python/THttpClient' 154 | script = os.path.basename(sys.argv[0]) 155 | if script: 156 | user_agent = '%s (%s)' % (user_agent, urllib.quote(script)) 157 | self.__http.putheader('User-Agent', user_agent) 158 | 159 | if self.__custom_headers: 160 | for key, val in self.__custom_headers.iteritems(): 161 | self.__http.putheader(key, val) 162 | 163 | self.__http.endheaders() 164 | 165 | # Write payload 166 | self.__http.send(data) 167 | 168 | # Get reply to flush the request 169 | self.code, self.message, self.headers = self.__http.getreply() 170 | 171 | # Decorate if we know how to timeout 172 | if hasattr(socket, 'getdefaulttimeout'): 173 | flush = __withTimeout(flush) 174 | -------------------------------------------------------------------------------- /thrift/transport/TSocket.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | from TTransport import * 21 | import os 22 | import errno 23 | import socket 24 | import sys 25 | 26 | class TSocketBase(TTransportBase): 27 | def _resolveAddr(self): 28 | if self._unix_socket is not None: 29 | return [(socket.AF_UNIX, socket.SOCK_STREAM, None, None, self._unix_socket)] 30 | else: 31 | return socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE | socket.AI_ADDRCONFIG) 32 | 33 | def close(self): 34 | if self.handle: 35 | self.handle.close() 36 | self.handle = None 37 | 38 | class TSocket(TSocketBase): 39 | """Socket implementation of TTransport base.""" 40 | 41 | def __init__(self, host='localhost', port=9090, unix_socket=None): 42 | """Initialize a TSocket 43 | 44 | @param host(str) The host to connect to. 45 | @param port(int) The (TCP) port to connect to. 46 | @param unix_socket(str) The filename of a unix socket to connect to. 47 | (host and port will be ignored.) 48 | """ 49 | 50 | self.host = host 51 | self.port = port 52 | self.handle = None 53 | self._unix_socket = unix_socket 54 | self._timeout = None 55 | 56 | def setHandle(self, h): 57 | self.handle = h 58 | 59 | def isOpen(self): 60 | return self.handle is not None 61 | 62 | def setTimeout(self, ms): 63 | if ms is None: 64 | self._timeout = None 65 | else: 66 | self._timeout = ms/1000.0 67 | 68 | if self.handle is not None: 69 | self.handle.settimeout(self._timeout) 70 | 71 | def open(self): 72 | try: 73 | res0 = self._resolveAddr() 74 | for res in res0: 75 | self.handle = socket.socket(res[0], res[1]) 76 | self.handle.settimeout(self._timeout) 77 | try: 78 | self.handle.connect(res[4]) 79 | except socket.error, e: 80 | if res is not res0[-1]: 81 | continue 82 | else: 83 | raise e 84 | break 85 | except socket.error, e: 86 | if self._unix_socket: 87 | message = 'Could not connect to socket %s' % self._unix_socket 88 | else: 89 | message = 'Could not connect to %s:%d' % (self.host, self.port) 90 | raise TTransportException(type=TTransportException.NOT_OPEN, message=message) 91 | 92 | def read(self, sz): 93 | try: 94 | buff = self.handle.recv(sz) 95 | except socket.error, e: 96 | if (e.args[0] == errno.ECONNRESET and 97 | (sys.platform == 'darwin' or sys.platform.startswith('freebsd'))): 98 | # freebsd and Mach don't follow POSIX semantic of recv 99 | # and fail with ECONNRESET if peer performed shutdown. 100 | # See corresponding comment and code in TSocket::read() 101 | # in lib/cpp/src/transport/TSocket.cpp. 102 | self.close() 103 | # Trigger the check to raise the END_OF_FILE exception below. 104 | buff = '' 105 | else: 106 | raise 107 | if len(buff) == 0: 108 | raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket read 0 bytes') 109 | return buff 110 | 111 | def write(self, buff): 112 | if not self.handle: 113 | raise TTransportException(type=TTransportException.NOT_OPEN, message='Transport not open') 114 | sent = 0 115 | have = len(buff) 116 | while sent < have: 117 | plus = self.handle.send(buff) 118 | if plus == 0: 119 | raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket sent 0 bytes') 120 | sent += plus 121 | buff = buff[plus:] 122 | 123 | def flush(self): 124 | pass 125 | 126 | class TServerSocket(TSocketBase, TServerTransportBase): 127 | """Socket implementation of TServerTransport base.""" 128 | 129 | def __init__(self, host=None, port=9090, unix_socket=None): 130 | self.host = host 131 | self.port = port 132 | self._unix_socket = unix_socket 133 | self.handle = None 134 | 135 | def listen(self): 136 | res0 = self._resolveAddr() 137 | for res in res0: 138 | if res[0] is socket.AF_INET6 or res is res0[-1]: 139 | break 140 | 141 | # We need remove the old unix socket if the file exists and 142 | # nobody is listening on it. 143 | if self._unix_socket: 144 | tmp = socket.socket(res[0], res[1]) 145 | try: 146 | tmp.connect(res[4]) 147 | except socket.error, err: 148 | eno, message = err.args 149 | if eno == errno.ECONNREFUSED: 150 | os.unlink(res[4]) 151 | 152 | self.handle = socket.socket(res[0], res[1]) 153 | self.handle.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 154 | if hasattr(self.handle, 'settimeout'): 155 | self.handle.settimeout(None) 156 | self.handle.bind(res[4]) 157 | self.handle.listen(128) 158 | 159 | def accept(self): 160 | client, addr = self.handle.accept() 161 | result = TSocket() 162 | result.setHandle(client) 163 | return result 164 | -------------------------------------------------------------------------------- /thrift/transport/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | 20 | __all__ = ['TTransport', 'TSocket', 'THttpClient','TZlibTransport', 'httpslib'] 21 | -------------------------------------------------------------------------------- /thrift/transport/httpslib.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Replacement of HTTPS client from standart httplib module 3 | 4 | workaround of ssl bug: https://bugs.launchpad.net/ubuntu/source/openssl/bug/965371 5 | Copyright (C) http://docs.python.org/license.html 6 | 7 | Marat Khayrullin 8 | ''' 9 | 10 | import httplib 11 | import socket 12 | try: 13 | import ssl 14 | except ImportError: 15 | pass 16 | else: 17 | 18 | class HTTPSConnection(httplib.HTTPConnection): 19 | "This class allows communication via SSL." 20 | 21 | default_port = httplib.HTTPS_PORT 22 | 23 | def __init__(self, host, port=None, key_file=None, cert_file=None, 24 | strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, 25 | source_address=None): 26 | httplib.HTTPConnection.__init__(self, host, port, strict, timeout, 27 | source_address) 28 | self.key_file = key_file 29 | self.cert_file = cert_file 30 | 31 | def connect(self): 32 | "Connect to a host on a given (SSL) port." 33 | 34 | sock = socket.create_connection((self.host, self.port), 35 | self.timeout, self.source_address) 36 | if self._tunnel_host: 37 | self.sock = sock 38 | self._tunnel() 39 | self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_TLSv1) 40 | 41 | #__all__.append("HTTPSConnection") 42 | 43 | class HTTPS(httplib.HTTP): 44 | """Compatibility with 1.5 httplib interface 45 | 46 | Python 1.5.2 did not have an HTTPS class, but it defined an 47 | interface for sending http requests that is also useful for 48 | https. 49 | """ 50 | 51 | _connection_class = HTTPSConnection 52 | 53 | def __init__(self, host='', port=None, key_file=None, cert_file=None, 54 | strict=None): 55 | # provide a default host, pass the X509 cert info 56 | 57 | # urf. compensate for bad input. 58 | if port == 0: 59 | port = None 60 | self._setup(self._connection_class(host, port, key_file, 61 | cert_file, strict)) 62 | 63 | # we never actually use these for anything, but we keep them 64 | # here for compatibility with post-1.5.2 CVS. 65 | self.key_file = key_file 66 | self.cert_file = cert_file 67 | --------------------------------------------------------------------------------