mirror of
https://github.com/sismics/docs.git
synced 2024-12-23 11:43:49 +01:00
commit
f01d78a9ea
@ -24,13 +24,14 @@ Features
|
|||||||
- Support image and PDF files
|
- Support image and PDF files
|
||||||
- Flexible search engine
|
- Flexible search engine
|
||||||
- Full text search in image and PDF
|
- Full text search in image and PDF
|
||||||
- SHA-256 encryption
|
- 256-bit AES encryption
|
||||||
- Tag system
|
- Tag system
|
||||||
- Multi-users ACL system
|
- Multi-users ACL system
|
||||||
- Audit log
|
- Audit log
|
||||||
- Document sharing by URL
|
- Document sharing by URL
|
||||||
- RESTful Web API
|
- RESTful Web API
|
||||||
- Fully featured Android client
|
- Fully featured Android client
|
||||||
|
- Tested to 100k documents
|
||||||
|
|
||||||
Download
|
Download
|
||||||
--------
|
--------
|
||||||
|
@ -12,10 +12,12 @@
|
|||||||
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
||||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||||
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
|
|
||||||
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
|
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
|
||||||
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
|
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
|
||||||
<option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" />
|
<afterSyncTasks>
|
||||||
|
<task>generateDebugAndroidTestSources</task>
|
||||||
|
<task>generateDebugSources</task>
|
||||||
|
</afterSyncTasks>
|
||||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||||
@ -24,7 +26,7 @@
|
|||||||
</configuration>
|
</configuration>
|
||||||
</facet>
|
</facet>
|
||||||
</component>
|
</component>
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
|
||||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
|
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
@ -34,13 +36,13 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
|
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||||
@ -75,9 +77,9 @@
|
|||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.2.1/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/22.0.0/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/22.2.1/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.2.1/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.shamanland/fab/0.0.6/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.shamanland/fab/0.0.6/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.easing/android-easing/1.0.3/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.easing/android-easing/1.0.3/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.imagezoom/imagezoom/1.0.5/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/it.sephiroth.android.library.imagezoom/imagezoom/1.0.5/jars" />
|
||||||
@ -106,16 +108,16 @@
|
|||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
|
<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" exported="" name="appcompat-v7-22.1.1" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="fab-0.0.6" level="project" />
|
<orderEntry type="library" exported="" name="fab-0.0.6" level="project" />
|
||||||
<orderEntry type="library" exported="" name="android-easing-1.0.3" level="project" />
|
<orderEntry type="library" exported="" name="android-easing-1.0.3" level="project" />
|
||||||
<orderEntry type="library" exported="" name="imagezoom-1.0.5" level="project" />
|
<orderEntry type="library" exported="" name="imagezoom-1.0.5" level="project" />
|
||||||
|
<orderEntry type="library" exported="" name="support-v4-22.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="eventbus-2.4.0" level="project" />
|
<orderEntry type="library" exported="" name="eventbus-2.4.0" level="project" />
|
||||||
|
<orderEntry type="library" exported="" name="recyclerview-v7-22.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="android-query.0.26.8" level="project" />
|
<orderEntry type="library" exported="" name="android-query.0.26.8" level="project" />
|
||||||
<orderEntry type="library" exported="" name="tokenautocomplete-1.2.1" level="project" />
|
<orderEntry type="library" exported="" name="tokenautocomplete-1.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="support-v4-22.1.1" level="project" />
|
<orderEntry type="library" exported="" name="support-annotations-22.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" />
|
<orderEntry type="library" exported="" name="appcompat-v7-22.2.1" level="project" />
|
||||||
<orderEntry type="library" exported="" name="recyclerview-v7-22.0.0" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="android-async-http-1.4.6" level="project" />
|
<orderEntry type="library" exported="" name="android-async-http-1.4.6" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -3,7 +3,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:1.2.3'
|
classpath 'com.android.tools.build:gradle:1.3.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
@ -14,7 +14,7 @@ repositories {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 22
|
compileSdkVersion 22
|
||||||
buildToolsVersion "22.0.1"
|
buildToolsVersion "23.0.2"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
@ -50,8 +50,8 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(dir: 'libs', include: '*.jar')
|
compile fileTree(dir: 'libs', include: '*.jar')
|
||||||
compile 'com.android.support:appcompat-v7:22.1.1'
|
compile 'com.android.support:appcompat-v7:22.+'
|
||||||
compile 'com.android.support:recyclerview-v7:22.0.0'
|
compile 'com.android.support:recyclerview-v7:22.+'
|
||||||
compile 'com.loopj.android:android-async-http:1.4.6'
|
compile 'com.loopj.android:android-async-http:1.4.6'
|
||||||
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
compile 'it.sephiroth.android.library.imagezoom:imagezoom:1.0.5'
|
||||||
compile 'de.greenrobot:eventbus:2.4.0'
|
compile 'de.greenrobot:eventbus:2.4.0'
|
||||||
|
@ -168,7 +168,7 @@ public class DocumentViewActivity extends AppCompatActivity {
|
|||||||
createdDateTextView.setText(date);
|
createdDateTextView.setText(date);
|
||||||
|
|
||||||
TextView descriptionTextView = (TextView) findViewById(R.id.descriptionTextView);
|
TextView descriptionTextView = (TextView) findViewById(R.id.descriptionTextView);
|
||||||
if (description == null || description.isEmpty()) {
|
if (description == null || description.isEmpty() || description.equals(JSONObject.NULL.toString())) {
|
||||||
descriptionTextView.setVisibility(View.GONE);
|
descriptionTextView.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
descriptionTextView.setVisibility(View.VISIBLE);
|
descriptionTextView.setVisibility(View.VISIBLE);
|
||||||
|
@ -11,7 +11,7 @@ public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListen
|
|||||||
private OnItemClickListener mListener;
|
private OnItemClickListener mListener;
|
||||||
|
|
||||||
public interface OnItemClickListener {
|
public interface OnItemClickListener {
|
||||||
public void onItemClick(View view, int position);
|
void onItemClick(View view, int position);
|
||||||
}
|
}
|
||||||
|
|
||||||
GestureDetector mGestureDetector;
|
GestureDetector mGestureDetector;
|
||||||
@ -25,7 +25,8 @@ public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListen
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
|
@Override
|
||||||
|
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
|
||||||
View childView = view.findChildViewUnder(e.getX(), e.getY());
|
View childView = view.findChildViewUnder(e.getX(), e.getY());
|
||||||
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
|
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
|
||||||
mListener.onItemClick(childView, view.getChildPosition(childView));
|
mListener.onItemClick(childView, view.getChildPosition(childView));
|
||||||
@ -33,5 +34,9 @@ public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListen
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
|
@Override
|
||||||
|
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
|
||||||
}
|
}
|
@ -113,6 +113,11 @@
|
|||||||
<artifactId>bcprov-jdk15on</artifactId>
|
<artifactId>bcprov-jdk15on</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.levigo.jbig2</groupId>
|
||||||
|
<artifactId>levigo-jbig2-imageio</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- OCR dependencies -->
|
<!-- OCR dependencies -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jna</groupId>
|
<groupId>jna</groupId>
|
||||||
|
@ -103,7 +103,7 @@ public class TagDao {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<TagDto> getByDocumentId(String documentId, String userId) {
|
public List<TagDto> getByDocumentId(String documentId, String userId) {
|
||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C from T_DOCUMENT_TAG dt ");
|
StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C, t.TAG_IDPARENT_C from T_DOCUMENT_TAG dt ");
|
||||||
sb.append(" join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C ");
|
sb.append(" join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C ");
|
||||||
sb.append(" where dt.DOT_IDDOCUMENT_C = :documentId and t.TAG_DELETEDATE_D is null ");
|
sb.append(" where dt.DOT_IDDOCUMENT_C = :documentId and t.TAG_DELETEDATE_D is null ");
|
||||||
sb.append(" and t.TAG_IDUSER_C = :userId and dt.DOT_DELETEDATE_D is null ");
|
sb.append(" and t.TAG_IDUSER_C = :userId and dt.DOT_DELETEDATE_D is null ");
|
||||||
@ -123,6 +123,7 @@ public class TagDao {
|
|||||||
tagDto.setId((String) o[i++]);
|
tagDto.setId((String) o[i++]);
|
||||||
tagDto.setName((String) o[i++]);
|
tagDto.setName((String) o[i++]);
|
||||||
tagDto.setColor((String) o[i++]);
|
tagDto.setColor((String) o[i++]);
|
||||||
|
tagDto.setParentId((String) o[i++]);
|
||||||
tagDtoList.add(tagDto);
|
tagDtoList.add(tagDto);
|
||||||
}
|
}
|
||||||
return tagDtoList;
|
return tagDtoList;
|
||||||
@ -137,7 +138,7 @@ public class TagDao {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<TagStatDto> getStats(String userId) {
|
public List<TagStatDto> getStats(String userId) {
|
||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C, count(d.DOC_ID_C) ");
|
StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C, t.TAG_IDPARENT_C, count(d.DOC_ID_C) ");
|
||||||
sb.append(" from T_TAG t ");
|
sb.append(" from T_TAG t ");
|
||||||
sb.append(" left join T_DOCUMENT_TAG dt on t.TAG_ID_C = dt.DOT_IDTAG_C and dt.DOT_DELETEDATE_D is null ");
|
sb.append(" left join T_DOCUMENT_TAG dt on t.TAG_ID_C = dt.DOT_IDTAG_C and dt.DOT_DELETEDATE_D is null ");
|
||||||
sb.append(" left join T_DOCUMENT d on d.DOC_ID_C = dt.DOT_IDDOCUMENT_C and d.DOC_DELETEDATE_D is null and d.DOC_IDUSER_C = :userId ");
|
sb.append(" left join T_DOCUMENT d on d.DOC_ID_C = dt.DOT_IDDOCUMENT_C and d.DOC_DELETEDATE_D is null and d.DOC_IDUSER_C = :userId ");
|
||||||
@ -158,6 +159,7 @@ public class TagDao {
|
|||||||
tagDto.setId((String) o[i++]);
|
tagDto.setId((String) o[i++]);
|
||||||
tagDto.setName((String) o[i++]);
|
tagDto.setName((String) o[i++]);
|
||||||
tagDto.setColor((String) o[i++]);
|
tagDto.setColor((String) o[i++]);
|
||||||
|
tagDto.setParentId((String) o[i++]);
|
||||||
tagDto.setCount(((Number) o[i++]).intValue());
|
tagDto.setCount(((Number) o[i++]).intValue());
|
||||||
tagStatDtoList.add(tagDto);
|
tagStatDtoList.add(tagDto);
|
||||||
}
|
}
|
||||||
@ -281,6 +283,7 @@ public class TagDao {
|
|||||||
// Update the tag
|
// Update the tag
|
||||||
tagFromDb.setName(tag.getName());
|
tagFromDb.setName(tag.getName());
|
||||||
tagFromDb.setColor(tag.getColor());
|
tagFromDb.setColor(tag.getColor());
|
||||||
|
tagFromDb.setParentId(tag.getParentId());
|
||||||
|
|
||||||
// Create audit log
|
// Create audit log
|
||||||
AuditLogUtil.create(tagFromDb, AuditLogType.UPDATE);
|
AuditLogUtil.create(tagFromDb, AuditLogType.UPDATE);
|
||||||
|
@ -23,6 +23,11 @@ public class TagDto {
|
|||||||
* Color.
|
* Color.
|
||||||
*/
|
*/
|
||||||
private String color;
|
private String color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent ID.
|
||||||
|
*/
|
||||||
|
private String parentId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter of id.
|
* Getter of id.
|
||||||
@ -77,4 +82,22 @@ public class TagDto {
|
|||||||
public void setColor(String color) {
|
public void setColor(String color) {
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter of parentId.
|
||||||
|
*
|
||||||
|
* @return the parentId
|
||||||
|
*/
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter of parentId.
|
||||||
|
*
|
||||||
|
* @param color parentId
|
||||||
|
*/
|
||||||
|
public void setParentId(String parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package com.sismics.docs.core.dao.jpa.dto;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag DTO.
|
* Tag stat DTO.
|
||||||
*
|
*
|
||||||
* @author bgamard
|
* @author bgamard
|
||||||
*/
|
*/
|
||||||
|
@ -39,6 +39,12 @@ public class Tag implements Loggable {
|
|||||||
@Column(name = "TAG_IDUSER_C", nullable = false, length = 36)
|
@Column(name = "TAG_IDUSER_C", nullable = false, length = 36)
|
||||||
private String userId;
|
private String userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User ID.
|
||||||
|
*/
|
||||||
|
@Column(name = "TAG_IDPARENT_C", length = 36)
|
||||||
|
private String parentId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creation date.
|
* Creation date.
|
||||||
*/
|
*/
|
||||||
@ -165,12 +171,31 @@ public class Tag implements Loggable {
|
|||||||
public void setDeleteDate(Date deleteDate) {
|
public void setDeleteDate(Date deleteDate) {
|
||||||
this.deleteDate = deleteDate;
|
this.deleteDate = deleteDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter of parentId.
|
||||||
|
*
|
||||||
|
* @return parentId
|
||||||
|
*/
|
||||||
|
public String getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter of parentId.
|
||||||
|
*
|
||||||
|
* @param parentId parentId
|
||||||
|
*/
|
||||||
|
public void setParentId(String parentId) {
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
.add("id", id)
|
.add("id", id)
|
||||||
.add("name", name)
|
.add("name", name)
|
||||||
|
.add("parentId", parentId)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ public class FileUtil {
|
|||||||
PDDocument pdfDocument = null;
|
PDDocument pdfDocument = null;
|
||||||
try {
|
try {
|
||||||
PDFTextStripper stripper = new PDFTextStripper();
|
PDFTextStripper stripper = new PDFTextStripper();
|
||||||
pdfDocument = PDDocument.load(inputStream, true);
|
pdfDocument = PDDocument.load(inputStream);
|
||||||
content = stripper.getText(pdfDocument);
|
content = stripper.getText(pdfDocument);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error while extracting text from the PDF", e);
|
log.error("Error while extracting text from the PDF", e);
|
||||||
@ -157,7 +157,7 @@ public class FileUtil {
|
|||||||
// Generate preview from the first page of the PDF
|
// Generate preview from the first page of the PDF
|
||||||
PDDocument pdfDocument = null;
|
PDDocument pdfDocument = null;
|
||||||
try {
|
try {
|
||||||
pdfDocument = PDDocument.load(inputStream, true);
|
pdfDocument = PDDocument.load(inputStream);
|
||||||
PDFRenderer renderer = new PDFRenderer(pdfDocument);
|
PDFRenderer renderer = new PDFRenderer(pdfDocument);
|
||||||
image = renderer.renderImage(0);
|
image = renderer.renderImage(0);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -1 +1 @@
|
|||||||
db.version=1
|
db.version=2
|
@ -0,0 +1,2 @@
|
|||||||
|
alter table T_TAG add column TAG_IDPARENT_C varchar(36);
|
||||||
|
update T_CONFIG set CFG_VALUE_C = '2' where CFG_ID_C = 'DB_VERSION';
|
@ -35,6 +35,7 @@
|
|||||||
<joda-time.joda-time.version>2.8.2</joda-time.joda-time.version>
|
<joda-time.joda-time.version>2.8.2</joda-time.joda-time.version>
|
||||||
<org.hibernate.hibernate.version>4.1.0.Final</org.hibernate.hibernate.version>
|
<org.hibernate.hibernate.version>4.1.0.Final</org.hibernate.hibernate.version>
|
||||||
<javax.servlet.javax.servlet-api.version>3.1.0</javax.servlet.javax.servlet-api.version>
|
<javax.servlet.javax.servlet-api.version>3.1.0</javax.servlet.javax.servlet-api.version>
|
||||||
|
<com.levigo.jbig2.levigo-jbig2-imageio.version>1.6.3</com.levigo.jbig2.levigo-jbig2-imageio.version>
|
||||||
|
|
||||||
<org.eclipse.jetty.jetty-server.version>9.2.13.v20150730</org.eclipse.jetty.jetty-server.version>
|
<org.eclipse.jetty.jetty-server.version>9.2.13.v20150730</org.eclipse.jetty.jetty-server.version>
|
||||||
<org.eclipse.jetty.jetty-webapp.version>9.2.13.v20150730</org.eclipse.jetty.jetty-webapp.version>
|
<org.eclipse.jetty.jetty-webapp.version>9.2.13.v20150730</org.eclipse.jetty.jetty-webapp.version>
|
||||||
@ -66,6 +67,12 @@
|
|||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
|
<repository>
|
||||||
|
<id>jbig2.googlecode</id>
|
||||||
|
<name>JBIG2 ImageIO-Plugin repository at googlecode.com</name>
|
||||||
|
<url>http://jbig2-imageio.googlecode.com/svn/maven-repository</url>
|
||||||
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|
||||||
@ -252,11 +259,11 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
|
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
|
||||||
<artifactId>jersey-test-framework-provider-bundle</artifactId>
|
<artifactId>jersey-test-framework-provider-bundle</artifactId>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<version>${org.glassfish.jersey.version}</version>
|
<version>${org.glassfish.jersey.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
|
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
|
||||||
@ -295,10 +302,10 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hibernate</groupId>
|
<groupId>org.hibernate</groupId>
|
||||||
<artifactId>hibernate-validator</artifactId>
|
<artifactId>hibernate-validator</artifactId>
|
||||||
<version>${org.hibernate.hibernate.version}</version>
|
<version>${org.hibernate.hibernate.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-dbcp</groupId>
|
<groupId>commons-dbcp</groupId>
|
||||||
@ -354,6 +361,13 @@
|
|||||||
<version>${org.bouncycastle.bcprov-jdk15on.version}</version>
|
<version>${org.bouncycastle.bcprov-jdk15on.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Used to read JBIG2 images. See https://github.com/sismics/docs/issues/38 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.levigo.jbig2</groupId>
|
||||||
|
<artifactId>levigo-jbig2-imageio</artifactId>
|
||||||
|
<version>${com.levigo.jbig2.levigo-jbig2-imageio.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- OCR dependencies -->
|
<!-- OCR dependencies -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jna</groupId>
|
<groupId>jna</groupId>
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=1
|
db.version=2
|
@ -19,6 +19,7 @@ import com.sismics.docs.core.util.jpa.PaginatedLists;
|
|||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
import com.sismics.rest.exception.ForbiddenClientException;
|
import com.sismics.rest.exception.ForbiddenClientException;
|
||||||
import com.sismics.rest.exception.ServerException;
|
import com.sismics.rest.exception.ServerException;
|
||||||
|
import com.sismics.rest.util.JsonUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audit log REST resources.
|
* Audit log REST resources.
|
||||||
@ -70,7 +71,7 @@ public class AuditLogResource extends BaseResource {
|
|||||||
.add("target", auditLogDto.getEntityId())
|
.add("target", auditLogDto.getEntityId())
|
||||||
.add("class", auditLogDto.getEntityClass())
|
.add("class", auditLogDto.getEntityClass())
|
||||||
.add("type", auditLogDto.getType().name())
|
.add("type", auditLogDto.getType().name())
|
||||||
.add("message", auditLogDto.getMessage())
|
.add("message", JsonUtil.nullable(auditLogDto.getMessage()))
|
||||||
.add("create_date", auditLogDto.getCreateTimestamp()));
|
.add("create_date", auditLogDto.getCreateTimestamp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import com.sismics.docs.core.dao.jpa.dto.TagStatDto;
|
|||||||
import com.sismics.docs.core.model.jpa.Tag;
|
import com.sismics.docs.core.model.jpa.Tag;
|
||||||
import com.sismics.rest.exception.ClientException;
|
import com.sismics.rest.exception.ClientException;
|
||||||
import com.sismics.rest.exception.ForbiddenClientException;
|
import com.sismics.rest.exception.ForbiddenClientException;
|
||||||
|
import com.sismics.rest.util.JsonUtil;
|
||||||
import com.sismics.rest.util.ValidationUtil;
|
import com.sismics.rest.util.ValidationUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +51,8 @@ public class TagResource extends BaseResource {
|
|||||||
items.add(Json.createObjectBuilder()
|
items.add(Json.createObjectBuilder()
|
||||||
.add("id", tag.getId())
|
.add("id", tag.getId())
|
||||||
.add("name", tag.getName())
|
.add("name", tag.getName())
|
||||||
.add("color", tag.getColor()));
|
.add("color", tag.getColor())
|
||||||
|
.add("parent", JsonUtil.nullable(tag.getParentId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
@ -96,7 +98,8 @@ public class TagResource extends BaseResource {
|
|||||||
@PUT
|
@PUT
|
||||||
public Response add(
|
public Response add(
|
||||||
@FormParam("name") String name,
|
@FormParam("name") String name,
|
||||||
@FormParam("color") String color) {
|
@FormParam("color") String color,
|
||||||
|
@FormParam("parent") String parentId) {
|
||||||
if (!authenticate()) {
|
if (!authenticate()) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
@ -117,11 +120,22 @@ public class TagResource extends BaseResource {
|
|||||||
throw new ClientException("AlreadyExistingTag", MessageFormat.format("Tag already exists: {0}", name));
|
throw new ClientException("AlreadyExistingTag", MessageFormat.format("Tag already exists: {0}", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the parent
|
||||||
|
if (StringUtils.isEmpty(parentId)) {
|
||||||
|
parentId = null;
|
||||||
|
} else {
|
||||||
|
Tag parentTag = tagDao.getByTagId(principal.getId(), parentId);
|
||||||
|
if (parentTag == null) {
|
||||||
|
throw new ClientException("ParentNotFound", MessageFormat.format("Parent not found: {0}", parentId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create the tag
|
// Create the tag
|
||||||
tag = new Tag();
|
tag = new Tag();
|
||||||
tag.setName(name);
|
tag.setName(name);
|
||||||
tag.setColor(color);
|
tag.setColor(color);
|
||||||
tag.setUserId(principal.getId());
|
tag.setUserId(principal.getId());
|
||||||
|
tag.setParentId(parentId);
|
||||||
String id = tagDao.create(tag);
|
String id = tagDao.create(tag);
|
||||||
|
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
@ -140,7 +154,8 @@ public class TagResource extends BaseResource {
|
|||||||
public Response update(
|
public Response update(
|
||||||
@PathParam("id") String id,
|
@PathParam("id") String id,
|
||||||
@FormParam("name") String name,
|
@FormParam("name") String name,
|
||||||
@FormParam("color") String color) {
|
@FormParam("color") String color,
|
||||||
|
@FormParam("parent") String parentId) {
|
||||||
if (!authenticate()) {
|
if (!authenticate()) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
@ -161,6 +176,16 @@ public class TagResource extends BaseResource {
|
|||||||
throw new ClientException("TagNotFound", MessageFormat.format("Tag not found: {0}", id));
|
throw new ClientException("TagNotFound", MessageFormat.format("Tag not found: {0}", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the parent
|
||||||
|
if (StringUtils.isEmpty(parentId)) {
|
||||||
|
parentId = null;
|
||||||
|
} else {
|
||||||
|
Tag parentTag = tagDao.getByTagId(principal.getId(), parentId);
|
||||||
|
if (parentTag == null) {
|
||||||
|
throw new ClientException("ParentNotFound", MessageFormat.format("Parent not found: {0}", parentId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for name duplicate
|
// Check for name duplicate
|
||||||
Tag tagDuplicate = tagDao.getByName(principal.getId(), name);
|
Tag tagDuplicate = tagDao.getByName(principal.getId(), name);
|
||||||
if (tagDuplicate != null && !tagDuplicate.getId().equals(id)) {
|
if (tagDuplicate != null && !tagDuplicate.getId().equals(id)) {
|
||||||
@ -174,6 +199,8 @@ public class TagResource extends BaseResource {
|
|||||||
if (!StringUtils.isEmpty(color)) {
|
if (!StringUtils.isEmpty(color)) {
|
||||||
tag.setColor(color);
|
tag.setColor(color);
|
||||||
}
|
}
|
||||||
|
// Parent tag is always updated to have the possibility to delete it
|
||||||
|
tag.setParentId(parentId);
|
||||||
|
|
||||||
tagDao.update(tag);
|
tagDao.update(tag);
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ import com.sismics.docs.rest.constant.BaseFunction;
|
|||||||
import com.sismics.rest.exception.ClientException;
|
import com.sismics.rest.exception.ClientException;
|
||||||
import com.sismics.rest.exception.ForbiddenClientException;
|
import com.sismics.rest.exception.ForbiddenClientException;
|
||||||
import com.sismics.rest.exception.ServerException;
|
import com.sismics.rest.exception.ServerException;
|
||||||
|
import com.sismics.rest.util.JsonUtil;
|
||||||
import com.sismics.rest.util.ValidationUtil;
|
import com.sismics.rest.util.ValidationUtil;
|
||||||
import com.sismics.security.UserPrincipal;
|
import com.sismics.security.UserPrincipal;
|
||||||
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
||||||
@ -513,8 +514,8 @@ public class UserResource extends BaseResource {
|
|||||||
for (AuthenticationToken authenticationToken : authenticationTokenDao.getByUserId(principal.getId())) {
|
for (AuthenticationToken authenticationToken : authenticationTokenDao.getByUserId(principal.getId())) {
|
||||||
JsonObjectBuilder session = Json.createObjectBuilder()
|
JsonObjectBuilder session = Json.createObjectBuilder()
|
||||||
.add("create_date", authenticationToken.getCreationDate().getTime())
|
.add("create_date", authenticationToken.getCreationDate().getTime())
|
||||||
.add("ip", authenticationToken.getIp())
|
.add("ip", JsonUtil.nullable(authenticationToken.getIp()))
|
||||||
.add("user_agent", authenticationToken.getUserAgent());
|
.add("user_agent", JsonUtil.nullable(authenticationToken.getUserAgent()));
|
||||||
if (authenticationToken.getLastConnectionDate() != null) {
|
if (authenticationToken.getLastConnectionDate() != null) {
|
||||||
session.add("last_connection_date", authenticationToken.getLastConnectionDate().getTime());
|
session.add("last_connection_date", authenticationToken.getLastConnectionDate().getTime());
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,9 @@ angular.module('docs',
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.state('document.default.search', {
|
||||||
|
url: '/search/:search'
|
||||||
|
})
|
||||||
.state('document.default.file', {
|
.state('document.default.file', {
|
||||||
url: '/file/:fileId',
|
url: '/file/:fileId',
|
||||||
views: {
|
views: {
|
||||||
@ -153,6 +156,7 @@ angular.module('docs',
|
|||||||
})
|
})
|
||||||
.state('document.view', {
|
.state('document.view', {
|
||||||
url: '/view/:id',
|
url: '/view/:id',
|
||||||
|
redirectTo: 'document.view.content',
|
||||||
views: {
|
views: {
|
||||||
'document': {
|
'document': {
|
||||||
templateUrl: 'partial/docs/document.view.html',
|
templateUrl: 'partial/docs/document.view.html',
|
||||||
@ -160,6 +164,33 @@ angular.module('docs',
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.state('document.view.content', {
|
||||||
|
url: '/content',
|
||||||
|
views: {
|
||||||
|
'tab': {
|
||||||
|
templateUrl: 'partial/docs/document.view.content.html',
|
||||||
|
controller: 'DocumentViewContent'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.state('document.view.permissions', {
|
||||||
|
url: '/permissions',
|
||||||
|
views: {
|
||||||
|
'tab': {
|
||||||
|
templateUrl: 'partial/docs/document.view.permissions.html',
|
||||||
|
controller: 'DocumentViewPermissions'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.state('document.view.activity', {
|
||||||
|
url: '/activity',
|
||||||
|
views: {
|
||||||
|
'tab': {
|
||||||
|
templateUrl: 'partial/docs/document.view.activity.html',
|
||||||
|
controller: 'DocumentViewActivity'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.state('document.view.file', {
|
.state('document.view.file', {
|
||||||
url: '/file/:fileId',
|
url: '/file/:fileId',
|
||||||
views: {
|
views: {
|
||||||
@ -228,4 +259,18 @@ angular.module('docs',
|
|||||||
$rootScope.$state = $state;
|
$rootScope.$state = $state;
|
||||||
$rootScope.$stateParams = $stateParams;
|
$rootScope.$stateParams = $stateParams;
|
||||||
$rootScope.pageTitle = 'Sismics Docs';
|
$rootScope.pageTitle = 'Sismics Docs';
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Redirection support for ui-router.
|
||||||
|
* Thanks to https://github.com/acollard
|
||||||
|
* See https://github.com/angular-ui/ui-router/issues/1584#issuecomment-76993045
|
||||||
|
*/
|
||||||
|
.run(function($rootScope, $state){
|
||||||
|
$rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
|
||||||
|
var redirect = toState.redirectTo;
|
||||||
|
if (redirect) {
|
||||||
|
event.preventDefault();
|
||||||
|
$state.go(redirect, toParams);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
@ -12,7 +12,8 @@ angular.module('docs').controller('Document', function($scope, $timeout, $state,
|
|||||||
$scope.offset = 0;
|
$scope.offset = 0;
|
||||||
$scope.currentPage = 1;
|
$scope.currentPage = 1;
|
||||||
$scope.limit = _.isUndefined(localStorage.documentsPageSize) ? 10 : localStorage.documentsPageSize;
|
$scope.limit = _.isUndefined(localStorage.documentsPageSize) ? 10 : localStorage.documentsPageSize;
|
||||||
$scope.search = '';
|
$scope.search = $state.params.search ? $state.params.search : '';
|
||||||
|
$scope.setSearch = function(search) { $scope.search = search };
|
||||||
|
|
||||||
// A timeout promise is used to slow down search requests to the server
|
// A timeout promise is used to slow down search requests to the server
|
||||||
// We keep track of it for cancellation purpose
|
// We keep track of it for cancellation purpose
|
||||||
@ -65,6 +66,17 @@ angular.module('docs').controller('Document', function($scope, $timeout, $state,
|
|||||||
$timeout.cancel(timeoutPromise);
|
$timeout.cancel(timeoutPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($state.current.name == 'document.default'
|
||||||
|
|| $state.current.name == 'document.default.search') {
|
||||||
|
$state.go($scope.search == '' ?
|
||||||
|
'document.default' : 'document.default.search', {
|
||||||
|
search: $scope.search
|
||||||
|
}, {
|
||||||
|
location: 'replace',
|
||||||
|
notify: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Call API later
|
// Call API later
|
||||||
timeoutPromise = $timeout(function () {
|
timeoutPromise = $timeout(function () {
|
||||||
$scope.loadDocuments();
|
$scope.loadDocuments();
|
||||||
@ -99,6 +111,22 @@ angular.module('docs').controller('Document', function($scope, $timeout, $state,
|
|||||||
* Display a document.
|
* Display a document.
|
||||||
*/
|
*/
|
||||||
$scope.viewDocument = function(id) {
|
$scope.viewDocument = function(id) {
|
||||||
$state.transitionTo('document.view', { id: id });
|
$state.go('document.view', { id: id });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load tags
|
||||||
|
var tags = [];
|
||||||
|
Restangular.one('tag/list').getList().then(function(data) {
|
||||||
|
tags = data.tags;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find children tags.
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
$scope.getChildrenTags = function(parent) {
|
||||||
|
return _.filter(tags, function(tag) {
|
||||||
|
return tag.parent == parent;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
@ -81,7 +81,7 @@ angular.module('docs').controller('DocumentDefault', function($scope, $state, Re
|
|||||||
* Navigate to the selected file.
|
* Navigate to the selected file.
|
||||||
*/
|
*/
|
||||||
$scope.openFile = function (file) {
|
$scope.openFile = function (file) {
|
||||||
$state.transitionTo('document.default.file', { fileId: file.id })
|
$state.go('document.default.file', { fileId: file.id })
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,6 +107,6 @@ angular.module('docs').controller('DocumentDefault', function($scope, $state, Re
|
|||||||
* Add a document with checked files.
|
* Add a document with checked files.
|
||||||
*/
|
*/
|
||||||
$scope.addDocument = function() {
|
$scope.addDocument = function() {
|
||||||
$state.transitionTo('document.add', { files: _.pluck($scope.checkedFiles(), 'id') });
|
$state.go('document.add', { files: _.pluck($scope.checkedFiles(), 'id') });
|
||||||
};
|
};
|
||||||
});
|
});
|
@ -95,7 +95,7 @@ angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $
|
|||||||
if ($scope.isEdit()) {
|
if ($scope.isEdit()) {
|
||||||
// Go back to the edited document
|
// Go back to the edited document
|
||||||
$scope.pageDocuments();
|
$scope.pageDocuments();
|
||||||
$state.transitionTo('document.view', { id: $stateParams.id });
|
$state.go('document.view', { id: $stateParams.id });
|
||||||
} else {
|
} else {
|
||||||
// Reset the scope and stay here
|
// Reset the scope and stay here
|
||||||
var fileUploadCount = _.size($scope.newFiles) + resolve.length;
|
var fileUploadCount = _.size($scope.newFiles) + resolve.length;
|
||||||
@ -188,9 +188,9 @@ angular.module('docs').controller('DocumentEdit', function($rootScope, $scope, $
|
|||||||
*/
|
*/
|
||||||
$scope.cancel = function() {
|
$scope.cancel = function() {
|
||||||
if ($scope.isEdit()) {
|
if ($scope.isEdit()) {
|
||||||
$state.transitionTo('document.view', { id: $stateParams.id });
|
$state.go('document.view', { id: $stateParams.id });
|
||||||
} else {
|
} else {
|
||||||
$state.transitionTo('document.default');
|
$state.go('document.default');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Document view controller.
|
* Document view controller.
|
||||||
*/
|
*/
|
||||||
angular.module('docs').controller('DocumentView', function ($scope, $state, $stateParams, $location, $dialog, $modal, Restangular, $upload, $q) {
|
angular.module('docs').controller('DocumentView', function ($scope, $state, $stateParams, $location, $dialog, $modal, Restangular, $timeout) {
|
||||||
// Load document data from server
|
// Load document data from server
|
||||||
Restangular.one('document', $stateParams.id).get().then(function(data) {
|
Restangular.one('document', $stateParams.id).get().then(function(data) {
|
||||||
$scope.document = data;
|
$scope.document = data;
|
||||||
@ -11,60 +11,6 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
|||||||
$scope.error = response;
|
$scope.error = response;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load audit log data from server
|
|
||||||
Restangular.one('auditlog').get({
|
|
||||||
document: $stateParams.id
|
|
||||||
}).then(function(data) {
|
|
||||||
$scope.logs = data.logs;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Watch for ACLs change and group them for easy displaying
|
|
||||||
$scope.$watch('document.acls', function(acls) {
|
|
||||||
$scope.acls = _.groupBy(acls, function(acl) {
|
|
||||||
return acl.id;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize add ACL
|
|
||||||
$scope.acl = { perm: 'READ' };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration for file sorting.
|
|
||||||
*/
|
|
||||||
$scope.fileSortableOptions = {
|
|
||||||
forceHelperSize: true,
|
|
||||||
forcePlaceholderSize: true,
|
|
||||||
tolerance: 'pointer',
|
|
||||||
handle: '.handle',
|
|
||||||
stop: function () {
|
|
||||||
// Send new positions to server
|
|
||||||
$scope.$apply(function () {
|
|
||||||
Restangular.one('file').post('reorder', {
|
|
||||||
id: $stateParams.id,
|
|
||||||
order: _.pluck($scope.files, 'id')
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load files from server.
|
|
||||||
*/
|
|
||||||
$scope.loadFiles = function () {
|
|
||||||
Restangular.one('file').getList('list', { id: $stateParams.id }).then(function (data) {
|
|
||||||
$scope.files = data.files;
|
|
||||||
// TODO Keep currently uploading files
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$scope.loadFiles();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigate to the selected file.
|
|
||||||
*/
|
|
||||||
$scope.openFile = function (file) {
|
|
||||||
$state.transitionTo('document.view.file', { id: $stateParams.id, fileId: file.id })
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a document.
|
* Delete a document.
|
||||||
*/
|
*/
|
||||||
@ -80,27 +26,7 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
|||||||
if (result == 'ok') {
|
if (result == 'ok') {
|
||||||
Restangular.one('document', document.id).remove().then(function () {
|
Restangular.one('document', document.id).remove().then(function () {
|
||||||
$scope.loadDocuments();
|
$scope.loadDocuments();
|
||||||
$state.transitionTo('document.default');
|
$state.go('document.default');
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a file.
|
|
||||||
*/
|
|
||||||
$scope.deleteFile = function (file) {
|
|
||||||
var title = 'Delete file';
|
|
||||||
var msg = 'Do you really want to delete this file?';
|
|
||||||
var btns = [
|
|
||||||
{result: 'cancel', label: 'Cancel'},
|
|
||||||
{result: 'ok', label: 'OK', cssClass: 'btn-primary'}
|
|
||||||
];
|
|
||||||
|
|
||||||
$dialog.messageBox(title, msg, btns, function (result) {
|
|
||||||
if (result == 'ok') {
|
|
||||||
Restangular.one('file', file.id).remove().then(function () {
|
|
||||||
$scope.loadFiles();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -157,125 +83,4 @@ angular.module('docs').controller('DocumentView', function ($scope, $state, $sta
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* File has been drag & dropped.
|
|
||||||
*/
|
|
||||||
$scope.fileDropped = function(files) {
|
|
||||||
if (!$scope.document.writable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files && files.length) {
|
|
||||||
// Adding files to the UI
|
|
||||||
var newfiles = [];
|
|
||||||
_.each(files, function(file) {
|
|
||||||
var newfile = {
|
|
||||||
progress: 0,
|
|
||||||
name: file.name,
|
|
||||||
create_date: new Date().getTime(),
|
|
||||||
mimetype: file.type,
|
|
||||||
status: 'Pending...'
|
|
||||||
};
|
|
||||||
$scope.files.push(newfile);
|
|
||||||
newfiles.push(newfile);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Uploading files sequentially
|
|
||||||
var key = 0;
|
|
||||||
var then = function() {
|
|
||||||
if (files[key]) {
|
|
||||||
$scope.uploadFile(files[key], newfiles[key++]).then(then);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
then();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload a file.
|
|
||||||
*/
|
|
||||||
$scope.uploadFile = function(file, newfile) {
|
|
||||||
// Upload the file
|
|
||||||
newfile.status = 'Uploading...';
|
|
||||||
return $upload.upload({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '../api/file',
|
|
||||||
file: file,
|
|
||||||
fields: {
|
|
||||||
id: $stateParams.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.progress(function (e) {
|
|
||||||
newfile.progress = parseInt(100.0 * e.loaded / e.total);
|
|
||||||
})
|
|
||||||
.success(function (data) {
|
|
||||||
newfile.id = data.id;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an ACL.
|
|
||||||
*/
|
|
||||||
$scope.deleteAcl = function(acl) {
|
|
||||||
Restangular.one('acl/' + $stateParams.id + '/' + acl.perm + '/' + acl.id, null).remove().then(function () {
|
|
||||||
$scope.document.acls = _.reject($scope.document.acls, function(s) {
|
|
||||||
return angular.equals(acl, s);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an ACL.
|
|
||||||
*/
|
|
||||||
$scope.addAcl = function() {
|
|
||||||
// Compute ACLs to add
|
|
||||||
$scope.acl.source = $stateParams.id;
|
|
||||||
var acls = [];
|
|
||||||
if ($scope.acl.perm == 'READWRITE') {
|
|
||||||
acls = [{
|
|
||||||
source: $stateParams.id,
|
|
||||||
username: $scope.acl.username,
|
|
||||||
perm: 'READ'
|
|
||||||
}, {
|
|
||||||
source: $stateParams.id,
|
|
||||||
username: $scope.acl.username,
|
|
||||||
perm: 'WRITE'
|
|
||||||
}];
|
|
||||||
} else {
|
|
||||||
acls = [{
|
|
||||||
source: $stateParams.id,
|
|
||||||
username: $scope.acl.username,
|
|
||||||
perm: $scope.acl.perm
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add ACLs
|
|
||||||
_.each(acls, function(acl) {
|
|
||||||
Restangular.one('acl').put(acl).then(function(acl) {
|
|
||||||
if (_.isUndefined(acl.id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$scope.document.acls.push(acl);
|
|
||||||
$scope.document.acls = angular.copy($scope.document.acls);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reset form
|
|
||||||
$scope.acl = { perm: 'READ' };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-complete on ACL target.
|
|
||||||
*/
|
|
||||||
$scope.getTargetAclTypeahead = function($viewValue) {
|
|
||||||
var deferred = $q.defer();
|
|
||||||
Restangular.one('acl/target/search')
|
|
||||||
.get({
|
|
||||||
search: $viewValue
|
|
||||||
}).then(function(data) {
|
|
||||||
deferred.resolve(_.pluck(data.users, 'username'), true);
|
|
||||||
});
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
});
|
});
|
@ -0,0 +1,13 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document view activity controller.
|
||||||
|
*/
|
||||||
|
angular.module('docs').controller('DocumentViewActivity', function ($scope, $stateParams, Restangular) {
|
||||||
|
// Load audit log data from server
|
||||||
|
Restangular.one('auditlog').get({
|
||||||
|
document: $stateParams.id
|
||||||
|
}).then(function(data) {
|
||||||
|
$scope.logs = data.logs;
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,119 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document view content controller.
|
||||||
|
*/
|
||||||
|
angular.module('docs').controller('DocumentViewContent', function ($scope, $stateParams, Restangular, $dialog, $state, $upload) {
|
||||||
|
/**
|
||||||
|
* Configuration for file sorting.
|
||||||
|
*/
|
||||||
|
$scope.fileSortableOptions = {
|
||||||
|
forceHelperSize: true,
|
||||||
|
forcePlaceholderSize: true,
|
||||||
|
tolerance: 'pointer',
|
||||||
|
handle: '.handle',
|
||||||
|
stop: function () {
|
||||||
|
// Send new positions to server
|
||||||
|
$scope.$apply(function () {
|
||||||
|
Restangular.one('file').post('reorder', {
|
||||||
|
id: $stateParams.id,
|
||||||
|
order: _.pluck($scope.files, 'id')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load files from server.
|
||||||
|
*/
|
||||||
|
$scope.loadFiles = function () {
|
||||||
|
Restangular.one('file').getList('list', { id: $stateParams.id }).then(function (data) {
|
||||||
|
$scope.files = data.files;
|
||||||
|
// TODO Keep currently uploading files
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$scope.loadFiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the selected file.
|
||||||
|
*/
|
||||||
|
$scope.openFile = function (file) {
|
||||||
|
$state.go('document.view.file', { id: $stateParams.id, fileId: file.id })
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a file.
|
||||||
|
*/
|
||||||
|
$scope.deleteFile = function (file) {
|
||||||
|
var title = 'Delete file';
|
||||||
|
var msg = 'Do you really want to delete this file?';
|
||||||
|
var btns = [
|
||||||
|
{result: 'cancel', label: 'Cancel'},
|
||||||
|
{result: 'ok', label: 'OK', cssClass: 'btn-primary'}
|
||||||
|
];
|
||||||
|
|
||||||
|
$dialog.messageBox(title, msg, btns, function (result) {
|
||||||
|
if (result == 'ok') {
|
||||||
|
Restangular.one('file', file.id).remove().then(function () {
|
||||||
|
$scope.loadFiles();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File has been drag & dropped.
|
||||||
|
*/
|
||||||
|
$scope.fileDropped = function(files) {
|
||||||
|
if (!$scope.document.writable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files && files.length) {
|
||||||
|
// Adding files to the UI
|
||||||
|
var newfiles = [];
|
||||||
|
_.each(files, function(file) {
|
||||||
|
var newfile = {
|
||||||
|
progress: 0,
|
||||||
|
name: file.name,
|
||||||
|
create_date: new Date().getTime(),
|
||||||
|
mimetype: file.type,
|
||||||
|
status: 'Pending...'
|
||||||
|
};
|
||||||
|
$scope.files.push(newfile);
|
||||||
|
newfiles.push(newfile);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Uploading files sequentially
|
||||||
|
var key = 0;
|
||||||
|
var then = function() {
|
||||||
|
if (files[key]) {
|
||||||
|
$scope.uploadFile(files[key], newfiles[key++]).then(then);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
then();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload a file.
|
||||||
|
*/
|
||||||
|
$scope.uploadFile = function(file, newfile) {
|
||||||
|
// Upload the file
|
||||||
|
newfile.status = 'Uploading...';
|
||||||
|
return $upload.upload({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '../api/file',
|
||||||
|
file: file,
|
||||||
|
fields: {
|
||||||
|
id: $stateParams.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.progress(function (e) {
|
||||||
|
newfile.progress = parseInt(100.0 * e.loaded / e.total);
|
||||||
|
})
|
||||||
|
.success(function (data) {
|
||||||
|
newfile.id = data.id;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
@ -0,0 +1,81 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document view permissions controller.
|
||||||
|
*/
|
||||||
|
angular.module('docs').controller('DocumentViewPermissions', function ($scope, $stateParams, Restangular, $q) {
|
||||||
|
// Watch for ACLs change and group them for easy displaying
|
||||||
|
$scope.$watch('document.acls', function(acls) {
|
||||||
|
$scope.acls = _.groupBy(acls, function(acl) {
|
||||||
|
return acl.id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize add ACL
|
||||||
|
$scope.acl = { perm: 'READ' };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an ACL.
|
||||||
|
*/
|
||||||
|
$scope.deleteAcl = function(acl) {
|
||||||
|
Restangular.one('acl/' + $stateParams.id + '/' + acl.perm + '/' + acl.id, null).remove().then(function () {
|
||||||
|
$scope.document.acls = _.reject($scope.document.acls, function(s) {
|
||||||
|
return angular.equals(acl, s);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an ACL.
|
||||||
|
*/
|
||||||
|
$scope.addAcl = function() {
|
||||||
|
// Compute ACLs to add
|
||||||
|
$scope.acl.source = $stateParams.id;
|
||||||
|
var acls = [];
|
||||||
|
if ($scope.acl.perm == 'READWRITE') {
|
||||||
|
acls = [{
|
||||||
|
source: $stateParams.id,
|
||||||
|
username: $scope.acl.username,
|
||||||
|
perm: 'READ'
|
||||||
|
}, {
|
||||||
|
source: $stateParams.id,
|
||||||
|
username: $scope.acl.username,
|
||||||
|
perm: 'WRITE'
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
acls = [{
|
||||||
|
source: $stateParams.id,
|
||||||
|
username: $scope.acl.username,
|
||||||
|
perm: $scope.acl.perm
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add ACLs
|
||||||
|
_.each(acls, function(acl) {
|
||||||
|
Restangular.one('acl').put(acl).then(function(acl) {
|
||||||
|
if (_.isUndefined(acl.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$scope.document.acls.push(acl);
|
||||||
|
$scope.document.acls = angular.copy($scope.document.acls);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset form
|
||||||
|
$scope.acl = { perm: 'READ' };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-complete on ACL target.
|
||||||
|
*/
|
||||||
|
$scope.getTargetAclTypeahead = function($viewValue) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
Restangular.one('acl/target/search')
|
||||||
|
.get({
|
||||||
|
search: $viewValue
|
||||||
|
}).then(function(data) {
|
||||||
|
deferred.resolve(_.pluck(data.users, 'username'), true);
|
||||||
|
});
|
||||||
|
return deferred.promise;
|
||||||
|
};
|
||||||
|
});
|
@ -9,7 +9,7 @@ angular.module('docs').controller('Login', function($scope, $rootScope, $state,
|
|||||||
User.userInfo(true).then(function(data) {
|
User.userInfo(true).then(function(data) {
|
||||||
$rootScope.userInfo = data;
|
$rootScope.userInfo = data;
|
||||||
});
|
});
|
||||||
$state.transitionTo('document.default');
|
$state.go('document.default');
|
||||||
}, function() {
|
}, function() {
|
||||||
var title = 'Login failed';
|
var title = 'Login failed';
|
||||||
var msg = 'Username or password invalid';
|
var msg = 'Username or password invalid';
|
||||||
|
@ -6,9 +6,13 @@
|
|||||||
angular.module('docs').controller('Main', function($scope, $rootScope, $state, User) {
|
angular.module('docs').controller('Main', function($scope, $rootScope, $state, User) {
|
||||||
User.userInfo().then(function(data) {
|
User.userInfo().then(function(data) {
|
||||||
if (data.anonymous) {
|
if (data.anonymous) {
|
||||||
$state.transitionTo('login');
|
$state.go('login', {}, {
|
||||||
|
location: 'replace'
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
$state.transitionTo('document.default');
|
$state.go('document.default', {}, {
|
||||||
|
location: 'replace'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -41,7 +41,7 @@ angular.module('docs').controller('Navigation', function($scope, $http, $state,
|
|||||||
*/
|
*/
|
||||||
$scope.openLogs = function() {
|
$scope.openLogs = function() {
|
||||||
$scope.errorNumber = 0;
|
$scope.errorNumber = 0;
|
||||||
$state.transitionTo('settings.log');
|
$state.go('settings.log');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +52,7 @@ angular.module('docs').controller('Navigation', function($scope, $http, $state,
|
|||||||
User.userInfo(true).then(function(data) {
|
User.userInfo(true).then(function(data) {
|
||||||
$rootScope.userInfo = data;
|
$rootScope.userInfo = data;
|
||||||
});
|
});
|
||||||
$state.transitionTo('main');
|
$state.go('main');
|
||||||
});
|
});
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,6 @@ angular.module('docs').controller('SettingsUser', function($scope, $state, Resta
|
|||||||
* Edit a user.
|
* Edit a user.
|
||||||
*/
|
*/
|
||||||
$scope.editUser = function(user) {
|
$scope.editUser = function(user) {
|
||||||
$state.transitionTo('settings.user.edit', { username: user.username });
|
$state.go('settings.user.edit', { username: user.username });
|
||||||
};
|
};
|
||||||
});
|
});
|
@ -38,7 +38,7 @@ angular.module('docs').controller('SettingsUserEdit', function($scope, $dialog,
|
|||||||
|
|
||||||
promise.then(function() {
|
promise.then(function() {
|
||||||
$scope.loadUsers();
|
$scope.loadUsers();
|
||||||
$state.transitionTo('settings.user');
|
$state.go('settings.user');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -54,9 +54,9 @@ angular.module('docs').controller('SettingsUserEdit', function($scope, $dialog,
|
|||||||
if (result == 'ok') {
|
if (result == 'ok') {
|
||||||
Restangular.one('user', $stateParams.username).remove().then(function() {
|
Restangular.one('user', $stateParams.username).remove().then(function() {
|
||||||
$scope.loadUsers();
|
$scope.loadUsers();
|
||||||
$state.transitionTo('settings.user');
|
$state.go('settings.user');
|
||||||
}, function () {
|
}, function () {
|
||||||
$state.transitionTo('settings.user');
|
$state.go('settings.user');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -24,7 +24,7 @@ angular.module('share').controller('FileModalView', function($rootScope, $modalI
|
|||||||
if (value.id == $stateParams.fileId) {
|
if (value.id == $stateParams.fileId) {
|
||||||
var next = $scope.files[key + 1];
|
var next = $scope.files[key + 1];
|
||||||
if (next) {
|
if (next) {
|
||||||
$state.transitionTo('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: next.id });
|
$state.go('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: next.id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -38,7 +38,7 @@ angular.module('share').controller('FileModalView', function($rootScope, $modalI
|
|||||||
if (value.id == $stateParams.fileId) {
|
if (value.id == $stateParams.fileId) {
|
||||||
var previous = $scope.files[key - 1];
|
var previous = $scope.files[key - 1];
|
||||||
if (previous) {
|
if (previous) {
|
||||||
$state.transitionTo('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: previous.id });
|
$state.go('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: previous.id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -16,6 +16,6 @@ angular.module('share').controller('FileView', function($modal, $state, $statePa
|
|||||||
modal.closed = true;
|
modal.closed = true;
|
||||||
},function(result) {
|
},function(result) {
|
||||||
modal.closed = true;
|
modal.closed = true;
|
||||||
$state.transitionTo('share', { documentId: $stateParams.documentId, shareId: $stateParams.shareId });
|
$state.go('share', { documentId: $stateParams.documentId, shareId: $stateParams.shareId });
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -10,7 +10,7 @@ angular.module('share').controller('Share', function($scope, $state, $stateParam
|
|||||||
$scope.document = data;
|
$scope.document = data;
|
||||||
}, function (response) {
|
}, function (response) {
|
||||||
if (response.status == 403) {
|
if (response.status == 403) {
|
||||||
$state.transitionTo('403');
|
$state.go('403');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -24,6 +24,6 @@ angular.module('share').controller('Share', function($scope, $state, $stateParam
|
|||||||
* Navigate to the selected file.
|
* Navigate to the selected file.
|
||||||
*/
|
*/
|
||||||
$scope.openFile = function (file) {
|
$scope.openFile = function (file) {
|
||||||
$state.transitionTo('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: file.id })
|
$state.go('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: file.id })
|
||||||
};
|
};
|
||||||
});
|
});
|
@ -43,6 +43,9 @@
|
|||||||
<script src="app/docs/controller/DocumentDefault.js" type="text/javascript"></script>
|
<script src="app/docs/controller/DocumentDefault.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/DocumentEdit.js" type="text/javascript"></script>
|
<script src="app/docs/controller/DocumentEdit.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/DocumentView.js" type="text/javascript"></script>
|
<script src="app/docs/controller/DocumentView.js" type="text/javascript"></script>
|
||||||
|
<script src="app/docs/controller/DocumentViewContent.js" type="text/javascript"></script>
|
||||||
|
<script src="app/docs/controller/DocumentViewPermissions.js" type="text/javascript"></script>
|
||||||
|
<script src="app/docs/controller/DocumentViewActivity.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/DocumentModalShare.js" type="text/javascript"></script>
|
<script src="app/docs/controller/DocumentModalShare.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/FileView.js" type="text/javascript"></script>
|
<script src="app/docs/controller/FileView.js" type="text/javascript"></script>
|
||||||
<script src="app/docs/controller/FileModalView.js" type="text/javascript"></script>
|
<script src="app/docs/controller/FileModalView.js" type="text/javascript"></script>
|
||||||
|
@ -5,49 +5,61 @@
|
|||||||
<a href="#/document/add" class="btn btn-primary"><span class="glyphicon glyphicon-plus"></span> Add a document</a>
|
<a href="#/document/add" class="btn btn-primary"><span class="glyphicon glyphicon-plus"></span> Add a document</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="row">
|
||||||
<span class="input-group-addon">
|
<div class="dropdown col-md-2 tag-tree-dropdown" dropdown>
|
||||||
<span class="glyphicon glyphicon glyphicon-info-sign"
|
<button class="btn btn-block btn-default" dropdown-toggle ng-disabled="disabled">
|
||||||
tooltip-placement="bottom"
|
Tags <span class="caret"></span>
|
||||||
tooltip-html-unsafe="before:2012-05<br/>
|
</button>
|
||||||
after:2012-05<br/>
|
<ul class="dropdown-menu tag-tree">
|
||||||
at:2012-05<br/>
|
<li ng-if="getChildrenTags().length == 0">No tags</li>
|
||||||
tag:car<br/>
|
<li ng-repeat="tag in getChildrenTags()" ng-include="'tag-tree-item'"></li>
|
||||||
full:led<br/>
|
</ul>
|
||||||
shared:yes<br/>
|
</div>
|
||||||
lang:fra"></span>
|
|
||||||
</span>
|
<div class="col-md-10 input-group">
|
||||||
<input type="search" class="form-control" placeholder="Search" ng-model="search" />
|
<span class="input-group-addon">
|
||||||
<span class="input-group-addon">
|
<span class="glyphicon glyphicon glyphicon-info-sign"
|
||||||
<span class="glyphicon glyphicon-search" ng-show="search.length == 0"></span>
|
tooltip-placement="bottom"
|
||||||
<span class="glyphicon glyphicon-remove" ng-show="search.length > 0" ng-click="search = ''"></span>
|
tooltip-html-unsafe="before:2012-05<br/>
|
||||||
</span>
|
after:2012-05<br/>
|
||||||
|
at:2012-05<br/>
|
||||||
|
tag:car<br/>
|
||||||
|
full:led<br/>
|
||||||
|
shared:yes<br/>
|
||||||
|
lang:fra"></span>
|
||||||
|
</span>
|
||||||
|
<input type="search" class="form-control" placeholder="Search" ng-model="search" />
|
||||||
|
<span class="input-group-addon">
|
||||||
|
<span class="glyphicon glyphicon-search" ng-show="search.length == 0"></span>
|
||||||
|
<span class="glyphicon glyphicon-remove" ng-show="search.length > 0" ng-click="search = ''"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="row table table-striped table-hover table-documents">
|
<table class="row table table-striped table-hover table-documents">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-xs-6" ng-click="sortDocuments(1)"><span class="glyphicon glyphicon-chevron-{{ sortColumn == 1 ? (asc ? 'down' : 'up') : '' }}"></span> Title</th>
|
<th class="col-xs-6" ng-click="sortDocuments(1)"><span class="glyphicon glyphicon-chevron-{{ sortColumn == 1 ? (asc ? 'down' : 'up') : '' }}"></span> Title</th>
|
||||||
<th class="col-xs-3" ng-click="sortDocuments(3)"><span class="glyphicon glyphicon-chevron-{{ sortColumn == 3 ? (asc ? 'down' : 'up') : '' }}"></span> Creation date</th>
|
<th class="col-xs-3" ng-click="sortDocuments(3)"><span class="glyphicon glyphicon-chevron-{{ sortColumn == 3 ? (asc ? 'down' : 'up') : '' }}"></span> Creation date</th>
|
||||||
<th class="col-xs-3 hidden-xs">Tags</th>
|
<th class="col-xs-3 hidden-xs">Tags</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-click="viewDocument(document.id)" ng-repeat="document in documents">
|
<tr ng-click="viewDocument(document.id)" ng-repeat="document in documents">
|
||||||
<td>
|
<td>
|
||||||
{{ document.title }} ({{ document.file_count }})
|
{{ document.title }} ({{ document.file_count }})
|
||||||
<span class="glyphicon glyphicon-share" ng-if="document.shared" tooltip="Shared"></span>
|
<span class="glyphicon glyphicon-share" ng-if="document.shared" tooltip="Shared"></span>
|
||||||
<a href="#/document/view/{{ document.id }}" ng-click="$event.stopPropagation()"><span class="glyphicon glyphicon-link"></span></a>
|
<a href="#/document/view/{{ document.id }}" target="_blank" ng-click="$event.stopPropagation()"><span class="glyphicon glyphicon-link"></span></a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ document.create_date | date: 'yyyy-MM-dd' }}</td>
|
<td>{{ document.create_date | date: 'yyyy-MM-dd' }}</td>
|
||||||
<td class="hidden-xs cell-tags">
|
<td class="hidden-xs cell-tags">
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<span class="label label-info" ng-repeat="tag in document.tags" ng-style="{ 'background': tag.color }">
|
<span class="label label-info" ng-repeat="tag in document.tags" ng-style="{ 'background': tag.color }">
|
||||||
<span class="shorten">{{ tag.name | shorten }}</span><span class="full">{{ tag.name }}</span>
|
<span class="shorten">{{ tag.name | shorten }}</span><span class="full">{{ tag.name }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@ -70,4 +82,14 @@
|
|||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div ui-view="document"></div>
|
<div ui-view="document"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="tag-tree-item">
|
||||||
|
<span class="btn" ng-style="{ 'background-color': tag.color }"></span>
|
||||||
|
<span class="btn btn-link" ng-click="setSearch('tag:' + tag.name)">
|
||||||
|
{{ tag.name }}
|
||||||
|
</span>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li ng-repeat="tag in getChildrenTags(tag.id)" ng-include="'tag-tree-item'"></li>
|
||||||
|
</ul>
|
||||||
|
</script>
|
@ -0,0 +1 @@
|
|||||||
|
<audit-log logs="logs" />
|
@ -0,0 +1,37 @@
|
|||||||
|
<p ng-bind-html="document.description | newline"></p>
|
||||||
|
|
||||||
|
<div ng-file-drop drag-over-class="bg-success" ng-multiple="true" allow-dir="false" ng-model="dropFiles"
|
||||||
|
accept="image/*,application/pdf,application/zip" ng-file-change="fileDropped($files, $event, $rejectedFiles)">
|
||||||
|
<div class="row upload-zone" ui-sortable="fileSortableOptions" ng-model="files">
|
||||||
|
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 text-center" ng-repeat="file in files">
|
||||||
|
<div class="thumbnail" ng-if="file.id">
|
||||||
|
<a ng-click="openFile(file)">
|
||||||
|
<img class="thumbnail-file" ng-src="../api/file/{{ file.id }}/data?size=thumb" tooltip="{{ file.mimetype }}" tooltip-placement="top" />
|
||||||
|
</a>
|
||||||
|
<div class="caption" ng-show="document.writable">
|
||||||
|
<div class="pull-left">
|
||||||
|
<div class="btn btn-default handle"><span class="glyphicon glyphicon-resize-horizontal"></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-right">
|
||||||
|
<button class="btn btn-danger" ng-click="deleteFile(file)"><span class="glyphicon glyphicon-trash"></span></button>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="thumbnail" ng-if="!file.id">
|
||||||
|
<p class="text-center lead">
|
||||||
|
{{ file.status }}
|
||||||
|
</p>
|
||||||
|
<div class="caption">
|
||||||
|
<progressbar value="file.progress" class="progress-info active"></progressbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-center well-lg" ng-if="files.length == 0">
|
||||||
|
<span class="glyphicon glyphicon-move"></span>
|
||||||
|
Drag & drop files here to upload
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -38,119 +38,24 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<tabset>
|
<ul class="nav nav-tabs">
|
||||||
<tab>
|
<li ng-class="{ active: $state.current.name == 'document.view.content' }">
|
||||||
<tab-heading class="pointer">
|
<a href="#/document/view/{{ document.id }}/content">
|
||||||
<span class="glyphicon glyphicon-file"></span> Content
|
<span class="glyphicon glyphicon-file"></span> Content
|
||||||
</tab-heading>
|
</a>
|
||||||
|
</li>
|
||||||
<p ng-bind-html="document.description | newline"></p>
|
<li ng-class="{ active: $state.current.name == 'document.view.permissions' }">
|
||||||
|
<a href="#/document/view/{{ document.id }}/permissions">
|
||||||
<div ng-file-drop drag-over-class="bg-success" ng-multiple="true" allow-dir="false" ng-model="dropFiles"
|
|
||||||
accept="image/*,application/pdf,application/zip" ng-file-change="fileDropped($files, $event, $rejectedFiles)">
|
|
||||||
<div class="row upload-zone" ui-sortable="fileSortableOptions" ng-model="files">
|
|
||||||
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 text-center" ng-repeat="file in files">
|
|
||||||
<div class="thumbnail" ng-if="file.id">
|
|
||||||
<a ng-click="openFile(file)">
|
|
||||||
<img class="thumbnail-file" ng-src="../api/file/{{ file.id }}/data?size=thumb" tooltip="{{ file.mimetype }}" tooltip-placement="top" />
|
|
||||||
</a>
|
|
||||||
<div class="caption" ng-show="document.writable">
|
|
||||||
<div class="pull-left">
|
|
||||||
<div class="btn btn-default handle"><span class="glyphicon glyphicon-resize-horizontal"></span></div>
|
|
||||||
</div>
|
|
||||||
<div class="pull-right">
|
|
||||||
<button class="btn btn-danger" ng-click="deleteFile(file)"><span class="glyphicon glyphicon-trash"></span></button>
|
|
||||||
</div>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="thumbnail" ng-if="!file.id">
|
|
||||||
<p class="text-center lead">
|
|
||||||
{{ file.status }}
|
|
||||||
</p>
|
|
||||||
<div class="caption">
|
|
||||||
<progressbar value="file.progress" class="progress-info active"></progressbar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="text-center well-lg" ng-if="files.length == 0">
|
|
||||||
<span class="glyphicon glyphicon-move"></span>
|
|
||||||
Drag & drop files here to upload
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</tab>
|
|
||||||
|
|
||||||
<tab>
|
|
||||||
<tab-heading class="pointer">
|
|
||||||
<span class="glyphicon glyphicon-user"></span> Permissions
|
<span class="glyphicon glyphicon-user"></span> Permissions
|
||||||
</tab-heading>
|
</a>
|
||||||
|
</li>
|
||||||
<table class="table">
|
<li ng-class="{ active: $state.current.name == 'document.view.activity' }">
|
||||||
<tr>
|
<a href="#/document/view/{{ document.id }}/activity">
|
||||||
<th style="width: 40%">For</th>
|
|
||||||
<th style="width: 40%">Permission</th>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr ng-repeat="(id, acl) in acls">
|
|
||||||
<td><em>{{ acl[0].type == 'SHARE' ? 'Shared' : 'User' }}</em> {{ acl[0].name }}</td>
|
|
||||||
<td>
|
|
||||||
<span class="label label-default" style="margin-right: 6px;" ng-repeat="a in acl | orderBy: 'perm'">
|
|
||||||
{{ a.perm }}
|
|
||||||
<span ng-show="(document.creator != a.name && a.type == 'USER' || a.type != 'USER') && document.writable"
|
|
||||||
class="glyphicon glyphicon-remove pointer"
|
|
||||||
ng-click="deleteAcl(a)"></span>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div ng-show="document.writable">
|
|
||||||
<h4>Add a permission</h4>
|
|
||||||
|
|
||||||
<form name="aclForm" class="form-horizontal">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class=" col-sm-2 control-label" for="inputTarget">User</label>
|
|
||||||
<div class="col-sm-3">
|
|
||||||
<input required ng-maxlength="50" class="form-control" type="text" id="inputTarget"
|
|
||||||
placeholder="Type a username" name="username" ng-model="acl.username" autocomplete="off"
|
|
||||||
typeahead="username for username in getTargetAclTypeahead($viewValue) | filter: $viewValue"
|
|
||||||
typeahead-wait-ms="200" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class=" col-sm-2 control-label" for="inputPermission">Permission</label>
|
|
||||||
<div class="col-sm-3">
|
|
||||||
<select class="form-control" ng-model="acl.perm" id="inputPermission">
|
|
||||||
<option value="READ">Can read</option>
|
|
||||||
<option value="READWRITE">Can edit</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
|
||||||
<button type="submit" class="btn btn-primary" ng-disabled="!aclForm.$valid" ng-click="addAcl()">
|
|
||||||
<span class="glyphicon glyphicon-plus"></span>
|
|
||||||
Add
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</tab>
|
|
||||||
|
|
||||||
<tab>
|
|
||||||
<tab-heading class="pointer">
|
|
||||||
<span class="glyphicon glyphicon-tasks"></span> Activity
|
<span class="glyphicon glyphicon-tasks"></span> Activity
|
||||||
</tab-heading>
|
</a>
|
||||||
|
</li>
|
||||||
<audit-log logs="logs" />
|
</ul>
|
||||||
</tab>
|
|
||||||
</tabset>
|
|
||||||
|
|
||||||
|
<div ui-view="tab"></div>
|
||||||
<div ui-view="file"></div>
|
<div ui-view="file"></div>
|
||||||
</div>
|
</div>
|
@ -0,0 +1,53 @@
|
|||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<th style="width: 40%">For</th>
|
||||||
|
<th style="width: 40%">Permission</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr ng-repeat="(id, acl) in acls">
|
||||||
|
<td><em>{{ acl[0].type == 'SHARE' ? 'Shared' : 'User' }}</em> {{ acl[0].name }}</td>
|
||||||
|
<td>
|
||||||
|
<span class="label label-default" style="margin-right: 6px;" ng-repeat="a in acl | orderBy: 'perm'">
|
||||||
|
{{ a.perm }}
|
||||||
|
<span ng-show="(document.creator != a.name && a.type == 'USER' || a.type != 'USER') && document.writable"
|
||||||
|
class="glyphicon glyphicon-remove pointer"
|
||||||
|
ng-click="deleteAcl(a)"></span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div ng-show="document.writable">
|
||||||
|
<h4>Add a permission</h4>
|
||||||
|
|
||||||
|
<form name="aclForm" class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class=" col-sm-2 control-label" for="inputTarget">User</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<input required ng-maxlength="50" class="form-control" type="text" id="inputTarget"
|
||||||
|
placeholder="Type a username" name="username" ng-model="acl.username" autocomplete="off"
|
||||||
|
typeahead="username for username in getTargetAclTypeahead($viewValue) | filter: $viewValue"
|
||||||
|
typeahead-wait-ms="200" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class=" col-sm-2 control-label" for="inputPermission">Permission</label>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<select class="form-control" ng-model="acl.perm" id="inputPermission">
|
||||||
|
<option value="READ">Can read</option>
|
||||||
|
<option value="READWRITE">Can edit</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<button type="submit" class="btn btn-primary" ng-disabled="!aclForm.$valid" ng-click="addAcl()">
|
||||||
|
<span class="glyphicon glyphicon-plus"></span>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
@ -21,6 +21,15 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="tag in tags | filter:search">
|
<tr ng-repeat="tag in tags | filter:search">
|
||||||
<td><inline-edit value="tag.name" on-edit="updateTag(tag)" /></td>
|
<td><inline-edit value="tag.name" on-edit="updateTag(tag)" /></td>
|
||||||
|
<td class="col-xs-4">
|
||||||
|
<select class="form-control" ng-model="tag.parent" ng-change="updateTag(tag)">
|
||||||
|
<option value="" ng-selected="!tag.parent"></option>
|
||||||
|
<option ng-repeat="tag0 in tags"
|
||||||
|
ng-if="tag0.id != tag.id"
|
||||||
|
ng-selected="tag.parent == tag0.id"
|
||||||
|
value="{{ tag0.id }}">Parent: {{ tag0.name }}</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
<td class="col-xs-1"><span colorpicker class="btn" on-hide="updateTag(tag)" data-color="" ng-model="tag.color" ng-style="{ 'background': tag.color }"> </span></td>
|
<td class="col-xs-1"><span colorpicker class="btn" on-hide="updateTag(tag)" data-color="" ng-model="tag.color" ng-style="{ 'background': tag.color }"> </span></td>
|
||||||
<td class="col-xs-1"><button class="btn btn-danger pull-right" ng-click="deleteTag(tag)"><span class="glyphicon glyphicon-trash"></span></button></td>
|
<td class="col-xs-1"><button class="btn btn-danger pull-right" ng-click="deleteTag(tag)"><span class="glyphicon glyphicon-trash"></span></button></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -183,4 +183,17 @@ input[readonly].share-link {
|
|||||||
|
|
||||||
.tab-pane {
|
.tab-pane {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag tree
|
||||||
|
.tag-tree-dropdown {
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
.tag-tree {
|
||||||
|
li {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=1
|
db.version=2
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=1
|
db.version=2
|
@ -2,6 +2,7 @@ package com.sismics.docs.rest;
|
|||||||
|
|
||||||
import javax.json.JsonArray;
|
import javax.json.JsonArray;
|
||||||
import javax.json.JsonObject;
|
import javax.json.JsonObject;
|
||||||
|
import javax.json.JsonValue;
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
import javax.ws.rs.core.Form;
|
import javax.ws.rs.core.Form;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
@ -44,7 +45,8 @@ public class TestTagResource extends BaseJerseyTest {
|
|||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token)
|
||||||
.put(Entity.form(new Form()
|
.put(Entity.form(new Form()
|
||||||
.param("name", "Tag4")
|
.param("name", "Tag4")
|
||||||
.param("color", "#00ff00")), JsonObject.class);
|
.param("color", "#00ff00")
|
||||||
|
.param("parent", tag3Id)), JsonObject.class);
|
||||||
String tag4Id = json.getString("id");
|
String tag4Id = json.getString("id");
|
||||||
Assert.assertNotNull(tag4Id);
|
Assert.assertNotNull(tag4Id);
|
||||||
|
|
||||||
@ -129,6 +131,7 @@ public class TestTagResource extends BaseJerseyTest {
|
|||||||
Assert.assertTrue(tags.size() > 0);
|
Assert.assertTrue(tags.size() > 0);
|
||||||
Assert.assertEquals("Tag4", tags.getJsonObject(1).getString("name"));
|
Assert.assertEquals("Tag4", tags.getJsonObject(1).getString("name"));
|
||||||
Assert.assertEquals("#00ff00", tags.getJsonObject(1).getString("color"));
|
Assert.assertEquals("#00ff00", tags.getJsonObject(1).getString("color"));
|
||||||
|
Assert.assertEquals(tag3Id, tags.getJsonObject(1).getString("parent"));
|
||||||
|
|
||||||
// Update a tag
|
// Update a tag
|
||||||
json = target().path("/tag/" + tag4Id).request()
|
json = target().path("/tag/" + tag4Id).request()
|
||||||
@ -146,6 +149,7 @@ public class TestTagResource extends BaseJerseyTest {
|
|||||||
Assert.assertTrue(tags.size() > 0);
|
Assert.assertTrue(tags.size() > 0);
|
||||||
Assert.assertEquals("UpdatedName", tags.getJsonObject(1).getString("name"));
|
Assert.assertEquals("UpdatedName", tags.getJsonObject(1).getString("name"));
|
||||||
Assert.assertEquals("#0000ff", tags.getJsonObject(1).getString("color"));
|
Assert.assertEquals("#0000ff", tags.getJsonObject(1).getString("color"));
|
||||||
|
Assert.assertEquals(JsonValue.NULL, tags.getJsonObject(1).get("parent"));
|
||||||
|
|
||||||
// Deletes a tag
|
// Deletes a tag
|
||||||
target().path("/tag/" + tag4Id).request()
|
target().path("/tag/" + tag4Id).request()
|
||||||
|
Loading…
Reference in New Issue
Block a user