Commit 07858dff0017d2387d04beda61fa4204946657a5

Authored by Carlos Purificação
Committed by Michel Felipe
1 parent a1375e04

Initial implementation

bower.json
... ... @@ -40,12 +40,19 @@
40 40 "ng-ckeditor": "^0.2.1",
41 41 "angular-tag-cloud": "^0.3.0",
42 42 "angular-ui-switch": "^0.1.1",
43   - "angular-password": "^1.0.1"
  43 + "angular-password": "^1.0.1",
  44 + "ng-file-upload": "^12.0.4",
  45 + "ng-img-crop": "^0.3.2"
44 46 },
45 47 "devDependencies": {
46 48 "angular-mocks": "~1.5.0"
47 49 },
48 50 "overrides": {
  51 + "ng-file-upload": {
  52 + "main": [
  53 + "ng-file-upload-all.js"
  54 + ]
  55 + },
49 56 "ng-ckeditor": {
50 57 "main": [
51 58 "ng-ckeditor.js",
... ...
gulp/watch.js
... ... @@ -46,6 +46,7 @@ gulp.task('watch', ['inject'], function () {
46 46 watchPaths.push(path.join(src, '/app/**/*.html'));
47 47 watchPaths.push(path.join(src, conf.paths.plugins, '/**/*.html'));
48 48 });
  49 + watchPaths.push(stylePaths);
49 50 gulp.watch(watchPaths, function(event) {
50 51 browserSync.reload(event.path);
51 52 });
... ...
src/app/layout/blocks/profile-image/profile-image-block.html
1 1 <div class="center-block text-center profile-image-block">
2 2 <a ui-sref="main.profile.info({profile: ctrl.owner.identifier})">
3   - <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image>
  3 + <noosfero-profile-image [profile]="ctrl.owner" [editable]="true" [edit-class]="'profile-image-block-editable'"></noosfero-profile-image>
4 4 </a>
5 5 <a class="settings-link" target="_self" ui-sref="main.profile.settings({profile: ctrl.owner.identifier})">{{"blocks.profile_image.control_panel" | translate}}</a>
6 6 <div class="actions" ng-show="ctrl.isMember!=null">
... ...
src/app/layout/blocks/profile-image/profile-image-block.scss
... ... @@ -2,4 +2,22 @@
2 2 .settings-link {
3 3 display: block;
4 4 }
  5 + .upload-camera-container {
  6 + top: 305px;
  7 + left: 23px;
  8 + }
  9 +}
  10 +
  11 +.profile-image-block-editable {
  12 + top: 287px;
  13 + width: 284px;
  14 + font-weight: 700;
  15 + height: 43px;
  16 + padding-left: 30px;
  17 + padding-top: 13px;
5 18 }
  19 +
  20 +
  21 +
  22 +
  23 +
... ...
src/app/main/main.component.ts
... ... @@ -129,7 +129,7 @@ export class EnvironmentContent {
129 129 "angular-bind-html-compile", "angularMoment", "angular.filter", "akoenig.deckgrid",
130 130 "angular-timeline", "duScroll", "oitozero.ngSweetAlert",
131 131 "pascalprecht.translate", "tmh.dynamicLocale", "angularLoad",
132   - "angular-click-outside", "ngTagCloud", "noosfero.init", "uiSwitch"]
  132 + "angular-click-outside", "ngTagCloud", "noosfero.init", "uiSwitch", "ngFileUpload", "ngImgCrop"]
133 133 })
134 134 @StateConfig([
135 135 {
... ...
src/app/profile/image/image.component.ts
1   -import {Inject, Input, Component} from "ng-forward";
2   -
  1 +import {Inject, Input, Component, provide} from "ng-forward";
  2 +import {PersonService} from "../../../lib/ng-noosfero-api/http/person.service";
  3 +import {ProfileImageEditorComponent} from "./profile-image-editor.component";
3 4  
4 5 /**
5 6 * @ngdoc controller
... ... @@ -10,7 +11,9 @@ import {Inject, Input, Component} from &quot;ng-forward&quot;;
10 11 @Component({
11 12 selector: "noosfero-profile-image",
12 13 templateUrl: 'app/profile/image/image.html',
  14 + providers: [ provide('personService', { useClass: PersonService }) ]
13 15 })
  16 +@Inject(PersonService, "$uibModal", "Upload", "$timeout", "$scope")
14 17 export class ProfileImageComponent {
15 18  
16 19 /**
... ... @@ -29,7 +32,53 @@ export class ProfileImageComponent {
29 32 * The default icon used by this profile
30 33 */
31 34 defaultIcon: string;
  35 +
  36 + @Input() editable: boolean;
  37 +
  38 + @Input() editClass: string;
  39 +
  40 + picFile: any;
  41 + croppedDataUrl: any;
  42 + modalInstance: any;
32 43  
  44 + constructor(private personService: PersonService, private $uibModal: any, private Upload: any,
  45 + private $timeout: any, private $scope: ng.IScope) {
  46 + //console.log("ImageComponent.Created with upload: ", this.Upload);
  47 + //console.log("ImageComponent.Cropped: ", this.croppedDataUrl);
  48 + //console.log("ImageComponent.PicFile: ", this.picFile);
  49 + }
  50 +
  51 + fileSelected(file: any, errFiles: any) {
  52 + console.log("File selected: ", file);
  53 + if (file) {
  54 + this.picFile = file;
  55 + this.modalInstance = this.$uibModal.open({
  56 + templateUrl: 'app/profile/image/profile-image-editor.html',
  57 + controller: ProfileImageEditorComponent,
  58 + controllerAs: 'ctrl',
  59 + scope: this.$scope,
  60 + bindToController: true,
  61 + backdrop: 'static',
  62 + resolve: {
  63 + picFile: this.picFile,
  64 + profile: this.profile,
  65 + personService: this.personService
  66 + }
  67 + });
  68 + }
  69 + }
  70 +
  71 + private _showCamera: boolean = false;
  72 +
  73 + showChange(show: boolean) {
  74 + this._showCamera = show;
  75 + }
  76 +
  77 + showCamera() {
  78 + return this._showCamera;
  79 + }
  80 +
  81 +
33 82 /**
34 83 * @ngdoc method
35 84 * @name ngOnInit
... ... @@ -43,5 +92,11 @@ export class ProfileImageComponent {
43 92 this.defaultIcon = 'fa-user';
44 93 }
45 94 }
  95 +
  96 + ngAfterViewInit() {
  97 + console.log("Parent scope: ", this.$scope.$parent['ctrl']['__proto__']);
  98 + console.log("Editable: " + this.editable);
  99 + console.log("Edit_class: " + this.editClass);
  100 + }
46 101 }
47 102  
... ...
src/app/profile/image/image.html
1   -<span class="profile-image-wrap" title="{{ctrl.profile.name}}">
2   - <img ng-if="ctrl.profile.image" ng-src="{{ctrl.profile.image.url}}" class="img-responsive profile-image">
3   - <i ng-if="!ctrl.profile.image" class="fa {{ctrl.defaultIcon}} fa-5x profile-image"></i>
4   -</span>
  1 +<div id="profile-image-container" style="">
  2 + <div class="profile-image-wrap" title="{{ctrl.profile.name}}" ng-mouseenter="ctrl.showChange(true)" ng-mouseleave="ctrl.showChange(false)">
  3 + <img ng-if="ctrl.profile.image" ng-src="{{ctrl.profile.image.url}}" class="img-responsive profile-image">
  4 + <i ng-if="!ctrl.profile.image" class="fa {{ctrl.defaultIcon}} fa-5x profile-image"></i>
  5 + <div ng-if="ctrl.editable" class="upload-camera-container">
  6 + <i id="camera" class="fa fa-camera upload-camera" aria-hidden="true"></i>
  7 + </div>
  8 + <div ng-if="ctrl.editable" id="select-photo-container" name="select-photo-container" class="select-photo-container container" ng-class="ctrl.editClass">
  9 + <a id="upload-container" class="upload-container" href="#" rel="dialog" role="button">
  10 + <!-- The upload button hidden behind the camera -->
  11 + <div class="upload-button" ngf-select="ctrl.fileSelected($file)"
  12 + ngf-pattern="'image/*'" ngf-accept="'image/*'"
  13 + ngf-max-size="20MB" ngf-resize="{width: 100, height: 100}"
  14 + data-toggle="modal" data-target=".crop-dialog">
  15 + {{"profile.image.upload" | translate}}
  16 + </div>
  17 + </a>
  18 + </div>
  19 +
  20 + </div>
  21 +</div>
... ...
src/app/profile/image/image.scss
... ... @@ -5,3 +5,94 @@ i.profile-image {
5 5 background-clip: padding-box;
6 6 margin-bottom: 15px;
7 7 }
  8 +
  9 +.profile-image-wrap {
  10 + display: inline;
  11 +}
  12 +
  13 +#profile-image-container {
  14 + display: inline;
  15 +}
  16 +
  17 +#profile-image-container:hover {
  18 + .select-photo-container {
  19 + z-index: 1;
  20 + }
  21 + .upload-camera-container {
  22 + transform: scale(.75);
  23 + }
  24 +}
  25 +
  26 +.upload-camera-container {
  27 + text-align: left;
  28 + position: absolute;
  29 + z-index: 5;
  30 +}
  31 +
  32 +.upload-camera {
  33 + color: white;
  34 + position: absolute;
  35 + transition: all .3s cubic-bezier(.175, .885, .32, 1.275);
  36 + opacity: 1;
  37 +}
  38 +
  39 +.select-photo-container {
  40 + //overflow: hidden;
  41 + position: absolute;
  42 + z-index: -1;
  43 + background: #000;
  44 + background: rgba(0, 0, 0, .6);
  45 + background: linear-gradient(transparent, rgba(0, 0, 0, .6) 70%, rgba(0, 0, 0, .6) 100%);
  46 + transition: top .13s ease-out;
  47 +}
  48 +
  49 +#upload-container {
  50 + position: relative;
  51 + text-decoration: none;
  52 +}
  53 +
  54 +.upload-container a:hover {
  55 + text-decoration: none;
  56 +}
  57 +
  58 +.upload-button {
  59 + -webkit-font-smoothing: antialiased;
  60 + color: #fff;
  61 + //word-wrap: break-word;
  62 +}
  63 +
  64 +.upload-container {
  65 + color:#fff;
  66 + display: block;
  67 + overflow: hidden;
  68 + position: relative;
  69 + text-align: left;
  70 + min-width: 89px;
  71 +}
  72 +
  73 +.cropArea {
  74 + background: #E4E4E4;
  75 + overflow: hidden;
  76 + width:300px;
  77 + height:150px;
  78 +}
  79 +
  80 +.crop-area {
  81 + display: none;
  82 +}
  83 +
  84 +form .progress {
  85 + line-height: 15px;
  86 +}
  87 +
  88 +.progress {
  89 + display: inline-block;
  90 + width: 100px;
  91 + border: 3px groove #CCC;
  92 +}
  93 +.progress div {
  94 + font-size: smaller;
  95 + background: orange;
  96 + width: 0;
  97 +}
  98 +
... ...
src/app/profile/image/profile-image-editor.component.ts 0 → 100644
... ... @@ -0,0 +1,61 @@
  1 +import {StateConfig, Component, Input, Output, Inject, provide} from 'ng-forward';
  2 +import {TranslateProfile} from "../../shared/pipes/translate-profile.filter";
  3 +import {PersonService} from "../../../lib/ng-noosfero-api/http/person.service";
  4 +
  5 +export class ProfileImageEditorComponent {
  6 +
  7 + activities: any;
  8 + croppedDataUrl: string;
  9 + static $inject = ["Upload", "$timeout", "$scope", "picFile", "profile", "personService", "$uibModalInstance"];
  10 +
  11 + constructor(
  12 + private upload: any, private $timeout: any, private $scope: ng.IScope,
  13 + public picFile: any, private profile: noosfero.Profile, private personService: PersonService,
  14 + private $uibModalInstance: any) {
  15 + //this.picFile = this.picFile;
  16 + console.log("Value set: ", this.picFile);
  17 + }
  18 +
  19 + uploadImage(dataUrl: any, name: any) {
  20 + console.log("Uploading [" + name + "] with data: ", dataUrl);
  21 + let data = dataUrl.substring(dataUrl.indexOf('base64,') + 7);
  22 + let image_name = this.profile.name + "_" + name;
  23 + let base64_image_json = {
  24 + tempfile: data,
  25 + filename: image_name,
  26 + type: this.picFile.type
  27 + };
  28 + console.log("Base64Image JSON: ", base64_image_json);
  29 + this.personService.uploadImage(this.profile, base64_image_json).then( (result: any) => {
  30 + console.log("Upload finished: ", result);
  31 + this.$uibModalInstance.close(name);
  32 + });
  33 + }
  34 +
  35 + uploadFiles(file: any, errFiles: any) {
  36 + console.log("Going to upload: ", file);
  37 +
  38 + //$scope.f = file;
  39 + let errFile = errFiles && errFiles[0];
  40 + if (file) {
  41 + let base64 = this.upload.base64DataUrl(file);
  42 + console.log("Base64", base64);
  43 + base64.then( (base64Urls: any) => {
  44 + console.log("Uploading base64Urls: ", base64Urls);
  45 + let data = base64Urls.substring(base64Urls.indexOf('base64,') + 7);
  46 + let image_name = this.profile.name + "_" + file.name;
  47 + let base64_image_json = {
  48 + tempfile: data,
  49 + filename: image_name,
  50 + type: file.type
  51 + };
  52 + console.log("Base64Image JSON: ", base64_image_json);
  53 + this.personService.uploadImage(this.profile, base64_image_json);
  54 + });
  55 + }
  56 + }
  57 +
  58 + cancel() {
  59 + this.$uibModalInstance.close();
  60 + }
  61 +}
... ...
src/app/profile/image/profile-image-editor.html 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +<div class="modal-header">
  2 + <h3>{{"profile.image.edit" | translate}}</h3>
  3 +</div>
  4 +<div class="modal-body">
  5 + <form class="">
  6 + <div ngf-drop ng-model="ctrl.picFile" ngf-pattern="image/*" class="cropArea">
  7 + <img-crop image="ctrl.picFile | ngfDataUrl" area-type="square"
  8 + result-image="ctrl.croppedDataUrl" ng-init="ctrl.croppedDataUrl=''">
  9 + </img-crop>
  10 + </div>
  11 + <div>
  12 + <img ng-src="{{ctrl.croppedDataUrl}}" />
  13 + </div>
  14 + <span class="progress" ng-show="progress >= 0">
  15 + <div style="width: {{progress" ng-bind="progress + '%'"></div>
  16 + </span> <span ng-show="ctrl.result">Upload Successful</span> <span class="err"
  17 + ng-show="ctrl.errorMsg">{{errorMsg}}</span>
  18 + </form>
  19 +
  20 + <div class="actions">
  21 + <button type="submit" class="btn btn-default" ng-click="ctrl.uploadImage(ctrl.croppedDataUrl, ctrl.picFile.name)">Upload</button>
  22 + <button type="submit" class="btn btn-danger" ng-click="ctrl.cancel()">Cancel</button>
  23 + </div>
  24 +</div>
... ...
src/app/profile/info/profile-info.html
... ... @@ -6,10 +6,12 @@
6 6 <h2>{{vm.profile.name}}</h2>
7 7 </header>
8 8 <div id="profile-left" class="main-box-body clearfix">
9   - <noosfero-profile-image [profile]="vm.profile" class="img-responsive center-block"></noosfero-profile-image>
10   - <span class="label" ng-class="{'label-danger': vm.profile.type == 'Community', 'label-info': vm.profile.type == 'Person'}">{{vm.profile | translateProfile}}</span>
11   - <div class="profile-since">
12   - {{"profile.member_since" | translate}}: {{vm.profile.created_at | amDateFormat:'MMMM YYYY'}}
  9 + <noosfero-profile-image [profile]="vm.profile" [editable]="true" [edit-class]="'profile-info-editable'" class="img-responsive center-block profile-info"></noosfero-profile-image>
  10 + <div id="profile-info-extrainfo" class="profile-info-extrainfo">
  11 + <span class="label" ng-class="{'label-danger': vm.profile.type == 'Community', 'label-info': vm.profile.type == 'Person'}">{{vm.profile | translateProfile}}</span>
  12 + <div class="profile-since">
  13 + {{"profile.member_since" | translate}}: {{vm.profile.created_at | amDateFormat:'MMMM YYYY'}}
  14 + </div>
13 15 </div>
14 16 </div>
15 17 </div>
... ...
src/app/profile/info/profile-info.scss 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +.profile-info .upload-camera-container {
  2 + top: 162px;
  3 + left: 39px;
  4 +}
  5 +
  6 +.profile-info-editable {
  7 + top: 151px;
  8 + width: 103px;
  9 + height: 28px;
  10 +}
  11 +
  12 +.profile-info-editable .upload-button {
  13 + font-size: 0.8em;
  14 + padding-top: 9px;
  15 + padding-left: 6px;
  16 + font-weight: bold;
  17 +}
  18 +
  19 +.profile-info-extrainfo {
  20 + margin-top: 10px;
  21 +}
0 22 \ No newline at end of file
... ...
src/languages/en.json
... ... @@ -23,6 +23,7 @@
23 23 "profile.others_info": "Others",
24 24 "profile.community.title": "Community",
25 25 "profile.person.title": "Person",
  26 + "profile.image.upload": "Upload Photo",
26 27 "activities.title": "Activities",
27 28 "activities.create_article.description": "has published on",
28 29 "activities.scrap.description": "wrote in its timeline",
... ...
src/languages/pt.json
... ... @@ -23,6 +23,7 @@
23 23 "profile.others_info": "Outras informações",
24 24 "profile.community.title": "Comunidade",
25 25 "profile.person.title": "Pessoa",
  26 + "profile.image.upload": "Enviar photo",
26 27 "activities.title": "Atividades",
27 28 "activities.create_article.description": "publicou em",
28 29 "activities.scrap.description": "escreveu em sua linha do tempo",
... ...