├── .dockerignore ├── .github └── workflows │ └── maven.yml ├── .gitignore ├── .travis.yml ├── Dockerfile ├── Dockerfile.mvn ├── LICENSE ├── README.md ├── check_style.xml ├── oposter-war ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── orienteer │ │ │ └── oposter │ │ │ └── web │ │ │ ├── OPosterAppModule.java │ │ │ ├── OPosterWebApplication.java │ │ │ └── package-info.java │ ├── resources │ │ ├── log4j2.xml │ │ └── org │ │ │ └── orienteer │ │ │ └── oposter │ │ │ └── web │ │ │ └── OPosterWebApplication.properties │ └── webapp │ │ └── WEB-INF │ │ └── web.xml │ └── test │ ├── java │ └── org │ │ └── orienteer │ │ └── oposter │ │ └── web │ │ └── TestMyWebApplication.java │ └── resources │ └── keystore ├── oposter ├── README.md ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── orienteer │ │ │ └── oposter │ │ │ ├── Initializer.java │ │ │ ├── OPScheduler.java │ │ │ ├── OPUtils.java │ │ │ ├── OPosterModule.java │ │ │ ├── component │ │ │ ├── AbstractEventPayload.java │ │ │ ├── FullCalendar.java │ │ │ ├── attachment │ │ │ │ ├── AttachmentsEditPanel.html │ │ │ │ ├── AttachmentsEditPanel.java │ │ │ │ ├── AttachmentsViewPanel.html │ │ │ │ ├── AttachmentsViewPanel.java │ │ │ │ ├── AttachmentsVisualizer.java │ │ │ │ ├── DeleteAttachmentEventPayload.java │ │ │ │ ├── OImageValidator.java │ │ │ │ ├── SingleAttachmentEditPanel.html │ │ │ │ ├── SingleAttachmentEditPanel.java │ │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ └── widget │ │ │ │ ├── AbstractCalendarContentWidget.html │ │ │ │ ├── AbstractCalendarContentWidget.java │ │ │ │ ├── CalendarAllContentWidget.java │ │ │ │ ├── CalendarChannelContentWidget.java │ │ │ │ ├── CalendarContentPlanWidget.java │ │ │ │ ├── CalendarPlatformAppWidget.java │ │ │ │ └── package-info.java │ │ │ ├── facebook │ │ │ ├── IFacebookApp.java │ │ │ ├── IFacebookConnection.java │ │ │ └── package-info.java │ │ │ ├── instagram │ │ │ ├── IIGAccount.java │ │ │ ├── IIGApp.java │ │ │ ├── SerializableCookieJar.java │ │ │ └── package-info.java │ │ │ ├── model │ │ │ ├── IChannel.java │ │ │ ├── IContent.java │ │ │ ├── IContentPlan.java │ │ │ ├── IImageAttachment.java │ │ │ ├── IOAuthReciever.java │ │ │ ├── IOPosterDAO.java │ │ │ ├── IPlatformApp.java │ │ │ ├── IPosting.java │ │ │ └── package-info.java │ │ │ ├── ok │ │ │ ├── FixedOdnoklassnikiApi.java │ │ │ ├── FixedOdnoklassnikiOAuthService.java │ │ │ ├── IOkApp.java │ │ │ ├── IOkChannel.java │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ ├── telegram │ │ │ ├── ITelegramBot.java │ │ │ ├── ITelegramChannel.java │ │ │ └── package-info.java │ │ │ ├── twitter │ │ │ ├── ITwitterAccount.java │ │ │ ├── ITwitterApp.java │ │ │ └── package-info.java │ │ │ ├── vk │ │ │ ├── IVkApp.java │ │ │ ├── IVkWall.java │ │ │ └── package-info.java │ │ │ └── web │ │ │ ├── OAuthCallbackResource.java │ │ │ └── package-info.java │ └── resources │ │ ├── META-INF │ │ └── services │ │ │ └── org.apache.wicket.IInitializer │ │ ├── log4j2.xml │ │ └── org │ │ └── orienteer │ │ └── oposter │ │ ├── Initializer.properties │ │ ├── META-INF │ │ └── services │ │ │ └── org.apache.wicket.IInitializer │ │ └── component │ │ └── attachment │ │ ├── carousel-load.js │ │ ├── carousel.css │ │ ├── edit-attachment.css │ │ └── edit-attachment.js │ └── test │ └── java │ └── org │ └── orienteer │ └── oposter │ └── TestModule.java ├── orienteer-test.properties ├── orienteer.properties └── pom.xml /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | **/target/ 4 | **/pom.xml.tag 5 | **/pom.xml.releaseBackup 6 | **/pom.xml.next 7 | **/release.properties 8 | **/*~ 9 | **/*.swp 10 | **/*.swo 11 | **/.classpath 12 | **/.project 13 | **/.settings/ 14 | build.cmd 15 | **/databases/ 16 | /Orienteer/ 17 | **/.idea/ 18 | **/*.iml 19 | **/*.log 20 | !oposter-war/target/oposter-war.war 21 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Set up JDK 8 16 | uses: actions/setup-java@v2 17 | with: 18 | java-version: 8 19 | distribution: 'adopt' 20 | server-id: ossrh 21 | server-username: MAVEN_USERNAME 22 | server-password: MAVEN_PASSWORD 23 | #- name: Cache Maven packages 24 | # uses: actions/cache@v2 25 | # with: 26 | # path: ~/.m2 27 | # key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 28 | # restore-keys: ${{ runner.os }}-m2 29 | - name: Build by Maven 30 | run: mvn -B -Ddockerfile.skip deploy 31 | env: 32 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 33 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} 34 | - name: Set up QEMU 35 | uses: docker/setup-qemu-action@v1 36 | - name: Set up Docker Buildx 37 | uses: docker/setup-buildx-action@v1 38 | - name: Login to DockerHub 39 | uses: docker/login-action@v1 40 | with: 41 | username: ${{ secrets.DOCKERHUB_USERNAME }} 42 | password: ${{ secrets.DOCKERHUB_TOKEN }} 43 | - name: Build Docker Images and Push 44 | uses: docker/build-push-action@v2 45 | with: 46 | context: . 47 | platforms: linux/amd64,linux/arm64 48 | file: Dockerfile.mvn 49 | push: true 50 | tags: orienteer/oposter:latest 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.next 5 | release.properties 6 | *~ 7 | *.swp 8 | *.swo 9 | .classpath 10 | .project 11 | .settings/ 12 | build.cmd 13 | databases/ 14 | */runtime/ 15 | /Orienteer/ 16 | /Orienteer.*/ 17 | /*/Orienteer/ 18 | /*/Orienteer.*/ 19 | /*/libs/ 20 | */app/ 21 | .idea/ 22 | *.iml 23 | *.log 24 | *.tar.gz 25 | -orienteer.properties 26 | -orienteer-test.properties 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk8 5 | 6 | #after_success: 7 | # - mvn clean cobertura:cobertura coveralls:report 8 | 9 | 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.6-jdk-8-alpine AS builder 2 | WORKDIR /tmp/src/ 3 | ADD . /tmp/src/ 4 | RUN mvn -Ddocker-build clean package 5 | 6 | 7 | FROM orienteer/orienteer:latest 8 | COPY --from=builder /tmp/src/oposter-war/target/oposter-war.war ${JETTY_BASE}/webapps/ROOT.war 9 | COPY --from=builder /tmp/src/orienteer.properties ${ORIENTEER_HOME} 10 | -------------------------------------------------------------------------------- /Dockerfile.mvn: -------------------------------------------------------------------------------- 1 | FROM orienteer/orienteer:latest 2 | COPY oposter-war/target/oposter-war.war ${JETTY_BASE}/webapps/ROOT.war 3 | COPY orienteer.properties ${ORIENTEER_HOME} 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/OrienteerBAP/OPoster.svg?branch=master)](https://travis-ci.org/OrienteerBAP/OPoster) [![Docker Pulls](https://img.shields.io/docker/pulls/orienteer/oposter.svg)](https://hub.docker.com/r/orienteer/oposter/) 2 | 3 | # OPoster 4 | OPoster is a planning, scheduling and posting to social media networks. 5 | Publishing with OPoster is easy: 6 | 7 | 1. Create **what** to post (aka content: text with some attached photos) 8 | 2. Select **where** to publish the post: one or more channels (for example: some telegram channel, facebook page, etc.) 9 | 3. Specify **when** to post 10 | 4. Relax and wait:) 11 | 12 | Inspired by feedbacks after the following articles about Orienteer: 13 | 14 | * ENG: [How to Build Posts Scheduler with Almost No-Coding](https://medium.com/orienteer/how-to-build-posts-scheduler-with-almost-no-coding-b52068f8c23b) 15 | * RUS: [Свой сервис отложенного постинга и почти без кода](https://habr.com/ru/company/orienteer/blog/530388/) 16 | 17 | ### OPoster supports: 18 | 19 | - [X] [Telegram](#telegram) 20 | - [X] [Facebook](#facebook) 21 | - [X] [Vkontakte](#vkontakte) 22 | - [X] [Instagram](#instagram) 23 | - [X] [Twitter](#twitter) 24 | - [X] [Odnoklassniki](#odnoklassniki) 25 | 26 | ### Planned to support 27 | 28 | - [ ] Pikabu 29 | - [ ] Yandex Dzen 30 | - [ ] TikTok 31 | - [ ] Tumblr 32 | - [ ] Pinterest 33 | 34 | If you need some network which is currently not in the list: please [create an issue](https://github.com/OrienteerBAP/OPoster/issues). 35 | 36 | ## Installation Guide 37 | 38 | There are 2 ways how you can install OPoster and start using it: 39 | 40 | 1. Standalone installation via [docker image](https://hub.docker.com/repository/docker/orienteer/oposter) 41 | 2. As add-on module on pre-installed [Orienteer](https://github.com/OrienteerBAP/Orienteer) instance 42 | 43 | ### OPoster as standalone docker application 44 | 45 | You can start OPoster by the following command: 46 | 47 | ``` 48 | docker run -it -p8080:8080 orienteer/oposter 49 | ``` 50 | 51 | For more advanced usage, it's recommended to used `docker-compose`. Here is template of `docker-compose.yml` file: 52 | 53 | ```yml 54 | version: '2.1' 55 | services: 56 | orienteer: 57 | image: orienteer/oposter:latest 58 | container_name: 59 | network_mode: 'bridge' 60 | healthcheck: 61 | test: ["CMD-SHELL", "curl --fail -s -I http://localhost:8080 | grep 'HTTP/1.1' || exit 1"] 62 | interval: 5m 63 | timeout: 5s 64 | ports: 65 | - "8080:8080" 66 | volumes: 67 | - ./runtime:/app/runtime 68 | - ./maven-repository:/app/repository 69 | environment: 70 | - ORIENTDB_ADMIN_PASSWORD= 71 | - ORIENTDB_GUEST_PASSWORD=&display=page&redirect_uri=https://oauth.vk.com/blank.html&scope=offline,wall,groups,video,photos&response_type=token&v=5.52`, click accept and extract token from final page URL | 153 | | Channel | OPVkWall | | OClass for defining details about wall in vkontakte you are going to post on | 154 | | Channel | OPVkWall | ownerId | Id of user or community to post to. Should be positive or null. Null means to post on wall of a specified user | 155 | | Channel | OPVkWall | community | Boolean flag to show that `ownerId` is actually id of a group or community | 156 | | Channel | OPVkWall | userId | Identification (integer) of a user to be used for posting on vkontakte. Overrides `defaultUserId` | 157 | | Channel | OPVkWall | userAcessToken | Access Token for user specified by id previously. Overrides `defaultUserAccessTonen` | 158 | 159 | #### Read More 160 | 161 | * [VKontakte Dev Home](https://vk.com/apps?act=manage) 162 | * [Why wall authorization is too complex?](https://habr.com/ru/post/179953/) 163 | 164 | ### Instagram 165 | 166 | | Nature | OClass | Property | Description | 167 | |--------|--------|----------|-------------| 168 | | Platform App | OPIGApp | | OClass for association with Instagram. Last one doesn't allow to create your own application. So instance of this class should be created just nominally if you want to post to Instagram | 169 | | Channel | OPIGAccount | | OClass for defining details about Instagram account to post to | 170 | | Channel | OPIGAccount | username | Instagram handle of a user to post on behalf of | 171 | | Channel | OPIGAccount | password | Password of a user to post on behalf of | 172 | 173 | ### Twitter 174 | 175 | | Nature | OClass | Property | Description | 176 | |--------|--------|----------|-------------| 177 | | Platform App | OPTwitterApp | | OClass for specifying details about Twitter Application which needs to be created to post with OPoster | 178 | | Platform App | OPTwitterApp | apiKey | Application Key which corresponds to application created in Twitter. Please check settings page of your app | 179 | | Platform App | OPTwitterApp | apiSecretKey | Application Secret Key string which was defined/generated by Twitter for your application | 180 | | Channel | OPTwitterAccount | | OClass for defining details about twitter account to post on | 181 | | Channel | OPTwitterAccount | accessToken | Access Token of a Twitter user to post on behalf of. Can be obtained by clicking button **Connect OAuth** | 182 | | Channel | OPTwitterAccount | accessTokenSecret | Access Token Secret of a Twitter user to post on behalf of. Can be obtained by clicking button **Connect OAuth** | 183 | 184 | #### Read More 185 | 186 | * [Twitter Dev Home Page](https://developer.twitter.com/apps) 187 | 188 | ### Odnoklassniki 189 | 190 | | Nature | OClass | Property | Description | 191 | |--------|--------|----------|-------------| 192 | | Platform App | OPOkApp | | OClass for specifying details about Odnoklassniki Application which needs to be created to post with OPoster | 193 | | Platform App | OPOkApp | appId | Application Identification which corresponds to application created in Odnoklassniki | 194 | | Platform App | OPOkApp | publicKey | Public Key for the app in Odnoklassniki: check your email after app creation | 195 | | Platform App | OPOkApp | secretKey | Secret Key for the app in Odnoklassniki: check your email after app creation | 196 | | Channel | OPOkChannel | | OClass for defining details about user, group/page in Odnoklassniki you are going to post on | 197 | | Channel | OPOkChannel | groupId | Id of a group/page to post to. Should be positive or null. Null means to post on wall of a specified user | 198 | | Channel | OPOkChannel | acessToken | Access Token for a user to post on behalf of | 199 | 200 | #### Read More 201 | 202 | * [Page to request dev access](https://ok.ru/devaccess) 203 | * [List of your apps](https://ok.ru/vitrine/myuploaded) 204 | * [How to create app in Odnoklassniki](https://apiok.ru/dev/app/create) 205 | * [Odnoklassniki REST methods](https://apiok.ru/dev/methods/rest/) 206 | Remember, that you have to work with support to get required rights for your application: **PUBLISH_TO_STREAM;VALUABLE_ACCESS;LONG_ACCESS_TOKEN;PHOTO_CONTENT;GROUP_CONTENT** 207 | -------------------------------------------------------------------------------- /check_style.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 71 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /oposter-war/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 4.0.0 20 | 21 | 22 | oposter-parent 23 | org.orienteer.oposter 24 | 1.0-SNAPSHOT 25 | 26 | oposter-war 27 | war 28 | 29 | oposter-war 30 | 36 | 37 | 38 | 39 | org.orienteer.oposter 40 | oposter 41 | ${project.version} 42 | 43 | 44 | 45 | org.orienteer 46 | orienteer-core 47 | ${orienteer.version} 48 | test-jar 49 | test 50 | 51 | 52 | ru.ydn.wicket.wicket-orientdb 53 | wicket-orientdb 54 | ${wicket.orientdb.version} 55 | test-jar 56 | test 57 | 58 | 59 | 60 | junit 61 | junit 62 | 4.11 63 | test 64 | 65 | 66 | 67 | javax.servlet 68 | javax.servlet-api 69 | 3.0.1 70 | test 71 | 72 | 73 | 74 | oposter-war 75 | 76 | 77 | src/main/resources 78 | 79 | 80 | src/main/java 81 | 82 | ** 83 | 84 | 85 | **/*.java 86 | 87 | 88 | 89 | 90 | 91 | src/test/resources 92 | 93 | 94 | src/test/java 95 | 96 | ** 97 | 98 | 99 | **/*.java 100 | 101 | 102 | 103 | 104 | 105 | org.apache.felix 106 | maven-bundle-plugin 107 | 2.3.6 108 | true 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-war-plugin 113 | 3.1.0 114 | 115 | 116 | 117 | true 118 | true 119 | 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-surefire-plugin 126 | 2.20 127 | 128 | 129 | ${settings.localRepository} 130 | 131 | 132 | 133 | 134 | true 135 | org.apache.maven.plugins 136 | maven-compiler-plugin 137 | 3.7.0 138 | 139 | 1.8 140 | 1.8 141 | UTF-8 142 | true 143 | true 144 | 145 | -parameters 146 | 147 | 148 | 149 | 150 | org.apache.maven.plugins 151 | maven-deploy-plugin 152 | 2.7 153 | 154 | true 155 | 156 | 157 | 158 | com.spotify 159 | dockerfile-maven-plugin 160 | 1.4.10 161 | 162 | 163 | default 164 | deploy 165 | 166 | build 167 | push 168 | 169 | 170 | 171 | 172 | orienteer/oposter 173 | latest 174 | .. 175 | ../Dockerfile.mvn 176 | true 177 | 178 | 179 | 180 | org.eclipse.jetty 181 | jetty-maven-plugin 182 | ${jetty.version} 183 | 184 | 185 | 186 | orienteer.loader.repository.local 187 | ${settings.localRepository} 188 | 189 | 190 | 191 | ^$ 192 | ^$ 193 | 194 | 195 | org.eclipse.jetty.server.nio.SelectChannelConnector 196 | 8080 197 | 3600000 198 | 199 | 200 | 201 | 202 | org.eclipse.jetty.aggregate 203 | jetty-all 204 | uber 205 | ${jetty.version} 206 | 207 | 208 | 209 | 210 | org.apache.maven.plugins 211 | maven-eclipse-plugin 212 | 2.9 213 | 214 | true 215 | ${wtp.version} 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | Apache Nexus 224 | https://repository.apache.org/content/repositories/snapshots/ 225 | 226 | false 227 | 228 | 229 | 230 | 231 | 232 | Sonatype Nexus 233 | https://oss.sonatype.org/content/repositories/snapshots/ 234 | 235 | false 236 | 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /oposter-war/src/main/java/org/orienteer/oposter/web/OPosterAppModule.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.web; 2 | 3 | import org.orienteer.core.OrienteerWebApplication; 4 | import org.orienteer.core.module.AbstractOrienteerModule; 5 | import org.orienteer.core.module.PerspectivesModule; 6 | import org.orienteer.oposter.OPosterModule; 7 | 8 | import com.orientechnologies.orient.core.db.ODatabaseSession; 9 | import com.orientechnologies.orient.core.metadata.security.ORole; 10 | import com.orientechnologies.orient.core.metadata.security.ORule.ResourceGeneric; 11 | import com.orientechnologies.orient.core.metadata.security.OSecurity; 12 | import com.orientechnologies.orient.core.record.impl.ODocument; 13 | 14 | import static ru.ydn.wicket.wicketorientdb.security.OrientPermission.*; 15 | 16 | import java.util.Optional; 17 | 18 | /** 19 | * Module to customize standalone OPoster installation 20 | */ 21 | public class OPosterAppModule extends AbstractOrienteerModule { 22 | public static final String NAME = "oposter-app"; 23 | 24 | 25 | protected OPosterAppModule() { 26 | super(NAME, 1, OPosterModule.NAME); 27 | } 28 | 29 | @Override 30 | public ODocument onInstall(OrienteerWebApplication app, ODatabaseSession db) { 31 | reduceReaderRights(db); 32 | changeDefaultPerspective(db); 33 | return null; 34 | } 35 | 36 | @Override 37 | public void onUpdate(OrienteerWebApplication app, ODatabaseSession db, int oldVersion, int newVersion) { 38 | onInstall(app, db); 39 | } 40 | 41 | protected void reduceReaderRights(ODatabaseSession db) { 42 | OSecurity security = db.getMetadata().getSecurity(); 43 | ORole readerRole = security.getRole("reader"); 44 | int permissionToRevoke = combinedPermission(CREATE, READ, UPDATE, DELETE, EXECUTE); 45 | readerRole.revoke(ResourceGeneric.CLASS, null, permissionToRevoke); 46 | readerRole.revoke(ResourceGeneric.CLUSTER, null, permissionToRevoke); 47 | } 48 | 49 | protected void changeDefaultPerspective(final ODatabaseSession db) { 50 | final PerspectivesModule perspectivesModule = OrienteerWebApplication.get().getServiceInstance(PerspectivesModule.class); 51 | perspectivesModule.getPerspectiveByAliasAsDocument(OPosterModule.PERSPECTIVE_ALIAS).ifPresent((p) -> { 52 | for(ODocument role : db.getMetadata().getSecurity().getAllRoles()) { 53 | perspectivesModule.updateUserPerspective(role, p); 54 | } 55 | }); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /oposter-war/src/main/java/org/orienteer/oposter/web/OPosterWebApplication.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.web; 2 | 3 | import org.orienteer.core.OrienteerWebApplication; 4 | import org.orienteer.core.module.PerspectivesModule; 5 | 6 | /** 7 | * OPoster dummy application to gather everything together. 8 | * It's dummy because everything is on Orienteer modules 9 | */ 10 | public class OPosterWebApplication extends OrienteerWebApplication 11 | { 12 | @Override 13 | public void init() 14 | { 15 | super.init(); 16 | registerModule(OPosterAppModule.class); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /oposter-war/src/main/java/org/orienteer/oposter/web/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Main package for 'oposter' web-app 3 | */ 4 | package org.orienteer.oposter.web; -------------------------------------------------------------------------------- /oposter-war/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /oposter-war/src/main/resources/org/orienteer/oposter/web/OPosterWebApplication.properties: -------------------------------------------------------------------------------- 1 | application.name=OPoster -------------------------------------------------------------------------------- /oposter-war/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | orienteer 7 | 8 | 9 | orienteer 10 | org.orienteer.core.OrienteerFilter 11 | 12 | 13 | orienteer 14 | /* 15 | REQUEST 16 | ERROR 17 | 18 | 19 | 20 | 404 21 | /404 22 | 23 | 24 | 403 25 | /403 26 | 27 | 28 | -------------------------------------------------------------------------------- /oposter-war/src/test/java/org/orienteer/oposter/web/TestMyWebApplication.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.web; 2 | 3 | import org.orienteer.junit.OrienteerTestRunner; 4 | 5 | import org.orienteer.junit.OrienteerTester; 6 | 7 | import static org.junit.Assert.*; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import com.google.inject.Inject; 12 | import com.google.inject.Singleton; 13 | 14 | 15 | @RunWith(OrienteerTestRunner.class) 16 | @Singleton 17 | public class TestMyWebApplication 18 | { 19 | @Inject 20 | private OrienteerTester tester; 21 | 22 | @Test 23 | public void testWebApplicationClass() 24 | { 25 | assertTrue(tester.getApplication() instanceof OPosterWebApplication); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /oposter-war/src/test/resources/keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrienteerBAP/OPoster/cabccb110a8b0a2ef00450a63d81996fabd5f4d5/oposter-war/src/test/resources/keystore -------------------------------------------------------------------------------- /oposter/README.md: -------------------------------------------------------------------------------- 1 | Read Me file -------------------------------------------------------------------------------- /oposter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 4.0.0 20 | 21 | 22 | oposter-parent 23 | org.orienteer.oposter 24 | 1.0-SNAPSHOT 25 | 26 | oposter 27 | 28 | oposter 29 | 35 | 36 | 37 | 38 | org.orienteer 39 | orienteer-core 40 | 41 | 42 | org.orienteer 43 | orienteer-logger-server 44 | 45 | 46 | org.orienteer.vuecket 47 | vuecket 48 | 1.0-SNAPSHOT 49 | 50 | 51 | com.squareup.okhttp3 52 | okhttp 53 | 4.9.0 54 | 55 | 56 | com.squareup.okhttp3 57 | okhttp-urlconnection 58 | 4.9.0 59 | 60 | 61 | com.squareup.okhttp3 62 | logging-interceptor 63 | 4.9.0 64 | 65 | 66 | com.github.pengrad 67 | java-telegram-bot-api 68 | 5.0.0 69 | 70 | 71 | com.restfb 72 | restfb 73 | 3.13.0 74 | 75 | 76 | com.vk.api 77 | sdk 78 | 1.0.8 79 | 80 | 81 | com.github.scribejava 82 | scribejava-apis 83 | 6.5.1 84 | 85 | 86 | com.github.instagram4j 87 | instagram4j 88 | 2.0.3 89 | 90 | 91 | com.github.redouane59.twitter 92 | twittered-jdk8 93 | 1.17 94 | 95 | 96 | com.google.guava 97 | guava-jdk5 98 | 99 | 100 | 101 | 102 | 103 | javax.servlet 104 | javax.servlet-api 105 | provided 106 | 107 | 112 | 117 | 122 | 127 | 132 | 137 | 142 | 147 | 152 | 157 | 162 | 167 | 172 | 177 | 178 | 179 | org.orienteer 180 | orienteer-core 181 | test-jar 182 | test 183 | 184 | 185 | ru.ydn.wicket.wicket-orientdb 186 | wicket-orientdb 187 | test-jar 188 | test 189 | 190 | 191 | 192 | junit 193 | junit 194 | test 195 | 196 | 197 | 198 | 199 | 200 | src/main/resources 201 | 202 | 203 | src/main/java 204 | 205 | ** 206 | 207 | 208 | **/*.java 209 | 210 | 211 | 212 | 213 | 214 | src/test/resources 215 | 216 | 217 | src/test/java 218 | 219 | ** 220 | 221 | 222 | **/*.java 223 | 224 | 225 | 226 | 227 | 228 | true 229 | org.apache.maven.plugins 230 | maven-compiler-plugin 231 | 3.7.0 232 | 233 | 1.8 234 | 1.8 235 | UTF-8 236 | true 237 | true 238 | 239 | -parameters 240 | 241 | 242 | 243 | 244 | org.apache.felix 245 | maven-bundle-plugin 246 | 2.3.6 247 | true 248 | 249 | 250 | org.apache.maven.plugins 251 | maven-eclipse-plugin 252 | 2.9 253 | 254 | true 255 | ${wtp.version} 256 | 257 | 258 | 259 | org.apache.maven.plugins 260 | maven-jar-plugin 261 | 262 | 263 | 264 | true 265 | true 266 | 267 | 268 | 269 | 270 | 271 | org.apache.maven.plugins 272 | maven-surefire-plugin 273 | 274 | 275 | ${settings.localRepository} 276 | 277 | 278 | 279 | 280 | org.eclipse.jetty 281 | jetty-maven-plugin 282 | ${jetty.version} 283 | 284 | 285 | 286 | orienteer.loader.repository.local 287 | ${settings.localRepository} 288 | 289 | 290 | 291 | jar 292 | 293 | 294 | ../oposter-war/src/main/webapp/WEB-INF/web.xml 295 | src/main/resources 296 | / 297 | ^$ 298 | ^$ 299 | 300 | 301 | org.eclipse.jetty.server.nio.SelectChannelConnector 302 | 8080 303 | 3600000 304 | 305 | 306 | 307 | 308 | org.eclipse.jetty.aggregate 309 | jetty-all 310 | uber 311 | ${jetty.version} 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | Apache Nexus 321 | https://repository.apache.org/content/repositories/snapshots/ 322 | 323 | false 324 | 325 | 326 | 327 | 328 | 329 | 330 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/Initializer.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter; 2 | 3 | import org.apache.wicket.Application; 4 | import org.apache.wicket.IInitializer; 5 | import org.orienteer.core.OrienteerWebApplication; 6 | 7 | /** 8 | * {@link IInitializer} for 'oposter' module 9 | */ 10 | public class Initializer implements IInitializer 11 | { 12 | @Override 13 | public void init(Application application) { 14 | OrienteerWebApplication app = (OrienteerWebApplication)application; 15 | app.registerModule(OPosterModule.class); 16 | } 17 | 18 | @Override 19 | public void destroy(Application application) { 20 | OrienteerWebApplication app = (OrienteerWebApplication)application; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/OPScheduler.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter; 2 | 3 | import java.util.List; 4 | 5 | import org.apache.wicket.ThreadContext; 6 | import org.orienteer.core.OrienteerWebApplication; 7 | import org.orienteer.core.dao.DAO; 8 | import org.orienteer.logger.OLogger; 9 | import org.orienteer.oposter.model.IChannel; 10 | import org.orienteer.oposter.model.IContent; 11 | import org.orienteer.oposter.model.IOPosterDAO; 12 | import org.orienteer.oposter.model.IPosting; 13 | 14 | import com.google.inject.Inject; 15 | import com.google.inject.Provider; 16 | import com.google.inject.Singleton; 17 | import com.orientechnologies.orient.core.db.ODatabaseSession; 18 | import com.orientechnologies.orient.core.sql.executor.OResultSet; 19 | 20 | import lombok.extern.slf4j.Slf4j; 21 | import ru.ydn.wicket.wicketorientdb.utils.DBClosure; 22 | 23 | /** 24 | * Class for handling invokations from scheduler 25 | */ 26 | @Singleton 27 | @Slf4j 28 | public class OPScheduler { 29 | 30 | @Inject 31 | private Provider posterDAOProvider; 32 | 33 | public static synchronized final OPScheduler getInstance() { 34 | return OrienteerWebApplication.lookupApplication().getServiceInstance(OPScheduler.class); 35 | } 36 | 37 | public void tick() { 38 | new DBClosure() { 39 | 40 | @Override 41 | protected Boolean execute(ODatabaseSession db) { 42 | ThreadContext.setApplication(OrienteerWebApplication.lookupApplication()); 43 | try { 44 | try { 45 | tick(db); 46 | } catch (Throwable e) { 47 | log.error("Problem in OPoster scheduler", e); 48 | OLogger.log(e); 49 | } 50 | return true; 51 | } finally { 52 | ThreadContext.detach(); 53 | } 54 | } 55 | }.execute(); 56 | } 57 | 58 | protected void tick(ODatabaseSession db) throws Throwable { 59 | List contentToSend = posterDAOProvider.get().findContentToSend(); 60 | if(contentToSend!=null && !contentToSend.isEmpty()) { 61 | for (IContent content : contentToSend) { 62 | List channels = content.getChannels(); 63 | if(channels!=null && !channels.isEmpty()) { 64 | for (IChannel channel : channels) { 65 | channel.getPlatformApp().sendSafe(channel, content); 66 | } 67 | content.published(); 68 | DAO.save(content); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/OPUtils.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | 7 | import org.apache.wicket.WicketRuntimeException; 8 | import org.orienteer.vuecket.VueSettings; 9 | import org.orienteer.vuecket.util.VuecketUtils; 10 | 11 | import com.fasterxml.jackson.core.JsonProcessingException; 12 | import com.fasterxml.jackson.databind.JsonNode; 13 | import com.fasterxml.jackson.databind.ObjectMapper; 14 | 15 | import lombok.experimental.UtilityClass; 16 | import net.coobird.thumbnailator.Thumbnails; 17 | 18 | /** 19 | * Collection of useful functions 20 | */ 21 | @UtilityClass 22 | public class OPUtils { 23 | private static final int MAX_IMAGE_SIZE = 5_000_000; 24 | 25 | 26 | public byte[] resizeImage(byte[] image) throws IOException { 27 | byte [] data = image; 28 | 29 | while (data.length > MAX_IMAGE_SIZE) { 30 | ByteArrayOutputStream thumbnailOS = new ByteArrayOutputStream(); 31 | Thumbnails.of(new ByteArrayInputStream(data)) 32 | .scale(0.8) 33 | .toOutputStream(thumbnailOS); 34 | data = thumbnailOS.toByteArray(); 35 | } 36 | 37 | return data; 38 | } 39 | 40 | public static JsonNode toJsonNode(String json) { 41 | return VuecketUtils.toJsonNode(json); 42 | } 43 | 44 | public static ObjectMapper getObjectMapper() { 45 | return VueSettings.get().getObjectMapper(); 46 | } 47 | 48 | public static String firstLine(String data) { 49 | if(data==null) return null; 50 | int firstBreak = data.indexOf('\n'); 51 | return firstBreak<0?data:data.substring(0, firstBreak).trim(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/OPosterModule.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter; 2 | 3 | import org.orienteer.core.OrienteerWebApplication; 4 | import org.orienteer.core.component.FAIconType; 5 | import org.orienteer.core.dao.DAO; 6 | import org.orienteer.core.method.OMethodsManager; 7 | import org.orienteer.core.module.AbstractOrienteerModule; 8 | import org.orienteer.core.module.IOrienteerModule; 9 | import org.orienteer.core.module.PerspectivesModule; 10 | import org.orienteer.core.module.PerspectivesModule.IOPerspective; 11 | import org.orienteer.core.util.OSchemaHelper; 12 | import org.orienteer.mail.OMailModule; 13 | import org.orienteer.oposter.component.attachment.AttachmentsVisualizer; 14 | import org.orienteer.oposter.component.widget.AbstractCalendarContentWidget; 15 | import org.orienteer.oposter.facebook.IFacebookApp; 16 | import org.orienteer.oposter.facebook.IFacebookConnection; 17 | import org.orienteer.oposter.instagram.IIGAccount; 18 | import org.orienteer.oposter.instagram.IIGApp; 19 | import org.orienteer.oposter.model.IChannel; 20 | import org.orienteer.oposter.model.IContent; 21 | import org.orienteer.oposter.model.IContentPlan; 22 | import org.orienteer.oposter.model.IPlatformApp; 23 | import org.orienteer.oposter.model.IPosting; 24 | import org.orienteer.oposter.ok.IOkApp; 25 | import org.orienteer.oposter.ok.IOkChannel; 26 | import org.orienteer.oposter.telegram.ITelegramBot; 27 | import org.orienteer.oposter.telegram.ITelegramChannel; 28 | import org.orienteer.oposter.twitter.ITwitterAccount; 29 | import org.orienteer.oposter.twitter.ITwitterApp; 30 | import org.orienteer.oposter.vk.IVkApp; 31 | import org.orienteer.oposter.vk.IVkWall; 32 | 33 | import com.orientechnologies.orient.core.db.ODatabaseSession; 34 | import com.orientechnologies.orient.core.db.OrientDBInternal; 35 | import com.orientechnologies.orient.core.record.impl.ODocument; 36 | import com.orientechnologies.orient.core.schedule.OScheduledEvent; 37 | 38 | /** 39 | * {@link IOrienteerModule} for 'oposter' module 40 | */ 41 | public class OPosterModule extends AbstractOrienteerModule{ 42 | 43 | public static final String NAME = "oposter"; 44 | public static final String PERSPECTIVE_ALIAS = "oposter"; 45 | 46 | protected OPosterModule() { 47 | super(NAME, 15, PerspectivesModule.NAME, OMailModule.NAME); 48 | } 49 | 50 | @Override 51 | public ODocument onInstall(OrienteerWebApplication app, ODatabaseSession db) { 52 | super.onInstall(app, db); 53 | OSchemaHelper helper = OSchemaHelper.bind(db); 54 | DAO.define(IContentPlan.class, 55 | IContent.class, 56 | IChannel.class, 57 | IPlatformApp.class, 58 | // Telegram 59 | ITelegramChannel.class, 60 | ITelegramBot.class, 61 | //Facebook 62 | IFacebookConnection.class, 63 | IFacebookApp.class, 64 | //VKontakte 65 | IVkWall.class, 66 | IVkApp.class, 67 | //Instagram 68 | IIGApp.class, 69 | IIGAccount.class, 70 | //Twitter 71 | ITwitterApp.class, 72 | ITwitterAccount.class, 73 | //Odnoklassniki 74 | IOkApp.class, 75 | IOkChannel.class); 76 | 77 | helper.oClass("OFunction") 78 | .oDocument("name", "Scheduler") 79 | .field("language", "nashorn") 80 | .field("code", "org.orienteer.oposter.OPScheduler.getInstance().tick()") 81 | .saveDocument(); 82 | ODocument schedulerFunc = helper.getODocument(); 83 | helper.oClass("OSchedule") 84 | .oDocument("name", "OPoster") 85 | .field("rule", "0 * * * * ?") 86 | .field("function", schedulerFunc) 87 | .saveDocument(); 88 | installPerspective(helper); 89 | 90 | return null; 91 | } 92 | 93 | @Override 94 | public void onUpdate(OrienteerWebApplication app, ODatabaseSession db, int oldVersion, int newVersion) { 95 | onInstall(app, db); 96 | } 97 | 98 | protected void installPerspective(OSchemaHelper helper) { 99 | 100 | IOPerspective perspective = IOPerspective.getOrCreateByAlias("oposter", 101 | "perspective.oposter", 102 | FAIconType.bolt.name(), 103 | "/browse/"+IContentPlan.CLASS_NAME); 104 | 105 | perspective.getOrCreatePerspectiveItem("content_plans", 106 | "perspective.oposter.contentplans", 107 | FAIconType.bolt.name(), 108 | "/browse/"+IContentPlan.CLASS_NAME); 109 | 110 | perspective.getOrCreatePerspectiveItem("contens", 111 | "perspective.oposter.contents", 112 | FAIconType.envelope.name(), 113 | "/browse/"+IContent.CLASS_NAME); 114 | 115 | perspective.getOrCreatePerspectiveItem("platform_appss", 116 | "perspective.oposter.platformapps", 117 | FAIconType.rocket.name(), 118 | "/browse/"+IPlatformApp.CLASS_NAME); 119 | 120 | perspective.getOrCreatePerspectiveItem("channels", 121 | "perspective.oposter.channels", 122 | FAIconType.link.name(), 123 | "/browse/"+IChannel.CLASS_NAME); 124 | 125 | perspective.getOrCreatePerspectiveItem("postings", 126 | "perspective.oposter.postings", 127 | FAIconType.share_alt.name(), 128 | "/browse/"+IPosting.CLASS_NAME); 129 | 130 | } 131 | 132 | @Override 133 | public void onInitialize(OrienteerWebApplication app, ODatabaseSession db) { 134 | super.onInitialize(app, db); 135 | app.mountPackage("org.orienteer.oposter.web"); 136 | app.getUIVisualizersRegistry().registerUIComponentFactory(new AttachmentsVisualizer()); 137 | 138 | OMethodsManager.get().addModule(OPosterModule.class); 139 | OMethodsManager.get().reload(); 140 | app.registerWidgets(AbstractCalendarContentWidget.class.getPackage().getName()); 141 | 142 | 143 | //Kicking-off scheduled events. 144 | //TODO: remove after fixing issue in OrientDB: https://github.com/orientechnologies/orientdb/issues/9500 145 | String dbName = db.getName(); 146 | OrientDBInternal orientDbInternal = OrientDBInternal.extract(app.getServer().getContext()); 147 | db.browseClass(OScheduledEvent.CLASS_NAME) 148 | .forEach(d -> { 149 | new OScheduledEvent(d).schedule(dbName, "admin", orientDbInternal); 150 | }); 151 | } 152 | 153 | @Override 154 | public void onDestroy(OrienteerWebApplication app, ODatabaseSession db) { 155 | super.onDestroy(app, db); 156 | app.unmountPackage("org.orienteer.oposter.web"); 157 | app.unregisterWidgets(AbstractCalendarContentWidget.class.getPackage().getName()); 158 | OMethodsManager.get().removeModule(OPosterModule.class); 159 | OMethodsManager.get().reload(); 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/AbstractEventPayload.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component; 2 | 3 | import org.apache.wicket.Component; 4 | import org.apache.wicket.ajax.AjaxRequestTarget; 5 | import org.apache.wicket.model.IModel; 6 | import org.orienteer.core.component.property.DisplayMode; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * EventPayload to be used as basis for all events 12 | */ 13 | public abstract class AbstractEventPayload implements Serializable { 14 | 15 | private final Component component; 16 | private final IModel modeModel; 17 | private final AjaxRequestTarget target; 18 | 19 | private boolean processed; 20 | 21 | protected AbstractEventPayload(Component component, IModel modeModel, AjaxRequestTarget target) { 22 | this.component = component; 23 | this.modeModel = modeModel; 24 | this.target = target; 25 | this.processed = false; 26 | } 27 | 28 | protected AbstractEventPayload(Component component, AjaxRequestTarget target) { 29 | this(component, DisplayMode.VIEW.asModel(), target); 30 | } 31 | 32 | public Component getComponent() { 33 | return component; 34 | } 35 | 36 | public IModel getModeModel() { 37 | return modeModel; 38 | } 39 | 40 | public AjaxRequestTarget getTarget() { 41 | return target; 42 | } 43 | 44 | public boolean isProcessed() { 45 | return processed; 46 | } 47 | 48 | public AbstractEventPayload setProcessed(boolean processed) { 49 | this.processed = processed; 50 | return this; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/FullCalendar.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component; 2 | 3 | import java.util.Date; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import org.apache.wicket.markup.head.CssHeaderItem; 8 | import org.apache.wicket.markup.head.IHeaderResponse; 9 | import org.apache.wicket.markup.head.JavaScriptHeaderItem; 10 | import org.apache.wicket.model.IModel; 11 | import org.apache.wicket.model.LambdaModel; 12 | import org.apache.wicket.model.Model; 13 | import org.apache.wicket.util.io.IClusterable; 14 | import org.orienteer.vuecket.VueComponent; 15 | import org.orienteer.vuecket.VueSettings; 16 | import org.orienteer.vuecket.descriptor.VueNpm; 17 | import org.orienteer.vuecket.method.IVuecketMethod.Context; 18 | import org.orienteer.vuecket.npmprovider.INPMPackageProvider; 19 | import org.orienteer.vuecket.method.JsFunction; 20 | 21 | import com.fasterxml.jackson.annotation.JsonInclude; 22 | import com.fasterxml.jackson.databind.JsonNode; 23 | import com.fasterxml.jackson.databind.node.ArrayNode; 24 | 25 | import lombok.Data; 26 | import lombok.extern.slf4j.Slf4j; 27 | 28 | import org.orienteer.vuecket.method.VueMethod; 29 | import org.orienteer.vuecket.util.VuecketUtils; 30 | 31 | /** 32 | * Vuecket component for FullCalendar js library 33 | */ 34 | @VueNpm(packageName = "@fullcalendar/vue", path = "dist/main.global.js", enablement = "") 35 | @Slf4j 36 | public class FullCalendar extends VueComponent { 37 | 38 | /** 39 | * Class for passing infor about events requests 40 | */ 41 | @Data 42 | public static class EventsRequestInfo implements IClusterable { 43 | private Date start; 44 | private Date end; 45 | private String startStr; 46 | private String endStr; 47 | private String timeZone; 48 | } 49 | 50 | /** 51 | * Container for events data 52 | */ 53 | @Data 54 | @JsonInclude(JsonInclude.Include.NON_NULL) 55 | public static class Event implements IClusterable { 56 | private String id; 57 | private String groupId; 58 | private Boolean allDay; 59 | private Date start; 60 | private Date end; 61 | private String title; 62 | private String url; 63 | } 64 | 65 | /** 66 | * Options for FullCalendar 67 | */ 68 | @Data 69 | @JsonInclude(JsonInclude.Include.NON_NULL) 70 | public static class Options implements IClusterable { 71 | private Object events; 72 | private Object headerToolbar = VuecketUtils.toJsonNode("{ center: 'dayGridMonth,timeGridWeek' }"); 73 | 74 | public Options(Object events) { 75 | this.events = events; 76 | } 77 | 78 | public Options(FullCalendar calendar) { 79 | this(JsFunction.call(calendar, "lookupEvents")); 80 | } 81 | } 82 | 83 | public FullCalendar(String id) { 84 | super(id); 85 | setDefaultModel(Model.of(new Options(this))); 86 | } 87 | 88 | public FullCalendar(String id, IModel optionsModel) { 89 | super(id, optionsModel); 90 | } 91 | 92 | @Override 93 | protected void onInitialize() { 94 | super.onInitialize(); 95 | dataFiberBuilder("options").bindToProperty().init().bind(); 96 | } 97 | 98 | @Override 99 | public void renderHead(IHeaderResponse response) { 100 | INPMPackageProvider provider = VueSettings.get().getNpmPackageProvider(); 101 | response.render(CssHeaderItem.forReference(provider.provide("fullcalendar", "main.css"))); 102 | response.render(JavaScriptHeaderItem.forReference(provider.provide("fullcalendar", "main.min.js"), "fullcalendar")); 103 | super.renderHead(response); 104 | } 105 | 106 | @VueMethod("lookupEvents") 107 | public final List lookupEvents(Context ctx, EventsRequestInfo eventsRequestInfo) { 108 | return lookupEvents(eventsRequestInfo.getStart(), eventsRequestInfo.getEnd()); 109 | } 110 | 111 | public List lookupEvents(Date start, Date end) { 112 | return new ArrayList(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/AttachmentsEditPanel.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 | 7 |
8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 |
-------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/AttachmentsEditPanel.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.attachment; 2 | 3 | import java.io.IOException; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | import org.apache.wicket.AttributeModifier; 11 | import org.apache.wicket.ajax.AjaxRequestTarget; 12 | import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior; 13 | import org.apache.wicket.event.IEvent; 14 | import org.apache.wicket.markup.head.CssHeaderItem; 15 | import org.apache.wicket.markup.head.IHeaderResponse; 16 | import org.apache.wicket.markup.head.JavaScriptHeaderItem; 17 | import org.apache.wicket.markup.head.OnDomReadyHeaderItem; 18 | import org.apache.wicket.markup.html.WebMarkupContainer; 19 | import org.apache.wicket.markup.html.form.FormComponentPanel; 20 | import org.apache.wicket.markup.html.form.upload.FileUpload; 21 | import org.apache.wicket.markup.html.form.upload.FileUploadField; 22 | import org.apache.wicket.markup.html.list.ListItem; 23 | import org.apache.wicket.markup.html.list.ListView; 24 | import org.apache.wicket.model.IModel; 25 | import org.apache.wicket.model.LoadableDetachableModel; 26 | import org.apache.wicket.model.util.ListModel; 27 | import org.apache.wicket.request.resource.CssResourceReference; 28 | import org.apache.wicket.request.resource.JavaScriptResourceReference; 29 | import org.orienteer.core.dao.DAO; 30 | import org.orienteer.oposter.OPUtils; 31 | import org.orienteer.oposter.model.IContent; 32 | import org.orienteer.oposter.model.IImageAttachment; 33 | 34 | import com.orientechnologies.orient.core.db.record.OIdentifiable; 35 | import com.orientechnologies.orient.core.record.impl.ODocument; 36 | 37 | import lombok.extern.slf4j.Slf4j; 38 | 39 | /** 40 | * Component for editing attachments 41 | */ 42 | @Slf4j 43 | public class AttachmentsEditPanel extends FormComponentPanel> { 44 | 45 | private static final CssResourceReference EDIT_ATTACHMENT_CSS = new CssResourceReference(AttachmentsEditPanel.class, "edit-attachment.css"); 46 | private static final JavaScriptResourceReference EDIT_ATTACHMENT_JS = new JavaScriptResourceReference(AttachmentsEditPanel.class, "edit-attachment.js"); 47 | 48 | private final IModel contentModel; 49 | 50 | private final IModel> attachmentsForDelete; 51 | private WebMarkupContainer uploadWrapper; 52 | private WebMarkupContainer label; 53 | private FileUploadField uploadField; 54 | 55 | private boolean submitOnChange = false; 56 | private boolean immediateDelete = false; 57 | private boolean uploadOnce = false; 58 | 59 | public AttachmentsEditPanel(String id, IModel contentModel, IModel> model) { 60 | super(id, model); 61 | this.contentModel = contentModel; 62 | this.attachmentsForDelete = new ListModel<>(new LinkedList<>()); 63 | } 64 | 65 | @Override 66 | public void convertInput() { 67 | List fileUploads = uploadField.getFileUploads(); 68 | List attachments = new LinkedList<>(getPreparedAttachments()); 69 | if (!fileUploads.isEmpty()) { 70 | int lastOrder = attachments.isEmpty() ? 0 : attachments.get(attachments.size() - 1).getOrder(); 71 | 72 | for (int i = 0; i < fileUploads.size(); i++) { 73 | FileUpload fu = fileUploads.get(i); 74 | attachments.add(IImageAttachment.create(DAO.provide(IContent.class, contentModel.getObject()), 75 | fu.getClientFileName(), 76 | getImage(fu), 77 | lastOrder + (i + 1) * 10, 78 | fu.getContentType())); 79 | } 80 | } 81 | attachmentsForDelete.getObject().forEach(a -> { 82 | DAO.asDocument(a).delete(); 83 | }); 84 | 85 | if (!attachments.isEmpty()) { 86 | setConvertedInput(attachments.stream().map(DAO::asDocument).collect(Collectors.toList())); 87 | } else { 88 | setConvertedInput(null); 89 | } 90 | } 91 | 92 | @Override 93 | protected void onInitialize() { 94 | super.onInitialize(); 95 | uploadWrapper = createUploadWrapper("uploadWrapper"); 96 | uploadWrapper.setOutputMarkupPlaceholderTag(true); 97 | uploadField = createUploadImagesField("uploadImages"); 98 | 99 | label = new WebMarkupContainer("label"); 100 | label.setOutputMarkupPlaceholderTag(true); 101 | 102 | uploadWrapper.add(uploadField); 103 | uploadWrapper.add(label); 104 | 105 | add(createAttachmentsList("attachments")); 106 | add(uploadWrapper); 107 | 108 | 109 | uploadField.setOutputMarkupId(true); 110 | setOutputMarkupPlaceholderTag(true); 111 | } 112 | 113 | @Override 114 | public void onEvent(IEvent event) { 115 | super.onEvent(event); 116 | 117 | Object payload = event.getPayload(); 118 | if (payload instanceof DeleteAttachmentEventPayload) { 119 | DeleteAttachmentEventPayload deletePayload = (DeleteAttachmentEventPayload) payload; 120 | 121 | if (immediateDelete) { 122 | List attachments = getModelObject(); 123 | if (attachments != null && !attachments.isEmpty()) { 124 | attachments = new LinkedList<>(attachments); 125 | attachments.remove(DAO.asDocument(deletePayload.getModel().getObject())); 126 | setModelObject(attachments); 127 | } 128 | deletePayload.getTarget().add(this); 129 | } else { 130 | List attachments = attachmentsForDelete.getObject(); 131 | IImageAttachment attachment = deletePayload.getModel().getObject(); 132 | 133 | if (attachments.contains(attachment)) { 134 | attachments.remove(attachment); 135 | } else { 136 | attachments.add(attachment); 137 | } 138 | 139 | deletePayload.getTarget().add(deletePayload.getComponent()); 140 | } 141 | 142 | deletePayload.setProcessed(true); 143 | } 144 | } 145 | 146 | @Override 147 | public void renderHead(IHeaderResponse response) { 148 | super.renderHead(response); 149 | response.render(CssHeaderItem.forReference(EDIT_ATTACHMENT_CSS)); 150 | response.render(JavaScriptHeaderItem.forReference(EDIT_ATTACHMENT_JS)); 151 | 152 | String initJs = String.format("initEditAttachments('%s', '%s', '%s');", 153 | uploadWrapper.getMarkupId(), uploadField.getMarkupId(), label.getMarkupId()); 154 | 155 | response.render(OnDomReadyHeaderItem.forScript(initJs)); 156 | } 157 | 158 | public void setSubmitOnChange(boolean submitOnChange) { 159 | this.submitOnChange = submitOnChange; 160 | } 161 | 162 | public void setImmediateDelete(boolean immediateDelete) { 163 | this.immediateDelete = immediateDelete; 164 | } 165 | 166 | public void setUploadOnce(boolean uploadOnce) { 167 | this.uploadOnce = uploadOnce; 168 | } 169 | 170 | private List getPreparedAttachments() { 171 | List attachments = getModelObject(); 172 | if(attachments==null || attachments.isEmpty()) return Collections.emptyList(); 173 | else return getModelObject() 174 | .stream() 175 | .map(d->DAO.provide(IImageAttachment.class, (ODocument)d.getRecord())) 176 | .filter(a -> !attachmentsForDelete.getObject().contains(a)) 177 | .sorted(Comparator.comparing(IImageAttachment::getOrder, Comparator.nullsFirst(Comparator.naturalOrder()))) 178 | .collect(Collectors.toList()); 179 | } 180 | 181 | private FileUploadField createUploadImagesField(String id) { 182 | return new FileUploadField(id, new ListModel<>(new LinkedList<>())) { 183 | @Override 184 | protected void onInitialize() { 185 | super.onInitialize(); 186 | setOutputMarkupId(true); 187 | add(new OImageValidator()); 188 | if (submitOnChange) { 189 | add(new AjaxFormSubmitBehavior("change") { 190 | @Override 191 | protected void onSubmit(AjaxRequestTarget target) { 192 | target.add(AttachmentsEditPanel.this); 193 | } 194 | }); 195 | } 196 | } 197 | }; 198 | } 199 | 200 | private WebMarkupContainer createUploadWrapper(String id) { 201 | return new WebMarkupContainer(id) { 202 | @Override 203 | protected void onConfigure() { 204 | super.onConfigure(); 205 | if (uploadOnce) { 206 | setVisible(getModelObject() == null || getModelObject().isEmpty()); 207 | } 208 | } 209 | 210 | @Override 211 | protected void onInitialize() { 212 | super.onInitialize(); 213 | setOutputMarkupPlaceholderTag(true); 214 | } 215 | }; 216 | } 217 | 218 | private ListView createAttachmentsList(String id) { 219 | return new ListView(id, createAttachmentsModel()) { 220 | @Override 221 | protected void populateItem(ListItem item) { 222 | if (item.getIndex() > 0) { 223 | item.add(AttributeModifier.append("class", "mt-2")); 224 | } 225 | item.add(createOfferAttachmentEditPanel("attachment", item.getModel())); 226 | } 227 | 228 | @Override 229 | protected void onInitialize() { 230 | super.onInitialize(); 231 | setReuseItems(true); 232 | } 233 | 234 | @Override 235 | protected void onConfigure() { 236 | super.onConfigure(); 237 | if (immediateDelete) { 238 | getModel().detach(); 239 | } 240 | } 241 | }; 242 | } 243 | 244 | private IModel> createAttachmentsModel() { 245 | return new LoadableDetachableModel>() { 246 | @Override 247 | protected List load() { 248 | return getPreparedAttachments(); 249 | } 250 | }; 251 | } 252 | 253 | private SingleAttachmentEditPanel createOfferAttachmentEditPanel(String id, IModel model) { 254 | return new SingleAttachmentEditPanel(id, model) { 255 | @Override 256 | protected void onConfigure() { 257 | super.onConfigure(); 258 | if (attachmentsForDelete.getObject().contains(getModelObject())) { 259 | add(AttributeModifier.replace("class", "delete-attachment")); 260 | } else { 261 | add(AttributeModifier.replace("class", "")); 262 | } 263 | } 264 | }; 265 | } 266 | 267 | private byte[] getImage(FileUpload fileUpload) { 268 | byte[] originalImage = fileUpload.getBytes(); 269 | try { 270 | return OPUtils.resizeImage(fileUpload.getBytes()); 271 | } catch (IOException e) { 272 | log.error("Can't resize image. Using original image. Original image: name = {}, content type = {}, size = {} b", 273 | fileUpload.getClientFileName(), fileUpload.getContentType(), originalImage.length, e); 274 | } 275 | return originalImage; 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/AttachmentsViewPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/AttachmentsViewPanel.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.attachment; 2 | 3 | import com.orientechnologies.orient.core.metadata.schema.OProperty; 4 | import com.orientechnologies.orient.core.record.impl.ODocument; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.wicket.AttributeModifier; 7 | import org.apache.wicket.markup.ComponentTag; 8 | import org.apache.wicket.markup.head.CssHeaderItem; 9 | import org.apache.wicket.markup.head.IHeaderResponse; 10 | import org.apache.wicket.markup.head.JavaScriptHeaderItem; 11 | import org.apache.wicket.markup.head.OnDomReadyHeaderItem; 12 | import org.apache.wicket.markup.html.WebMarkupContainer; 13 | import org.apache.wicket.markup.html.image.Image; 14 | import org.apache.wicket.markup.html.link.Link; 15 | import org.apache.wicket.markup.html.list.ListItem; 16 | import org.apache.wicket.markup.html.list.ListView; 17 | import org.apache.wicket.markup.html.panel.GenericPanel; 18 | import org.apache.wicket.model.IModel; 19 | import org.apache.wicket.model.util.ListModel; 20 | import org.apache.wicket.request.resource.CssResourceReference; 21 | import org.apache.wicket.request.resource.JavaScriptResourceReference; 22 | import org.orienteer.core.resource.OContentShareResource; 23 | 24 | import java.util.Collections; 25 | import java.util.List; 26 | import java.util.stream.Collectors; 27 | import java.util.stream.IntStream; 28 | 29 | /** 30 | * Component for displaying attachments 31 | */ 32 | @Slf4j 33 | public class AttachmentsViewPanel extends GenericPanel { 34 | 35 | public static final JavaScriptResourceReference CAROUSEL_LOAD_JS = new JavaScriptResourceReference(AttachmentsViewPanel.class, "carousel-load.js"); 36 | public static final CssResourceReference CAROUSEL_CSS = new CssResourceReference(AttachmentsViewPanel.class, "carousel.css"); 37 | 38 | private final int size; 39 | private final IModel docModel; 40 | 41 | public AttachmentsViewPanel(String id, int size, IModel docModel, IModel model) { 42 | super(id, model); 43 | this.size = size; 44 | this.docModel = docModel; 45 | } 46 | 47 | @Override 48 | protected void onInitialize() { 49 | super.onInitialize(); 50 | add(createCarousel("carousel")); 51 | setOutputMarkupPlaceholderTag(true); 52 | } 53 | 54 | private WebMarkupContainer createCarousel(String id) { 55 | return new WebMarkupContainer(id) { 56 | @Override 57 | protected void onInitialize() { 58 | super.onInitialize(); 59 | add(createIndicators("carouselIndicators")); 60 | add(createImagesList("carouselImagesList")); 61 | add(createButton("prevButton")); 62 | add(createButton("nextButton")); 63 | setOutputMarkupPlaceholderTag(true); 64 | } 65 | 66 | @Override 67 | protected void onConfigure() { 68 | super.onConfigure(); 69 | setVisible(size > 0); 70 | } 71 | }; 72 | } 73 | 74 | private ListView createIndicators(String id) { 75 | return new ListView(id, new ListModel<>(getIndicators())) { 76 | @Override 77 | protected void populateItem(ListItem item) { 78 | if (item.getModelObject() == 0) { 79 | item.add(AttributeModifier.append("class", "active")); 80 | } 81 | 82 | item.add(AttributeModifier.replace("data-slide-to", item.getModelObject())); 83 | item.add(AttributeModifier.replace("data-target", "#" + getParent().getMarkupId())); 84 | } 85 | 86 | @Override 87 | protected void onInitialize() { 88 | super.onInitialize(); 89 | setReuseItems(true); 90 | } 91 | }; 92 | } 93 | 94 | private ListView createImagesList(String id) { 95 | List images = IntStream.range(0, size).boxed().collect(Collectors.toList()); 96 | 97 | return new ListView(id, new ListModel<>(images)) { 98 | @Override 99 | protected void populateItem(ListItem item) { 100 | if (item.getIndex() == 0) { 101 | item.add(AttributeModifier.append("class", "active")); 102 | } 103 | 104 | String field = AttachmentsViewPanel.this.getModelObject().getName() + "[" + item.getModelObject() + "].data"; 105 | 106 | item.add(new Image("image") { 107 | @Override 108 | protected String buildSrcAttribute(ComponentTag tag) { 109 | return "/" + OContentShareResource.urlFor(docModel.getObject(), field, null, 640,false).toString(); 110 | } 111 | }); 112 | } 113 | }; 114 | } 115 | 116 | private Link createButton(String id) { 117 | return new Link(id) { 118 | @Override 119 | public void onClick() {} 120 | 121 | @Override 122 | protected void onInitialize() { 123 | super.onInitialize(); 124 | add(AttributeModifier.replace("href", "#" + getParent().getMarkupId())); 125 | } 126 | }; 127 | } 128 | 129 | private List getIndicators() { 130 | if (size == 0) { 131 | return Collections.emptyList(); 132 | } 133 | 134 | return IntStream.range(0, size) 135 | .boxed() 136 | .collect(Collectors.toList()); 137 | } 138 | 139 | @Override 140 | public void renderHead(IHeaderResponse response) { 141 | super.renderHead(response); 142 | response.render(CssHeaderItem.forReference(CAROUSEL_CSS)); 143 | response.render(JavaScriptHeaderItem.forReference(CAROUSEL_LOAD_JS)); 144 | 145 | 146 | String initScript = String.format("initLoad('%s');", get("carousel").getMarkupId()); 147 | 148 | response.render(OnDomReadyHeaderItem.forScript(initScript)); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/AttachmentsVisualizer.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.attachment; 2 | 3 | import com.orientechnologies.orient.core.db.record.OIdentifiable; 4 | import com.orientechnologies.orient.core.metadata.schema.OProperty; 5 | import com.orientechnologies.orient.core.metadata.schema.OType; 6 | import com.orientechnologies.orient.core.record.impl.ODocument; 7 | import org.apache.wicket.Component; 8 | import org.apache.wicket.model.IModel; 9 | import org.orienteer.core.component.property.DisplayMode; 10 | import org.orienteer.core.component.visualizer.AbstractSimpleVisualizer; 11 | import org.orienteer.core.component.visualizer.IVisualizer; 12 | import org.orienteer.oposter.model.IImageAttachment; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * {@link IVisualizer} for attachments 18 | */ 19 | public class AttachmentsVisualizer extends AbstractSimpleVisualizer { 20 | 21 | public static final String NAME = "attachments"; 22 | 23 | public AttachmentsVisualizer() { 24 | super(NAME, false, OType.LINKLIST, OType.LINKSET); 25 | } 26 | 27 | @Override 28 | @SuppressWarnings("unchecked") 29 | public Component createComponent(String id, DisplayMode mode, 30 | IModel documentModel, 31 | IModel propertyModel, IModel valueModel) { 32 | if (!propertyModel.getObject().getLinkedClass().isSubClassOf(IImageAttachment.CLASS_NAME)) { 33 | return null; 34 | } 35 | 36 | switch (mode) { 37 | case EDIT: 38 | return new AttachmentsEditPanel(id, documentModel, (IModel>) valueModel); 39 | case VIEW: 40 | 41 | IModel> attachments = (IModel>) valueModel; 42 | int size = attachments.getObject() != null ? attachments.getObject().size() : 0; 43 | return new AttachmentsViewPanel(id, size, documentModel, propertyModel); 44 | } 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/DeleteAttachmentEventPayload.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.attachment; 2 | 3 | import org.apache.wicket.Component; 4 | import org.apache.wicket.ajax.AjaxRequestTarget; 5 | import org.apache.wicket.model.IModel; 6 | import org.orienteer.core.component.property.DisplayMode; 7 | import org.orienteer.oposter.component.AbstractEventPayload; 8 | import org.orienteer.oposter.model.IImageAttachment; 9 | 10 | /** 11 | * Payload for events about deletion of some attachment 12 | */ 13 | public class DeleteAttachmentEventPayload extends AbstractEventPayload { 14 | 15 | private final IModel model; 16 | 17 | public DeleteAttachmentEventPayload(Component component, IModel modeModel, 18 | IModel model, AjaxRequestTarget target) { 19 | super(component, modeModel, target); 20 | this.model = model; 21 | } 22 | 23 | public IModel getModel() { 24 | return model; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/OImageValidator.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.attachment; 2 | 3 | import com.google.common.base.Strings; 4 | import org.apache.tika.Tika; 5 | import org.apache.wicket.markup.html.form.upload.FileUpload; 6 | import org.apache.wicket.model.ResourceModel; 7 | import org.apache.wicket.validation.IValidatable; 8 | import org.apache.wicket.validation.IValidator; 9 | import org.apache.wicket.validation.ValidationError; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | /** 15 | * Image validator for files which are being uploaded 16 | */ 17 | public class OImageValidator implements IValidator> { 18 | 19 | private static final List SUPPORTED_IMAGES = Arrays.asList("png", "jpg", "jpeg", "bmp", "wbmp", "gif"); 20 | 21 | @Override 22 | public void validate(IValidatable> validatable) { 23 | Tika tika = new Tika(); 24 | List uploads = validatable.getValue(); 25 | 26 | for (FileUpload upload : uploads) { 27 | byte[] image = upload.getBytes(); 28 | String type = tika.detect(image); 29 | 30 | if (!isSupportedImageType(type)) { 31 | ValidationError error = new ValidationError(); 32 | error.setMessage(new ResourceModel("validation.image.upload.not.supported.type").getObject()); 33 | validatable.error(error); 34 | break; 35 | } 36 | } 37 | } 38 | 39 | private boolean isSupportedImageType(String type) { 40 | if (Strings.isNullOrEmpty(type) || !type.startsWith("image")) { 41 | return false; 42 | } 43 | String imageType = type.split("/")[1]; 44 | return SUPPORTED_IMAGES.contains(imageType.toLowerCase()); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/SingleAttachmentEditPanel.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 |
12 |
13 |
-------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/SingleAttachmentEditPanel.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.attachment; 2 | 3 | import org.apache.tika.Tika; 4 | import org.apache.wicket.ajax.AjaxRequestTarget; 5 | import org.apache.wicket.ajax.markup.html.AjaxLink; 6 | import org.apache.wicket.event.Broadcast; 7 | import org.apache.wicket.markup.html.basic.Label; 8 | import org.apache.wicket.markup.html.image.Image; 9 | import org.apache.wicket.markup.html.panel.GenericPanel; 10 | import org.apache.wicket.model.IModel; 11 | import org.apache.wicket.model.PropertyModel; 12 | import org.apache.wicket.request.resource.ByteArrayResource; 13 | import org.orienteer.core.component.property.DisplayMode; 14 | import org.orienteer.oposter.model.IImageAttachment; 15 | 16 | /** 17 | * Component for editing single attachment 18 | */ 19 | public class SingleAttachmentEditPanel extends GenericPanel { 20 | 21 | public SingleAttachmentEditPanel(String id, IModel model) { 22 | super(id, model); 23 | } 24 | 25 | @Override 26 | protected void onInitialize() { 27 | super.onInitialize(); 28 | byte[] data = getModelObject().getData(); 29 | add(new Label("name", PropertyModel.of(getModel(), "name"))); 30 | add(new Image("image", new ByteArrayResource(new Tika().detect(data), data))); 31 | add(createDeleteButton("deleteBtn")); 32 | setOutputMarkupPlaceholderTag(true); 33 | } 34 | 35 | 36 | private AjaxLink createDeleteButton(String id) { 37 | return new AjaxLink(id) { 38 | @Override 39 | public void onClick(AjaxRequestTarget target) { 40 | IModel model = SingleAttachmentEditPanel.this.getModel(); 41 | send( 42 | SingleAttachmentEditPanel.this.getParent(), 43 | Broadcast.BUBBLE, 44 | new DeleteAttachmentEventPayload(SingleAttachmentEditPanel.this, DisplayMode.EDIT.asModel(), model, target) 45 | ); 46 | } 47 | }; 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/attachment/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package contains classes for attachments UI components 3 | */ 4 | package org.orienteer.oposter.component.attachment; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package contains required components 3 | */ 4 | package org.orienteer.oposter.component; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/widget/AbstractCalendarContentWidget.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/widget/AbstractCalendarContentWidget.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.widget; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | import org.apache.wicket.Component; 8 | import org.apache.wicket.model.IModel; 9 | import org.apache.wicket.model.ResourceModel; 10 | import org.apache.wicket.request.Url; 11 | import org.apache.wicket.request.UrlRenderer; 12 | import org.apache.wicket.request.Url.StringMode; 13 | import org.apache.wicket.request.cycle.RequestCycle; 14 | import org.apache.wicket.request.mapper.parameter.PageParameters; 15 | import org.apache.wicket.request.resource.SharedResourceReference; 16 | import org.orienteer.core.component.FAIcon; 17 | import org.orienteer.core.component.FAIconType; 18 | import org.orienteer.core.component.property.DisplayMode; 19 | import org.orienteer.core.dao.DAO; 20 | import org.orienteer.core.web.ODocumentPage; 21 | import org.orienteer.core.widget.AbstractWidget; 22 | import org.orienteer.core.widget.Widget; 23 | import org.orienteer.oposter.component.FullCalendar; 24 | import org.orienteer.oposter.component.FullCalendar.Event; 25 | import org.orienteer.oposter.model.IContent; 26 | import org.orienteer.oposter.model.IOPosterDAO; 27 | import org.orienteer.oposter.web.OAuthCallbackResource; 28 | import org.orienteer.vuecket.descriptor.VueFile; 29 | import org.orienteer.vuecket.descriptor.VueJson; 30 | 31 | import com.google.inject.Inject; 32 | import com.orientechnologies.orient.core.metadata.schema.OClass; 33 | import com.orientechnologies.orient.core.record.impl.ODocument; 34 | 35 | /** 36 | * Abstract class for all calendars widgets 37 | * @param the type of main data object linked to this widget 38 | */ 39 | @VueJson 40 | public abstract class AbstractCalendarContentWidget extends AbstractWidget { 41 | 42 | @Inject 43 | protected IOPosterDAO dao; 44 | 45 | public AbstractCalendarContentWidget(String id, IModel model, IModel widgetDocumentModel) { 46 | super(id, model, widgetDocumentModel); 47 | add(new FullCalendar("fullCalendar") { 48 | @Override 49 | public List lookupEvents(Date start, Date end) { 50 | return AbstractCalendarContentWidget.this.lookupEvents(start, end); 51 | } 52 | }); 53 | } 54 | 55 | @Override 56 | protected FAIcon newIcon(String id) { 57 | return new FAIcon(id, FAIconType.calendar); 58 | } 59 | 60 | @Override 61 | protected IModel getDefaultTitleModel() { 62 | return new ResourceModel("widget.calendar"); 63 | } 64 | 65 | public List lookupEvents(Date start, Date end) { 66 | UrlRenderer renderer = RequestCycle.get().getUrlRenderer(); 67 | return lookupContent(start, end).stream().map(c -> { 68 | Event e = new Event(); 69 | e.setTitle(c.getTitle()); 70 | e.setStart(c.getWhen()); 71 | e.setUrl(renderer.renderRelativeUrl(ODocumentPage.getLinkToTheDocument(DAO.asDocument(c), DisplayMode.VIEW))); 72 | return e; 73 | }).collect(Collectors.toList()); 74 | } 75 | 76 | public abstract List lookupContent(Date start, Date end); 77 | 78 | } 79 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/widget/CalendarAllContentWidget.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.widget; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | import org.apache.wicket.Component; 7 | import org.apache.wicket.model.IModel; 8 | import org.orienteer.core.widget.Widget; 9 | import org.orienteer.oposter.model.IContent; 10 | 11 | import com.orientechnologies.orient.core.metadata.schema.OClass; 12 | import com.orientechnologies.orient.core.record.impl.ODocument; 13 | 14 | /** 15 | * Calendar widget for page with all content 16 | */ 17 | @Widget(id="cal-all-content", domain="browse", tab = "calendar", selector=IContent.CLASS_NAME, order=10, autoEnable=true) 18 | public class CalendarAllContentWidget extends AbstractCalendarContentWidget { 19 | 20 | public CalendarAllContentWidget(String id, IModel model, IModel widgetDocumentModel) { 21 | super(id, model, widgetDocumentModel); 22 | } 23 | 24 | @Override 25 | public List lookupContent(Date start, Date end) { 26 | return dao.findContent(start, end); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/widget/CalendarChannelContentWidget.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.widget; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | import org.apache.wicket.model.IModel; 7 | import org.orienteer.core.widget.Widget; 8 | import org.orienteer.oposter.model.IChannel; 9 | import org.orienteer.oposter.model.IContent; 10 | import org.orienteer.oposter.model.IContentPlan; 11 | 12 | import com.orientechnologies.orient.core.record.impl.ODocument; 13 | 14 | /** 15 | * Calendar widget for content on particular channel 16 | */ 17 | @Widget(id="cal-channel", domain="document", tab = "calendar", selector=IChannel.CLASS_NAME, order=10, autoEnable=true) 18 | public class CalendarChannelContentWidget extends AbstractCalendarContentWidget { 19 | 20 | public CalendarChannelContentWidget(String id, IModel model, IModel widgetDocumentModel) { 21 | super(id, model, widgetDocumentModel); 22 | } 23 | 24 | @Override 25 | public List lookupContent(Date start, Date end) { 26 | return dao.findContentByChannel(getModelObject(), start, end); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/widget/CalendarContentPlanWidget.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.widget; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | import org.apache.wicket.Component; 7 | import org.apache.wicket.model.IModel; 8 | import org.orienteer.core.widget.Widget; 9 | import org.orienteer.oposter.model.IContent; 10 | import org.orienteer.oposter.model.IContentPlan; 11 | 12 | import com.orientechnologies.orient.core.record.impl.ODocument; 13 | 14 | /** 15 | * Calendar window for content within particular content plan 16 | */ 17 | @Widget(id="cal-content-plan", domain="document", tab = "calendar", selector=IContentPlan.CLASS_NAME, order=10, autoEnable=true) 18 | public class CalendarContentPlanWidget extends AbstractCalendarContentWidget { 19 | 20 | public CalendarContentPlanWidget(String id, IModel model, IModel widgetDocumentModel) { 21 | super(id, model, widgetDocumentModel); 22 | } 23 | 24 | @Override 25 | public List lookupContent(Date start, Date end) { 26 | return dao.findContentByContentPlan(getModelObject(), start, end); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/widget/CalendarPlatformAppWidget.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.component.widget; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | import org.apache.wicket.Component; 7 | import org.apache.wicket.model.IModel; 8 | import org.orienteer.core.widget.Widget; 9 | import org.orienteer.oposter.model.IContent; 10 | import org.orienteer.oposter.model.IContentPlan; 11 | import org.orienteer.oposter.model.IPlatformApp; 12 | 13 | import com.orientechnologies.orient.core.record.impl.ODocument; 14 | 15 | /** 16 | * Calendar widget for content to be sent to particular platform app 17 | */ 18 | @Widget(id="cal-platform-app", domain="document", tab = "calendar", selector=IPlatformApp.CLASS_NAME, order=10, autoEnable=true) 19 | public class CalendarPlatformAppWidget extends AbstractCalendarContentWidget { 20 | 21 | public CalendarPlatformAppWidget(String id, IModel model, IModel widgetDocumentModel) { 22 | super(id, model, widgetDocumentModel); 23 | } 24 | 25 | @Override 26 | public List lookupContent(Date start, Date end) { 27 | return dao.findContentByPlatformApp(getModelObject(), start, end); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/component/widget/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package contains OPoster widgets 3 | */ 4 | package org.orienteer.oposter.component.widget; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/facebook/IFacebookApp.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.facebook; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | import org.orienteer.core.OClassDomain; 7 | import org.orienteer.core.OrienteerWebApplication; 8 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 9 | import org.orienteer.core.dao.DAO; 10 | import org.orienteer.core.dao.ODocumentWrapperProvider; 11 | import org.orienteer.core.dao.OrienteerOClass; 12 | import org.orienteer.core.dao.OrienteerOProperty; 13 | import org.orienteer.oposter.model.IChannel; 14 | import org.orienteer.oposter.model.IContent; 15 | import org.orienteer.oposter.model.IImageAttachment; 16 | import org.orienteer.oposter.model.IPlatformApp; 17 | import org.orienteer.oposter.model.IPosting; 18 | import org.orienteer.transponder.annotation.EntityType; 19 | import org.orienteer.transponder.orientdb.OrientDBProperty; 20 | 21 | import com.google.inject.ProvidedBy; 22 | import com.orientechnologies.orient.core.metadata.schema.OType; 23 | import com.restfb.BinaryAttachment; 24 | import com.restfb.DefaultFacebookClient; 25 | import com.restfb.FacebookClient; 26 | import com.restfb.FacebookClient.AccessToken; 27 | import com.restfb.Parameter; 28 | import com.restfb.types.GraphResponse; 29 | import com.restfb.types.Post; 30 | import com.restfb.Version; 31 | 32 | /** 33 | * {@link IPlatformApp} for Facebook 34 | */ 35 | @ProvidedBy(ODocumentWrapperProvider.class) 36 | @EntityType(value = IFacebookApp.CLASS_NAME, orderOffset = 100) 37 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 38 | public interface IFacebookApp extends IPlatformApp{ 39 | public static final String CLASS_NAME = "OPFacebookApp"; 40 | 41 | @OrientDBProperty(notNull = true) 42 | public String getAppId(); 43 | public void setAppId(String value); 44 | 45 | @OrientDBProperty(notNull = true) 46 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_PASSWORD) 47 | public String getAppSecret(); 48 | public void setAppSecret(String value); 49 | 50 | @OrienteerOProperty(uiReadOnly = true) 51 | public String getAppAccessToken(); 52 | public void setAppAccessToken(String value); 53 | 54 | @OrientDBProperty(type = OType.DATETIME) 55 | @OrienteerOProperty(uiReadOnly = true) 56 | public Date getAppAccessTokenExpires(); 57 | public void setAppAccessTokenExpires(Date value); 58 | 59 | 60 | @Override 61 | public default IPosting send (IChannel channel, IContent content) throws Exception { 62 | IFacebookConnection fp = checkChannelType(channel, IFacebookConnection.class); 63 | FacebookClient facebookClient = getFacebookClient().createClientWithAccessToken(fp.getAccessToken()); 64 | GraphResponse response; 65 | if(!content.hasImages()) { 66 | response = facebookClient.publish(fp.getFacebookIdAsString()+"/feed", GraphResponse.class, Parameter.with("message", content.getContent())); 67 | } else { 68 | IImageAttachment image = content.getImages().get(0); 69 | response = facebookClient.publish(fp.getFacebookIdAsString()+"/photos", 70 | GraphResponse.class, 71 | BinaryAttachment.with(image.getName(), image.getData(), image.detectContentType()), 72 | Parameter.with("message", content.getContent())); 73 | } 74 | Post post = facebookClient.fetchObject(response.getPostId(), Post.class); 75 | return IPosting.createFor(channel, content) 76 | .setExternalPostingId(response.getPostId()) 77 | .setUrl(post.getPermalinkUrl()); 78 | } 79 | 80 | 81 | public default FacebookClient getFacebookClient() { 82 | String key = getMetadataKey(); 83 | FacebookClient ret = OrienteerWebApplication.lookupApplication().getMetaData(key); 84 | if(ret==null) { 85 | String appAccessToken = getAppAccessToken(); 86 | Date expires = getAppAccessTokenExpires(); 87 | if(appAccessToken!=null && expires!=null && expires.compareTo(new Date())>0) { 88 | ret = new DefaultFacebookClient(appAccessToken, Version.LATEST); 89 | } else { 90 | ret = new DefaultFacebookClient(Version.LATEST); 91 | AccessToken token = ret.obtainAppAccessToken(getAppId(), getAppSecret()); 92 | setAppAccessToken(token.getAccessToken()); 93 | setAppAccessTokenExpires(token.getExpires()); 94 | DAO.save(this); 95 | ret = token.getClient(); 96 | } 97 | OrienteerWebApplication.lookupApplication().setMetaData(key, ret); 98 | } 99 | return ret; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/facebook/IFacebookConnection.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.facebook; 2 | 3 | import org.apache.wicket.request.flow.RedirectToUrlException; 4 | import org.orienteer.core.OClassDomain; 5 | import org.orienteer.core.component.BootstrapType; 6 | import org.orienteer.core.component.FAIconType; 7 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 8 | import org.orienteer.core.dao.DAO; 9 | import org.orienteer.core.dao.ODocumentWrapperProvider; 10 | import org.orienteer.core.dao.OrienteerOClass; 11 | import org.orienteer.core.dao.OrienteerOProperty; 12 | import org.orienteer.core.method.IMethodContext; 13 | import org.orienteer.core.method.OFilter; 14 | import org.orienteer.core.method.OMethod; 15 | import org.orienteer.core.method.filters.PlaceFilter; 16 | import org.orienteer.core.method.filters.WidgetTypeFilter; 17 | import org.orienteer.oposter.model.IChannel; 18 | import org.orienteer.oposter.model.IOAuthReciever; 19 | import org.orienteer.oposter.model.IPlatformApp; 20 | import org.orienteer.oposter.web.OAuthCallbackResource; 21 | import org.orienteer.transponder.annotation.EntityType; 22 | import org.orienteer.transponder.orientdb.OrientDBProperty; 23 | 24 | import com.google.inject.ProvidedBy; 25 | import com.restfb.FacebookClient; 26 | import com.restfb.FacebookClient.AccessToken; 27 | import com.restfb.Parameter; 28 | import com.restfb.scope.FacebookPermissions; 29 | import com.restfb.scope.ScopeBuilder; 30 | import com.restfb.types.Page; 31 | 32 | /** 33 | * {@link IChannel} for Facebook 34 | */ 35 | @ProvidedBy(ODocumentWrapperProvider.class) 36 | @EntityType(value = IFacebookConnection.CLASS_NAME, orderOffset = 100) 37 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 38 | public interface IFacebookConnection extends IChannel, IOAuthReciever{ 39 | public static final String CLASS_NAME = "OPFacebookConnection"; 40 | 41 | @OrientDBProperty(notNull = true) 42 | public Long getFacebookId(); 43 | public void setFacebookId(Long value); 44 | 45 | public default String getFacebookIdAsString() { 46 | Long pageId = getFacebookId(); 47 | return pageId!=null?Long.toUnsignedString(pageId):null; 48 | } 49 | 50 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_PASSWORD) 51 | public String getAccessToken(); 52 | public void setAccessToken(String value); 53 | 54 | @OrientDBProperty(notNull = true, defaultValue = "false") 55 | public boolean isPage(); 56 | public void setPage(boolean value); 57 | 58 | 59 | @OMethod( 60 | titleKey = "command.connectoauth", 61 | order=10,bootstrap=BootstrapType.SUCCESS,icon = FAIconType.play, 62 | filters={ 63 | @OFilter(fClass = PlaceFilter.class, fData = "STRUCTURE_TABLE"), 64 | @OFilter(fClass = WidgetTypeFilter.class, fData = "parameters"), 65 | } 66 | ) 67 | public default void connectOAuth(IMethodContext ctx) { 68 | IPlatformApp app = getPlatformApp(); 69 | if(app instanceof IFacebookApp) { 70 | IFacebookApp fbApp = (IFacebookApp) app; 71 | ScopeBuilder scope = new ScopeBuilder(); 72 | scope.addPermission(isPage()?FacebookPermissions.PAGES_MANAGE_POSTS:FacebookPermissions.PUBLISH_TO_GROUPS); 73 | String redirectTo = fbApp.getFacebookClient().getLoginDialogUrl(fbApp.getAppId(), 74 | OAuthCallbackResource.urlFor(DAO.asDocument(this)), 75 | scope); 76 | throw new RedirectToUrlException(redirectTo); 77 | } 78 | } 79 | 80 | @Override 81 | public default void codeObtained(String code) throws Exception { 82 | IPlatformApp app = getPlatformApp(); 83 | if(app instanceof IFacebookApp) { 84 | IFacebookApp fbApp = (IFacebookApp) app; 85 | FacebookClient facebookClient = fbApp.getFacebookClient(); 86 | AccessToken token = facebookClient.obtainUserAccessToken(fbApp.getAppId(), 87 | fbApp.getAppSecret(), 88 | OAuthCallbackResource.urlFor(DAO.asDocument(this)), 89 | code); 90 | if(token.getExpires()!=null) { 91 | token = facebookClient.obtainExtendedAccessToken(fbApp.getAppId(), fbApp.getAppSecret(), token.getAccessToken()); 92 | } 93 | String accessToken = token.getAccessToken(); 94 | if(isPage()) { 95 | facebookClient = facebookClient.createClientWithAccessToken(token.getAccessToken()); 96 | Page page = facebookClient.fetchObject(getFacebookIdAsString(), Page.class, Parameter.with("fields","access_token")); 97 | accessToken = page.getAccessToken(); 98 | } 99 | setAccessToken(accessToken); 100 | DAO.save(this); 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/facebook/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package for Facebook support classes 3 | */ 4 | package org.orienteer.oposter.facebook; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/instagram/IIGAccount.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.instagram; 2 | 3 | import org.joor.Reflect; 4 | import org.orienteer.core.OClassDomain; 5 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 6 | import org.orienteer.core.dao.DAO; 7 | import org.orienteer.core.dao.ODocumentWrapperProvider; 8 | import org.orienteer.core.dao.OrienteerOClass; 9 | import org.orienteer.core.dao.OrienteerOProperty; 10 | import org.orienteer.oposter.model.IChannel; 11 | import org.orienteer.transponder.annotation.EntityType; 12 | import org.orienteer.transponder.orientdb.OrientDBProperty; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import com.github.instagram4j.instagram4j.IGClient; 17 | import com.github.instagram4j.instagram4j.exceptions.IGLoginException; 18 | import com.github.instagram4j.instagram4j.utils.IGUtils; 19 | import com.google.inject.ProvidedBy; 20 | import com.orientechnologies.orient.core.metadata.schema.OType; 21 | 22 | import okhttp3.OkHttpClient; 23 | import okhttp3.logging.HttpLoggingInterceptor; 24 | 25 | /** 26 | * {@link IChannel} which represents Account in Instagram 27 | */ 28 | @ProvidedBy(ODocumentWrapperProvider.class) 29 | @EntityType(value = IIGAccount.CLASS_NAME, orderOffset = 100) 30 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 31 | public interface IIGAccount extends IChannel { 32 | public static final String CLASS_NAME = "OPIGAccount"; 33 | public static final Logger LOG = LoggerFactory.getLogger(IIGAccount.class); 34 | 35 | @OrientDBProperty(notNull = true) 36 | public String getUsername(); 37 | public void setUsername(String value); 38 | 39 | @OrientDBProperty(notNull = true) 40 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_PASSWORD) 41 | public String getPassword(); 42 | public void setPassword(String value); 43 | 44 | @OrientDBProperty(type = OType.CUSTOM) 45 | @OrienteerOProperty(hidden = true) 46 | public IGClient getIGClient(); 47 | public void setIGClient(IGClient value); 48 | 49 | @OrientDBProperty(type = OType.CUSTOM) 50 | @OrienteerOProperty(hidden = true) 51 | public SerializableCookieJar getCookieJar(); 52 | public void setCookieJar(SerializableCookieJar value); 53 | 54 | 55 | public default IGClient obtainIGClient() throws IGLoginException { 56 | IGClient igClient = getIGClient(); 57 | SerializableCookieJar jar = getCookieJar(); 58 | if(igClient==null || jar==null || !igClient.isLoggedIn()) { 59 | jar = new SerializableCookieJar(); 60 | OkHttpClient okHttpClient = createOkHttpClient(jar); 61 | igClient = IGClient.builder() 62 | .client(okHttpClient) 63 | .username(getUsername()) 64 | .password(getPassword()) 65 | .login(); 66 | setCookieJar(jar); 67 | setIGClient(igClient); 68 | DAO.save(this); 69 | } else { 70 | OkHttpClient okHttpClient = createOkHttpClient(jar); 71 | Reflect.on(igClient).set("httpClient", okHttpClient); 72 | } 73 | return igClient; 74 | } 75 | 76 | public static OkHttpClient createOkHttpClient(SerializableCookieJar jar) { 77 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor((m) -> LOG.info(m)); 78 | return new OkHttpClient.Builder() 79 | .cookieJar(jar) 80 | .addNetworkInterceptor(logging) 81 | .build(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/instagram/IIGApp.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.instagram; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.CompletableFuture; 5 | import java.util.stream.Collectors; 6 | 7 | import org.orienteer.core.OClassDomain; 8 | import org.orienteer.core.dao.ODocumentWrapperProvider; 9 | import org.orienteer.core.dao.OrienteerOClass; 10 | import org.orienteer.oposter.model.IChannel; 11 | import org.orienteer.oposter.model.IContent; 12 | import org.orienteer.oposter.model.IImageAttachment; 13 | import org.orienteer.oposter.model.IPlatformApp; 14 | import org.orienteer.oposter.model.IPosting; 15 | import org.orienteer.oposter.vk.IVkApp; 16 | import org.orienteer.transponder.annotation.EntityType; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | import com.github.instagram4j.instagram4j.IGClient; 21 | import com.github.instagram4j.instagram4j.actions.timeline.TimelineAction.SidecarInfo; 22 | import com.github.instagram4j.instagram4j.actions.timeline.TimelineAction.SidecarPhoto; 23 | import com.github.instagram4j.instagram4j.exceptions.IGLoginException; 24 | import com.github.instagram4j.instagram4j.responses.media.MediaResponse.MediaConfigureTimelineResponse; 25 | import com.github.instagram4j.instagram4j.utils.IGUtils; 26 | import com.google.inject.ProvidedBy; 27 | 28 | /** 29 | * {@link IPlatformApp} for Instagram: no configuration - can be single for all IG Accounts 30 | */ 31 | @ProvidedBy(ODocumentWrapperProvider.class) 32 | @EntityType(value = IIGApp.CLASS_NAME, orderOffset = 100) 33 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 34 | public interface IIGApp extends IPlatformApp{ 35 | public static final String CLASS_NAME = "OPIGApp"; 36 | public static final Logger LOG = LoggerFactory.getLogger(IIGApp.class); 37 | 38 | @Override 39 | public default IPosting send(IChannel channel, IContent content) throws Exception { 40 | IIGAccount igAccount = checkChannelType(channel, IIGAccount.class); 41 | if(!content.hasImages()) throw new IllegalStateException("Instagram require at least one photo"); 42 | else { 43 | IGClient igClient = igAccount.obtainIGClient(); 44 | List images = content.getImages(); 45 | CompletableFuture future; 46 | if(images.size()==1) { 47 | future = igClient.actions().timeline().uploadPhoto(images.get(0).getData(), content.getContent()); 48 | } else { 49 | List photos = images.stream().map(a -> new SidecarPhoto(a.getData())).collect(Collectors.toList()); 50 | future = igClient.actions().timeline().uploadAlbum(photos, content.getContent()); 51 | } 52 | 53 | MediaConfigureTimelineResponse response = future.join(); 54 | String code = response.getMedia().getCode(); 55 | return IPosting.createFor(channel, content) 56 | .setExternalPostingId(code) 57 | .setUrl("https://www.instagram.com/p/%s/", code) 58 | .setMessage(response.getMessage()); 59 | } 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/instagram/SerializableCookieJar.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.instagram; 2 | 3 | import java.io.IOException; 4 | import java.io.ObjectInputStream; 5 | import java.io.ObjectOutputStream; 6 | import java.io.Serializable; 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.stream.Collectors; 12 | 13 | import lombok.AllArgsConstructor; 14 | import okhttp3.Cookie; 15 | import okhttp3.CookieJar; 16 | import okhttp3.HttpUrl; 17 | 18 | /** 19 | * OkHttp {@link CookieJar} which can be serialized and stored for subsequent reuse 20 | */ 21 | public class SerializableCookieJar implements CookieJar, Serializable { 22 | 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | private Map> map = new HashMap<>(); 27 | 28 | @Override 29 | public List loadForRequest(HttpUrl arg0) { 30 | return map.getOrDefault(arg0.host(), new ArrayList()).stream() 31 | .map(c -> c.cookie) 32 | .collect(Collectors.toList()); 33 | } 34 | 35 | @Override 36 | public void saveFromResponse(HttpUrl arg0, List arg1) { 37 | List list = 38 | arg1.stream().map(c -> new SerializableCookie(c)).collect(Collectors.toList()); 39 | if (map.putIfAbsent(arg0.host(), list) != null) { 40 | map.get(arg0.host()).addAll(list); 41 | } 42 | } 43 | 44 | /** 45 | * Serializable representation of a cookie 46 | */ 47 | @AllArgsConstructor 48 | public static class SerializableCookie implements Serializable { 49 | 50 | private static final long serialVersionUID = -8594045714036645534L; 51 | 52 | private transient Cookie cookie; 53 | 54 | private static final long NON_VALID_EXPIRES_AT = -1L; 55 | 56 | private void writeObject(ObjectOutputStream out) throws IOException { 57 | out.writeObject(cookie.name()); 58 | out.writeObject(cookie.value()); 59 | out.writeLong(cookie.persistent() ? cookie.expiresAt() : NON_VALID_EXPIRES_AT); 60 | out.writeObject(cookie.domain()); 61 | out.writeObject(cookie.path()); 62 | out.writeBoolean(cookie.secure()); 63 | out.writeBoolean(cookie.httpOnly()); 64 | out.writeBoolean(cookie.hostOnly()); 65 | } 66 | 67 | private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 68 | Cookie.Builder builder = new Cookie.Builder(); 69 | 70 | builder.name((String) in.readObject()); 71 | 72 | builder.value((String) in.readObject()); 73 | 74 | long expiresAt = in.readLong(); 75 | if (expiresAt != NON_VALID_EXPIRES_AT) { 76 | builder.expiresAt(expiresAt); 77 | } 78 | 79 | final String domain = (String) in.readObject(); 80 | builder.domain(domain); 81 | 82 | builder.path((String) in.readObject()); 83 | 84 | if (in.readBoolean()) 85 | builder.secure(); 86 | 87 | if (in.readBoolean()) 88 | builder.httpOnly(); 89 | 90 | if (in.readBoolean()) 91 | builder.hostOnlyDomain(domain); 92 | 93 | cookie = builder.build(); 94 | } 95 | 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/instagram/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package for Instagram support classes 3 | */ 4 | package org.orienteer.oposter.instagram; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/model/IChannel.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.model; 2 | 3 | import java.util.List; 4 | 5 | import org.apache.wicket.feedback.FeedbackMessage; 6 | import org.apache.wicket.model.Model; 7 | import org.orienteer.core.OClassDomain; 8 | import org.orienteer.core.component.BootstrapType; 9 | import org.orienteer.core.component.FAIconType; 10 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 11 | import org.orienteer.core.dao.DAO; 12 | import org.orienteer.core.dao.ODocumentWrapperProvider; 13 | import org.orienteer.core.dao.OrienteerOClass; 14 | import org.orienteer.core.dao.OrienteerOProperty; 15 | import org.orienteer.core.method.IMethodContext; 16 | import org.orienteer.core.method.OFilter; 17 | import org.orienteer.core.method.OMethod; 18 | import org.orienteer.core.method.filters.PlaceFilter; 19 | import org.orienteer.core.method.filters.WidgetTypeFilter; 20 | import org.orienteer.logger.OLogger; 21 | import org.orienteer.transponder.annotation.EntityProperty; 22 | import org.orienteer.transponder.annotation.EntityType; 23 | import org.orienteer.transponder.orientdb.OrientDBProperty; 24 | 25 | import com.google.common.base.Throwables; 26 | import com.google.inject.ProvidedBy; 27 | 28 | /** 29 | * Channel to send content to 30 | */ 31 | @ProvidedBy(ODocumentWrapperProvider.class) 32 | @EntityType(value = IChannel.CLASS_NAME, isAbstract = true) 33 | @OrienteerOClass(domain = OClassDomain.BUSINESS, 34 | parentProperty = "platformApp", 35 | displayable = {"name", "url", "description", "platformApp"}) 36 | public interface IChannel { 37 | public static final String CLASS_NAME = "OPChannel"; 38 | 39 | public String getName(); 40 | public void setName(String name); 41 | 42 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_URL_LINK) 43 | public String getUrl(); 44 | public void setUrl(String value); 45 | 46 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TEXTAREA) 47 | public String getDescription(); 48 | public void setDescription(String value); 49 | 50 | @EntityProperty(inverse = "channels") 51 | @OrientDBProperty(notNull = true) 52 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_LISTBOX) 53 | public IPlatformApp getPlatformApp(); 54 | public void setPlatformApp(IPlatformApp value); 55 | 56 | @EntityProperty(inverse = "channels") 57 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TABLE) 58 | public List getContent(); 59 | public void setContent(List value); 60 | 61 | @EntityProperty(inverse = "channel") 62 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TABLE) 63 | public List getPostings(); 64 | public void setPostings(List value); 65 | 66 | @OMethod( 67 | titleKey = "channel.testsend", 68 | order=10,bootstrap=BootstrapType.SUCCESS,icon = FAIconType.play, 69 | filters={ 70 | @OFilter(fClass = PlaceFilter.class, fData = "STRUCTURE_TABLE"), 71 | @OFilter(fClass = WidgetTypeFilter.class, fData = "parameters"), 72 | } 73 | ) 74 | public default void testSend(IMethodContext ctx) { 75 | IPlatformApp platformApp = getPlatformApp(); 76 | if(platformApp==null) ctx.showFeedback(FeedbackMessage.ERROR, "channel.error.noplatformapp", null); 77 | else { 78 | try { 79 | IContent content = DAO.create(IContent.class); 80 | content.setTitle("Test Content for channel "+getName()); 81 | content.setContent("This is test content for channel "+getName()); 82 | platformApp.send(this, content); 83 | ctx.showFeedback(FeedbackMessage.INFO, "channel.info.testwassent", null); 84 | } catch (Exception e) { 85 | Throwable rootCause = Throwables.getRootCause(e); 86 | ctx.showFeedback(FeedbackMessage.ERROR, "content.error.cantsend", Model.of(rootCause.getMessage())); 87 | OLogger.log(rootCause, DAO.asDocument(this).getIdentity().toString()); 88 | } 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/model/IContent.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.model; 2 | 3 | import java.util.Collections; 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | import org.apache.wicket.feedback.FeedbackMessage; 8 | import org.apache.wicket.model.Model; 9 | import org.apache.wicket.util.string.Strings; 10 | import org.orienteer.core.component.BootstrapType; 11 | import org.orienteer.core.component.FAIconType; 12 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 13 | import org.orienteer.core.dao.ODocumentWrapperProvider; 14 | import org.orienteer.core.dao.OrienteerOClass; 15 | import org.orienteer.core.dao.OrienteerOProperty; 16 | import org.orienteer.core.method.IMethodContext; 17 | import org.orienteer.core.method.OFilter; 18 | import org.orienteer.core.method.OMethod; 19 | import org.orienteer.core.method.filters.PlaceFilter; 20 | import org.orienteer.core.method.filters.WidgetTypeFilter; 21 | import org.orienteer.oposter.component.attachment.AttachmentsVisualizer; 22 | import org.orienteer.transponder.annotation.EntityProperty; 23 | import org.orienteer.transponder.annotation.EntityType; 24 | import org.orienteer.transponder.orientdb.OrientDBProperty; 25 | 26 | import com.google.common.base.Splitter; 27 | import com.google.common.base.Throwables; 28 | import com.google.inject.ProvidedBy; 29 | import com.orientechnologies.orient.core.metadata.schema.OType; 30 | 31 | /** 32 | * Content to be distributed to channels 33 | */ 34 | @ProvidedBy(ODocumentWrapperProvider.class) 35 | @EntityType(value = IContent.CLASS_NAME) 36 | @OrienteerOClass(parentProperty = "contentPlan", 37 | displayable = {"title", "when", "published", "created", "contentPlan", "channels"}) 38 | public interface IContent { 39 | public static final String CLASS_NAME = "OPContent"; 40 | 41 | public String getTitle(); 42 | public void setTitle(String title); 43 | 44 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TEXTAREA) 45 | public String getContent(); 46 | public void setContent(String value); 47 | 48 | @OrientDBProperty(type = OType.DATETIME) 49 | public Date getWhen(); 50 | public void setWhen(Date value); 51 | 52 | @OrientDBProperty(defaultValue = "false") 53 | public boolean isPublished(); 54 | public void setPublished(boolean published); 55 | 56 | @OrientDBProperty(type = OType.DATETIME, defaultValue = "sysdate()", readOnly = true) 57 | public Date getCreated(); 58 | public void setCreated(Date value); 59 | 60 | 61 | public default void published() { 62 | setPublished(true); 63 | } 64 | 65 | @EntityProperty(inverse = "content") 66 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_SUGGEST) 67 | public List getChannels(); 68 | public void setChannels(List value); 69 | 70 | @EntityProperty(inverse = "content") 71 | public IContentPlan getContentPlan(); 72 | public void setContentPlan(IContentPlan value); 73 | 74 | @EntityProperty(inverse = "content") 75 | @OrienteerOProperty(visualization = AttachmentsVisualizer.NAME) 76 | public List getImages(); 77 | public void setImages(List value); 78 | 79 | @EntityProperty(inverse = "content") 80 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TABLE) 81 | public List getPostings(); 82 | public void setPostings(List value); 83 | 84 | 85 | public default boolean hasImages() { 86 | List images = getImages(); 87 | return images!=null && !images.isEmpty(); 88 | } 89 | 90 | @OMethod( 91 | titleKey = "content.sendnow", 92 | order=10,bootstrap=BootstrapType.SUCCESS,icon = FAIconType.play, 93 | filters={ 94 | @OFilter(fClass = PlaceFilter.class, fData = "STRUCTURE_TABLE"), 95 | @OFilter(fClass = WidgetTypeFilter.class, fData = "parameters"), 96 | } 97 | ) 98 | public default void sendNow(IMethodContext ctx) { 99 | List channels = getChannels(); 100 | if(channels==null || channels.isEmpty()) { 101 | ctx.showFeedback(FeedbackMessage.WARNING, "content.warning.nochannels", null); 102 | } else { 103 | int errors = 0; 104 | for (IChannel iChannel : channels) { 105 | IPosting posting = iChannel.getPlatformApp().sendSafe(iChannel, this); 106 | if(!posting.isSuccessful()) { 107 | errors++; 108 | ctx.showFeedback(FeedbackMessage.ERROR, 109 | "content.error.cantsend", 110 | Model.of(posting.getMessageSummary())); 111 | } 112 | } 113 | if(errors==0) ctx.showFeedback(FeedbackMessage.INFO, "content.info.wassent", null); 114 | } 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/model/IContentPlan.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.model; 2 | 3 | import java.util.List; 4 | 5 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 6 | import org.orienteer.core.dao.ODocumentWrapperProvider; 7 | import org.orienteer.core.dao.OrienteerOClass; 8 | import org.orienteer.core.dao.OrienteerOProperty; 9 | import org.orienteer.transponder.annotation.EntityProperty; 10 | import org.orienteer.transponder.annotation.EntityType; 11 | 12 | import com.google.inject.ProvidedBy; 13 | 14 | /** 15 | * Content Plan - way to organize content 16 | */ 17 | @ProvidedBy(ODocumentWrapperProvider.class) 18 | @EntityType(IContentPlan.CLASS_NAME) 19 | @OrienteerOClass(displayable = {"name", "description"}) 20 | public interface IContentPlan { 21 | public static final String CLASS_NAME = "OPContentPlan"; 22 | 23 | public String getName(); 24 | public void setName(String name); 25 | 26 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TEXTAREA) 27 | public String getDescription(); 28 | public void setDescription(String value); 29 | 30 | @EntityProperty(inverse = "contentPlan") 31 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TABLE) 32 | public List getContent(); 33 | public void setContent(List content); 34 | } 35 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/model/IImageAttachment.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.model; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.file.Files; 6 | 7 | import org.apache.tika.Tika; 8 | import org.apache.wicket.util.string.Strings; 9 | import org.orienteer.core.dao.DAO; 10 | import org.orienteer.core.dao.ODocumentWrapperProvider; 11 | import org.orienteer.core.dao.OrienteerOProperty; 12 | import org.orienteer.transponder.annotation.EntityProperty; 13 | import org.orienteer.transponder.annotation.EntityType; 14 | 15 | import com.google.common.io.FileBackedOutputStream; 16 | import com.google.inject.ProvidedBy; 17 | 18 | /** 19 | * OClass for image attachments to a content 20 | */ 21 | @ProvidedBy(ODocumentWrapperProvider.class) 22 | @EntityType(IImageAttachment.CLASS_NAME) 23 | public interface IImageAttachment { 24 | public static final String CLASS_NAME = "OPImageAttachment"; 25 | 26 | public String getName(); 27 | public void setName(String value); 28 | 29 | public byte[] getData(); 30 | public void setData(byte[] value); 31 | 32 | public String getContentType(); 33 | public void setContentType(String value); 34 | 35 | public Integer getOrder(); 36 | public void setOrder(Integer value); 37 | 38 | @EntityProperty(inverse = "images") 39 | public IContent getContent(); 40 | public void setContent(IContent value); 41 | 42 | @OrienteerOProperty(hidden = true) 43 | public String getTempFilePath(); 44 | public void setTempFilePath(String value); 45 | 46 | 47 | public static IImageAttachment create(IContent content, String name, byte[] data, int order, String contentType) { 48 | IImageAttachment ret = DAO.create(IImageAttachment.class); 49 | ret.setContent(content); 50 | ret.setName(name); 51 | ret.setData(data); 52 | ret.setOrder(order); 53 | ret.setContentType(contentType); 54 | return ret; 55 | } 56 | 57 | public default String detectContentType() { 58 | String contentType = getContentType(); 59 | if(Strings.isEmpty(contentType)) { 60 | byte[] data = getData(); 61 | if(data!=null) contentType = new Tika().detect(getData()); 62 | } 63 | return contentType; 64 | } 65 | 66 | public default File asFile() throws IOException { 67 | String path = getTempFilePath(); 68 | File file= path!=null?new File(path):null; 69 | if(file!=null && file.exists()) return file; 70 | file = Files.createTempDirectory("oposter").resolve(getName()).toFile(); 71 | Files.write(file.toPath(), getData()); 72 | setTempFilePath(file.getAbsolutePath()); 73 | if(DAO.asDocument(this).getIdentity().isPersistent()) DAO.save(this); 74 | file.deleteOnExit(); 75 | return file; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/model/IOAuthReciever.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.model; 2 | 3 | import static org.orienteer.core.util.CommonUtils.localize; 4 | 5 | import org.apache.wicket.request.http.WebRequest; 6 | import org.apache.wicket.util.string.StringValue; 7 | import org.orienteer.core.web.ODocumentPage; 8 | 9 | /** 10 | * Interface (non-DAO) for classes which can receive code as part of OAuth 11 | */ 12 | public interface IOAuthReciever { 13 | public default void callback(WebRequest request, ODocumentPage targetPage) throws Exception { 14 | StringValue codeSV = request.getRequestParameters().getParameterValue("code"); 15 | if(codeSV.isEmpty()) { 16 | targetPage.error(localize("error.oauthcallback.nocode")); 17 | } 18 | codeObtained(codeSV.toString()); 19 | } 20 | 21 | public default void codeObtained(String code) throws Exception { 22 | throw new IllegalStateException("Either 'codeObtained' or 'callback' should be overrided"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/model/IOPosterDAO.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.model; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | import org.orienteer.core.dao.DAO; 7 | import org.orienteer.core.dao.DAOProvider; 8 | import org.orienteer.transponder.annotation.Query; 9 | 10 | import com.google.inject.ProvidedBy; 11 | import com.orientechnologies.orient.core.db.record.OIdentifiable; 12 | import com.orientechnologies.orient.core.record.impl.ODocument; 13 | 14 | /** 15 | * DAO for OPoster 16 | */ 17 | @ProvidedBy(DAOProvider.class) 18 | public interface IOPosterDAO { 19 | 20 | @Query("select from "+IContent.CLASS_NAME+" where published!=true and when < sysdate()") 21 | public List findContentToSend(); 22 | 23 | @Query("select from "+IContent.CLASS_NAME+" where when between :start and :end") 24 | public List findContent(Date start, Date end); 25 | 26 | @Query("select from "+IContent.CLASS_NAME+" where contentPlan = :contentPlan and when between :start and :end") 27 | public List findContentByContentPlan(OIdentifiable contentPlan, Date start, Date end); 28 | 29 | public default List findContentByContentPlan(IContentPlan contentPlan, Date start, Date end) { 30 | return findContentByContentPlan(DAO.asDocument(contentPlan), start, end); 31 | } 32 | 33 | @Query("select from "+IContent.CLASS_NAME+" where channels contains :channel and when between :start and :end") 34 | public List findContentByChannel(OIdentifiable channel, Date start, Date end); 35 | 36 | public default List findContentByChannel(IChannel channel, Date start, Date end) { 37 | return findContentByChannel(DAO.asDocument(channel), start, end); 38 | } 39 | 40 | @Query("select from "+IContent.CLASS_NAME+" where channels.platformApp contains :app and when between :start and :end") 41 | public List findContentByPlatformApp(OIdentifiable app, Date start, Date end); 42 | 43 | public default List findContentByPlatformApp(IPlatformApp app, Date start, Date end) { 44 | return findContentByPlatformApp(DAO.asDocument(app), start, end); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/model/IPlatformApp.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.model; 2 | 3 | import java.util.List; 4 | 5 | import org.apache.commons.lang.exception.ExceptionUtils; 6 | import org.apache.wicket.util.lang.Exceptions; 7 | import org.orienteer.core.OClassDomain; 8 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 9 | import org.orienteer.core.dao.DAO; 10 | import org.orienteer.core.dao.ODocumentWrapperProvider; 11 | import org.orienteer.core.dao.OrienteerOClass; 12 | import org.orienteer.core.dao.OrienteerOProperty; 13 | import org.orienteer.logger.OLogger; 14 | import org.orienteer.transponder.annotation.EntityProperty; 15 | import org.orienteer.transponder.annotation.EntityType; 16 | 17 | import com.google.inject.ProvidedBy; 18 | import com.orientechnologies.orient.core.record.impl.ODocument; 19 | 20 | /** 21 | * Class which holds configuration about connectivity to some social media 22 | */ 23 | @ProvidedBy(ODocumentWrapperProvider.class) 24 | @EntityType(value = IPlatformApp.CLASS_NAME, isAbstract = true) 25 | @OrienteerOClass(domain = OClassDomain.BUSINESS, 26 | displayable = {"name", "description"}) 27 | public interface IPlatformApp { 28 | public static final String CLASS_NAME = "OPPlatformApp"; 29 | 30 | public String getName(); 31 | public void setName(String name); 32 | 33 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TEXTAREA) 34 | public String getDescription(); 35 | public void setDescription(String value); 36 | 37 | @EntityProperty(inverse = "platformApp") 38 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TABLE) 39 | public List getChannels(); 40 | public void setChannels(List value); 41 | 42 | public default IPosting send(IChannel channel, IContent content) throws Exception { 43 | throw new IllegalStateException("Class "+this.getClass().getName()+" should override send(...) method"); 44 | } 45 | 46 | public default IPosting sendSafe(IChannel channel, IContent content) { 47 | IPosting ret; 48 | try { 49 | ret = send(channel, content); 50 | } catch (Throwable e) { 51 | OLogger.log(e, DAO.asDocument(channel).getIdentity().toString()); 52 | ret = IPosting.createFor(channel, content); 53 | ret.setSuccessful(false); 54 | ret.setMessage(ExceptionUtils.getStackTrace(e)); 55 | } 56 | DAO.save(ret); 57 | return ret; 58 | } 59 | 60 | public default String getMetadataKey() { 61 | ODocument doc = DAO.asDocument(this); 62 | return doc.getSchemaClass().getName()+doc.getIdentity()+doc.getVersion(); 63 | } 64 | 65 | public default M checkChannelType(IChannel channel, Class requiredClass){ 66 | if(requiredClass.isInstance(channel)) return (M) channel; 67 | else throw new IllegalStateException("Incorrect channel class. Expected: "+requiredClass.getName()); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/model/IPosting.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.model; 2 | 3 | import java.util.Date; 4 | 5 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 6 | import org.orienteer.core.dao.DAO; 7 | import org.orienteer.core.dao.ODocumentWrapperProvider; 8 | import org.orienteer.core.dao.OrienteerOClass; 9 | import org.orienteer.core.dao.OrienteerOProperty; 10 | import org.orienteer.oposter.OPUtils; 11 | import org.orienteer.transponder.annotation.EntityProperty; 12 | import org.orienteer.transponder.annotation.EntityType; 13 | import org.orienteer.transponder.orientdb.OrientDBProperty; 14 | 15 | import com.google.inject.ProvidedBy; 16 | import com.orientechnologies.orient.core.metadata.schema.OType; 17 | 18 | /** 19 | * {@link IPosting} is an DAO class for storing of facts of postings to a social networks 20 | */ 21 | @ProvidedBy(ODocumentWrapperProvider.class) 22 | @EntityType(IPosting.CLASS_NAME) 23 | @OrienteerOClass(displayable = {"content", "channel", "posted", "successful", "url"}, 24 | nameProperty = "content", 25 | parentProperty = "channel") 26 | public interface IPosting { 27 | public static final String CLASS_NAME = "OPPosting"; 28 | 29 | @EntityProperty(inverse = "postings") 30 | public IContent getContent(); 31 | public IPosting setContent(IContent value); 32 | 33 | @EntityProperty(inverse = "postings") 34 | public IChannel getChannel(); 35 | public IPosting setChannel(IChannel value); 36 | 37 | @OrientDBProperty(type = OType.DATETIME, defaultValue = "sysdate()", readOnly = true) 38 | public Date getPosted(); 39 | public IPosting setPosted(Date value); 40 | 41 | @OrientDBProperty(notNull = true, defaultValue = "true") 42 | public boolean isSuccessful(); 43 | public IPosting setSuccessful(boolean value); 44 | 45 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_URL_LINK) 46 | public String getUrl(); 47 | public IPosting setUrl(String value); 48 | 49 | public default IPosting setUrl(String format, Object... args) { 50 | return setUrl(String.format(format, args)); 51 | } 52 | 53 | public String getExternalPostingId(); 54 | public IPosting setExternalPostingId(String value); 55 | 56 | public default IPosting setExternalPostingId(Object value) { 57 | if(value!=null) setExternalPostingId(value.toString()); 58 | return this; 59 | } 60 | 61 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_TEXTAREA) 62 | public String getMessage(); 63 | public IPosting setMessage(String value); 64 | 65 | public default String getMessageSummary() { 66 | return OPUtils.firstLine(getMessage()); 67 | } 68 | 69 | public static IPosting createFor(IChannel channel, IContent content) { 70 | IPosting posting = DAO.create(IPosting.class); 71 | posting.setChannel(channel); 72 | posting.setContent(content); 73 | return posting; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/model/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package contains Data Model 3 | */ 4 | package org.orienteer.oposter.model; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/ok/FixedOdnoklassnikiApi.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.ok; 2 | 3 | import com.github.scribejava.apis.OdnoklassnikiApi; 4 | import com.github.scribejava.apis.odnoklassniki.OdnoklassnikiOAuthService; 5 | import com.github.scribejava.core.httpclient.HttpClient; 6 | import com.github.scribejava.core.httpclient.HttpClientConfig; 7 | 8 | /** 9 | * Temporal solution while https://github.com/scribejava/scribejava/pull/998 is awaiting to be merged 10 | */ 11 | public class FixedOdnoklassnikiApi extends OdnoklassnikiApi { 12 | 13 | private static class InstanceHolder { 14 | private static final FixedOdnoklassnikiApi INSTANCE = new FixedOdnoklassnikiApi(); 15 | } 16 | 17 | public static FixedOdnoklassnikiApi instance() { 18 | return InstanceHolder.INSTANCE; 19 | } 20 | 21 | @Override 22 | public OdnoklassnikiOAuthService createService(String apiKey, String apiSecret, String callback, 23 | String defaultScope, String responseType, String userAgent, HttpClientConfig httpClientConfig, 24 | HttpClient httpClient) { 25 | return new FixedOdnoklassnikiOAuthService(this, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, 26 | httpClientConfig, httpClient); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/ok/FixedOdnoklassnikiOAuthService.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.ok; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLDecoder; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | import com.github.scribejava.apis.odnoklassniki.OdnoklassnikiOAuthService; 9 | import com.github.scribejava.core.builder.api.DefaultApi20; 10 | import com.github.scribejava.core.httpclient.HttpClient; 11 | import com.github.scribejava.core.httpclient.HttpClientConfig; 12 | import com.github.scribejava.core.model.OAuthRequest; 13 | import com.github.scribejava.core.model.Parameter; 14 | import com.github.scribejava.core.model.ParameterList; 15 | 16 | /** 17 | * Temporal solution while https://github.com/scribejava/scribejava/pull/998 is awaiting to be merged 18 | */ 19 | public class FixedOdnoklassnikiOAuthService extends OdnoklassnikiOAuthService { 20 | 21 | public FixedOdnoklassnikiOAuthService(DefaultApi20 api, String apiKey, String apiSecret, String callback, 22 | String defaultScope, String responseType, String userAgent, HttpClientConfig httpClientConfig, 23 | HttpClient httpClient) { 24 | super(api, apiKey, apiSecret, callback, defaultScope, responseType, userAgent, httpClientConfig, httpClient); 25 | } 26 | 27 | @Override 28 | public void signRequest(String accessToken, OAuthRequest request) { 29 | //sig = lower(md5( sorted_request_params_composed_string + md5(access_token + application_secret_key))) 30 | final String tokenDigest = md5(accessToken + getApiSecret()); 31 | 32 | final ParameterList queryParams = request.getQueryStringParams(); 33 | queryParams.addAll(request.getBodyParams()); 34 | final List allParams = queryParams.getParams(); 35 | 36 | Collections.sort(allParams); 37 | 38 | final StringBuilder stringParams = new StringBuilder(); 39 | for (Parameter param : allParams) { 40 | stringParams.append(param.getKey()) 41 | .append('=') 42 | .append(param.getValue()); 43 | } 44 | 45 | final String sigSource = stringParams + tokenDigest; 46 | request.addQuerystringParameter("sig", md5(sigSource).toLowerCase()); 47 | 48 | super.signRequest(accessToken, request); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/ok/IOkApp.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.ok; 2 | 3 | import static org.orienteer.core.util.CommonUtils.toMap; 4 | 5 | import java.io.IOException; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.stream.Collectors; 10 | 11 | import org.apache.logging.log4j.util.Strings; 12 | import org.orienteer.core.OClassDomain; 13 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 14 | import org.orienteer.core.dao.DAO; 15 | import org.orienteer.core.dao.ODocumentWrapperProvider; 16 | import org.orienteer.core.dao.OrienteerOClass; 17 | import org.orienteer.core.dao.OrienteerOProperty; 18 | import org.orienteer.logger.OLogger; 19 | import org.orienteer.oposter.OPUtils; 20 | import org.orienteer.oposter.model.IChannel; 21 | import org.orienteer.oposter.model.IContent; 22 | import org.orienteer.oposter.model.IImageAttachment; 23 | import org.orienteer.oposter.model.IOAuthReciever; 24 | import org.orienteer.oposter.model.IPlatformApp; 25 | import org.orienteer.oposter.model.IPosting; 26 | import org.orienteer.oposter.web.OAuthCallbackResource; 27 | import org.orienteer.transponder.annotation.EntityType; 28 | import org.orienteer.transponder.orientdb.OrientDBProperty; 29 | import org.slf4j.Logger; 30 | import org.slf4j.LoggerFactory; 31 | 32 | import com.fasterxml.jackson.databind.JsonNode; 33 | import com.fasterxml.jackson.databind.ObjectMapper; 34 | import com.fasterxml.jackson.databind.node.ArrayNode; 35 | import com.fasterxml.jackson.databind.node.ObjectNode; 36 | import com.fasterxml.jackson.databind.node.TextNode; 37 | import com.github.scribejava.core.builder.ServiceBuilder; 38 | import com.github.scribejava.core.httpclient.multipart.FileByteArrayBodyPartPayload; 39 | import com.github.scribejava.core.model.OAuthRequest; 40 | import com.github.scribejava.core.model.Response; 41 | import com.github.scribejava.core.model.Verb; 42 | import com.github.scribejava.core.oauth.OAuth20Service; 43 | import com.google.inject.ProvidedBy; 44 | import com.orientechnologies.orient.core.record.impl.ODocument; 45 | 46 | /** 47 | * {@link IPlatformApp} for Odnoklassniki 48 | */ 49 | @ProvidedBy(ODocumentWrapperProvider.class) 50 | @EntityType(value = IOkApp.CLASS_NAME, orderOffset = 100) 51 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 52 | public interface IOkApp extends IPlatformApp { 53 | public static final Logger LOG = LoggerFactory.getLogger(IOkApp.class); 54 | public static final String CLASS_NAME = "OPOkApp"; 55 | 56 | @OrientDBProperty(notNull = true) 57 | public Long getAppId(); 58 | public void setAppId(Long value); 59 | 60 | @OrientDBProperty(notNull = true) 61 | public String getPublicKey(); 62 | public void setPublicKey(String value); 63 | 64 | @OrientDBProperty(notNull = true) 65 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_PASSWORD) 66 | public String getSecretKey(); 67 | public void setSecretKey(String value); 68 | 69 | @Override 70 | public default IPosting send(IChannel channel, IContent content) throws Exception { 71 | IOkChannel account = checkChannelType(channel, IOkChannel.class); 72 | try(OAuth20Service service = getService(null)) { 73 | String groupId = account.getGroupId(); 74 | boolean toGroup = !Strings.isEmpty(groupId); 75 | ObjectMapper om = OPUtils.getObjectMapper(); 76 | 77 | ObjectNode attachment = om.createObjectNode(); 78 | ObjectNode text = om.createObjectNode(); 79 | text.put("type", "text"); 80 | text.put("text", content.getContent()); 81 | ArrayNode media = om.createArrayNode(); 82 | media.add(text); 83 | if(content.hasImages()) { 84 | List images = content.getImages(); 85 | // 1. Request Upload URL for images 86 | Map params = new HashMap<>(); 87 | params.put("count", images.size()); 88 | params.put("sizes", images.stream().map(i ->i.getData()==null?"0":Integer.toString(i.getData().length)) 89 | .collect(Collectors.joining(","))); 90 | if(toGroup) params.put("gid", groupId); 91 | JsonNode uploadUrlResult = invokeOKMethod(service, account.getAccessToken(), Verb.GET, "photosV2.getUploadUrl", params, null); 92 | LOG.info("UploadUrl Request results: "+uploadUrlResult); 93 | // 2. Upload images 94 | final OAuthRequest uploadRequest = new OAuthRequest(Verb.POST, uploadUrlResult.get("upload_url").asText()); 95 | uploadRequest.initMultipartPayload(); 96 | for (int i=0; i { 110 | String token = e.getValue().get("token").asText(); 111 | photosList.add(om.createObjectNode().put("id", token)); 112 | 113 | }); 114 | photos.set("list", photosList); 115 | media.add(photos); 116 | } 117 | attachment.set("media", media); 118 | 119 | Map params = toGroup?toMap("gid", groupId, "type", "GROUP_THEME"):toMap("type", "USER"); 120 | JsonNode response = invokeOKMethod(service, account.getAccessToken(), Verb.POST, "mediatopic.post", params, toMap("attachment", attachment)); 121 | if(response instanceof TextNode) { 122 | String topicId = ((TextNode)response).asText(); 123 | return IPosting.createFor(channel, content) 124 | .setExternalPostingId(((TextNode)response).asText()) 125 | .setUrl("https://ok.ru/%s/%s/topic/%s", 126 | toGroup?"group":"profile", 127 | toGroup?groupId:account.getUserId(), 128 | topicId); 129 | } else throw new IOException("Unknown content recieved from OK: "+response.toString()); 130 | } 131 | } 132 | public default OAuth20Service getService(IOAuthReciever reciever) { 133 | 134 | ODocument reciverDoc = DAO.asDocument(reciever!=null?reciever:this); 135 | return new ServiceBuilder(getAppId().toString()) 136 | .apiSecret(getSecretKey()) 137 | .defaultScope("PUBLISH_TO_STREAM;VALUABLE_ACCESS;LONG_ACCESS_TOKEN;PHOTO_CONTENT;GROUP_CONTENT") // replace with desired scope 138 | .callback(OAuthCallbackResource.urlFor(reciverDoc)) 139 | .build(FixedOdnoklassnikiApi.instance()); 140 | } 141 | 142 | public default JsonNode invokeOKMethod(OAuth20Service service, String accessToken, Verb verb, String method, Map queryParams, Map bodyParams) throws Exception { 143 | final OAuthRequest request = new OAuthRequest(verb, "https://api.ok.ru/api/"+method.replace('.', '/')); 144 | request.addQuerystringParameter("application_key", getPublicKey()); 145 | 146 | if(queryParams!=null && !queryParams.isEmpty()) { 147 | for (Map.Entry entry : queryParams.entrySet()) { 148 | request.addQuerystringParameter(entry.getKey(), entry.getValue().toString()); 149 | } 150 | } 151 | if(bodyParams!=null && !bodyParams.isEmpty()) { 152 | for (Map.Entry entry : bodyParams.entrySet()) { 153 | if(entry.getValue() instanceof FileByteArrayBodyPartPayload) { 154 | request.addFileByteArrayBodyPartPayloadInMultipartPayload((FileByteArrayBodyPartPayload)entry.getValue()); 155 | } else if(entry.getValue() instanceof IImageAttachment) { 156 | IImageAttachment image = (IImageAttachment)entry.getValue(); 157 | request.addFileByteArrayBodyPartPayloadInMultipartPayload(image.getContentType(), image.getData(), entry.getKey(), image.getName()); 158 | } else { 159 | request.addBodyParameter(entry.getKey(), entry.getValue().toString()); 160 | } 161 | } 162 | } 163 | service.signRequest(accessToken, request); 164 | 165 | Response response = service.execute(request); 166 | if(!response.isSuccessful()) 167 | throw new IOException("Request to "+request.getUrl()+" returned errorcode: "+response.getCode()); 168 | else { 169 | JsonNode ret = OPUtils.toJsonNode(response.getBody()); 170 | if(ret.has("error_code")) throw new IOException("Error response was recieved: "+response.getBody()); 171 | else return ret; 172 | } 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/ok/IOkChannel.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.ok; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.wicket.feedback.FeedbackMessage; 6 | import org.apache.wicket.model.Model; 7 | import org.apache.wicket.request.flow.RedirectToUrlException; 8 | import org.orienteer.core.OClassDomain; 9 | import org.orienteer.core.component.BootstrapType; 10 | import org.orienteer.core.component.FAIconType; 11 | import org.orienteer.core.dao.DAO; 12 | import org.orienteer.core.dao.ODocumentWrapperProvider; 13 | import org.orienteer.core.dao.OrienteerOClass; 14 | import org.orienteer.core.method.IMethodContext; 15 | import org.orienteer.core.method.OFilter; 16 | import org.orienteer.core.method.OMethod; 17 | import org.orienteer.core.method.filters.PlaceFilter; 18 | import org.orienteer.core.method.filters.WidgetTypeFilter; 19 | import org.orienteer.core.util.CommonUtils; 20 | import org.orienteer.logger.OLogger; 21 | import org.orienteer.oposter.model.IChannel; 22 | import org.orienteer.oposter.model.IOAuthReciever; 23 | import org.orienteer.oposter.model.IPlatformApp; 24 | import org.orienteer.transponder.annotation.EntityType; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | import com.fasterxml.jackson.databind.JsonNode; 29 | import com.github.scribejava.core.model.Verb; 30 | import com.github.scribejava.core.oauth.OAuth20Service; 31 | import com.google.inject.ProvidedBy; 32 | 33 | /** 34 | * {@link IChannel} which user, group or page in Odnoklassniki 35 | */ 36 | @ProvidedBy(ODocumentWrapperProvider.class) 37 | @EntityType(value = IOkChannel.CLASS_NAME, orderOffset = 100) 38 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 39 | public interface IOkChannel extends IChannel, IOAuthReciever { 40 | public static final String CLASS_NAME = "OPOkChannel"; 41 | public static final Logger LOG = LoggerFactory.getLogger(IOkChannel.class); 42 | 43 | public String getGroupId(); 44 | public void setGroupId(String value); 45 | 46 | public String getUserId(); 47 | public void setUserId(String value); 48 | 49 | 50 | public String getAccessToken(); 51 | public void setAccessToken(String value); 52 | 53 | @OMethod( 54 | titleKey = "command.connectoauth", 55 | order=10,bootstrap=BootstrapType.SUCCESS,icon = FAIconType.play, 56 | filters={ 57 | @OFilter(fClass = PlaceFilter.class, fData = "STRUCTURE_TABLE"), 58 | @OFilter(fClass = WidgetTypeFilter.class, fData = "parameters"), 59 | } 60 | ) 61 | public default void connectOAuth(IMethodContext ctx) { 62 | IPlatformApp app = getPlatformApp(); 63 | if(app instanceof IOkApp) { 64 | IOkApp okApp = (IOkApp) app; 65 | OAuth20Service service = okApp.getService(this); 66 | String redirectTo = service.getAuthorizationUrl(); 67 | throw new RedirectToUrlException(redirectTo); 68 | } 69 | } 70 | 71 | @Override 72 | public default void codeObtained(String code) throws Exception { 73 | IPlatformApp app = getPlatformApp(); 74 | if(app instanceof IOkApp) { 75 | IOkApp okApp = (IOkApp) app; 76 | try(OAuth20Service service = okApp.getService(this)) { 77 | setAccessToken(service.getAccessToken(code).getAccessToken()); 78 | JsonNode response = okApp.invokeOKMethod(service, getAccessToken(), Verb.GET, "users.getCurrentUser", null, null); 79 | setUserId(response.get("uid").asText()); 80 | DAO.save(this); 81 | } 82 | } 83 | } 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/ok/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package for Odnoklassniki support classes 3 | */ 4 | package org.orienteer.oposter.ok; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Main package for 'oposter' module 3 | */ 4 | package org.orienteer.oposter; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/telegram/ITelegramBot.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.telegram; 2 | 3 | import java.util.Iterator; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | import org.apache.wicket.MetaDataKey; 8 | import org.orienteer.core.OClassDomain; 9 | import org.orienteer.core.OrienteerWebApplication; 10 | import org.orienteer.core.dao.DAO; 11 | import org.orienteer.core.dao.ODocumentWrapperProvider; 12 | import org.orienteer.core.dao.OrienteerOClass; 13 | import org.orienteer.oposter.model.IChannel; 14 | import org.orienteer.oposter.model.IContent; 15 | import org.orienteer.oposter.model.IImageAttachment; 16 | import org.orienteer.oposter.model.IPlatformApp; 17 | import org.orienteer.oposter.model.IPosting; 18 | import org.orienteer.transponder.annotation.EntityProperty; 19 | import org.orienteer.transponder.annotation.EntityType; 20 | import org.orienteer.transponder.orientdb.OrientDBProperty; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import com.google.inject.ProvidedBy; 25 | import com.pengrad.telegrambot.TelegramBot; 26 | import com.pengrad.telegrambot.model.Message; 27 | import com.pengrad.telegrambot.model.request.InputMedia; 28 | import com.pengrad.telegrambot.model.request.InputMediaPhoto; 29 | import com.pengrad.telegrambot.request.BaseRequest; 30 | import com.pengrad.telegrambot.request.SendMediaGroup; 31 | import com.pengrad.telegrambot.request.SendMessage; 32 | import com.pengrad.telegrambot.request.SendPhoto; 33 | import com.pengrad.telegrambot.response.BaseResponse; 34 | import com.pengrad.telegrambot.response.MessagesResponse; 35 | import com.pengrad.telegrambot.response.SendResponse; 36 | 37 | /** 38 | * {@link IPlatformApp} for Telegram 39 | */ 40 | @ProvidedBy(ODocumentWrapperProvider.class) 41 | @EntityType(value = ITelegramBot.CLASS_NAME, orderOffset = 100) 42 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 43 | public interface ITelegramBot extends IPlatformApp { 44 | public static final Logger LOG = LoggerFactory.getLogger(ITelegramBot.class); 45 | public static final String CLASS_NAME = "OPTelegramBot"; 46 | 47 | @OrientDBProperty(notNull = true) 48 | public String getToken(); 49 | public void setToken(String value); 50 | 51 | @Override 52 | public default IPosting send (IChannel channel, IContent content) { 53 | ITelegramChannel tChannel = checkChannelType(channel, ITelegramChannel.class); 54 | TelegramBot bot = getTelegramBot(); 55 | BaseResponse response = bot.execute(prepareRequest(tChannel, content)); 56 | Message publishedMessage = extractMessage(response); 57 | LOG.info("Message to generate link to: "+publishedMessage); 58 | if(publishedMessage!=null) { 59 | return IPosting.createFor(tChannel, content) 60 | .setExternalPostingId(publishedMessage.messageId()) 61 | .setUrl("https://t.me/%s/%s", publishedMessage.chat().title(), publishedMessage.messageId()); 62 | } else throw new IllegalStateException("Unknown response recieved from telegram: "+response); 63 | } 64 | 65 | public default BaseRequest prepareRequest(ITelegramChannel channel, IContent content) { 66 | List images = content.getImages(); 67 | if(images==null || images.isEmpty()) { 68 | return new SendMessage(channel.getTelegramChatId(), content.getContent()); 69 | } else if(images.size()==1) { 70 | return new SendPhoto(channel.getTelegramChatId(), images.get(0).getData()).caption(content.getContent()); 71 | } else { 72 | InputMediaPhoto[] photosToSend = new InputMediaPhoto[images.size()]; 73 | for(int i=0; i< images.size(); i++) { 74 | photosToSend[i] = new InputMediaPhoto(images.get(i).getData()); 75 | } 76 | photosToSend[0].caption(content.getContent()); 77 | return new SendMediaGroup(channel.getTelegramChatId(), photosToSend); 78 | } 79 | } 80 | 81 | public default Message extractMessage(BaseResponse response) { 82 | if(response instanceof SendResponse) { 83 | return ((SendResponse)response).message(); 84 | } else if (response instanceof MessagesResponse) { 85 | return ((MessagesResponse)response).messages()[0]; 86 | } 87 | return null; 88 | } 89 | 90 | public default TelegramBot getTelegramBot() { 91 | String key = getMetadataKey(); 92 | TelegramBot ret = OrienteerWebApplication.lookupApplication().getMetaData(key); 93 | if(ret==null) { 94 | ret = new TelegramBot(getToken()); 95 | OrienteerWebApplication.lookupApplication().setMetaData(key, ret); 96 | } 97 | return ret; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/telegram/ITelegramChannel.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.telegram; 2 | 3 | import org.orienteer.core.OClassDomain; 4 | import org.orienteer.core.dao.ODocumentWrapperProvider; 5 | import org.orienteer.core.dao.OrienteerOClass; 6 | import org.orienteer.oposter.model.IChannel; 7 | import org.orienteer.transponder.annotation.EntityType; 8 | import org.orienteer.transponder.orientdb.OrientDBProperty; 9 | 10 | import com.google.inject.ProvidedBy; 11 | 12 | /** 13 | * {@link IChannel} for Telegram 14 | */ 15 | @ProvidedBy(ODocumentWrapperProvider.class) 16 | @EntityType(value = ITelegramChannel.CLASS_NAME, orderOffset = 100) 17 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 18 | public interface ITelegramChannel extends IChannel { 19 | public static final String CLASS_NAME = "OPTelegramChannel"; 20 | 21 | @OrientDBProperty(notNull = true) 22 | public String getTelegramChatId(); 23 | public void setTelegramChatId(String value); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/telegram/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package for Telegram support classes 3 | */ 4 | package org.orienteer.oposter.telegram; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/twitter/ITwitterAccount.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.twitter; 2 | 3 | import java.io.IOException; 4 | import java.util.concurrent.ExecutionException; 5 | 6 | import org.apache.wicket.MetaDataKey; 7 | import org.apache.wicket.feedback.FeedbackMessage; 8 | import org.apache.wicket.model.Model; 9 | import org.apache.wicket.request.flow.RedirectToUrlException; 10 | import org.apache.wicket.request.http.WebRequest; 11 | import org.orienteer.core.OClassDomain; 12 | import org.orienteer.core.OrienteerWebSession; 13 | import org.orienteer.core.component.BootstrapType; 14 | import org.orienteer.core.component.FAIconType; 15 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 16 | import org.orienteer.core.dao.DAO; 17 | import org.orienteer.core.dao.ODocumentWrapperProvider; 18 | import org.orienteer.core.dao.OrienteerOClass; 19 | import org.orienteer.core.dao.OrienteerOProperty; 20 | import org.orienteer.core.method.IMethodContext; 21 | import org.orienteer.core.method.OFilter; 22 | import org.orienteer.core.method.OMethod; 23 | import org.orienteer.core.method.filters.PlaceFilter; 24 | import org.orienteer.core.method.filters.WidgetTypeFilter; 25 | import org.orienteer.core.util.CommonUtils; 26 | import org.orienteer.core.web.ODocumentPage; 27 | import org.orienteer.logger.OLogger; 28 | import org.orienteer.oposter.model.IChannel; 29 | import org.orienteer.oposter.model.IOAuthReciever; 30 | import org.orienteer.transponder.annotation.EntityType; 31 | 32 | import com.github.scribejava.core.model.OAuth1AccessToken; 33 | import com.github.scribejava.core.model.OAuth1RequestToken; 34 | import com.github.scribejava.core.oauth.OAuth10aService; 35 | import com.google.inject.ProvidedBy; 36 | 37 | /** 38 | * {@link IChannel} which represents twitter account 39 | */ 40 | @ProvidedBy(ODocumentWrapperProvider.class) 41 | @EntityType(value = ITwitterAccount.CLASS_NAME, orderOffset = 100) 42 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 43 | public interface ITwitterAccount extends IChannel, IOAuthReciever { 44 | public static final String CLASS_NAME = "OPTwitterAccount"; 45 | public static final MetaDataKey REQUEST_TOKEN_KEY = new MetaDataKey() {}; 46 | 47 | public String getAccessToken(); 48 | public void setAccessToken(String value); 49 | 50 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_PASSWORD) 51 | public String getAccessTokenSecret(); 52 | public void setAccessTokenSecret(String value); 53 | 54 | @OMethod( 55 | titleKey = "command.connectoauth", 56 | order=10,bootstrap=BootstrapType.SUCCESS,icon = FAIconType.play, 57 | filters={ 58 | @OFilter(fClass = PlaceFilter.class, fData = "STRUCTURE_TABLE"), 59 | @OFilter(fClass = WidgetTypeFilter.class, fData = "parameters"), 60 | } 61 | ) 62 | public default void connectOAuth(IMethodContext ctx) { 63 | if(getPlatformApp() instanceof ITwitterApp) { 64 | ITwitterApp twitterApp = (ITwitterApp) getPlatformApp(); 65 | OAuth10aService service = twitterApp.getOAuthService(this); 66 | OAuth1RequestToken requestToken; 67 | try { 68 | requestToken = service.getRequestToken(); 69 | } catch (Exception e) { 70 | ctx.showFeedback(FeedbackMessage.ERROR, "error.oauthrequest", Model.of(e.getMessage())); 71 | OLogger.log(e, DAO.asDocument(this).getIdentity().toString()); 72 | return; 73 | } 74 | String redirectTo = service.getAuthorizationUrl(requestToken); 75 | OrienteerWebSession.get().setMetaData(REQUEST_TOKEN_KEY, requestToken); 76 | throw new RedirectToUrlException(redirectTo); 77 | 78 | } else { 79 | ctx.showFeedback(FeedbackMessage.ERROR, "error.wrongplatform", Model.of(ITwitterApp.class)); 80 | } 81 | } 82 | @Override 83 | public default void callback(WebRequest request, ODocumentPage targetPage) throws Exception { 84 | if(getPlatformApp() instanceof ITwitterApp) { 85 | ITwitterApp twitterApp = (ITwitterApp) getPlatformApp(); 86 | OAuth10aService service = twitterApp.getOAuthService(this); 87 | OAuth1RequestToken requestToken = OrienteerWebSession.get().getMetaData(REQUEST_TOKEN_KEY); 88 | String oauthVerifier = request.getRequestParameters().getParameterValue("oauth_verifier").toString(); 89 | OAuth1AccessToken token = service.getAccessToken(requestToken, oauthVerifier); 90 | setAccessToken(token.getToken()); 91 | setAccessTokenSecret(token.getTokenSecret()); 92 | DAO.save(this); 93 | } else { 94 | targetPage.error(CommonUtils.localize("error.wrongplatform", "simpleName", ITwitterApp.class.getSimpleName())); 95 | } 96 | } 97 | 98 | 99 | 100 | } 101 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/twitter/ITwitterApp.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.twitter; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import org.apache.wicket.MetaDataKey; 8 | import org.apache.wicket.WicketRuntimeException; 9 | import org.apache.wicket.util.crypt.StringUtils; 10 | import org.orienteer.core.OClassDomain; 11 | import org.orienteer.core.OrienteerWebApplication; 12 | import org.orienteer.core.component.visualizer.UIVisualizersRegistry; 13 | import org.orienteer.core.dao.DAO; 14 | import org.orienteer.core.dao.ODocumentWrapperProvider; 15 | import org.orienteer.core.dao.OrienteerOClass; 16 | import org.orienteer.core.dao.OrienteerOProperty; 17 | import org.orienteer.logger.OLogger; 18 | import org.orienteer.oposter.model.IChannel; 19 | import org.orienteer.oposter.model.IContent; 20 | import org.orienteer.oposter.model.IImageAttachment; 21 | import org.orienteer.oposter.model.IPlatformApp; 22 | import org.orienteer.oposter.model.IPosting; 23 | import org.orienteer.oposter.web.OAuthCallbackResource; 24 | import org.orienteer.transponder.annotation.EntityType; 25 | import org.orienteer.transponder.orientdb.OrientDBProperty; 26 | 27 | import com.github.redouane59.twitter.TwitterClient; 28 | import com.github.redouane59.twitter.dto.tweet.MediaCategory; 29 | import com.github.redouane59.twitter.dto.tweet.Tweet; 30 | import com.github.redouane59.twitter.signature.TwitterCredentials; 31 | import com.github.scribejava.apis.TwitterApi; 32 | import com.github.scribejava.core.builder.ServiceBuilder; 33 | import com.github.scribejava.core.oauth.OAuth10aService; 34 | import com.google.common.base.Joiner; 35 | import com.google.common.base.Strings; 36 | import com.google.inject.ProvidedBy; 37 | 38 | /** 39 | * {@link IPlatformApp} for Twitter. Corresponding application should be created and than 40 | * key and secret key configured on app document 41 | */ 42 | @ProvidedBy(ODocumentWrapperProvider.class) 43 | @EntityType(value = ITwitterApp.CLASS_NAME, orderOffset = 100) 44 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 45 | public interface ITwitterApp extends IPlatformApp { 46 | public static final String CLASS_NAME = "OPTwitterApp"; 47 | 48 | @OrientDBProperty(notNull = true) 49 | public String getApiKey(); 50 | public void setApiKey(String value); 51 | 52 | @OrientDBProperty(notNull = true) 53 | @OrienteerOProperty(visualization = UIVisualizersRegistry.VISUALIZER_PASSWORD) 54 | public String getApiSecretKey(); 55 | public void setApiSecretKey(String value); 56 | 57 | 58 | @Override 59 | public default IPosting send(IChannel channel, IContent content) throws Exception { 60 | ITwitterAccount account = checkChannelType(channel, ITwitterAccount.class); 61 | String accessToken = account.getAccessToken(); 62 | String accessTokenSecret = account.getAccessTokenSecret(); 63 | if(Strings.isNullOrEmpty(accessToken) || Strings.isNullOrEmpty(accessTokenSecret)) 64 | throw new WicketRuntimeException("Please authentificate first with twitter"); 65 | TwitterClient client = new TwitterClient( 66 | new TwitterCredentials(getApiKey(), 67 | getApiSecretKey(), 68 | accessToken, 69 | accessTokenSecret)); 70 | Tweet tweet; 71 | if(content.hasImages()) { 72 | List images = content.getImages(); 73 | List mediaIds = new ArrayList(); 74 | try { 75 | for(int i=0; i VK_APP_KEY = new MetaDataKey() {}; 61 | 62 | public static final String CLASS_NAME = "OPVkApp"; 63 | 64 | @OrientDBProperty(notNull = true) 65 | public Integer getAppId(); 66 | public void setAppId(Integer value); 67 | 68 | 69 | @OrientDBProperty(notNull = true) 70 | public String getAppSecret(); 71 | public void setAppSecret(String value); 72 | 73 | @OrientDBProperty(notNull = true) 74 | public String getServiceToken(); 75 | public void setServiceToken(String value); 76 | 77 | public Long getDefaultUserId(); 78 | public void setDefaultUserId(Long value); 79 | 80 | public String getDefaultUserAccessKey(); 81 | public void setDefaultUserAccessKey(String value); 82 | 83 | @Override 84 | public default IPosting send (IChannel channel, IContent content) throws Exception { 85 | IVkWall wall = checkChannelType(channel, IVkWall.class); 86 | VkApiClient vk = getVkApiClient(); 87 | UserActor userActor = new UserActor(wall.getEffectiveUserId().intValue(), wall.getEffectiveAccessKey()); 88 | WallPostQuery post = vk.wall().post(userActor); 89 | if(wall.getOwnerId()!=null) post.ownerId(wall.getAdjustedOwnerId().intValue()); 90 | post.message(content.getContent()); 91 | if(content.hasImages()) { 92 | PhotosGetWallUploadServerQuery getWallServer = vk.photos().getWallUploadServer(userActor); 93 | if(wall.getOwnerId()!=null) getWallServer.groupId(wall.getOwnerId().intValue()); 94 | GetWallUploadServerResponse uploadServer = getWallServer.execute(); 95 | 96 | List attachments = new ArrayList<>(); 97 | for(IImageAttachment image : content.getImages()) { 98 | WallUploadResponse upload = vk.upload() 99 | .photoWall(uploadServer.getUploadUrl().toString(), image.asFile()) 100 | .execute(); 101 | PhotosSaveWallPhotoQuery savePhotoWall = vk.photos().saveWallPhoto(userActor, upload.getPhoto()) 102 | .server(upload.getServer()) 103 | .hash(upload.getHash()); 104 | if(wall.getOwnerId()!=null) savePhotoWall.groupId(wall.getOwnerId().intValue()); 105 | List photoList = savePhotoWall.execute(); 106 | attachments.add("photo" + photoList.get(0).getOwnerId() + "_" + photoList.get(0).getId()); 107 | } 108 | post.attachments(attachments); 109 | } 110 | PostResponse response = post.execute(); 111 | return IPosting.createFor(channel, content) 112 | .setExternalPostingId(response.getPostId()) 113 | .setUrl("https://vk.com/wall%d_%d", wall.getAdjustedOwnerId(), response.getPostId()); 114 | } 115 | 116 | public default OAuth20Service getService(IOAuthReciever reciever) { 117 | 118 | ODocument reciverDoc = DAO.asDocument(reciever!=null?reciever:this); 119 | return new ServiceBuilder(getAppId().toString()) 120 | .apiSecret(getAppSecret()) 121 | .defaultScope("offline,wall,groups,video,photos") // replace with desired scope 122 | // .callback(OAuthCallbackResource.urlFor(reciverDoc)) 123 | .callback("https://oauth.vk.com/blank.html") 124 | .responseType("token") 125 | .build(VkontakteApi.instance()); 126 | } 127 | 128 | public default VkApiClient getVkApiClient() { 129 | VkApiClient ret = OrienteerWebApplication.lookupApplication().getMetaData(VK_APP_KEY); 130 | if(ret==null) { 131 | TransportClient transportClient = new HttpTransportClient(); 132 | ret = new VkApiClient(transportClient); 133 | OrienteerWebApplication.lookupApplication().setMetaData(VK_APP_KEY, ret); 134 | } 135 | return ret; 136 | } 137 | 138 | @OMethod( 139 | titleKey = "command.connectoauth", 140 | order=10,bootstrap=BootstrapType.SUCCESS,icon = FAIconType.play, 141 | filters={ 142 | @OFilter(fClass = PlaceFilter.class, fData = "STRUCTURE_TABLE"), 143 | @OFilter(fClass = WidgetTypeFilter.class, fData = "parameters"), 144 | } 145 | ) 146 | public default void connectOAuth(IMethodContext ctx) { 147 | try(OAuth20Service service = getService(this)) { 148 | String redirectTo = service.getAuthorizationUrl(); 149 | throw new RedirectToUrlException(redirectTo); 150 | } catch (IOException e) { 151 | ctx.showFeedback(FeedbackMessage.ERROR, "error.oauthrequest", Model.of(e.getMessage())); 152 | OLogger.log(e, DAO.asDocument(this).getIdentity().toString()); 153 | } 154 | } 155 | 156 | @Override 157 | public default void codeObtained(String code) throws Exception { 158 | try(OAuth20Service service = getService(this)) { 159 | setDefaultUserAccessKey(service.getAccessToken(code).getAccessToken()); 160 | setDefaultUserId(getVkApiClient().users().get( 161 | new UserActor(null, getDefaultUserAccessKey())).execute().get(0).getId().longValue()); 162 | DAO.save(this); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/vk/IVkWall.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.vk; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.wicket.feedback.FeedbackMessage; 6 | import org.apache.wicket.model.Model; 7 | import org.apache.wicket.request.cycle.RequestCycle; 8 | import org.apache.wicket.request.flow.RedirectToUrlException; 9 | import org.orienteer.core.OClassDomain; 10 | import org.orienteer.core.component.BootstrapType; 11 | import org.orienteer.core.component.FAIconType; 12 | import org.orienteer.core.dao.DAO; 13 | import org.orienteer.core.dao.ODocumentWrapperProvider; 14 | import org.orienteer.core.dao.OrienteerOClass; 15 | import org.orienteer.core.method.IMethodContext; 16 | import org.orienteer.core.method.OFilter; 17 | import org.orienteer.core.method.OMethod; 18 | import org.orienteer.core.method.filters.PlaceFilter; 19 | import org.orienteer.core.method.filters.WidgetTypeFilter; 20 | import org.orienteer.core.util.CommonUtils; 21 | import org.orienteer.logger.OLogger; 22 | import org.orienteer.oposter.model.IChannel; 23 | import org.orienteer.oposter.model.IContent; 24 | import org.orienteer.oposter.model.IOAuthReciever; 25 | import org.orienteer.oposter.model.IPlatformApp; 26 | import org.orienteer.transponder.annotation.DefaultValue; 27 | import org.orienteer.transponder.annotation.EntityType; 28 | import org.orienteer.transponder.orientdb.OrientDBProperty; 29 | 30 | import com.github.scribejava.core.oauth.OAuth20Service; 31 | import com.google.common.base.Throwables; 32 | import com.google.inject.ProvidedBy; 33 | import com.vk.api.sdk.client.actors.UserActor; 34 | 35 | /** 36 | * {@link IChannel} for VKontakte 37 | */ 38 | @ProvidedBy(ODocumentWrapperProvider.class) 39 | @EntityType(value = IVkWall.CLASS_NAME, orderOffset = 100) 40 | @OrienteerOClass(domain = OClassDomain.SPECIFICATION) 41 | public interface IVkWall extends IChannel, IOAuthReciever { 42 | public static final String CLASS_NAME = "OPVkWall"; 43 | 44 | public Long getOwnerId(); 45 | public void setOwnerId(Long value); 46 | 47 | @OrientDBProperty(defaultValue = "true") 48 | @DefaultValue("true") 49 | public boolean isCommunity(); 50 | public void setCommunity(boolean value); 51 | 52 | public Long getUserId(); 53 | public void setUserId(Long value); 54 | 55 | public String getUserAccessKey(); 56 | public void setUserAccessKey(String value); 57 | 58 | public default Long getAdjustedOwnerId() { 59 | Long ownerId = getOwnerId(); 60 | if(ownerId!=null && isCommunity()) return -ownerId; 61 | else return ownerId; 62 | } 63 | 64 | public default Long getEffectiveUserId() { 65 | Long userId = getUserId(); 66 | if(userId!=null) return userId; 67 | IPlatformApp app = getPlatformApp(); 68 | if(app!=null && app instanceof IVkApp) return ((IVkApp)app).getDefaultUserId(); 69 | else return null; 70 | } 71 | 72 | public default String getEffectiveAccessKey( ) { 73 | String userAccessKey = getUserAccessKey(); 74 | if(userAccessKey!=null) return userAccessKey; 75 | IPlatformApp app = getPlatformApp(); 76 | if(app!=null && app instanceof IVkApp) return ((IVkApp)app).getDefaultUserAccessKey(); 77 | else return null; 78 | } 79 | 80 | @OMethod( 81 | titleKey = "command.connectoauth", 82 | order=10,bootstrap=BootstrapType.SUCCESS,icon = FAIconType.play, 83 | filters={ 84 | @OFilter(fClass = PlaceFilter.class, fData = "STRUCTURE_TABLE"), 85 | @OFilter(fClass = WidgetTypeFilter.class, fData = "parameters"), 86 | } 87 | ) 88 | public default void connectOAuth(IMethodContext ctx) { 89 | IPlatformApp app = getPlatformApp(); 90 | if(app instanceof IVkApp) { 91 | IVkApp vkApp = (IVkApp) app; 92 | try(OAuth20Service service = vkApp.getService(this)) { 93 | String redirectTo = service.getAuthorizationUrl(); 94 | /*String redirectTo; 95 | if(getOwnerId()!=null && Boolean.TRUE.equals(isCommunity())) { 96 | redirectTo = service.getAuthorizationUrl(CommonUtils.toMap("group_ids",getOwnerId().toString())); 97 | } else { 98 | redirectTo = service.getAuthorizationUrl(); 99 | }*/ 100 | throw new RedirectToUrlException(redirectTo); 101 | } catch (IOException e) { 102 | ctx.showFeedback(FeedbackMessage.ERROR, "error.oauthrequest", Model.of(e.getMessage())); 103 | OLogger.log(e, DAO.asDocument(this).getIdentity().toString()); 104 | } 105 | } 106 | } 107 | 108 | @Override 109 | public default void codeObtained(String code) throws Exception { 110 | IPlatformApp app = getPlatformApp(); 111 | if(app instanceof IVkApp) { 112 | IVkApp vkApp = (IVkApp) app; 113 | try(OAuth20Service service = vkApp.getService(this)) { 114 | setUserAccessKey(service.getAccessToken(code).getAccessToken()); 115 | setUserId(vkApp.getVkApiClient().users().get( 116 | new UserActor(null, getUserAccessKey())).execute().get(0).getId().longValue()); 117 | DAO.save(this); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/vk/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package for Vkontakte support classes 3 | */ 4 | package org.orienteer.oposter.vk; -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/web/OAuthCallbackResource.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter.web; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.http.HttpServletResponse; 6 | 7 | import org.apache.wicket.markup.html.WebPage; 8 | import org.apache.wicket.request.Url; 9 | import org.apache.wicket.request.cycle.RequestCycle; 10 | import org.apache.wicket.request.http.WebRequest; 11 | import org.apache.wicket.request.mapper.parameter.PageParameters; 12 | import org.apache.wicket.request.resource.AbstractResource; 13 | import org.apache.wicket.request.resource.SharedResourceReference; 14 | import org.apache.wicket.util.string.StringValue; 15 | import org.orienteer.core.MountPath; 16 | import org.orienteer.core.OrienteerWebApplication; 17 | import org.orienteer.core.dao.DAO; 18 | import static org.orienteer.core.util.CommonUtils.localize; 19 | import org.orienteer.core.web.ODocumentPage; 20 | import org.orienteer.logger.OLogger; 21 | import org.orienteer.oposter.model.IOAuthReciever; 22 | 23 | import com.orientechnologies.orient.core.id.ORecordId; 24 | import com.orientechnologies.orient.core.record.impl.ODocument; 25 | 26 | /** 27 | * Resource for recieving OAuth callbacks 28 | */ 29 | @MountPath("/op/callback") 30 | public class OAuthCallbackResource extends AbstractResource { 31 | 32 | @Override 33 | protected ResourceResponse newResourceResponse(Attributes attributes) { 34 | ResourceResponse response = new ResourceResponse(); 35 | if (response.dataNeedsToBeWritten(attributes)) { 36 | OrienteerWebApplication app = OrienteerWebApplication.get(); 37 | String rid = "#"+attributes.getParameters().get("rid").toString(); 38 | if(!ORecordId.isA(rid)) { 39 | response.setError(HttpServletResponse.SC_BAD_REQUEST, "Rid of requestor document is not specified"); 40 | } else { 41 | ODocument doc = new ORecordId(rid).getRecord(); 42 | ODocumentPage targetPage = new ODocumentPage(doc); 43 | WebRequest request = (WebRequest) attributes.getRequest(); 44 | StringValue errorSV = request.getRequestParameters().getParameterValue("error"); 45 | StringValue errorDescriptionSV = request.getRequestParameters().getParameterValue("error_description"); 46 | if(!errorSV.isEmpty() || !errorDescriptionSV.isEmpty()) { 47 | targetPage.error(localize("error.oauthcallback", "error", errorSV.toOptionalString(), 48 | "errorDescription", errorDescriptionSV.toOptionalString())); 49 | } else { 50 | try { 51 | IOAuthReciever reciever = DAO.provide(IOAuthReciever.class, doc); 52 | reciever.callback(request, targetPage); 53 | } catch (Exception e) { 54 | targetPage.error(e.getMessage()); 55 | OLogger.log(e, doc.getIdentity().toString()); 56 | } 57 | } 58 | // RequestCycle.get().setResponsePage(targetPage); 59 | response.setWriteCallback(createWriteCallback(targetPage)); 60 | } 61 | } 62 | return response; 63 | } 64 | 65 | private WriteCallback createWriteCallback(final WebPage targetPage) { 66 | return new WriteCallback() { 67 | @Override 68 | public void writeData(Attributes attributes) throws IOException { 69 | RequestCycle.get().setResponsePage(targetPage); 70 | // throw new RedirectToUrlException("/browse/" + OfferRequest.CLASS_NAME); 71 | } 72 | }; 73 | } 74 | 75 | public static String urlFor(IOAuthReciever dao) { 76 | return urlFor(DAO.asDocument(dao)); 77 | } 78 | 79 | public static String urlFor(ODocument document) { 80 | return RequestCycle.get().getUrlRenderer() 81 | .renderFullUrl(Url.parse( 82 | RequestCycle.get().urlFor( 83 | new SharedResourceReference(OAuthCallbackResource.class.getName()), 84 | new PageParameters().add("rid", document.getIdentity().toString().substring(1))))) 85 | .toString(); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /oposter/src/main/java/org/orienteer/oposter/web/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package for a project web pages 3 | */ 4 | package org.orienteer.oposter.web; -------------------------------------------------------------------------------- /oposter/src/main/resources/META-INF/services/org.apache.wicket.IInitializer: -------------------------------------------------------------------------------- 1 | org.orienteer.oposter.Initializer -------------------------------------------------------------------------------- /oposter/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /oposter/src/main/resources/org/orienteer/oposter/Initializer.properties: -------------------------------------------------------------------------------- 1 | panel.attachment.upload=Upload 2 | panel.attachment.cancel.upload=Cancel 3 | 4 | channel.testsend=Test Sending 5 | 6 | channel.error.noplatformapp=Platform was not specified 7 | channel.info.testwassent=Test content was successfully sent 8 | 9 | content.sendnow=Send Now 10 | 11 | content.warning.nochannels=No channels were specified 12 | content.error.cantsend=Can't send due to the following exception: ${} 13 | content.info.wassent=Content was sent 14 | 15 | error.oauthrequest=Can't request OAuth: ${} 16 | error.oauthcallback=Problem with OAuth authorization. Error: ${error}, Error Description ${errorDescription} 17 | error.oauthcallback.nocode=No code was specified in the OAuth response 18 | error.wrongplatform=Incorrect Platform is defined: should be ${simpleName} 19 | 20 | command.connectoauth=Connect OAuth 21 | 22 | widget.calendar=Calendar 23 | 24 | perspective.oposter=OPoster 25 | perspective.oposter.contentplans=Content Plans 26 | perspective.oposter.contents=Contents 27 | perspective.oposter.platformapps=Platform Apps 28 | perspective.oposter.channels=Channels 29 | perspective.oposter.postings=Postings -------------------------------------------------------------------------------- /oposter/src/main/resources/org/orienteer/oposter/META-INF/services/org.apache.wicket.IInitializer: -------------------------------------------------------------------------------- 1 | org.orienteer.oposter.Initializer -------------------------------------------------------------------------------- /oposter/src/main/resources/org/orienteer/oposter/component/attachment/carousel-load.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function initLoad(carouselId) { 4 | var $carousel = $('#' + carouselId); 5 | var $loadImage = $('#' + carouselId + ' > .carousel-inner > .carousel-item > img').first(); 6 | 7 | if ($loadImage.length > 0) { 8 | 9 | var $indicator = $(''); 10 | 11 | $carousel.hide(); 12 | $carousel.before($indicator); 13 | 14 | $loadImage.on('load', function () { 15 | $indicator.fadeOut('slow', function () { 16 | $indicator.remove(); 17 | $carousel.show('slow'); 18 | }); 19 | }); 20 | } 21 | } -------------------------------------------------------------------------------- /oposter/src/main/resources/org/orienteer/oposter/component/attachment/carousel.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .carousel-indicators li { 4 | cursor: pointer !important; 5 | } -------------------------------------------------------------------------------- /oposter/src/main/resources/org/orienteer/oposter/component/attachment/edit-attachment.css: -------------------------------------------------------------------------------- 1 | .delete-attachment { 2 | opacity: 0.5 !important; 3 | } 4 | 5 | .file-upload-wrapper { 6 | cursor: pointer; 7 | } 8 | 9 | .file-upload-wrapper:hover { 10 | opacity: 0.7; 11 | } -------------------------------------------------------------------------------- /oposter/src/main/resources/org/orienteer/oposter/component/attachment/edit-attachment.js: -------------------------------------------------------------------------------- 1 | 2 | function initEditAttachments(wrapperId, uploadId, labelId) { 3 | var $uploadField = $('#' + uploadId); 4 | var lock = false; 5 | 6 | $('#' + wrapperId).click(function (e) { 7 | if (!lock) { 8 | lock = true; 9 | $uploadField.click(); 10 | lock = false; 11 | } 12 | }); 13 | 14 | $('#' + wrapperId).change(function (e) { 15 | var files = e.target.files; 16 | 17 | if (files && files.length > 0) { 18 | var title = files[0].name; 19 | 20 | if (files.length > 1) { 21 | title += ', ...'; 22 | } 23 | 24 | $('#' + labelId).html(title); 25 | } 26 | }); 27 | } -------------------------------------------------------------------------------- /oposter/src/test/java/org/orienteer/oposter/TestModule.java: -------------------------------------------------------------------------------- 1 | package org.orienteer.oposter; 2 | 3 | import org.orienteer.core.OrienteerWebApplication; 4 | import org.orienteer.core.module.IOrienteerModule; 5 | 6 | import org.orienteer.junit.OrienteerTestRunner; 7 | import org.orienteer.junit.OrienteerTester; 8 | 9 | import static org.junit.Assert.*; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | import com.google.inject.Inject; 14 | import com.google.inject.Singleton; 15 | 16 | 17 | @RunWith(OrienteerTestRunner.class) 18 | @Singleton 19 | public class TestModule 20 | { 21 | @Inject 22 | private OrienteerTester tester; 23 | 24 | @Test 25 | public void testModuleLoaded() 26 | { 27 | OrienteerWebApplication app = tester.getApplication(); 28 | assertNotNull(app); 29 | IOrienteerModule module = app.getModuleByName("oposter"); 30 | assertNotNull(module); 31 | assertTrue(module instanceof OPosterModule); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /orienteer-test.properties: -------------------------------------------------------------------------------- 1 | orienteer.production=false 2 | orienteer.application=org.orienteer.oposter.web.OPosterWebApplication 3 | orientdb.embedded=true 4 | orientdb.url=plocal:Orienteer 5 | orientdb.guest.username=reader 6 | orientdb.guest.password=reader 7 | orientdb.admin.username=admin 8 | orientdb.admin.password=admin 9 | orientdb.root.password=rootorientdb 10 | orienteer.image.logo=logo.png 11 | orienteer.image.icon=logo.png 12 | #orientdb.rest.url=http://localhost:2480 13 | #plantuml.url=http://custom-plantuml-url 14 | #plantuml.showuml=false; 15 | 16 | #webjars.readFromCacheTimeout=5 seconds 17 | #webjars.useCdnResources=true 18 | #webjars.cdnUrl=//maxcdn.bootstrapcdn.com:80 19 | 20 | -------------------------------------------------------------------------------- /orienteer.properties: -------------------------------------------------------------------------------- 1 | orienteer.production=false 2 | orienteer.application=org.orienteer.oposter.web.OPosterWebApplication 3 | orientdb.embedded=true 4 | orientdb.url=plocal:Orienteer 5 | #orientdb.url=plocal:oposter-war 6 | orientdb.guest.username=reader 7 | orientdb.guest.password=reader 8 | orientdb.admin.username=admin 9 | orientdb.admin.password=admin 10 | 11 | orientdb.root.password=rootorientdb 12 | #wicket.render.strategy=ONE_PASS_RENDER 13 | #orientdb.rest.url=http://localhost:2480 14 | #plantuml.url=http://custom-plantuml-url 15 | #plantuml.showuml=false; 16 | 17 | #webjars.readFromCacheTimeout=5 seconds 18 | #webjars.useCdnResources=true 19 | #webjars.cdnUrl=//maxcdn.bootstrapcdn.com:80 20 | 21 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.orienteer.oposter 6 | oposter-parent 7 | 1.0-SNAPSHOT 8 | pom 9 | 10 | OPoster 11 | OPoster - Scheduling Platform for Social Media 12 | 13 | https://orienteer.org 14 | 15 | https://github.com/OrienteerBAP/OPoster 16 | scm:git:ssh://github.com/OrienteerBAP/OPoster.git 17 | scm:git:ssh://git@github.com/OrienteerBAP/OPoster.git 18 | HEAD 19 | 20 | 21 | github 22 | https://github.com/OrienteerBAP/OPoster/issues 23 | 24 | 25 | 26 | ossrh 27 | https://oss.sonatype.org/content/repositories/snapshots 28 | 29 | 30 | ossrh 31 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 32 | 33 | 34 | 35 | 36 | owner 37 | Ilia Naryzhny 38 | phantom@ydn.ru 39 | 40 | 41 | 42 | 43 | The Apache Software License, Version 2.0 44 | http://www.apache.org/licenses/LICENSE-2.0.txt 45 | repo 46 | 47 | 48 | 49 | 2.0-SNAPSHOT 50 | 2.0-SNAPSHOT 51 | 9.4.12.v20180830 52 | UTF-8 53 | 54 | none 55 | 1.8 56 | 1.8 57 | 58 | 59 | 60 | snapshots-repo 61 | https://oss.sonatype.org/content/repositories/snapshots 62 | 63 | false 64 | 65 | 66 | true 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.orienteer 74 | orienteer-core 75 | ${orienteer.version} 76 | 77 | 78 | org.orienteer 79 | orienteer-logger-server 80 | ${orienteer.version} 81 | 82 | 83 | javax.servlet 84 | javax.servlet-api 85 | 3.0.1 86 | provided 87 | 88 | 89 | 90 | org.orienteer 91 | orienteer-core 92 | ${orienteer.version} 93 | test-jar 94 | test 95 | 96 | 97 | ru.ydn.wicket.wicket-orientdb 98 | wicket-orientdb 99 | ${wicket.orientdb.version} 100 | test-jar 101 | test 102 | 103 | 104 | junit 105 | junit 106 | 4.11 107 | test 108 | 109 | 110 | 111 | 112 | 113 | org.projectlombok 114 | lombok 115 | 1.18.16 116 | provided 117 | 118 | 119 | 120 | 121 | 122 | org.apache.felix 123 | maven-bundle-plugin 124 | 3.0.1 125 | true 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-release-plugin 130 | 2.5.3 131 | 132 | true 133 | release 134 | deploy 135 | v@{project.version} 136 | -Pskip-integration-tests 137 | 138 | 139 | 140 | org.eluder.coveralls 141 | coveralls-maven-plugin 142 | 4.2.0 143 | 144 | UTF8 145 | 146 | 147 | 148 | org.codehaus.mojo 149 | cobertura-maven-plugin 150 | 2.7 151 | 152 | 153 | html 154 | xml 155 | 156 | 256m 157 | 158 | true 159 | 160 | 161 | 162 | 163 | org.apache.maven.plugins 164 | maven-eclipse-plugin 165 | 2.10 166 | 167 | true 168 | ${wtp.version} 169 | 170 | 171 | 172 | org.apache.maven.plugins 173 | maven-checkstyle-plugin 174 | 3.0.0 175 | 176 | check_style.xml 177 | true 178 | false 179 | 180 | 181 | 182 | checkstyle 183 | verify 184 | 185 | check 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | maven-clean-plugin 197 | 3.1.0 198 | 199 | 200 | 201 | maven-resources-plugin 202 | 3.0.2 203 | 204 | 205 | true 206 | org.apache.maven.plugins 207 | maven-compiler-plugin 208 | 3.8.0 209 | 210 | UTF-8 211 | true 212 | true 213 | 214 | -parameters 215 | 216 | 217 | 218 | 219 | org.apache.maven.plugins 220 | maven-surefire-plugin 221 | 2.22.1 222 | 223 | 224 | ${settings.localRepository} 225 | 226 | 227 | **/*Slow* 228 | 229 | 230 | 231 | 232 | org.apache.maven.plugins 233 | maven-jar-plugin 234 | 3.0.2 235 | 236 | 237 | 238 | true 239 | true 240 | 241 | 242 | 243 | 244 | 245 | maven-install-plugin 246 | 2.5.2 247 | 248 | 249 | maven-deploy-plugin 250 | 2.8.2 251 | 252 | 253 | 254 | maven-site-plugin 255 | 3.7.1 256 | 257 | 258 | maven-project-info-reports-plugin 259 | 3.0.0 260 | 261 | 262 | 263 | 264 | 265 | oposter 266 | oposter-war 267 | 268 | 269 | 270 | release 271 | 272 | 273 | 274 | org.sonatype.plugins 275 | nexus-staging-maven-plugin 276 | 1.6.7 277 | true 278 | 279 | ossrh 280 | https://oss.sonatype.org/ 281 | true 282 | 283 | 284 | 285 | org.apache.maven.plugins 286 | maven-source-plugin 287 | 3.0.0 288 | 289 | 290 | attach-sources 291 | 292 | jar-no-fork 293 | 294 | 295 | 296 | 297 | 298 | org.apache.maven.plugins 299 | maven-javadoc-plugin 300 | 2.10.3 301 | 302 | 303 | attach-javadocs 304 | 305 | jar 306 | 307 | 308 | 309 | 310 | 311 | org.apache.maven.plugins 312 | maven-gpg-plugin 313 | 1.6 314 | 315 | 316 | sign-artifacts 317 | verify 318 | 319 | sign 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | --------------------------------------------------------------------------------